Index: tab-complete.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/tab-complete.c,v
retrieving revision 1.73
diff -c -r1.73 tab-complete.c
*** tab-complete.c	2003/02/06 20:25:33	1.73
--- tab-complete.c	2003/03/23 23:24:00
***************
*** 124,133 ****
   * the %s will be replaced by the text entered so far, the %d by its length.
   */
  
! #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_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_databases "SELECT datname FROM pg_catalog.pg_database WHERE substr(datname,1,%d)='%s'"
! #define Query_for_list_of_attributes "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and not a.attisdropped and substr(a.attname,1,%d)='%s' and c.relname='%s' and pg_catalog.pg_table_is_visible(c.oid)"
  #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
--- 124,246 ----
   * the %s will be replaced by the text entered so far, the %d by its length.
   */
  
! #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 "\
! " WHERE c.oid = a.attrelid "\
! "   AND a.attnum > 0 "\
! "   AND NOT a.attisdropped "\
! "   AND substr(a.attname,1,%d)='%s' "\
! "   AND c.relname='%s' "\
! "   AND pg_catalog.pg_table_is_visible(c.oid)"
! 
! #define Query_for_list_of_databases \
! "SELECT datname FROM pg_catalog.pg_database "\
! " 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 "\
! "  FROM pg_language "\
! " WHERE lanname != 'internal' "\
! "   AND substr(lanname,1,%d)='%s' "
! 
! #define Query_for_list_of_schemas \
! "SELECT nspname FROM pg_catalog.pg_namespace "\
! " 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
***************
*** 140,166 ****
  } pgsql_thing_t;
  
  pgsql_thing_t words_after_create[] = {
! 	{"AGGREGATE", "SELECT DISTINCT proname FROM pg_catalog.pg_proc WHERE proisagg AND substr(proname,1,%d)='%s'"},
  	{"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", "SELECT typname FROM pg_catalog.pg_type WHERE typtype = 'd' AND substr(typname,1,%d)='%s'"},
! 	{"FUNCTION", "SELECT DISTINCT proname FROM pg_catalog.pg_proc WHERE substr(proname,1,%d)='%s'"},
  	{"GROUP", "SELECT groname FROM pg_catalog.pg_group WHERE substr(groname,1,%d)='%s'"},
! 	{"LANGUAGE", "SELECT lanname FROM pg_catalog.pg_language WHERE substr(lanname,1,%d)='%s'"},
  	{"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", "SELECT nspname FROM pg_catalog.pg_namespace WHERE substr(nspname,1,%d)='%s'"},
! 	{"SEQUENCE", "SELECT relname FROM pg_catalog.pg_class WHERE relkind='S' and substr(relname,1,%d)='%s'"},
  	{"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", "SELECT typname FROM pg_catalog.pg_type WHERE substr(typname,1,%d)='%s'"},
  	{"UNIQUE", NULL},			/* for CREATE UNIQUE INDEX ... */
  	{"USER", Query_for_list_of_users},
! 	{"VIEW", "SELECT viewname FROM pg_catalog.pg_views WHERE substr(viewname,1,%d)='%s'"},
  	{NULL, NULL}				/* end of list */
  };
  
--- 253,279 ----
  } 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 */
  };
  
