*** tab-complete.c	Mon Mar 24 00:30:54 2003
--- /home/ian/devel/postgres/patches/tab-complete.c.2	Mon Mar 24 00:30:16 2003
***************
*** 78,83 ****
--- 78,85 ----
  static char **psql_completion(char *text, int start, int end);
  static char *create_command_generator(char *text, int state);
  static char *complete_from_query(char *text, int state);
+ static char *complete_from_schema_query(char *text, int state);
+ static char *_complete_from_query(int is_schema_query, char *text, int state);
  static char *complete_from_const(char *text, int state);
  static char *complete_from_list(char *text, int state);
  
***************
*** 125,132 ****
   */
  
  #define Query_for_list_of_aggregates \
! "SELECT DISTINCT proname FROM pg_catalog.pg_proc "\
! " WHERE proisagg AND substr(proname,1,%d)='%s'"
  
  #define Query_for_list_of_attributes \
  "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
--- 127,151 ----
   */
  
  #define Query_for_list_of_aggregates \
! " SELECT DISTINCT proname " \
! "   FROM pg_catalog.pg_proc" \
! "  WHERE proisagg " \
! "    AND substr(proname,1,%d)='%s'" \
! "        UNION" \
! " SELECT nspname || '.' AS relname" \
! "   FROM pg_catalog.pg_namespace" \
! "  WHERE substr(nspname,1,%d)='%s'" \
! "        UNION" \
! " SELECT DISTINCT nspname || '.' || proname AS relname" \
! "   FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n" \
! "  WHERE proisagg  " \
! "    AND substr(nspname || '.' || proname,1,%d)='%s'" \
! "    AND pronamespace = n.oid" \
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_attributes \
  "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
***************
*** 142,185 ****
  " WHERE substr(datname,1,%d)='%s'"
  
  #define Query_for_list_of_datatypes \
! "SELECT pg_catalog.format_type(t.oid, NULL) "\
! "  FROM pg_catalog.pg_type t "\
! " WHERE (t.typrelid = 0 "\
! "    OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "\
! "   AND t.typname !~ '^_' "\
! "   AND pg_catalog.pg_type_is_visible(t.oid) "\
! "   AND SUBSTR(pg_catalog.format_type(t.oid, NULL),1,%d)='%s'"
  
  #define Query_for_list_of_domains \
! "SELECT typname FROM pg_catalog.pg_type "\
! " WHERE typtype = 'd' "\
! "   AND substr(typname,1,%d)='%s'"
  
  #define Query_for_list_of_functions \
! "SELECT DISTINCT proname FROM pg_catalog.pg_proc "\
! " WHERE substr(proname,1,%d)='%s' "\
! "   AND pg_catalog.pg_function_is_visible(oid)"
! 
! #define Query_for_list_of_functions_nonsys \
! "SELECT DISTINCT proname || '()' FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
! " WHERE substr(proname,1,%d)='%s' "\
! "   AND pg_catalog.pg_function_is_visible(p.oid) "\
! "   AND pronamespace = n.oid "\
! "   AND n.nspname NOT IN ('pg_catalog', 'pg_toast')"
  
  #define Query_for_list_of_indexes \
! "SELECT relname FROM pg_catalog.pg_class "\
! " WHERE relkind='i' "\
! "   AND substr(relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(oid)"
  
- #define Query_for_list_of_indexes_nonsys \
- "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
- " WHERE c.relkind='i' "\
- "   AND substr(c.relname,1,%d)='%s' "\
- "   AND pg_catalog.pg_table_is_visible(c.oid)"\
- "   AND relnamespace = n.oid "\
- "   AND n.nspname NOT IN ('pg_catalog', 'pg_toast')"
  
  #define Query_for_list_of_languages \
  "SELECT lanname "\
--- 161,256 ----
  " WHERE substr(datname,1,%d)='%s'"
  
  #define Query_for_list_of_datatypes \
