diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 9a9e952ac9..c30758f1f3 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.166 2007/07/03 01:30:37 neilc Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.167 2007/09/14 04:25:24 momjian Exp $ */ /*---------------------------------------------------------------------- @@ -328,6 +328,10 @@ static const SchemaQuery Query_for_list_of_views = { " 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" + #define Query_for_list_of_databases \ "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'" @@ -419,6 +423,22 @@ static const SchemaQuery Query_for_list_of_views = { " (SELECT tgrelid FROM pg_catalog.pg_trigger "\ " WHERE pg_catalog.quote_ident(tgname)='%s')" +#define Query_for_list_of_ts_configurations \ +"SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\ +" WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'" + +#define Query_for_list_of_ts_dictionaries \ +"SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\ +" WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'" + +#define Query_for_list_of_ts_parsers \ +"SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\ +" WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'" + +#define Query_for_list_of_ts_templates \ +"SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\ +" WHERE substring(pg_catalog.quote_ident(tmplname),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. @@ -429,6 +449,7 @@ typedef struct const char *name; const char *query; /* simple query, or NULL */ const SchemaQuery *squery; /* schema query, or NULL */ + const bool noshow; /* NULL or true if this word should not show up after CREATE or DROP */ } pgsql_thing_t; static const pgsql_thing_t words_after_create[] = { @@ -440,8 +461,10 @@ static const pgsql_thing_t words_after_create[] = { * CREATE CONSTRAINT TRIGGER is not supported here because it is designed * to be used only by pg_dump. */ + {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, true}, {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"}, {"DATABASE", Query_for_list_of_databases}, + {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true}, {"DOMAIN", NULL, &Query_for_list_of_domains}, {"FUNCTION", NULL, &Query_for_list_of_functions}, {"GROUP", Query_for_list_of_roles}, @@ -449,6 +472,7 @@ static const pgsql_thing_t words_after_create[] = { {"INDEX", NULL, &Query_for_list_of_indexes}, {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a * good idea. */ + {"PARSER", Query_for_list_of_ts_parsers, NULL, true}, {"ROLE", Query_for_list_of_roles}, {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"}, {"SCHEMA", Query_for_list_of_schemas}, @@ -456,12 +480,14 @@ static const pgsql_thing_t words_after_create[] = { {"TABLE", NULL, &Query_for_list_of_tables}, {"TABLESPACE", Query_for_list_of_tablespaces}, {"TEMP", NULL, NULL}, /* for CREATE TEMP TABLE ... */ + {"TEMPLATE", Query_for_list_of_ts_templates, NULL, true}, + {"TEXT SEARCH", NULL, NULL}, {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"}, {"TYPE", NULL, &Query_for_list_of_datatypes}, {"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */ {"USER", Query_for_list_of_roles}, {"VIEW", NULL, &Query_for_list_of_views}, - {NULL, NULL, NULL} /* end of list */ + {NULL, NULL, NULL, false} /* end of list */ }; @@ -531,14 +557,14 @@ psql_completion(char *text, int start, int end) "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE", "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", - "UPDATE", "VACUUM", NULL + "UPDATE", "VACUUM", "VALUES", NULL }; static const char *const backslash_commands[] = { "\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright", "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df", - "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS", - "\\dt", "\\dT", "\\dv", "\\du", + "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\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", @@ -602,7 +628,7 @@ psql_completion(char *text, int start, int end) static const char *const list_ALTER[] = {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION", "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE", - "TABLESPACE", "TRIGGER", "TYPE", "USER", "VIEW", NULL}; + "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "VIEW", NULL}; COMPLETE_WITH_LIST(list_ALTER); } @@ -643,7 +669,7 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "INDEX") == 0) { static const char *const list_ALTERINDEX[] = - {"SET TABLESPACE", "OWNER TO", "RENAME TO", NULL}; + {"SET TABLESPACE", "OWNER TO", "RENAME TO", "SET", "RESET", NULL}; COMPLETE_WITH_LIST(list_ALTERINDEX); } @@ -714,7 +740,7 @@ psql_completion(char *text, int start, int end) { static const char *const list_ALTERSEQUENCE[] = {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", - "SET SCHEMA", "RENAME TO", NULL}; + "SET SCHEMA", "OWNED BY", "RENAME TO", NULL}; COMPLETE_WITH_LIST(list_ALTERSEQUENCE); } @@ -769,11 +795,38 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "TABLE") == 0) { static const char *const list_ALTER2[] = - {"ADD", "ALTER", "CLUSTER ON", "DROP", "RENAME", "OWNER TO", - "SET", NULL}; + {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT", + "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", NULL}; COMPLETE_WITH_LIST(list_ALTER2); } + /* ALTER TABLE xxx ENABLE */ + else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && + pg_strcasecmp(prev3_wd, "TABLE") == 0 && + pg_strcasecmp(prev_wd, "ENABLE") == 0) + { + static const char *const list_ALTERENABLE[] = + {"ALWAYS","REPLICA","RULE", "TRIGGER", NULL}; + COMPLETE_WITH_LIST(list_ALTERENABLE); + } + else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && + pg_strcasecmp(prev2_wd, "ENABLE") == 0 && + (pg_strcasecmp(prev_wd, "REPLICA") == 0 || + pg_strcasecmp(prev_wd, "ALWAYS") == 0)) + { + static const char *const list_ALTERENABLE2[] = + {"RULE", "TRIGGER", NULL}; + COMPLETE_WITH_LIST(list_ALTERENABLE2); + } + else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && + pg_strcasecmp(prev3_wd, "TABLE") == 0 && + pg_strcasecmp(prev_wd, "DISABLE") == 0) + { + static const char *const list_ALTERDISABLE[] = + {"RULE", "TRIGGER", NULL}; + COMPLETE_WITH_LIST(list_ALTERDISABLE); + } + /* If we have TABLE ALTER|RENAME, provide list of columns */ else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && (pg_strcasecmp(prev_wd, "ALTER") == 0 || @@ -871,6 +924,45 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERTSPC); } + /* ALTER TEXT SEARCH */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "TEXT") == 0 && + pg_strcasecmp(prev_wd, "SEARCH") == 0) + { + static const char *const list_ALTERTEXTSEARCH[] = + {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); + } + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0 && + (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 || + pg_strcasecmp(prev2_wd, "PARSER") == 0)) + COMPLETE_WITH_CONST("RENAME TO"); + + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0 && + pg_strcasecmp(prev2_wd, "DICTIONARY") == 0) + { + static const char *const list_ALTERTEXTSEARCH2[] = + {"OWNER TO", "RENAME TO", NULL}; + + COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2); + } + + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0 && + pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) + { + static const char *const list_ALTERTEXTSEARCH3[] = + {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", NULL}; + + COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3); + } + /* complete ALTER TYPE with OWNER TO, SET SCHEMA */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && pg_strcasecmp(prev2_wd, "TYPE") == 0) @@ -947,7 +1039,7 @@ psql_completion(char *text, int start, int end) } /* - * If we have CLUSTER ORDER BY, then add the index as well. + * If we have CLUSTER USING, then add the index as well. */ else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && pg_strcasecmp(prev_wd, "USING") == 0) @@ -966,12 +1058,25 @@ psql_completion(char *text, int start, int end) {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", - "TABLESPACE", "ROLE", NULL}; + "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; COMPLETE_WITH_LIST(list_COMMENT); } else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev2_wd, "TEXT") == 0 && + pg_strcasecmp(prev_wd, "SEARCH") == 0) + { + static const char *const list_TRANS2[] = + {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(list_TRANS2); + } + else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0) || + (pg_strcasecmp(prev5_wd, "ON") == 0 && + pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0)) COMPLETE_WITH_CONST("IS"); /* COPY */ @@ -1038,6 +1143,11 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_DATABASE); } + else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && + pg_strcasecmp(prev3_wd, "DATABASE") == 0 && + pg_strcasecmp(prev_wd, "TEMPLATE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); + /* CREATE INDEX */ /* First off we complete CREATE UNIQUE with "INDEX" */ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && @@ -1077,7 +1187,7 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev_wd, "USING") == 0) { static const char *const index_mth[] = - {"BTREE", "HASH", "GIST", NULL}; + {"BTREE", "HASH", "GIN", "GIST", NULL}; COMPLETE_WITH_LIST(index_mth); } @@ -1143,6 +1253,21 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_CONST("LOCATION"); } +/* CREATE TEXT SEARCH */ + else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && + pg_strcasecmp(prev2_wd, "TEXT") == 0 && + pg_strcasecmp(prev_wd, "SEARCH") == 0) + { + static const char *const list_CREATETEXTSEARCH[] = + {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(list_CREATETEXTSEARCH); + } + else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0 && + pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) + COMPLETE_WITH_CONST("("); + /* CREATE TRIGGER */ /* complete CREATE TRIGGER with BEFORE,AFTER */ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && @@ -1287,7 +1412,15 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "VIEW") == 0)) || (pg_strcasecmp(prev4_wd, "DROP") == 0 && pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && - prev_wd[strlen(prev_wd) - 1] == ')')) + prev_wd[strlen(prev_wd) - 1] == ')') || + (pg_strcasecmp(prev5_wd, "DROP") == 0 && + pg_strcasecmp(prev4_wd, "TEXT") == 0 && + pg_strcasecmp(prev3_wd, "SEARCH") == 0 && + (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 || + pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 || + pg_strcasecmp(prev2_wd, "PARSER") == 0 || + pg_strcasecmp(prev2_wd, "TEMPLATE") == 0)) + ) { if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) { @@ -1332,9 +1465,17 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "OWNED") == 0 && pg_strcasecmp(prev_wd, "BY") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && + pg_strcasecmp(prev2_wd, "TEXT") == 0 && + pg_strcasecmp(prev_wd, "SEARCH") == 0) + { + static const char *const list_ALTERTEXTSEARCH[] = + {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - + COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); + } + /* EXPLAIN */ /* @@ -1873,6 +2014,14 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + else if (strcmp(prev_wd, "\\dF") == 0 || strcmp(prev_wd, "\\dF+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); + else if (strcmp(prev_wd, "\\dFd") == 0 || strcmp(prev_wd, "\\dFd+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); + else if (strcmp(prev_wd, "\\dFp") == 0 || strcmp(prev_wd, "\\dFp+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers); + else if (strcmp(prev_wd, "\\dFt") == 0 || strcmp(prev_wd, "\\dFt+") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates); else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); else if (strcmp(prev_wd, "\\dn") == 0) @@ -1985,8 +2134,7 @@ psql_completion(char *text, int start, int end) static char * create_command_generator(const char *text, int state) { - static int list_index, - string_length; + static int list_index, string_length; const char *name; /* If this is the first time for this completion, init some values */ @@ -1998,9 +2146,10 @@ create_command_generator(const char *text, int state) /* find something that matches */ while ((name = words_after_create[list_index++].name)) - if (pg_strncasecmp(name, text, string_length) == 0) - return pg_strdup(name); - + { + if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow) + return pg_strdup(name); + } /* if nothing matches, return NULL */ return NULL; } @@ -2014,8 +2163,7 @@ create_command_generator(const char *text, int state) static char * drop_command_generator(const char *text, int state) { - static int list_index, - string_length; + static int list_index, string_length; const char *name; if (state == 0) @@ -2043,7 +2191,7 @@ drop_command_generator(const char *text, int state) */ while ((name = words_after_create[list_index++ - 1].name)) { - if (pg_strncasecmp(name, text, string_length) == 0) + if ((pg_strncasecmp(name, text, string_length) == 0) && (!words_after_create[list_index - 2].noshow)) return pg_strdup(name); }