diff -cprN head/src/bin/psql/tab-complete.c work/src/bin/psql/tab-complete.c
*** head/src/bin/psql/tab-complete.c	2010-02-15 11:56:18.651089000 +0900
--- work/src/bin/psql/tab-complete.c	2010-02-16 14:23:09.274053000 +0900
*************** static const SchemaQuery Query_for_list_
*** 500,505 ****
--- 500,510 ----
  "   FROM pg_catalog.pg_user_mappings "\
  "  WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"
  
+ #define Query_for_list_of_access_methods \
+ " SELECT pg_catalog.quote_ident(amname) "\
+ "   FROM pg_catalog.pg_am "\
+ "  WHERE substring(pg_catalog.quote_ident(amname),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.
*************** static PGresult *exec_query(const char *
*** 571,578 ****
  
  static char *previous_word(int point, int skip);
  
- static int	find_open_parenthesis(int end);
- 
  #if 0
  static char *quote_file_name(char *text, int match_type, char *quote_pointer);
  static char *dequote_file_name(char *text, char quote_char);
--- 576,581 ----
*************** psql_completion(char *text, int start, i
*** 638,645 ****
  		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
  	};
  
- 	(void) end;					/* not used */
- 
  #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
  	rl_completion_append_character = ' ';
  #endif
--- 641,646 ----
*************** psql_completion(char *text, int start, i
*** 749,754 ****
--- 750,782 ----
  
  		COMPLETE_WITH_LIST(list_ALTERINDEX);
  	}
+ 	/* ALTER INDEX <name> SET */
+ 	else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ 			 pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
+ 			 pg_strcasecmp(prev_wd, "SET") == 0)
+ 	{
+ 		static const char *const list_ALTERINDEXSET[] =
+ 		{"(", "TABLESPACE", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_ALTERINDEXSET);
+ 	}
+ 	/* ALTER INDEX <name> RESET */
+ 	else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ 			 pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
+ 			 pg_strcasecmp(prev_wd, "RESET") == 0)
+ 		COMPLETE_WITH_CONST("(");
+ 	/* ALTER INDEX <foo> SET|RESET ( */
+ 	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ 			 pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
+ 			 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
+ 			  pg_strcasecmp(prev2_wd, "RESET") == 0) &&
+ 			 pg_strcasecmp(prev_wd, "(") == 0)
+ 	{
+ 		static const char *const list_INDEXOPTIONS[] =
+ 		{"fillfactor", "fastupdate", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_INDEXOPTIONS);
+ 	}
  
  	/* ALTER LANGUAGE <name> */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
*************** psql_completion(char *text, int start, i
*** 977,986 ****
  			  pg_strcasecmp(prev2_wd, "ALTER") == 0))
  	{
  		static const char *const list_COLUMNALTER[] =
! 		{"TYPE", "SET", "DROP", NULL};
  
  		COMPLETE_WITH_LIST(list_COLUMNALTER);
  	}
  	else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
  			   pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
  			  (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
--- 1005,1015 ----
  			  pg_strcasecmp(prev2_wd, "ALTER") == 0))
  	{
  		static const char *const list_COLUMNALTER[] =
! 		{"TYPE", "SET", "RESET", "DROP", NULL};
  
  		COMPLETE_WITH_LIST(list_COLUMNALTER);
  	}