! " SELECT pg_catalog.format_type(t.oid, NULL) "\
! "   FROM pg_catalog.pg_type t "\
! "  WHERE (t.typrelid = 0 "\
! "     OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "\
! "    AND t.typname !~ '^_' "\
! "    AND substr(pg_catalog.format_type(t.oid, NULL),1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' AS relname "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || pg_catalog.format_type(t.oid, NULL) AS relname "\
! "   FROM pg_catalog.pg_type t, pg_catalog.pg_namespace n "\
! "  WHERE(t.typrelid = 0 "\
! "     OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "\
! "    AND t.typname !~ '^_' "\
! "    AND substr(nspname || '.' || pg_catalog.format_type(t.oid, NULL),1,%d)='%s' "\
! "    AND typnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_domains \
! " SELECT typname "\
! "   FROM pg_catalog.pg_type t "\
! "  WHERE typtype = 'd' "\
! "    AND substr(typname,1,%d)='%s' "\
! "        UNION" \
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || pg_catalog.format_type(t.oid, NULL) "\
! "   FROM pg_catalog.pg_type t, pg_catalog.pg_namespace n "\
! "  WHERE typtype = 'd' "\
! "    AND substr(nspname || '.' || typname,1,%d)='%s' "\
! "    AND typnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_functions \
! " SELECT DISTINCT proname || '()' "\
! "   FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
! "  WHERE substr(proname,1,%d)='%s'"\
! "    AND pg_catalog.pg_function_is_visible(p.oid) "\
! "    AND pronamespace = n.oid "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || proname "\
! "   FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
! "  WHERE substr(nspname || '.' || proname,1,%d)='%s' "\
! "    AND pronamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_indexes \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='i' "\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='i' "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  
  #define Query_for_list_of_languages \
  "SELECT lanname "\
***************
*** 192,279 ****
  " WHERE substr(nspname,1,%d)='%s'"
  
  #define Query_for_list_of_sequences \
! "SELECT relname FROM pg_catalog.pg_class "\
! " WHERE relkind='S' "\
! "   AND substr(relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(oid)"
  
  #define Query_for_list_of_system_relations \
  "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! " WHERE (c.relkind='r' OR c.relkind='v' OR c.relkind='s' OR c.relkind='S')"\
  "   AND substr(c.relname,1,%d)='%s' "\
  "   AND pg_catalog.pg_table_is_visible(c.oid)"\
  "   AND relnamespace = n.oid "\
  "   AND n.nspname = 'pg_catalog'"
  
  #define Query_for_list_of_tables \
! "SELECT relname FROM pg_catalog.pg_class "\
! " WHERE (relkind='r' or relkind='v') "\
! "   AND substr(relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(oid)"
! 
! #define Query_for_list_of_tables_nonsys \
! "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! " WHERE c.relkind='r' "\
! "   AND substr(c.relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(c.oid)"\
! "   AND relnamespace = n.oid "\
! "   AND n.nspname NOT IN ('pg_catalog', 'pg_toast')"
! 
! #define Query_for_list_of_tsv_nonsys \
! "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! " WHERE (c.relkind='r' OR c.relkind='v' OR c.relkind='S') "\
! "   AND substr(c.relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(c.oid)"\
! "   AND relnamespace = n.oid "\
! "   AND n.nspname NOT IN ('pg_catalog', 'pg_toast')"
  
  #define Query_for_list_of_views \
! "SELECT relname FROM pg_catalog.pg_class "\
! " WHERE relkind='v' "\
! "   AND substr(relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(oid)"
! 
! #define Query_for_list_of_views_nonsys \
! "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! " WHERE c.relkind='v' "\
! "   AND substr(c.relname,1,%d)='%s' "\
! "   AND pg_catalog.pg_table_is_visible(c.oid)"\
! "   AND relnamespace = n.oid "\
! "   AND n.nspname NOT IN ('pg_catalog', 'pg_toast')"
! 
! #define Query_for_list_of_users "SELECT usename FROM pg_catalog.pg_user WHERE substr(usename,1,%d)='%s'"
  
  /* This is a list of all "things" in Pgsql, which can show up after CREATE or
     DROP; and there is also a query to get a list of them.
  */
  typedef struct
  {
  	char	   *name;
  	char	   *query;
  } pgsql_thing_t;
  
  pgsql_thing_t words_after_create[] = {
! 	{"AGGREGATE", Query_for_list_of_aggregates},
! 	{"CAST", NULL},				/* Casts have complex structures for namees, so skip it */
! 	{"CONVERSION", "SELECT conname FROM pg_catalog.pg_conversion WHERE substr(conname,1,%d)='%s'"},
! 	{"DATABASE", Query_for_list_of_databases},
! 	{"DOMAIN", Query_for_list_of_domains},
! 	{"FUNCTION", Query_for_list_of_functions},
! 	{"GROUP", "SELECT groname FROM pg_catalog.pg_group WHERE substr(groname,1,%d)='%s'"},
! 	{"LANGUAGE", Query_for_list_of_languages },
! 	{"INDEX", Query_for_list_of_indexes},
! 	{"OPERATOR", NULL},			/* Querying for this is probably not such
  								 * a good idea. */
! 	{"RULE", "SELECT rulename FROM pg_catalog.pg_rules WHERE substr(rulename,1,%d)='%s'"},
! 	{"SCHEMA", Query_for_list_of_schemas},
! 	{"SEQUENCE", Query_for_list_of_sequences},
! 	{"TABLE", Query_for_list_of_tables},
! 	{"TEMP", NULL},				/* for CREATE TEMP TABLE ... */
! 	{"TRIGGER", "SELECT tgname FROM pg_catalog.pg_trigger WHERE substr(tgname,1,%d)='%s'"},
! 	{"TYPE", Query_for_list_of_datatypes },
! 	{"UNIQUE", NULL},			/* for CREATE UNIQUE INDEX ... */
! 	{"USER", Query_for_list_of_users},
! 	{"VIEW", Query_for_list_of_views},
  	{NULL, NULL}				/* end of list */
  };
  
--- 263,436 ----
  " WHERE substr(nspname,1,%d)='%s'"
  
  #define Query_for_list_of_sequences \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='S' "\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='S' "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_system_relations \
  "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! " WHERE (c.relkind='r' OR c.relkind='v' OR c.relkind='s' OR c.relkind='S') "\
  "   AND substr(c.relname,1,%d)='%s' "\
  "   AND pg_catalog.pg_table_is_visible(c.oid)"\
  "   AND relnamespace = n.oid "\
  "   AND n.nspname = 'pg_catalog'"
  
  #define Query_for_list_of_tables \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='r' "\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname  || '.',1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='r' "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace n1 "\
! "          WHERE substr(nspname ||'.',1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
! 
! #define Query_for_list_of_tisv \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE (relkind='r' OR relkind='i' OR relkind='S' OR relkind='v') "\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE (relkind='r' OR relkind='i' OR relkind='S' OR relkind='v') "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
! 
! #define Query_for_list_of_tsv \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE (relkind='r' OR relkind='S' OR relkind='v') "\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE (relkind='r' OR relkind='S' OR relkind='v') "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
  
  #define Query_for_list_of_views \
! " SELECT relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='v'"\
! "    AND substr(relname,1,%d)='%s' "\
! "    AND pg_catalog.pg_table_is_visible(c.oid) "\
! "    AND relnamespace = n.oid "\
! "    AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "\
! "        UNION "\
! " SELECT nspname || '.' "\
! "   FROM pg_catalog.pg_namespace "\
! "  WHERE substr(nspname,1,%d)='%s' "\
! "        UNION "\
! " SELECT nspname || '.' || relname "\
! "   FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
! "  WHERE relkind='v' "\
! "    AND substr(nspname || '.' || relname,1,%d)='%s' "\
! "    AND relnamespace = n.oid "\
! "    AND ('%s' ~ '^.*\\\\.' "\
! "     OR (SELECT TRUE "\
! "           FROM pg_catalog.pg_namespace "\
! "          WHERE substr(nspname,1,%d)='%s' "\
! "         HAVING COUNT(nspname)=1))"
! 
! #define Query_for_list_of_users \
! " SELECT usename "\
! "   FROM pg_catalog.pg_user "\
! "  WHERE substr(usename,1,%d)='%s'"
  
  /* This is a list of all "things" in Pgsql, which can show up after CREATE or
     DROP; and there is also a query to get a list of them.
  */
+ 
+ #define WITH_SCHEMA 1
+ #define NO_SCHEMA 0
+ 
  typedef struct
  {
  	char	   *name;
+ 	int        with_schema;
  	char	   *query;
  } pgsql_thing_t;
  
  pgsql_thing_t words_after_create[] = {
! 	{"AGGREGATE", WITH_SCHEMA, Query_for_list_of_aggregates},
! 	{"CAST", NULL, NULL},				/* Casts have complex structures for namees, so skip it */
! 	{"CONVERSION", NO_SCHEMA, "SELECT conname FROM pg_catalog.pg_conversion WHERE substr(conname,1,%d)='%s'"},
! 	{"DATABASE", NO_SCHEMA, Query_for_list_of_databases},
! 	{"DOMAIN", WITH_SCHEMA, Query_for_list_of_domains},
! 	{"FUNCTION", WITH_SCHEMA, Query_for_list_of_functions},
! 	{"GROUP", NO_SCHEMA, "SELECT groname FROM pg_catalog.pg_group WHERE substr(groname,1,%d)='%s'"},
! 	{"LANGUAGE", NO_SCHEMA, Query_for_list_of_languages},
! 	{"INDEX", WITH_SCHEMA,  Query_for_list_of_indexes},
! 	{"OPERATOR", NULL, NULL},			/* Querying for this is probably not such
  								 * a good idea. */
! 	{"RULE", NO_SCHEMA, "SELECT rulename FROM pg_catalog.pg_rules WHERE substr(rulename,1,%d)='%s'"},
! 	{"SCHEMA", NO_SCHEMA, Query_for_list_of_schemas},
! 	{"SEQUENCE", WITH_SCHEMA, Query_for_list_of_sequences},
! 	{"TABLE", WITH_SCHEMA, Query_for_list_of_tables},
! 	{"TEMP", NULL, NULL},				/* for CREATE TEMP TABLE ... */
! 	{"TRIGGER", NO_SCHEMA, "SELECT tgname FROM pg_catalog.pg_trigger WHERE substr(tgname,1,%d)='%s'"},
! 	{"TYPE", WITH_SCHEMA, Query_for_list_of_datatypes },
! 	{"UNIQUE", NULL, NULL},			/* for CREATE UNIQUE INDEX ... */
! 	{"USER", NO_SCHEMA,  Query_for_list_of_users},
! 	{"VIEW", WITH_SCHEMA, Query_for_list_of_views},
  	{NULL, NULL}				/* end of list */
  };
  
***************
*** 281,292 ****
  /* A couple of macros to ease typing. You can use these to complete the given
     string with
     1) The results from a query you pass it. (Perhaps one of those above?)
!    2) The items from a null-pointer-terminated list.
!    3) A string constant
!    4) The list of attributes to the given table.
  */
  #define COMPLETE_WITH_QUERY(query) \
  do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
  #define COMPLETE_WITH_LIST(list) \
  do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
  #define COMPLETE_WITH_CONST(string) \
--- 438,452 ----
  /* A couple of macros to ease typing. You can use these to complete the given
     string with
     1) The results from a query you pass it. (Perhaps one of those above?)
!    2) The results from a schema query you pass it.
!    3) The items from a null-pointer-terminated list.
!    4) A string constant
!    5) The list of attributes to the given table.
  */
  #define COMPLETE_WITH_QUERY(query) \
  do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
+ #define COMPLETE_WITH_SCHEMA_QUERY(query) \
+ do { completion_charp = query; matches = completion_matches(text, complete_from_schema_query); } while(0)
  #define COMPLETE_WITH_LIST(list) \
  do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
  #define COMPLETE_WITH_CONST(string) \
***************
*** 505,511 ****
  	else if (strcasecmp(prev4_wd, "ALTER") == 0 &&
  			 strcasecmp(prev3_wd, "TRIGGER") == 0 &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  	/*
  	 * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
--- 665,671 ----
  	else if (strcasecmp(prev4_wd, "ALTER") == 0 &&
  			 strcasecmp(prev3_wd, "TRIGGER") == 0 &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  	/*
  	 * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
***************
*** 562,568 ****
  /* ANALYZE */
  	/* If the previous word is ANALYZE, produce list of tables. */
  	else if (strcasecmp(prev_wd, "ANALYZE") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	/* If we have ANALYZE <table>, complete with semicolon. */
  	else if (strcasecmp(prev2_wd, "ANALYZE") == 0)
  		COMPLETE_WITH_CONST(";");
--- 722,728 ----
  /* ANALYZE */
  	/* If the previous word is ANALYZE, produce list of tables. */
  	else if (strcasecmp(prev_wd, "ANALYZE") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	/* If we have ANALYZE <table>, complete with semicolon. */
  	else if (strcasecmp(prev2_wd, "ANALYZE") == 0)
  		COMPLETE_WITH_CONST(";");
***************
*** 570,576 ****
  /* CLUSTER */
  	/* If the previous word is CLUSTER, produce list of indexes. */
  	else if (strcasecmp(prev_wd, "CLUSTER") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_indexes);
  	/* If we have CLUSTER <sth>, then add "ON" */
  	else if (strcasecmp(prev2_wd, "CLUSTER") == 0)
  		COMPLETE_WITH_CONST("ON");
--- 730,736 ----
  /* CLUSTER */
  	/* If the previous word is CLUSTER, produce list of indexes. */
  	else if (strcasecmp(prev_wd, "CLUSTER") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
  	/* If we have CLUSTER <sth>, then add "ON" */
  	else if (strcasecmp(prev2_wd, "CLUSTER") == 0)
  		COMPLETE_WITH_CONST("ON");
***************
*** 620,626 ****
  			 strcasecmp(prev_wd, "\\copy") == 0 ||
  			 (strcasecmp(prev2_wd, "COPY") == 0 &&
  			  strcasecmp(prev_wd, "BINARY") == 0))
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	/* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
  	else if (strcasecmp(prev2_wd, "COPY") == 0 ||
  			 strcasecmp(prev2_wd, "\\copy") == 0 ||
--- 780,786 ----
  			 strcasecmp(prev_wd, "\\copy") == 0 ||
  			 (strcasecmp(prev2_wd, "COPY") == 0 &&
  			  strcasecmp(prev_wd, "BINARY") == 0))
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	/* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
  	else if (strcasecmp(prev2_wd, "COPY") == 0 ||
  			 strcasecmp(prev2_wd, "\\copy") == 0 ||
***************
*** 644,650 ****
  	/* Complete ... INDEX <name> ON with a list of tables  */
  	else if (strcasecmp(prev3_wd, "INDEX") == 0 &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  	/*
  	 * Complete INDEX <name> ON <table> with a list of table columns
--- 804,810 ----
  	/* Complete ... INDEX <name> ON with a list of tables  */
  	else if (strcasecmp(prev3_wd, "INDEX") == 0 &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  	/*
  	 * Complete INDEX <name> ON <table> with a list of table columns
***************
*** 695,701 ****
  	else if (strcasecmp(prev4_wd, "AS") == 0 &&
  			 strcasecmp(prev3_wd, "ON") == 0 &&
  			 strcasecmp(prev_wd, "TO") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  /* CREATE TABLE */
  	/* Complete CREATE TEMP with "TABLE" */
--- 855,861 ----
  	else if (strcasecmp(prev4_wd, "AS") == 0 &&
  			 strcasecmp(prev3_wd, "ON") == 0 &&
  			 strcasecmp(prev_wd, "TO") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  /* CREATE TABLE */
  	/* Complete CREATE TEMP with "TABLE" */
***************
*** 732,738 ****
  	/* Complete DELETE FROM with a list of tables */
  	else if (strcasecmp(prev2_wd, "DELETE") == 0 &&
  			 strcasecmp(prev_wd, "FROM") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	/* Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?) */
  	else if (strcasecmp(prev3_wd, "DELETE") == 0 &&
  			 strcasecmp(prev2_wd, "FROM") == 0)
--- 892,898 ----
  	/* Complete DELETE FROM with a list of tables */
  	else if (strcasecmp(prev2_wd, "DELETE") == 0 &&
  			 strcasecmp(prev_wd, "FROM") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	/* Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?) */
  	else if (strcasecmp(prev3_wd, "DELETE") == 0 &&
  			 strcasecmp(prev2_wd, "FROM") == 0)
***************
*** 829,835 ****
  		if(strcasecmp(prev_wd, "DATABASE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
  		else if(strcasecmp(prev_wd, "FUNCTION") == 0)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_functions_nonsys);
  		else if(strcasecmp(prev_wd, "LANGUAGE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_languages);
  		else if(strcasecmp(prev_wd, "SCHEMA") == 0)
--- 989,995 ----
  		if(strcasecmp(prev_wd, "DATABASE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
  		else if(strcasecmp(prev_wd, "FUNCTION") == 0)
! 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
  		else if(strcasecmp(prev_wd, "LANGUAGE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_languages);
  		else if(strcasecmp(prev_wd, "SCHEMA") == 0)
***************
*** 851,857 ****
  	/* Complete INSERT INTO with table names */
  	else if (strcasecmp(prev2_wd, "INSERT") == 0 &&
  			 strcasecmp(prev_wd, "INTO") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	/* Complete "INSERT INTO <table> (" with attribute names */
  	else if (rl_line_buffer[start - 1] == '(' &&
  			 strcasecmp(prev3_wd, "INSERT") == 0 &&
--- 1011,1017 ----
  	/* Complete INSERT INTO with table names */
  	else if (strcasecmp(prev2_wd, "INSERT") == 0 &&
  			 strcasecmp(prev_wd, "INTO") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	/* Complete "INSERT INTO <table> (" with attribute names */
  	else if (rl_line_buffer[start - 1] == '(' &&
  			 strcasecmp(prev3_wd, "INSERT") == 0 &&
***************
*** 889,895 ****
  	else if (strcasecmp(prev_wd, "LOCK") == 0 ||
  	 		 (strcasecmp(prev_wd, "TABLE") == 0 &&
  			  strcasecmp(prev2_wd, "LOCK") == 0))
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  	/* For the following, handle the case of a single table only for now */
  
--- 1049,1055 ----
  	else if (strcasecmp(prev_wd, "LOCK") == 0 ||
  	 		 (strcasecmp(prev_wd, "TABLE") == 0 &&
  			  strcasecmp(prev2_wd, "LOCK") == 0))
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  	/* For the following, handle the case of a single table only for now */
  
***************
*** 929,939 ****
  	else if (strcasecmp(prev2_wd, "REINDEX") == 0)
  	{
  		if (strcasecmp(prev_wd, "TABLE") == 0)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  		else if (strcasecmp(prev_wd, "DATABASE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
  		else if (strcasecmp(prev_wd, "INDEX") == 0)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_indexes);
  	}
  
  /* SELECT */
--- 1089,1099 ----
  	else if (strcasecmp(prev2_wd, "REINDEX") == 0)
  	{
  		if (strcasecmp(prev_wd, "TABLE") == 0)
! 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  		else if (strcasecmp(prev_wd, "DATABASE") == 0)
  			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
  		else if (strcasecmp(prev_wd, "INDEX") == 0)
! 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
  	}
  
  /* SELECT */
***************
*** 1040,1046 ****
  
  /* TRUNCATE */
  	else if (strcasecmp(prev_wd, "TRUNCATE") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  /* UNLISTEN */
  	else if (strcasecmp(prev_wd, "UNLISTEN") == 0)
--- 1200,1206 ----
  
  /* TRUNCATE */
  	else if (strcasecmp(prev_wd, "TRUNCATE") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  /* UNLISTEN */
  	else if (strcasecmp(prev_wd, "UNLISTEN") == 0)
***************
*** 1049,1055 ****
  /* UPDATE */
  	/* If prev. word is UPDATE suggest a list of tables */
  	else if (strcasecmp(prev_wd, "UPDATE") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	/* Complete UPDATE <table> with "SET" */
  	else if (strcasecmp(prev2_wd, "UPDATE") == 0)
  		COMPLETE_WITH_CONST("SET");
--- 1209,1215 ----
  /* UPDATE */
  	/* If prev. word is UPDATE suggest a list of tables */
  	else if (strcasecmp(prev_wd, "UPDATE") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	/* Complete UPDATE <table> with "SET" */
  	else if (strcasecmp(prev2_wd, "UPDATE") == 0)
  		COMPLETE_WITH_CONST("SET");
***************
*** 1068,1074 ****
  	else if (strcasecmp(prev2_wd, "VACUUM") == 0 &&
  			 (strcasecmp(prev_wd, "FULL") == 0 ||
  			  strcasecmp(prev_wd, "ANALYZE") == 0))
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  /* WHERE */
  	/* Simple case of the word before the where being the table name */
--- 1228,1234 ----
  	else if (strcasecmp(prev2_wd, "VACUUM") == 0 &&
  			 (strcasecmp(prev_wd, "FULL") == 0 ||
  			  strcasecmp(prev_wd, "ANALYZE") == 0))
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  
  /* WHERE */
  	/* Simple case of the word before the where being the table name */
***************
*** 1076,1118 ****
  		COMPLETE_WITH_ATTR(prev2_wd);
  
  /* ... FROM ... */
  	else if (strcasecmp(prev_wd, "FROM") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  
  /* Backslash commands */
  /* TODO:  \dc \dd \dl */
  	else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_databases);
- 	/* FIXME: tab completion of '\d' returns only tables and views.
-      * '\d' alone returns sequences as well. Fix this behaviour?
-      */
  	else if (strcmp(prev_wd, "\\d") == 0 || strcmp(prev_wd, "\\d+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	else if (strcmp(prev_wd, "\\da") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_aggregates);
  	else if (strcmp(prev_wd, "\\dD") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_domains);
  	else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_functions);
  	else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_indexes_nonsys);
  	else if (strcmp(prev_wd, "\\dn") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
  	else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tsv_nonsys);
  	else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_sequences);
  	else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_system_relations);
  	else if (strcmp(prev_wd, "\\dt") == 0 || strcmp(prev_wd, "\\dt+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_nonsys);
  	else if (strcmp(prev_wd, "\\dT") == 0 || strcmp(prev_wd, "\\dT+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_datatypes);
  	else if (strcmp(prev_wd, "\\du") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_users);
  	else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_views_nonsys);
  	else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
  		COMPLETE_WITH_LIST(sql_commands);
  	else if (strcmp(prev_wd, "\\pset") == 0)
--- 1236,1276 ----
  		COMPLETE_WITH_ATTR(prev2_wd);
  
  /* ... FROM ... */
+ /* TODO: also include SRF ? */
  	else if (strcasecmp(prev_wd, "FROM") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv);
  
  
  /* Backslash commands */
  /* TODO:  \dc \dd \dl */
  	else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_databases);
  	else if (strcmp(prev_wd, "\\d") == 0 || strcmp(prev_wd, "\\d+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv);
  	else if (strcmp(prev_wd, "\\da") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates);
  	else if (strcmp(prev_wd, "\\dD") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
  	else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
  	else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
  	else if (strcmp(prev_wd, "\\dn") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
  	else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv);
  	else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
  	else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_system_relations);
  	else if (strcmp(prev_wd, "\\dt") == 0 || strcmp(prev_wd, "\\dt+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
  	else if (strcmp(prev_wd, "\\dT") == 0 || strcmp(prev_wd, "\\dT+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
  	else if (strcmp(prev_wd, "\\du") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_users);
  	else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
  	else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
  		COMPLETE_WITH_LIST(sql_commands);
  	else if (strcmp(prev_wd, "\\pset") == 0)
***************
*** 1146,1152 ****
  		for (i = 0; words_after_create[i].name; i++)
  			if (strcasecmp(prev_wd, words_after_create[i].name) == 0)
  			{
! 				COMPLETE_WITH_QUERY(words_after_create[i].query);
  				break;
  			}
  	}
--- 1304,1313 ----
  		for (i = 0; words_after_create[i].name; i++)
  			if (strcasecmp(prev_wd, words_after_create[i].name) == 0)
  			{
! 				if(words_after_create[i].with_schema == WITH_SCHEMA)
! 					COMPLETE_WITH_SCHEMA_QUERY(words_after_create[i].query);
! 				else
! 					COMPLETE_WITH_QUERY(words_after_create[i].query);
  				break;
  			}
  	}
***************
*** 1219,1235 ****
  }
  
  
  /* This creates a list of matching things, according to a query pointed to
!    by completion_charp. The query needs to have a %d and a %s in it, which will
!    be replaced by the string length of the text and the text itself. See some
!    example queries at the top.
!    The query may also have another %s in it, which will be replaced by the value
!    of completion_info_charp.
!    Ordinarily this would be used to get a list of matching tables or functions,
!    etc.
  */
  static char *
! complete_from_query(char *text, int state)
  {
  	static int	list_index,
  				string_length;
--- 1380,1418 ----
  }
  
  
+ /* The following two functions are wrappers for _complete_from_query */
+ 
+ static char *
+ complete_from_query(char *text, int state)
+ {
+   return _complete_from_query(0, text, state);
+ }
+ 
+ static char *
+ complete_from_schema_query(char *text, int state)
+ {
+   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
!    where %d is the string length of the text and %s the text itself.
! 
!    See top of file for examples of both kinds of query.
  */
+ 
  static char *
! _complete_from_query(int is_schema_query, char *text, int state)
  {
  	static int	list_index,
  				string_length;
***************
*** 1250,1259 ****
  		if (completion_charp == NULL)
  			return NULL;
  
! 		if (snprintf(query_buffer, BUF_SIZE, completion_charp, string_length, text, completion_info_charp) == -1)
  		{
! 			ERROR_QUERY_TOO_LONG;
! 			return NULL;
  		}
  
  		result = exec_query(query_buffer);
--- 1433,1452 ----
  		if (completion_charp == NULL)
  			return NULL;
  
! 		if(is_schema_query)
  		{
! 		  if (snprintf(query_buffer, BUF_SIZE, completion_charp, string_length, text, string_length, text, string_length, text, text,  string_length, text,string_length,text) == -1)
! 		  {
! 		      ERROR_QUERY_TOO_LONG;
! 		      return NULL;
! 		  }
! 		}
! 		else {
! 		  if (snprintf(query_buffer, BUF_SIZE, completion_charp, string_length, text, completion_info_charp) == -1)
! 		    {
! 		      ERROR_QUERY_TOO_LONG;
! 		      return NULL;
! 		    }
  		}
  
  		result = exec_query(query_buffer);
