33 *
44 * Copyright (c) 2000-2006, PostgreSQL Global Development Group
55 *
6- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.149 2006/03/05 15:58:52 momjian Exp $
6+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.150 2006/04/02 09:02:41 alvherre Exp $
77 */
88
99/*----------------------------------------------------------------------
@@ -465,6 +465,7 @@ static const pgsql_thing_t words_after_create[] = {
465465/* Forward declaration of functions */
466466static char * * psql_completion (char * text ,int start ,int end );
467467static char * create_command_generator (const char * text ,int state );
468+ static char * drop_command_generator (const char * text ,int state );
468469static char * complete_from_query (const char * text ,int state );
469470static char * complete_from_schema_query (const char * text ,int state );
470471static char * _complete_from_query (int is_schema_query ,
@@ -521,11 +522,13 @@ psql_completion(char *text, int start, int end)
521522* prev5_wd ;
522523
523524static const char * const sql_commands []= {
524- "ABORT" ,"ALTER" ,"ANALYZE" ,"BEGIN" ,"CHECKPOINT" ,"CLOSE" ,"CLUSTER" ,"COMMENT" ,
525- "COMMIT" ,"COPY" ,"CREATE" ,"DEALLOCATE" ,"DECLARE" ,"DELETE FROM" ,"DROP" ,"END" ,"EXECUTE" ,
526- "EXPLAIN" ,"FETCH" ,"GRANT" ,"INSERT" ,"LISTEN" ,"LOAD" ,"LOCK" ,"MOVE" ,"NOTIFY" ,
527- "PREPARE" ,"REINDEX" ,"RELEASE" ,"RESET" ,"REVOKE" ,"ROLLBACK" ,"SAVEPOINT" ,
528- "SELECT" ,"SET" ,"SHOW" ,"START" ,"TRUNCATE" ,"UNLISTEN" ,"UPDATE" ,"VACUUM" ,NULL
525+ "ABORT" ,"ALTER" ,"ANALYZE" ,"BEGIN" ,"CHECKPOINT" ,"CLOSE" ,"CLUSTER" ,
526+ "COMMENT" ,"COMMIT" ,"COPY" ,"CREATE" ,"DEALLOCATE" ,"DECLARE" ,
527+ "DELETE FROM" ,"DROP" ,"END" ,"EXECUTE" ,"EXPLAIN" ,"FETCH" ,"GRANT" ,
528+ "INSERT" ,"LISTEN" ,"LOAD" ,"LOCK" ,"MOVE" ,"NOTIFY" ,"PREPARE" ,
529+ "REASSIGN" ,"REINDEX" ,"RELEASE" ,"RESET" ,"REVOKE" ,"ROLLBACK" ,
530+ "SAVEPOINT" ,"SELECT" ,"SET" ,"SHOW" ,"START" ,"TRUNCATE" ,"UNLISTEN" ,
531+ "UPDATE" ,"VACUUM" ,NULL
529532};
530533
531534static const char * const backslash_commands []= {
@@ -536,7 +539,8 @@ psql_completion(char *text, int start, int end)
536539"\\e" ,"\\echo" ,"\\encoding" ,
537540"\\f" ,"\\g" ,"\\h" ,"\\help" ,"\\H" ,"\\i" ,"\\l" ,
538541"\\lo_import" ,"\\lo_export" ,"\\lo_list" ,"\\lo_unlink" ,
539- "\\o" ,"\\p" ,"\\password" ,"\\pset" ,"\\q" ,"\\qecho" ,"\\r" ,"\\set" ,"\\t" ,"\\T" ,
542+ "\\o" ,"\\p" ,"\\password" ,"\\pset" ,"\\q" ,"\\qecho" ,"\\r" ,
543+ "\\set" ,"\\t" ,"\\T" ,
540544"\\timing" ,"\\unset" ,"\\x" ,"\\w" ,"\\z" ,"\\!" ,NULL
541545};
542546
@@ -570,15 +574,19 @@ psql_completion(char *text, int start, int end)
570574else if (!prev_wd )
571575COMPLETE_WITH_LIST (sql_commands );
572576
573- /* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */
574- /* complete with something you can create or drop */
575- else if (pg_strcasecmp (prev_wd ,"CREATE" )== 0 ||
576- (pg_strcasecmp (prev_wd ,"DROP" )== 0 &&
577- pg_strcasecmp (prev3_wd ,"TABLE" )!= 0 &&
578- pg_strcasecmp (prev3_wd ,"DOMAIN" )!= 0 &&
579- pg_strcasecmp (prev3_wd ,"GROUP" )!= 0 ))
577+ /* CREATE */
578+ /* complete with something you can create */
579+ else if (pg_strcasecmp (prev_wd ,"CREATE" )== 0 )
580580matches = completion_matches (text ,create_command_generator );
581581
582+ /* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */
583+ /* complete with something you can drop */
584+ else if (pg_strcasecmp (prev_wd ,"DROP" )== 0 &&
585+ pg_strcasecmp (prev3_wd ,"TABLE" )!= 0 &&
586+ pg_strcasecmp (prev3_wd ,"DOMAIN" )!= 0 &&
587+ pg_strcasecmp (prev3_wd ,"GROUP" )!= 0 )
588+ matches = completion_matches (text ,drop_command_generator );
589+
582590/* ALTER */
583591
584592/*
@@ -1248,23 +1256,22 @@ psql_completion(char *text, int start, int end)
12481256pg_strcasecmp (prev3_wd ,"AGGREGATE" )== 0 &&
12491257prev_wd [strlen (prev_wd )- 1 ]== ')' ))
12501258{
1251-
12521259if ((pg_strcasecmp (prev3_wd ,"DROP" )== 0 )&& (pg_strcasecmp (prev2_wd ,"FUNCTION" )== 0 ))
1253- {
1254- if (find_open_parenthesis (end ))
1260+ {
1261+ if (find_open_parenthesis (end ))
12551262{
12561263static const char func_args_query []= "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'" ;
1257- char * tmp_buf = malloc (strlen (func_args_query )+ strlen (prev_wd ));
1258- sprintf (tmp_buf ,func_args_query ,prev_wd );
1259- COMPLETE_WITH_QUERY (tmp_buf );
1260- free (tmp_buf );
1264+ char * tmp_buf = malloc (strlen (func_args_query )+ strlen (prev_wd ));
1265+ sprintf (tmp_buf ,func_args_query ,prev_wd );
1266+ COMPLETE_WITH_QUERY (tmp_buf );
1267+ free (tmp_buf );
12611268}
12621269else
12631270{
1264- COMPLETE_WITH_CONST ("(" );
1271+ COMPLETE_WITH_CONST ("(" );
12651272}
1266- }
1267- else
1273+ }
1274+ else
12681275{
12691276static const char * const list_DROPCR []=
12701277{"CASCADE" ,"RESTRICT" ,NULL };
@@ -1274,14 +1281,22 @@ psql_completion(char *text, int start, int end)
12741281}
12751282else if (pg_strcasecmp (prev4_wd ,"DROP" )== 0 &&
12761283pg_strcasecmp (prev3_wd ,"FUNCTION" )== 0 &&
1277- pg_strcasecmp (prev_wd ,"(" )== 0 )
1284+ pg_strcasecmp (prev_wd ,"(" )== 0 )
12781285{
12791286static const char func_args_query []= "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'" ;
12801287char * tmp_buf = malloc (strlen (func_args_query )+ strlen (prev2_wd ));
12811288sprintf (tmp_buf ,func_args_query ,prev2_wd );
12821289COMPLETE_WITH_QUERY (tmp_buf );
12831290free (tmp_buf );
12841291}
1292+ /* DROP OWNED BY */
1293+ else if (pg_strcasecmp (prev2_wd ,"DROP" )== 0 &&
1294+ pg_strcasecmp (prev_wd ,"OWNED" )== 0 )
1295+ COMPLETE_WITH_CONST ("BY" );
1296+ else if (pg_strcasecmp (prev3_wd ,"DROP" )== 0 &&
1297+ pg_strcasecmp (prev2_wd ,"OWNED" )== 0 &&
1298+ pg_strcasecmp (prev_wd ,"BY" )== 0 )
1299+ COMPLETE_WITH_QUERY (Query_for_list_of_roles );
12851300
12861301
12871302
@@ -1502,7 +1517,7 @@ psql_completion(char *text, int start, int end)
15021517else if (pg_strcasecmp (prev_wd ,"NOTIFY" )== 0 )
15031518COMPLETE_WITH_QUERY ("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'" );
15041519
1505- /* OWNER TO - complete with available roles*/
1520+ /* OWNER TO - complete with available roles */
15061521else if (pg_strcasecmp (prev2_wd ,"OWNER" )== 0 &&
15071522pg_strcasecmp (prev_wd ,"TO" )== 0 )
15081523COMPLETE_WITH_QUERY (Query_for_list_of_roles );
@@ -1526,6 +1541,25 @@ psql_completion(char *text, int start, int end)
15261541COMPLETE_WITH_LIST (list_PREPARE );
15271542}
15281543
1544+ /* REASSIGN OWNED BY xxx TO yyy */
1545+ else if (pg_strcasecmp (prev_wd ,"REASSIGN" )== 0 )
1546+ COMPLETE_WITH_CONST ("OWNED" );
1547+ else if (pg_strcasecmp (prev_wd ,"OWNED" )== 0 &&
1548+ pg_strcasecmp (prev2_wd ,"REASSIGN" )== 0 )
1549+ COMPLETE_WITH_CONST ("BY" );
1550+ else if (pg_strcasecmp (prev_wd ,"BY" )== 0 &&
1551+ pg_strcasecmp (prev2_wd ,"OWNED" )== 0 &&
1552+ pg_strcasecmp (prev3_wd ,"REASSIGN" )== 0 )
1553+ COMPLETE_WITH_QUERY (Query_for_list_of_roles );
1554+ else if (pg_strcasecmp (prev2_wd ,"BY" )== 0 &&
1555+ pg_strcasecmp (prev3_wd ,"OWNED" )== 0 &&
1556+ pg_strcasecmp (prev4_wd ,"REASSIGN" )== 0 )
1557+ COMPLETE_WITH_CONST ("TO" );
1558+ else if (pg_strcasecmp (prev_wd ,"TO" )== 0 &&
1559+ pg_strcasecmp (prev3_wd ,"BY" )== 0 &&
1560+ pg_strcasecmp (prev4_wd ,"OWNED" )== 0 &&
1561+ pg_strcasecmp (prev5_wd ,"REASSIGN" )== 0 )
1562+ COMPLETE_WITH_QUERY (Query_for_list_of_roles );
15291563
15301564/* REINDEX */
15311565else if (pg_strcasecmp (prev_wd ,"REINDEX" )== 0 )
@@ -1909,7 +1943,7 @@ psql_completion(char *text, int start, int end)
19091943 something of that sort.
19101944*/
19111945
1912- /* This one gives you one from a list of things you can put after CREATE or DROP
1946+ /* This one gives you one from a list of things you can put after CREATE
19131947 as defined above.
19141948*/
19151949static char *
@@ -1935,6 +1969,51 @@ create_command_generator(const char *text, int state)
19351969return NULL ;
19361970}
19371971
1972+ /*
1973+ * This function gives you a list of things you can put after a DROP command.
1974+ * Very similar to create_command_generator, but has an additional entry for
1975+ * OWNED BY. (We do it this way in order not to duplicate the
1976+ * words_after_create list.)
1977+ */
1978+ static char *
1979+ drop_command_generator (const char * text ,int state )
1980+ {
1981+ static int list_index ,
1982+ string_length ;
1983+ const char * name ;
1984+
1985+ if (state == 0 )
1986+ {
1987+ /* If this is the first time for this completion, init some values */
1988+ list_index = 0 ;
1989+ string_length = strlen (text );
1990+
1991+ /*
1992+ * DROP can be followed by "OWNED BY", which is not found in the list
1993+ * for CREATE matches, so make it the first state. (We do not make it
1994+ * the last state because it would be more difficult to detect when we
1995+ * have to return NULL instead.)
1996+ *
1997+ * Make sure we advance to the next state.
1998+ */
1999+ list_index ++ ;
2000+ if (pg_strncasecmp ("OWNED" ,text ,string_length )== 0 )
2001+ return pg_strdup ("OWNED" );
2002+ }
2003+
2004+ /*
2005+ * In subsequent attempts, try to complete with the same items we use for
2006+ * CREATE
2007+ */
2008+ while ((name = words_after_create [list_index ++ - 1 ].name ))
2009+ {
2010+ if (pg_strncasecmp (name ,text ,string_length )== 0 )
2011+ return pg_strdup (name );
2012+ }
2013+
2014+ /* if nothing matches, return NULL */
2015+ return NULL ;
2016+ }
19382017
19392018/* The following two functions are wrappers for _complete_from_query */
19402019