(oops, this time with -c, thanks Alvaro) Full support for all schema and table name combinations when getting a list of attributes. All of the following will now work:
select * from information_schema.columns where <tab> select * from foo where <tab> select * from "user" where <tab> select * from "foo" where <tab> select * from "Uppercase".lower where <tab> select * from "gtsm.com"."foo.Bar" where <tab> select * from "GTSM.com".foo where <tab> Also applies to other places that get lists of columns: insert into, alter table, create index, etc. -- Greg Sabino Mullane [EMAIL PROTECTED] PGP Key: 0x14964AC8 200710211532 http://biglumber.com/x/web?pk=2529DF6AB8F79407E94445B4BC9B906714964AC8
Index: tab-complete.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/tab-complete.c,v retrieving revision 1.167 diff -c -r1.167 tab-complete.c *** tab-complete.c 14 Sep 2007 04:25:24 -0000 1.167 --- tab-complete.c 21 Oct 2007 19:30:48 -0000 *************** *** 53,58 **** --- 53,59 ---- #include "pqexpbuffer.h" #include "common.h" #include "settings.h" + #include "stringutils.h" #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION #define filename_completion_function rl_filename_completion_function *************** *** 124,133 **** * Communication variables set by COMPLETE_WITH_FOO macros and then used by * the completion callback functions. Ugly but there is no better way. */ ! static const char *completion_charp; /* to pass a string */ ! static const char *const * completion_charpp; /* to pass a list of strings */ ! static const char *completion_info_charp; /* to pass a second string */ ! static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ /* A couple of macros to ease typing. You can use these to complete the given string with --- 125,135 ---- * Communication variables set by COMPLETE_WITH_FOO macros and then used by * the completion callback functions. Ugly but there is no better way. */ ! static const char *completion_charp; /* to pass a string */ ! static const char *const * completion_charpp; /* to pass a list of strings */ ! static const char *completion_info_charp; /* to pass a second string */ ! static const char *completion_info_charp2; /* to pass a third string */ ! static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ /* A couple of macros to ease typing. You can use these to complete the given string with *************** *** 145,152 **** do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0) #define COMPLETE_WITH_CONST(string) \ do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0) ! #define COMPLETE_WITH_ATTR(table, addon) \ ! do {completion_charp = Query_for_list_of_attributes addon; completion_info_charp = table; matches = completion_matches(text, complete_from_query); } while(0) /* * Assembly instructions for schema queries --- 147,167 ---- do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0) #define COMPLETE_WITH_CONST(string) \ do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0) ! #define COMPLETE_WITH_ATTR(relation, addon) \ ! do { \ ! completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ ! strtokx(NULL, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ ! completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ ! if (NULL == completion_table) { \ ! completion_charp = Query_for_list_of_attributes addon; \ ! completion_info_charp = relation; \ ! } \ ! else { \ ! completion_charp = Query_for_list_of_attributes_with_schema addon; \ ! completion_info_charp = completion_table; \ ! completion_info_charp2 = completion_schema; \ ! } \ ! matches = completion_matches(text, complete_from_relation_query); } while(0) /* * Assembly instructions for schema queries *************** *** 312,318 **** * become a SQL literal string). %d will be replaced by the length of the * string (in unescaped form). A second %s, if present, will be replaced * by a suitably-escaped version of the string provided in ! * completion_info_charp. * * Beware that the allowed sequences of %s and %d are determined by * _complete_from_query(). --- 327,333 ---- * become a SQL literal string). %d will be replaced by the length of the * string (in unescaped form). A second %s, if present, will be replaced * by a suitably-escaped version of the string provided in ! * completion_info_charp. A third %s is replaced by completion_info_charp2. * * Beware that the allowed sequences of %s and %d are determined by * _complete_from_query(). *************** *** 325,333 **** " AND a.attnum > 0 "\ " AND NOT a.attisdropped "\ " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ ! " AND pg_catalog.quote_ident(relname)='%s' "\ " AND pg_catalog.pg_table_is_visible(c.oid)" #define Query_for_list_of_template_databases \ "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' and datistemplate IS TRUE" --- 340,362 ---- " AND a.attnum > 0 "\ " AND NOT a.attisdropped "\ " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ ! " AND (pg_catalog.quote_ident(relname)='%s' "\ ! " OR '\"' || pg_catalog.quote_ident(relname) || '\"'='%s') "\ " AND pg_catalog.pg_table_is_visible(c.oid)" + #define Query_for_list_of_attributes_with_schema \ + "SELECT pg_catalog.quote_ident(attname) "\ + " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\ + " WHERE c.oid = a.attrelid "\ + " AND n.oid = c.relnamespace "\ + " AND a.attnum > 0 "\ + " AND NOT a.attisdropped "\ + " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ + " AND (pg_catalog.quote_ident(relname)='%s' "\ + " OR '\"' || pg_catalog.quote_ident(relname) || '\"' ='%s') "\ + " AND (pg_catalog.quote_ident(nspname)='%s' "\ + " OR '\"' || pg_catalog.quote_ident(nspname) || '\"' ='%s') " + #define Query_for_list_of_template_databases \ "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' and datistemplate IS TRUE" *************** *** 497,502 **** --- 526,532 ---- static char *drop_command_generator(const char *text, int state); static char *complete_from_query(const char *text, int state); static char *complete_from_schema_query(const char *text, int state); + static char *complete_from_relation_query(const char *text, int state); static char *_complete_from_query(int is_schema_query, const char *text, int state); static char *complete_from_const(const char *text, int state); *************** *** 550,555 **** --- 580,588 ---- *prev4_wd, *prev5_wd; + /* We may want to separate a word into a table and schema */ + char *completion_schema, *completion_table; + static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", *************** *** 583,591 **** completion_charp = NULL; completion_charpp = NULL; completion_info_charp = NULL; /* ! * Scan the input line before our current position for the last four * words. According to those we'll make some smart decisions on what the * user is probably intending to type. TODO: Use strtokx() to do this. */ --- 616,627 ---- completion_charp = NULL; completion_charpp = NULL; completion_info_charp = NULL; + completion_info_charp2 = NULL; + completion_schema = NULL; + completion_table = NULL; /* ! * Scan the input line before our current position for the last five * words. According to those we'll make some smart decisions on what the * user is probably intending to type. TODO: Use strtokx() to do this. */ *************** *** 2199,2205 **** return NULL; } ! /* The following two functions are wrappers for _complete_from_query */ static char * complete_from_query(const char *text, int state) --- 2235,2241 ---- return NULL; } ! /* The following three functions are wrappers for _complete_from_query */ static char * complete_from_query(const char *text, int state) *************** *** 2213,2227 **** return _complete_from_query(1, text, state); } /* This creates a list of matching things, according to a query pointed to by completion_charp. ! The query can be one of two kinds: - A simple query which must contain a %d and a %s, which will be replaced by the string length of the text and the text itself. The query may also have another %s in it, which will be replaced by the value of completion_info_charp. or: - A schema query used for completion of both schema and relation names; these are more complex and must contain in the following order: %d %s %d %s %d %s %s %d %s --- 2249,2272 ---- return _complete_from_query(1, text, state); } + static char * + complete_from_relation_query(const char *text, int state) + { + return _complete_from_query(2, text, state); + } /* This creates a list of matching things, according to a query pointed to by completion_charp. ! The query can be one of three kinds: - A simple query which must contain a %d and a %s, which will be replaced by the string length of the text and the text itself. The query may also have another %s in it, which will be replaced by the value of completion_info_charp. or: + - A simple query as above, but with two or four additional %s in it, + which are replaced by completion_info_charp (first two), and + by completion_info_charp2 for the second two if needed. + or: - A schema query used for completion of both schema and relation names; these are more complex and must contain in the following order: %d %s %d %s %d %s %s %d %s *************** *** 2249,2254 **** --- 2294,2300 ---- PQExpBufferData query_buffer; char *e_text; char *e_info_charp; + char *e_info_charp2; list_index = 0; string_length = strlen(text); *************** *** 2273,2281 **** else e_info_charp = NULL; initPQExpBuffer(&query_buffer); ! if (is_schema_query) { /* completion_squery gives us the pieces to assemble */ const char *qualresult = completion_squery->qualresult; --- 2319,2339 ---- else e_info_charp = NULL; + if (completion_info_charp2) + { + size_t charp_len; + + charp_len = strlen(completion_info_charp2); + e_info_charp2 = pg_malloc(charp_len * 2 + 1); + PQescapeString(e_info_charp2, completion_info_charp2, + charp_len); + } + else + e_info_charp2 = NULL; + initPQExpBuffer(&query_buffer); ! if (1 == is_schema_query) { /* completion_squery gives us the pieces to assemble */ const char *qualresult = completion_squery->qualresult; *************** *** 2364,2369 **** --- 2422,2433 ---- if (completion_charp) appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); } + else if (2 == is_schema_query) + { + /* Last two args are doubled up to catch quoted tables and schemas */ + appendPQExpBuffer(&query_buffer, completion_charp, + string_length, e_text, e_info_charp, e_info_charp, e_info_charp2, e_info_charp2); + } else { /* completion_charp is an sprintf-style format string */ *************** *** 2381,2386 **** --- 2445,2452 ---- free(e_text); if (e_info_charp) free(e_info_charp); + if (e_info_charp2) + free(e_info_charp2); } /* Find something that matches */
---------------------------(end of broadcast)--------------------------- TIP 1: if posting/reading through Usenet, please send an appropriate subscribe-nomail command to [EMAIL PROTECTED] so that your message can get through to the mailing list cleanly