+ 	/* ALTER TABLE ALTER [COLUMN] <foo> SET */
  	else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
  			   pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
  			  (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
*************** psql_completion(char *text, int start, i
*** 988,997 ****
  			 pg_strcasecmp(prev_wd, "SET") == 0)
  	{
  		static const char *const list_COLUMNSET[] =
! 		{"DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
  
  		COMPLETE_WITH_LIST(list_COLUMNSET);
  	}
  	else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
  			   pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
  			  (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
--- 1017,1051 ----
  			 pg_strcasecmp(prev_wd, "SET") == 0)
  	{
  		static const char *const list_COLUMNSET[] =
! 		{"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
  
  		COMPLETE_WITH_LIST(list_COLUMNSET);
  	}
+ 	/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
+ 	else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ 			   pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
+ 			  pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
+ 			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
+ 			 pg_strcasecmp(prev_wd, "(") == 0)
+ 	{
+ 		static const char *const list_COLUMNOPTIONS[] =
+ 		{"n_distinct", "n_distinct_inherited", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_COLUMNOPTIONS);
+ 	}
+ 	/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
+ 	else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ 			   pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
+ 			  pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
+ 			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
+ 			 pg_strcasecmp(prev_wd, "STORAGE") == 0)
+ 	{
+ 		static const char *const list_COLUMNSTORAGE[] =
+ 		{"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_COLUMNSTORAGE);
+ 	}
+ 	/* ALTER TABLE ALTER [COLUMN] <foo> DROP */
  	else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
  			   pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
  			  (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
*************** psql_completion(char *text, int start, i
*** 1018,1024 ****
  			 pg_strcasecmp(prev_wd, "SET") == 0)
  	{
  		static const char *const list_TABLESET[] =
! 		{"WITHOUT", "TABLESPACE", "SCHEMA", NULL};
  
  		COMPLETE_WITH_LIST(list_TABLESET);
  	}
--- 1072,1078 ----
  			 pg_strcasecmp(prev_wd, "SET") == 0)
  	{
  		static const char *const list_TABLESET[] =
! 		{"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
  
  		COMPLETE_WITH_LIST(list_TABLESET);
  	}
*************** psql_completion(char *text, int start, i
*** 1037,1051 ****
  
  		COMPLETE_WITH_LIST(list_TABLESET2);
  	}
! 	/* we have ALTER TABLESPACE, so suggest RENAME TO, OWNER TO */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
  	{
  		static const char *const list_ALTERTSPC[] =
! 		{"RENAME TO", "OWNER TO", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERTSPC);
  	}
  	/* ALTER TEXT SEARCH */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
--- 1091,1163 ----
  
  		COMPLETE_WITH_LIST(list_TABLESET2);
  	}
! 	/* ALTER TABLE <foo> RESET */
! 	else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
! 			 pg_strcasecmp(prev_wd, "RESET") == 0)
! 		COMPLETE_WITH_CONST("(");
! 	/* ALTER TABLE <foo> SET|RESET ( */
! 	else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
! 			 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
! 			  pg_strcasecmp(prev2_wd, "RESET") == 0) &&
! 			 pg_strcasecmp(prev_wd, "(") == 0)
! 	{
! 		static const char *const list_TABLEOPTIONS[] =
! 		{
! 			"autovacuum_analyze_scale_factor",
! 			"autovacuum_analyze_threshold",
! 			"autovacuum_enabled",
! 			"autovacuum_freeze_max_age",
! 			"autovacuum_freeze_min_age",
! 			"autovacuum_freeze_table_age",
! 			"autovacuum_vacuum_cost_delay",
! 			"autovacuum_vacuum_cost_limit",
! 			"autovacuum_vacuum_scale_factor",
! 			"autovacuum_vacuum_threshold",
! 			"fillfactor",
! 			"toast.autovacuum_analyze_scale_factor",
! 			"toast.autovacuum_analyze_threshold",
! 			"toast.autovacuum_enabled",
! 			"toast.autovacuum_freeze_max_age",
! 			"toast.autovacuum_freeze_min_age",
! 			"toast.autovacuum_freeze_table_age",
! 			"toast.autovacuum_vacuum_cost_delay",
! 			"toast.autovacuum_vacuum_cost_limit",
! 			"toast.autovacuum_vacuum_scale_factor",
! 			"toast.autovacuum_vacuum_threshold",
! 			NULL
! 		};
! 
! 		COMPLETE_WITH_LIST(list_TABLEOPTIONS);
! 	}
! 
! 	/* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
  	{
  		static const char *const list_ALTERTSPC[] =
! 		{"RENAME TO", "OWNER TO", "SET", "RESET", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERTSPC);
  	}
+ 	/* ALTER TABLESPACE <foo> SET|RESET */
+ 	else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ 			 pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 &&
+ 			 (pg_strcasecmp(prev_wd, "SET") == 0 ||
+ 			  pg_strcasecmp(prev_wd, "RESET") == 0))
+ 		COMPLETE_WITH_CONST("(");
+ 	/* ALTER TABLESPACE <foo> SET|RESET ( */
+ 	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ 			 pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
+ 			 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
+ 			  pg_strcasecmp(prev2_wd, "RESET") == 0) &&
+ 			 pg_strcasecmp(prev_wd, "(") == 0)
+ 	{
+ 		static const char *const list_TABLESPACEOPTIONS[] =
+ 		{"seq_page_cost", "random_page_cost", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS);
+ 	}
+ 
  	/* ALTER TEXT SEARCH */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
*************** psql_completion(char *text, int start, i
*** 1282,1325 ****
  	else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
  			 pg_strcasecmp(prev_wd, "UNIQUE") == 0)
  		COMPLETE_WITH_CONST("INDEX");