***************
*** 314,321 ****
  
  	static char *backslash_commands[] = {
  		"\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright", 
! 		"\\d", "\\da", "\\dd", "\\dD", "\\df", "\\di", "\\dl", "\\do",
! 		"\\dp", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv","\\du",
  		"\\e", "\\echo", "\\encoding",
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
--- 427,435 ----
  
  	static char *backslash_commands[] = {
  		"\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright", 
! 		"\\d",  "\\da", "\\dc", "\\dC", "\\dd", "\\dD", "\\df", "\\di",
! 		"\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", 
! 		"\\dv", "\\du",
  		"\\e", "\\echo", "\\encoding",
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
***************
*** 683,708 ****
  	 * Complete GRANT/REVOKE <sth> ON with a list of tables, views,
  	 * sequences, and indexes
  	 *
! 	 * XXX should also offer DATABASE, FUNCTION, LANGUAGE, SCHEMA here
  	 */
  	else if ((strcasecmp(prev3_wd, "GRANT") == 0 ||
  			  strcasecmp(prev3_wd, "REVOKE") == 0) &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_class "
! 							"WHERE relkind in ('r','i','S','v') AND "
! 							"substr(relname,1,%d)='%s' AND pg_catalog.pg_table_is_visible(oid)");
! 	/* Complete "GRANT * ON * " with "TO" */
! 	else if (strcasecmp(prev4_wd, "GRANT") == 0 &&
! 			 strcasecmp(prev2_wd, "ON") == 0)
! 		COMPLETE_WITH_CONST("TO");
! 	/* Complete "REVOKE * ON * " with "FROM" */
! 	else if (strcasecmp(prev4_wd, "REVOKE") == 0 &&
  			 strcasecmp(prev2_wd, "ON") == 0)
! 		COMPLETE_WITH_CONST("FROM");
  
  	/*
  	 * TODO: to complete with user name we need prev5_wd -- wait for a
  	 * more general solution there
  	 */
  
  /* INSERT */
--- 797,847 ----
  	 * Complete GRANT/REVOKE <sth> ON with a list of tables, views,
  	 * sequences, and indexes
  	 *
! 	 * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result
!      * via UNION; seems to work intuitively
!      *
!      * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
!      * here will only work if the privilege list contains exactly one privilege
  	 */
  	else if ((strcasecmp(prev3_wd, "GRANT") == 0 ||
  			  strcasecmp(prev3_wd, "REVOKE") == 0) &&
  			 strcasecmp(prev_wd, "ON") == 0)
! 		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
! 							" WHERE relkind in ('r','S','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 'DATABASE' AS relname "
!                             " UNION "
!                             "SELECT 'FUNCTION' AS relname "
!                             " UNION "
!                             "SELECT 'LANGUAGE' AS relname "
!                             " UNION "
!                             "SELECT 'SCHEMA' AS relname ");
! 
! 	/* Complete "GRANT/REVOKE * ON * " with "TO" */
! 	else if ((strcasecmp(prev4_wd, "GRANT") == 0 || 
! 			  strcasecmp(prev4_wd, "REVOKE") == 0) &&
  			 strcasecmp(prev2_wd, "ON") == 0)
! 	{
! 		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)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
! 		else
! 			COMPLETE_WITH_CONST("TO");
! 	}
  
  	/*
  	 * TODO: to complete with user name we need prev5_wd -- wait for a
  	 * more general solution there
+      * same for GRANT <sth> ON { DATABASE | FUNCTION | LANGUAGE | SCHEMA } xxx TO
  	 */
  
  /* INSERT */
***************
*** 749,755 ****
  	/* Complete LOCK [TABLE] with a list of tables */
  	else if (strcasecmp(prev_wd, "LOCK") == 0 ||
  	 		 (strcasecmp(prev_wd, "TABLE") == 0 &&
! 			  strcasecmp(prev2_wd, "LOCK")))
  		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  
  	/* For the following, handle the case of a single table only for now */
--- 888,894 ----
  	/* Complete LOCK [TABLE] with a list of tables */
  	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 */
***************
*** 765,771 ****
  	else if (strcasecmp(prev_wd, "IN") == 0 &&
  			 (strcasecmp(prev3_wd, "LOCK") == 0 ||
  			  (strcasecmp(prev3_wd, "TABLE") == 0 &&
! 			   strcasecmp(prev3_wd, "LOCK"))))
  	{
  		char	   *lock_modes[] = {"ACCESS SHARE MODE",
  			"ROW SHARE MODE", "ROW EXCLUSIVE MODE",
--- 904,910 ----
  	else if (strcasecmp(prev_wd, "IN") == 0 &&
  			 (strcasecmp(prev3_wd, "LOCK") == 0 ||
  			  (strcasecmp(prev3_wd, "TABLE") == 0 &&
! 			   strcasecmp(prev4_wd, "LOCK") == 0)))
  	{
  		char	   *lock_modes[] = {"ACCESS SHARE MODE",
  			"ROW SHARE MODE", "ROW EXCLUSIVE MODE",
***************
*** 942,951 ****
  
  
  /* Backslash commands */
  	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)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);
  	else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
  		COMPLETE_WITH_LIST(sql_commands);
  	else if (strcmp(prev_wd, "\\pset") == 0)
--- 1081,1118 ----
  
  
  /* 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)
