(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

Reply via email to