! 	/* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" */
! 	else if (pg_strcasecmp(prev2_wd, "INDEX") == 0 &&
! 			 (pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
! 			  pg_strcasecmp(prev3_wd, "UNIQUE") == 0))
! 		COMPLETE_WITH_CONST("ON");
! 	/* Complete ... INDEX <name> ON with a list of tables  */
! 	else if (pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
  			 pg_strcasecmp(prev_wd, "ON") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
  
  	/*
  	 * Complete INDEX <name> ON <table> with a list of table columns (which
  	 * should really be in parens)
  	 */
! 	else if (pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
  			 pg_strcasecmp(prev2_wd, "ON") == 0)
  	{
! 		if (find_open_parenthesis(end))
! 			COMPLETE_WITH_ATTR(prev_wd, "");
! 		else
! 			COMPLETE_WITH_CONST("(");
  	}
! 	else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
  			 pg_strcasecmp(prev3_wd, "ON") == 0 &&
  			 pg_strcasecmp(prev_wd, "(") == 0)
  		COMPLETE_WITH_ATTR(prev2_wd, "");
  	/* same if you put in USING */
! 	else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
! 			 pg_strcasecmp(prev2_wd, "USING") == 0)
! 		COMPLETE_WITH_ATTR(prev3_wd, "");
  	/* Complete USING with an index method */
  	else if (pg_strcasecmp(prev_wd, "USING") == 0)
! 	{
! 		static const char *const index_mth[] =
! 		{"BTREE", "HASH", "GIN", "GIST", NULL};
! 
! 		COMPLETE_WITH_LIST(index_mth);
! 	}
  
  /* CREATE RULE */
  	/* Complete "CREATE RULE <sth>" with "AS" */
--- 1394,1459 ----
  	else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
  			 pg_strcasecmp(prev_wd, "UNIQUE") == 0)
  		COMPLETE_WITH_CONST("INDEX");
! 	/* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
! 	else if (pg_strcasecmp(prev_wd, "INDEX") == 0 &&
! 			 (pg_strcasecmp(prev2_wd, "CREATE") == 0 ||
! 			  pg_strcasecmp(prev2_wd, "UNIQUE") == 0))
! 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
! 								   " UNION SELECT 'ON'"
! 								   " UNION SELECT 'CONCURRENTLY'");
! 	/* Complete ... INDEX [<name>] ON with a list of tables  */
! 	else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) &&
  			 pg_strcasecmp(prev_wd, "ON") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ 	/* If we have CREATE|UNIQUE INDEX <sth> CONCURRENTLY, then add "ON" */
+ 	else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
+ 			  pg_strcasecmp(prev2_wd, "INDEX") == 0) &&
+ 			 pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0)
+ 		COMPLETE_WITH_CONST("ON");
+ 	/* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" or "CONCURRENTLY" */
+ 	else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
+ 			  pg_strcasecmp(prev3_wd, "UNIQUE") == 0) &&
+ 			 pg_strcasecmp(prev2_wd, "INDEX") == 0)
+ 	{
+ 		static const char *const list_CREATE_INDEX[] =
+ 		{"CONCURRENTLY", "ON", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_CREATE_INDEX);
+ 	}
  
  	/*
  	 * Complete INDEX <name> ON <table> with a list of table columns (which
  	 * should really be in parens)
  	 */
! 	else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) &&
  			 pg_strcasecmp(prev2_wd, "ON") == 0)
  	{
! 		static const char *const list_CREATE_INDEX2[] =
! 		{"(", "USING",  NULL};
! 
! 		COMPLETE_WITH_LIST(list_CREATE_INDEX2);
  	}
! 	else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
! 			  pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) &&
  			 pg_strcasecmp(prev3_wd, "ON") == 0 &&
  			 pg_strcasecmp(prev_wd, "(") == 0)
  		COMPLETE_WITH_ATTR(prev2_wd, "");
  	/* same if you put in USING */
! 	else if (pg_strcasecmp(prev5_wd, "ON") == 0 &&
! 			 pg_strcasecmp(prev3_wd, "USING") == 0 &&
! 			 pg_strcasecmp(prev_wd, "(") == 0)
! 		COMPLETE_WITH_ATTR(prev4_wd, "");
  	/* Complete USING with an index method */
  	else if (pg_strcasecmp(prev_wd, "USING") == 0)
! 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
! 	else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
! 			 pg_strcasecmp(prev2_wd, "USING") == 0)
! 		COMPLETE_WITH_CONST("(");
  
  /* CREATE RULE */
  	/* Complete "CREATE RULE <sth>" with "AS" */
*************** psql_completion(char *text, int start, i
*** 1417,1422 ****
--- 1551,1567 ----
  
  		COMPLETE_WITH_LIST(list_CREATETRIGGER);
  	}
