(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