+ 	/* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
+ 	else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
+ 			 pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
+ 			 (pg_strcasecmp(prev_wd, "BEFORE") == 0 ||
+ 			  pg_strcasecmp(prev_wd, "AFTER") == 0))
+ 	{
+ 		static const char *const list_CREATETRIGGER_EVENTS[] =
+ 		{"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
+ 	}
  	/* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
  	else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
  			 pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
*************** psql_completion(char *text, int start, i
*** 1428,1433 ****
--- 1573,1587 ----
  
  		COMPLETE_WITH_LIST(list_CREATETRIGGER2);
  	}
+ 	/* complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of tables */
+ 	else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
+ 			 (pg_strcasecmp(prev3_wd, "BEFORE") == 0 ||
+ 			  pg_strcasecmp(prev3_wd, "AFTER") == 0) &&
+ 			 pg_strcasecmp(prev_wd, "ON") == 0)
+ 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ 	/* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
+ 	else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0)
+ 		COMPLETE_WITH_CONST("PROCEDURE");
  
  /* CREATE ROLE,USER,GROUP */
  	else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
*************** psql_completion(char *text, int start, i
*** 1487,1492 ****
--- 1641,1647 ----
  		COMPLETE_WITH_LIST(list_DECLARE);
  	}
  
+ /* CURSOR */
  	else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
  	{
  		static const char *const list_DECLARECURSOR[] =
*************** psql_completion(char *text, int start, i
*** 1579,1599 ****
  			   pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
  		)
  	{
! 		if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
  		{
! 			if (find_open_parenthesis(end))
! 			{
! 				static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
! 				char	   *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
! 
! 				sprintf(tmp_buf, func_args_query, prev_wd);
! 				COMPLETE_WITH_QUERY(tmp_buf);
! 				free(tmp_buf);
! 			}
! 			else
! 			{
! 				COMPLETE_WITH_CONST("(");
! 			}
  		}
  		else
  		{
--- 1734,1743 ----
  			   pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
  		)
  	{
! 		if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
! 			pg_strcasecmp(prev2_wd, "FUNCTION") == 0)
  		{
! 			COMPLETE_WITH_CONST("(");
  		}
  		else
  		{
*************** psql_completion(char *text, int start, i
*** 1712,1723 ****
  	else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
  			 pg_strcasecmp(prev_wd, "REVOKE") == 0)
  	{
! 		static const char *const list_privileg[] =
  		{"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
  			"TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
  		"ALL", NULL};
  
! 		COMPLETE_WITH_LIST(list_privileg);
  	}
  	/* Complete GRANT/REVOKE <sth> with "ON" */
  	else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
--- 1856,1867 ----
  	else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
  			 pg_strcasecmp(prev_wd, "REVOKE") == 0)
  	{
! 		static const char *const list_privilege[] =
  		{"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
  			"TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
  		"ALL", NULL};
  
! 		COMPLETE_WITH_LIST(list_privilege);
  	}
  	/* Complete GRANT/REVOKE <sth> with "ON" */
  	else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
*************** psql_completion(char *text, int start, i
*** 1747,1754 ****
  								   " UNION SELECT 'LARGE OBJECT'"
  								   " UNION SELECT 'SCHEMA'"
  								   " UNION SELECT 'TABLESPACE'");
  
! 	/* Complete "GRANT/REVOKE * ON * " with "TO" */
  	else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
  			  pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
  			 pg_strcasecmp(prev2_wd, "ON") == 0)
--- 1891,1908 ----
  								   " UNION SELECT 'LARGE OBJECT'"
  								   " UNION SELECT 'SCHEMA'"
  								   " UNION SELECT 'TABLESPACE'");
+ 	else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
+ 			  pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
+ 			 pg_strcasecmp(prev2_wd, "ON") == 0 &&
+ 			 pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ 	{
+ 		static const char *const list_privilege_foreign[] =
+ 		{"DATA WRAPPER", "SERVER", NULL};
+ 
+ 		COMPLETE_WITH_LIST(list_privilege_foreign);
+ 	}
  
! 	/* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */
  	else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
  			  pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
  			 pg_strcasecmp(prev2_wd, "ON") == 0)
*************** psql_completion(char *text, int start, i
*** 1770,1781 ****
  	}
  
  	/* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
! 	else if (pg_strcasecmp(prev3_wd, "ON") == 0 &&
! 			 ((pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
! 			   pg_strcasecmp(prev_wd, "TO") == 0) ||
! 			  (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
! 			   pg_strcasecmp(prev_wd, "FROM") == 0)))
! 		COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
  
  /* GROUP BY */
  	else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
--- 1924,1945 ----
  	}
  
  	/* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
! 	else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
! 			 pg_strcasecmp(prev3_wd, "ON") == 0)
! 	{
! 		if (pg_strcasecmp(prev_wd, "TO") == 0)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
! 		else
! 			COMPLETE_WITH_CONST("TO");
! 	}
! 	else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
! 			 pg_strcasecmp(prev3_wd, "ON") == 0)
! 	{
! 		if (pg_strcasecmp(prev_wd, "FROM") == 0)
! 			COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
! 		else
! 			COMPLETE_WITH_CONST("FROM");
! 	}
  
  /* GROUP BY */
  	else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
*************** psql_completion(char *text, int start, i
*** 2056,2061 ****
--- 2220,2226 ----
  	else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
  			 pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
  			 pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
+ 			 pg_strcasecmp(prev_wd, "(") != 0 &&
  			 pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
  		COMPLETE_WITH_CONST("TO");
  	/* Suggest possible variable values */
*************** exec_query(const char *query)
*** 2767,2772 ****
--- 2932,2940 ----
   * skip that many words; e.g. skip=1 finds the word before the
   * previous one. Return value is NULL or a malloc'ed string.
   */
+ #define issep(c) \
+ 	(isspace((unsigned char) (c)) || (c) == '(' || (c) == ')')
+ 
  static char *
  previous_word(int point, int skip)
  {
*************** previous_word(int point, int skip)
*** 2776,2791 ****
  				inquotes = 0;
  	char	   *s;
  
  	while (skip-- >= 0)
  	{
- 		/* first we look for a space before the current word */
- 		for (i = point; i >= 0; i--)
- 			if (rl_line_buffer[i] == ' ')
- 				break;
- 
  		/* now find the first non-space which then constitutes the end */
! 		for (; i >= 0; i--)
! 			if (rl_line_buffer[i] != ' ')
  			{
  				end = i;
  				break;
--- 2944,2960 ----
  				inquotes = 0;
  	char	   *s;
  
+ 	/* first we look for a space before the current word */
+ 	for (i = point - 1; i >= 0; i--)
+ 		if (issep(rl_line_buffer[i]))
+ 			break;
+ 	point = i;
+ 
  	while (skip-- >= 0)
  	{
  		/* now find the first non-space which then constitutes the end */
! 		for (i = point; i >= 0; i--)
! 			if (!isspace((unsigned char) rl_line_buffer[i]))
  			{
  				end = i;
  				break;
*************** previous_word(int point, int skip)
*** 2798,2817 ****
  		if (end == -1)
  			return NULL;
  
! 		/*
! 		 * Otherwise we now look for the start. The start is either the last
! 		 * character before any space going backwards from the end, or it's
! 		 * simply character 0
! 		 */
! 		for (start = end; start > 0; start--)
  		{
! 			if (rl_line_buffer[start] == '"')
! 				inquotes = !inquotes;
! 			if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0)
! 				break;
  		}
! 
! 		point = start;
  	}
  
  	/* make a copy */
--- 2967,2994 ----
  		if (end == -1)
  			return NULL;
  
! 		if (issep(rl_line_buffer[end]))
  		{
! 			/* non-alphabet separator is a single-character word */
! 			start = end;
! 			point = start - 1;
  		}
! 		else
! 		{
! 			/*
! 			 * Otherwise we now look for the start. The start is either the
! 			 * last character before any space going backwards from the end,
! 			 * or it's simply character 0
! 			 */
! 			for (start = end; start > 0; start--)
! 			{
! 				if (rl_line_buffer[start] == '"')
! 					inquotes = !inquotes;
! 				if (inquotes == 0 && issep(rl_line_buffer[start - 1]))
! 					break;
! 			}
! 		}
! 		point = start - 1;
  	}
  
  	/* make a copy */
*************** previous_word(int point, int skip)
*** 2821,2852 ****
  	return s;
  }
  
- /* Find the parenthesis after the last word */
- 
- 
- static int
- find_open_parenthesis(int end)
- {
- 	int			i = end - 1;
- 
- 	while ((rl_line_buffer[i] != ' ') && (i >= 0))
- 	{
- 		if (rl_line_buffer[i] == '(')
- 			return 1;
- 		i--;
- 	}
- 	while ((rl_line_buffer[i] == ' ') && (i >= 0))
- 	{
- 		i--;
- 	}
- 	if (rl_line_buffer[i] == '(')
- 	{
- 		return 1;
- 	}
- 	return 0;
- 
- }
- 
  #if 0
  
  /*
--- 2998,3003 ----