diff options
Diffstat (limited to 'src/bin/psql/tab-complete.c')
-rw-r--r-- | src/bin/psql/tab-complete.c | 2869 |
1 files changed, 2343 insertions, 526 deletions
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 3a5fe0ab1f6..27d7469b935 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.137 2005/08/14 18:49:30 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.138 2005/10/15 02:49:40 momjian Exp $ */ /*---------------------------------------------------------------------- @@ -83,30 +83,28 @@ typedef struct SchemaQuery const char *catname; /* - * Selection condition --- only rows meeting this condition are - * candidates to display. If catname mentions multiple tables, - * include the necessary join condition here. For example, "c.relkind - * = 'r'". Write NULL (not an empty string) if not needed. + * Selection condition --- only rows meeting this condition are candidates + * to display. If catname mentions multiple tables, include the necessary + * join condition here. For example, "c.relkind = 'r'". Write NULL (not + * an empty string) if not needed. */ const char *selcondition; /* * Visibility condition --- which rows are visible without schema - * qualification? For example, - * "pg_catalog.pg_table_is_visible(c.oid)". + * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)". */ const char *viscondition; /* - * Namespace --- name of field to join to pg_namespace.oid. For - * example, "c.relnamespace". + * Namespace --- name of field to join to pg_namespace.oid. For example, + * "c.relnamespace". */ const char *namespace; /* - * Result --- the appropriately-quoted name to return, in the case of - * an unqualified name. For example, - * "pg_catalog.quote_ident(c.relname)". + * Result --- the appropriately-quoted name to return, in the case of an + * unqualified name. For example, "pg_catalog.quote_ident(c.relname)". */ const char *result; @@ -128,8 +126,7 @@ static int completion_max_records; * the completion callback functions. Ugly but there is no better way. */ static const char *completion_charp; /* to pass a string */ -static const char *const * completion_charpp; /* to pass a list of - * strings */ +static const char *const * completion_charpp; /* to pass a list of strings */ static const char *completion_info_charp; /* to pass a second string */ static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ @@ -433,9 +430,13 @@ typedef struct static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, - {"CAST", NULL, NULL}, /* Casts have complex structures for - * names, so skip it */ - /* CREATE CONSTRAINT TRIGGER is not supported here because it is designed to be used only by pg_dump. */ + {"CAST", NULL, NULL}, /* Casts have complex structures for names, so + * skip it */ + + /* + * CREATE CONSTRAINT TRIGGER is not supported here because it is designed + * to be used only by pg_dump. + */ {"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}, {"DOMAIN", NULL, &Query_for_list_of_domains}, @@ -443,8 +444,8 @@ static const pgsql_thing_t words_after_create[] = { {"GROUP", Query_for_list_of_roles}, {"LANGUAGE", Query_for_list_of_languages}, {"INDEX", NULL, &Query_for_list_of_indexes}, - {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such - * a good idea. */ + {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a + * good idea. */ {"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}, @@ -493,8 +494,8 @@ initialize_readline(void) completion_max_records = 1000; /* - * There is a variable rl_completion_query_items for this but - * apparently it's not defined everywhere. + * There is a variable rl_completion_query_items for this but apparently + * it's not defined everywhere. */ } @@ -550,9 +551,8 @@ psql_completion(char *text, int start, int end) /* * Scan the input line before our current position for the last four - * words. According to those we'll make some smart decisions on what - * the user is probably intending to type. TODO: Use strtokx() to do - * this. + * words. According to those we'll make some smart decisions on what the + * user is probably intending to type. TODO: Use strtokx() to do this. */ prev_wd = previous_word(start, 0); prev2_wd = previous_word(start, 1); @@ -580,8 +580,8 @@ psql_completion(char *text, int start, int end) /* ALTER */ /* - * complete with what you can alter (TABLE, GROUP, USER, ...) unless - * we're in ALTER TABLE sth ALTER + * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're + * in ALTER TABLE sth ALTER */ else if (pg_strcasecmp(prev_wd, "ALTER") == 0 && pg_strcasecmp(prev3_wd, "TABLE") != 0) @@ -595,18 +595,18 @@ psql_completion(char *text, int start, int end) } /* ALTER AGGREGATE,FUNCTION <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 || + (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 || pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) { static const char *const list_ALTERAGG[] = - {"OWNER TO", "RENAME TO","SET SCHEMA", NULL}; + {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - COMPLETE_WITH_LIST(list_ALTERAGG); + COMPLETE_WITH_LIST(list_ALTERAGG); } /* ALTER CONVERSION,SCHEMA <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || + (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || pg_strcasecmp(prev2_wd, "SCHEMA") == 0)) { static const char *const list_ALTERGEN[] = @@ -643,23 +643,23 @@ psql_completion(char *text, int start, int end) /* ALTER USER,ROLE <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && (pg_strcasecmp(prev2_wd, "USER") == 0 || - pg_strcasecmp(prev2_wd, "ROLE") == 0)) + pg_strcasecmp(prev2_wd, "ROLE") == 0)) { static const char *const list_ALTERUSER[] = {"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER", - "NOCREATEUSER","CREATEROLE","NOCREATEROLE","INHERIT","NOINHERIT", - "LOGIN","NOLOGIN","CONNECTION LIMIT", "VALID UNTIL", "RENAME TO", - "SUPERUSER","NOSUPERUSER", "SET", "RESET", NULL}; + "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT", + "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO", + "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL}; COMPLETE_WITH_LIST(list_ALTERUSER); } /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) + (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && + (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) { - COMPLETE_WITH_CONST("PASSWORD"); + COMPLETE_WITH_CONST("PASSWORD"); } /* ALTER DOMAIN <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && @@ -694,21 +694,21 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && pg_strcasecmp(prev2_wd, "SEQUENCE") == 0) { - static const char *const list_ALTERSEQUENCE[] = - {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", - "SET SCHEMA", NULL}; + static const char *const list_ALTERSEQUENCE[] = + {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", + "SET SCHEMA", NULL}; - COMPLETE_WITH_LIST(list_ALTERSEQUENCE); + COMPLETE_WITH_LIST(list_ALTERSEQUENCE); } /* ALTER SEQUENCE <name> NO */ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 && pg_strcasecmp(prev_wd, "NO") == 0) { - static const char *const list_ALTERSEQUENCE2[] = - {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; + static const char *const list_ALTERSEQUENCE2[] = + {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; - COMPLETE_WITH_LIST(list_ALTERSEQUENCE2); + COMPLETE_WITH_LIST(list_ALTERSEQUENCE2); } /* ALTER TRIGGER <name>, add ON */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && @@ -781,9 +781,9 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "ALTER") == 0)) { /* DROP ... does not work well yet */ - static const char *const list_COLUMNALTER[] = + static const char *const list_COLUMNALTER[] = {"TYPE", "SET DEFAULT", "DROP DEFAULT", "SET NOT NULL", - "DROP NOT NULL", "SET STATISTICS", "SET STORAGE", NULL}; + "DROP NOT NULL", "SET STATISTICS", "SET STORAGE", NULL}; COMPLETE_WITH_LIST(list_COLUMNALTER); } @@ -802,7 +802,7 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "SET") == 0) { static const char *const list_TABLESET[] = - {"WITHOUT", "TABLESPACE","SCHEMA", NULL}; + {"WITHOUT", "TABLESPACE", "SCHEMA", NULL}; COMPLETE_WITH_LIST(list_TABLESET); } @@ -836,6 +836,7 @@ psql_completion(char *text, int start, int end) { static const char *const list_ALTERTYPE[] = {"OWNER TO", "SET SCHEMA", NULL}; + COMPLETE_WITH_LIST(list_ALTERTYPE); } /* complete ALTER GROUP <foo> */ @@ -869,9 +870,9 @@ psql_completion(char *text, int start, int end) {"WORK", "TRANSACTION", NULL}; COMPLETE_WITH_LIST(list_TRANS); - } + } /* COMMIT */ - else if(pg_strcasecmp(prev_wd, "COMMIT") == 0) + else if (pg_strcasecmp(prev_wd, "COMMIT") == 0) { static const char *const list_COMMIT[] = {"WORK", "TRANSACTION", "PREPARED", NULL}; @@ -904,8 +905,7 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_CONST("ON"); /* - * If we have CLUSTER <sth> ON, then add the correct tablename as - * well. + * If we have CLUSTER <sth> ON, then add the correct tablename as well. */ else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && pg_strcasecmp(prev_wd, "ON") == 0) @@ -923,7 +923,7 @@ psql_completion(char *text, int start, int end) static const char *const list_COMMENT[] = {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION", - "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN","LARGE OBJECT", NULL}; + "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", NULL}; COMPLETE_WITH_LIST(list_COMMENT); } @@ -946,12 +946,12 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev2_wd, "COPY") == 0 || pg_strcasecmp(prev2_wd, "\\copy") == 0 || pg_strcasecmp(prev2_wd, "BINARY") == 0) - { - static const char *const list_FROMTO[] = - {"FROM", "TO", NULL}; + { + static const char *const list_FROMTO[] = + {"FROM", "TO", NULL}; - COMPLETE_WITH_LIST(list_FROMTO); - } + COMPLETE_WITH_LIST(list_FROMTO); + } /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */ else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 || pg_strcasecmp(prev3_wd, "\\copy") == 0 || @@ -966,22 +966,22 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev4_wd, "BINARY") == 0) && (pg_strcasecmp(prev2_wd, "FROM") == 0 || pg_strcasecmp(prev2_wd, "TO") == 0)) - { - static const char *const list_COPY[] = - {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL}; + { + static const char *const list_COPY[] = + {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL}; - COMPLETE_WITH_LIST(list_COPY); - } + COMPLETE_WITH_LIST(list_COPY); + } /* Handle COPY|BINARY <sth> FROM|TO filename CSV */ - else if (pg_strcasecmp(prev_wd, "CSV") == 0 && + else if (pg_strcasecmp(prev_wd, "CSV") == 0 && (pg_strcasecmp(prev3_wd, "FROM") == 0 || pg_strcasecmp(prev3_wd, "TO") == 0)) { - static const char *const list_CSV[] = - {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL}; + static const char *const list_CSV[] = + {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL}; - COMPLETE_WITH_LIST(list_CSV); + COMPLETE_WITH_LIST(list_CSV); } /* CREATE DATABASE */ @@ -990,7 +990,7 @@ psql_completion(char *text, int start, int end) { static const char *const list_DATABASE[] = {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT", - NULL}; + NULL}; COMPLETE_WITH_LIST(list_DATABASE); } @@ -1011,8 +1011,8 @@ psql_completion(char *text, int start, int end) 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) + * 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) @@ -1088,20 +1088,22 @@ psql_completion(char *text, int start, int end) /* CREATE TRIGGER */ /* complete CREATE TRIGGER <name> with BEFORE,AFTER */ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) + pg_strcasecmp(prev2_wd, "TRIGGER") == 0) { static const char *const list_CREATETRIGGER[] = - {"BEFORE", "AFTER", NULL}; - COMPLETE_WITH_LIST(list_CREATETRIGGER); + {"BEFORE", "AFTER", NULL}; + + COMPLETE_WITH_LIST(list_CREATETRIGGER); } /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */ else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) + pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && + (pg_strcasecmp(prev2_wd, "BEFORE") == 0 || + pg_strcasecmp(prev2_wd, "AFTER") == 0)) { static const char *const list_CREATETRIGGER2[] = - {"ON","OR",NULL}; + {"ON", "OR", NULL}; + COMPLETE_WITH_LIST(list_CREATETRIGGER2); } @@ -1111,29 +1113,35 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0)) { static const char *const list_CREATEROLE[] = - {"ADMIN","CONNECTION LIMIT","CREATEDB","CREATEROLE","CREATEUSER", - "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB", - "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID", - "UNENCRYPTED",NULL}; - COMPLETE_WITH_LIST(list_CREATEROLE); + {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER", + "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB", + "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID", + "UNENCRYPTED", NULL}; + + COMPLETE_WITH_LIST(list_CREATEROLE); } - /* complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with PASSWORD */ + + /* + * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with + * PASSWORD + */ else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) + (pg_strcasecmp(prev3_wd, "ROLE") == 0 || + pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && + (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) { - COMPLETE_WITH_CONST("PASSWORD"); + COMPLETE_WITH_CONST("PASSWORD"); } /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */ else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - pg_strcasecmp(prev_wd, "IN") == 0) + (pg_strcasecmp(prev3_wd, "ROLE") == 0 || + pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && + pg_strcasecmp(prev_wd, "IN") == 0) { static const char *const list_CREATEROLE3[] = - {"GROUP","ROLE",NULL}; - COMPLETE_WITH_LIST(list_CREATEROLE3); + {"GROUP", "ROLE", NULL}; + + COMPLETE_WITH_LIST(list_CREATEROLE3); } /* CREATE VIEW */ @@ -1150,20 +1158,23 @@ psql_completion(char *text, int start, int end) /* DECLARE */ else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0) { - static const char *const list_DECLARE[] = - {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; - COMPLETE_WITH_LIST(list_DECLARE); + static const char *const list_DECLARE[] = + {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; + + COMPLETE_WITH_LIST(list_DECLARE); } else if (pg_strcasecmp(prev_wd, "CURSOR") == 0) { - static const char *const list_DECLARECURSOR[] = - {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; - COMPLETE_WITH_LIST(list_DECLARECURSOR); + static const char *const list_DECLARECURSOR[] = + {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; + + COMPLETE_WITH_LIST(list_DECLARECURSOR); } /* DELETE */ + /* * Complete DELETE with FROM (only if the word before that is not "ON" * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) @@ -1211,27 +1222,31 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && prev_wd[strlen(prev_wd) - 1] == ')')) { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; - COMPLETE_WITH_LIST(list_DROPCR); + static const char *const list_DROPCR[] = + {"CASCADE", "RESTRICT", NULL}; + + COMPLETE_WITH_LIST(list_DROPCR); } /* EXPLAIN */ + /* * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands */ else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0) { - static const char *const list_EXPLAIN[] = - {"SELECT","INSERT","DELETE","UPDATE","DECLARE","ANALYZE","VERBOSE",NULL}; - COMPLETE_WITH_LIST(list_EXPLAIN); + static const char *const list_EXPLAIN[] = + {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL}; + + COMPLETE_WITH_LIST(list_EXPLAIN); } else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0) + pg_strcasecmp(prev_wd, "ANALYZE") == 0) { - static const char *const list_EXPLAIN[] = - {"SELECT","INSERT","DELETE","UPDATE","DECLARE","VERBOSE",NULL}; - COMPLETE_WITH_LIST(list_EXPLAIN); + static const char *const list_EXPLAIN[] = + {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL}; + + COMPLETE_WITH_LIST(list_EXPLAIN); } else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && pg_strcasecmp(prev3_wd, "VACUUM") != 0 && @@ -1239,9 +1254,10 @@ psql_completion(char *text, int start, int end) (pg_strcasecmp(prev2_wd, "ANALYZE") == 0 || pg_strcasecmp(prev2_wd, "EXPLAIN") == 0)) { - static const char *const list_EXPLAIN[] = - {"SELECT","INSERT","DELETE","UPDATE","DECLARE",NULL}; - COMPLETE_WITH_LIST(list_EXPLAIN); + static const char *const list_EXPLAIN[] = + {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL}; + + COMPLETE_WITH_LIST(list_EXPLAIN); } /* FETCH && MOVE */ @@ -1265,9 +1281,9 @@ psql_completion(char *text, int start, int end) } /* - * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are - * equivalent, but we may as well tab-complete both: perhaps some - * users prefer one variant or the other. + * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent, + * but we may as well tab-complete both: perhaps some users prefer one + * variant or the other. */ else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 || pg_strcasecmp(prev3_wd, "MOVE") == 0) @@ -1295,15 +1311,15 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_CONST("ON"); /* - * Complete GRANT/REVOKE <sth> ON with a list of tables, views, - * sequences, and indexes + * 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 + * 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 + * 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 ((pg_strcasecmp(prev3_wd, "GRANT") == 0 || pg_strcasecmp(prev3_wd, "REVOKE") == 0) && @@ -1564,7 +1580,7 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(constraint_list); } /* Complete SET ROLE */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && + else if (pg_strcasecmp(prev2_wd, "SET") == 0 && pg_strcasecmp(prev_wd, "ROLE") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */ @@ -1589,14 +1605,14 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_CONST("TO"); /* Suggest possible variable values */ else if (pg_strcasecmp(prev3_wd, "SET") == 0 && - (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0)) + (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0)) { if (pg_strcasecmp(prev2_wd, "DateStyle") == 0) { static const char *const my_list[] = {"ISO", "SQL", "Postgres", "German", - "YMD", "DMY", "MDY", - "US", "European", "NonEuropean", + "YMD", "DMY", "MDY", + "US", "European", "NonEuropean", "DEFAULT", NULL}; COMPLETE_WITH_LIST(my_list); @@ -1638,16 +1654,16 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_CONST("SET"); /* - * If the previous word is SET (and it wasn't caught above as the - * _first_ word) the word before it was (hopefully) a table name and - * we'll now make a list of attributes. + * If the previous word is SET (and it wasn't caught above as the _first_ + * word) the word before it was (hopefully) a table name and we'll now + * make a list of attributes. */ else if (pg_strcasecmp(prev_wd, "SET") == 0) COMPLETE_WITH_ATTR(prev2_wd); /* UPDATE xx SET yy = */ else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev4_wd, "UPDATE") == 0) + pg_strcasecmp(prev4_wd, "UPDATE") == 0) COMPLETE_WITH_CONST("="); /* @@ -1756,21 +1772,144 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(my_list); } else if (strcmp(prev_wd, "\\cd") == 0 || - strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 || + strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 || strcmp(prev_wd, "\\g") == 0 || - strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 || - strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 || + strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 || + strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 || strcmp(prev_wd, "\\s") == 0 || - strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 + strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 ) matches = completion_matches(text, filename_completion_function); - /* - * Finally, we look through the list of "things", such as TABLE, INDEX - * and check if that was the previous word. If so, execute the query - * to get a list of them. - */ + /* + * + * Fi + * n + * a + * l + * l + * y + * , + * + * we + * + * lo + * o + * k + * + * th + * r + * o + * u + * g + * h + * + * th + * e + * + * li + * s + * t + * + * of + * + * "t + * h + * i + * n + * g + * s + * " + * , + * + * su + * c + * h + * + * as + * + * TA + * B + * L + * E + * , + * + * IN + * D + * E + * X + * + * an + * d + * + * ch + * e + * c + * k + * + * if + * + * th + * a + * t + * + * wa + * s + * + * th + * e + * + * pr + * e + * v + * i + * o + * u + * s + * + * wo + * r + * d + * . + * + * If + * + * so + * , + * + * ex + * e + * c + * u + * t + * e + * + * th + * e + * + * qu + * e + * r + * y + * + * to + * + * ge + * t + * + * a + * li + * s + * t + * + * of + * + * th + * e + * m + * . + * */ else { int i; @@ -1789,30 +1928,236 @@ psql_completion(char *text, int start, int end) } } - /* - * If we still don't have anything to match we have to fabricate some - * sort of default list. If we were to just return NULL, readline - * automatically attempts filename completion, and that's usually no - * good. - */ - if (matches == NULL) - { - COMPLETE_WITH_CONST(""); + /* + * + * If + * + * we + * + * st + * i + * l + * l + * + * do + * n + * ' + * t + * + * ha + * v + * e + * + * an + * y + * t + * h + * i + * n + * g + * + * to + * + * ma + * t + * c + * h + * + * we + * + * ha + * v + * e + * + * to + * + * fa + * b + * r + * i + * c + * a + * t + * e + * + * so + * m + * e + * + * so + * r + * t + * + * of + * + * de + * f + * a + * u + * l + * t + * + * li + * s + * t + * . + * + * If + * + * we + * + * we + * r + * e + * + * to + * + * ju + * s + * t + * + * re + * t + * u + * r + * n + * + * NU + * L + * L + * , + * + * re + * a + * d + * l + * i + * n + * e + * + * au + * t + * o + * m + * a + * t + * i + * c + * a + * l + * l + * y + * + * at + * t + * e + * m + * p + * t + * s + * + * fi + * l + * e + * n + * a + * m + * e + * + * co + * m + * p + * l + * e + * t + * i + * o + * n + * , + * + * an + * d + * + * th + * a + * t + * ' + * s + * + * us + * u + * a + * l + * l + * y + * + * no + * + * go + * o + * d + * . + * */ + if (matches == NULL) + { + COMPLETE_WITH_CONST(""); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER - rl_completion_append_character = '\0'; + rl_completion_append_character = '\0'; #endif - } - - /* free storage */ - free(prev_wd); - free(prev2_wd); - free(prev3_wd); - free(prev4_wd); - free(prev5_wd); - - /* Return our Grand List O' Matches */ - return matches; -} + } + + /* + * f + * r + * e + * e + * + * st + * o + * r + * a + * g + * e + * + */ + free(prev_wd); + free(prev2_wd); + free(prev3_wd); + free(prev4_wd); + free(prev5_wd); + + /* + * R + * e + * t + * u + * r + * n + * + * ou + * r + * + * Gr + * a + * n + * d + * + * Li + * s + * t + * + * O' + * + * Ma + * t + * c + * h + * e + * s + * + */ + return matches; + } @@ -1832,43 +2177,152 @@ psql_completion(char *text, int start, int end) /* This one gives you one from a list of things you can put after CREATE or DROP as defined above. */ -static char * -create_command_generator(const char *text, int state) -{ - static int list_index, - string_length; - const char *name; - - /* If this is the first time for this completion, init some values */ - if (state == 0) - { - list_index = 0; - string_length = strlen(text); - } - - /* 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 nothing matches, return NULL */ - return NULL; -} + static char * + create_command_generator(const char *text, int state) + { + static int list_index, + string_length; + const char *name; + + /* + * I + * f + * + * th + * i + * s + * + * is + * + * th + * e + * + * fi + * r + * s + * t + * + * ti + * m + * e + * + * fo + * r + * + * th + * i + * s + * + * co + * m + * p + * l + * e + * t + * i + * o + * n + * , + * + * in + * i + * t + * + * so + * m + * e + * + * va + * l + * u + * e + * s + * + */ + if (state == 0) + { + list_index = 0; + string_length = strlen(text); + } + + /* + * f + * i + * n + * d + * + * so + * m + * e + * t + * h + * i + * n + * g + * + * th + * a + * t + * + * ma + * t + * c + * h + * e + * s + * + */ + while ((name = words_after_create[list_index++].name)) + if (pg_strncasecmp(name, text, string_length) == 0) + return pg_strdup(name); + + /* + * i + * f + * + * no + * t + * h + * i + * n + * g + * + * ma + * t + * c + * h + * e + * s + * , + * + * re + * t + * u + * r + * n + * + * NU + * L + * L + * + */ + return NULL; + } /* The following two functions are wrappers for _complete_from_query */ -static char * -complete_from_query(const char *text, int state) -{ - return _complete_from_query(0, text, state); -} + static char * + complete_from_query(const char *text, int state) + { + return _complete_from_query(0, text, state); + } -static char * -complete_from_schema_query(const char *text, int state) -{ - return _complete_from_query(1, text, state); -} + static char * + complete_from_schema_query(const char *text, int state) + { + return _complete_from_query(1, text, state); + } /* This creates a list of matching things, according to a query pointed to @@ -1890,229 +2344,1308 @@ complete_from_schema_query(const char *text, int state) See top of file for examples of both kinds of query. */ -static char * -_complete_from_query(int is_schema_query, const char *text, int state) -{ - static int list_index, - string_length; - static PGresult *result = NULL; - - /* - * If this is the first time for this completion, we fetch a list of - * our "things" from the backend. - */ - if (state == 0) - { - PQExpBufferData query_buffer; - char *e_text; - char *e_info_charp; - - list_index = 0; - string_length = strlen(text); - - /* Free any prior result */ - PQclear(result); - result = NULL; - - /* Set up suitably-escaped copies of textual inputs */ - e_text = pg_malloc(string_length * 2 + 1); - PQescapeString(e_text, text, string_length); - - if (completion_info_charp) - { - size_t charp_len; - - charp_len = strlen(completion_info_charp); - e_info_charp = pg_malloc(charp_len * 2 + 1); - PQescapeString(e_info_charp, completion_info_charp, - charp_len); - } - else - e_info_charp = NULL; - - initPQExpBuffer(&query_buffer); - - if (is_schema_query) - { - /* completion_squery gives us the pieces to assemble */ - const char *qualresult = completion_squery->qualresult; - - if (qualresult == NULL) - qualresult = completion_squery->result; - - /* Get unqualified names matching the input-so-far */ - appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ", - completion_squery->result, - completion_squery->catname); - if (completion_squery->selcondition) - appendPQExpBuffer(&query_buffer, "%s AND ", - completion_squery->selcondition); - appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'", - completion_squery->result, - string_length, e_text); - appendPQExpBuffer(&query_buffer, " AND %s", - completion_squery->viscondition); - - /* - * When fetching relation names, suppress system catalogs - * unless the input-so-far begins with "pg_". This is a - * compromise between not offering system catalogs for - * completion at all, and having them swamp the result when - * the input is just "p". - */ - if (strcmp(completion_squery->catname, - "pg_catalog.pg_class c") == 0 && - strncmp(text, "pg_", 3) !=0) - { - appendPQExpBuffer(&query_buffer, - " AND c.relnamespace <> (SELECT oid FROM" - " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); - } - - /* - * Add in matching schema names, but only if there is more - * than one potential match among schema names. - */ - appendPQExpBuffer(&query_buffer, "\nUNION\n" - "SELECT pg_catalog.quote_ident(n.nspname) || '.' " - "FROM pg_catalog.pg_namespace n " - "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", - string_length, e_text); - appendPQExpBuffer(&query_buffer, - " AND (SELECT pg_catalog.count(*)" - " FROM pg_catalog.pg_namespace" - " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", - string_length, e_text); - - /* - * Add in matching qualified names, but only if there is - * exactly one schema matching the input-so-far. - */ - appendPQExpBuffer(&query_buffer, "\nUNION\n" - "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " - "FROM %s, pg_catalog.pg_namespace n " - "WHERE %s = n.oid AND ", - qualresult, - completion_squery->catname, - completion_squery->namespace); - if (completion_squery->selcondition) - appendPQExpBuffer(&query_buffer, "%s AND ", - completion_squery->selcondition); - appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", - qualresult, - string_length, e_text); - - /* - * This condition exploits the single-matching-schema rule to - * speed up the query - */ - appendPQExpBuffer(&query_buffer, - " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", - string_length, e_text); - appendPQExpBuffer(&query_buffer, - " AND (SELECT pg_catalog.count(*)" - " FROM pg_catalog.pg_namespace" - " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", - string_length, e_text); - - /* If an addon query was provided, use it */ - if (completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); - } - else - { - /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, completion_charp, - string_length, e_text, e_info_charp); - } - - /* Limit the number of records in the result */ - appendPQExpBuffer(&query_buffer, "\nLIMIT %d", - completion_max_records); - - result = exec_query(query_buffer.data); - - termPQExpBuffer(&query_buffer); - free(e_text); - if (e_info_charp) - free(e_info_charp); - } - - /* Find something that matches */ - if (result && PQresultStatus(result) == PGRES_TUPLES_OK) - { - const char *item; - - while (list_index < PQntuples(result) && - (item = PQgetvalue(result, list_index++, 0))) - if (pg_strncasecmp(text, item, string_length) == 0) - return pg_strdup(item); - } - - /* If nothing matches, free the db structure and return null */ - PQclear(result); - result = NULL; - return NULL; -} + static char * + _complete_from_query(int is_schema_query, const char *text, int state) + { + static int list_index, + string_length; + static PGresult *result = NULL; + + /* + * + * If + * + * th + * i + * s + * + * is + * + * th + * e + * + * fi + * r + * s + * t + * + * ti + * m + * e + * + * fo + * r + * + * th + * i + * s + * + * co + * m + * p + * l + * e + * t + * i + * o + * n + * , + * + * we + * + * fe + * t + * c + * h + * + * a + * li + * s + * t + * + * of + * + * ou + * r + * + * "t + * h + * i + * n + * g + * s + * " + * + * fr + * o + * m + * + * th + * e + * + * ba + * c + * k + * e + * n + * d + * . + * */ + if (state == 0) + { + PQExpBufferData query_buffer; + char *e_text; + char *e_info_charp; + + list_index = 0; + string_length = strlen(text); + + /* + * F + * r + * e + * e + * + * an + * y + * + * pr + * i + * o + * r + * + * re + * s + * u + * l + * t + * + */ + PQclear(result); + result = NULL; + + /* + * S + * e + * t + * + * up + * + * su + * i + * t + * a + * b + * l + * y + * - + * e + * s + * c + * a + * p + * e + * d + * + * co + * p + * i + * e + * s + * + * of + * + * te + * x + * t + * u + * a + * l + * + * in + * p + * u + * t + * s + * + */ + e_text = pg_malloc(string_length * 2 + 1); + PQescapeString(e_text, text, string_length); + + if (completion_info_charp) + { + size_t charp_len; + + charp_len = strlen(completion_info_charp); + e_info_charp = pg_malloc(charp_len * 2 + 1); + PQescapeString(e_info_charp, completion_info_charp, + charp_len); + } + else + e_info_charp = NULL; + + initPQExpBuffer(&query_buffer); + + if (is_schema_query) + { + /* + * c + * o + * m + * p + * l + * e + * t + * i + * o + * n + * _ + * s + * q + * u + * e + * r + * y + * + * gi + * v + * e + * s + * + * us + * + * th + * e + * + * pi + * e + * c + * e + * s + * + * to + * + * as + * s + * e + * m + * b + * l + * e + * + */ + const char *qualresult = completion_squery->qualresult; + + if (qualresult == NULL) + qualresult = completion_squery->result; + + /* + * G + * e + * t + * + * un + * q + * u + * a + * l + * i + * f + * i + * e + * d + * + * na + * m + * e + * s + * + * ma + * t + * c + * h + * i + * n + * g + * + * th + * e + * + * in + * p + * u + * t + * - + * s + * o + * - + * f + * a + * r + * + */ + appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ", + completion_squery->result, + completion_squery->catname); + if (completion_squery->selcondition) + appendPQExpBuffer(&query_buffer, "%s AND ", + completion_squery->selcondition); + appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'", + completion_squery->result, + string_length, e_text); + appendPQExpBuffer(&query_buffer, " AND %s", + completion_squery->viscondition); + + /* + * + * Wh + * e + * n + * + * fe + * t + * c + * h + * i + * n + * g + * + * re + * l + * a + * t + * i + * o + * n + * + * na + * m + * e + * s + * , + * + * su + * p + * p + * r + * e + * s + * s + * + * sy + * s + * t + * e + * m + * + * ca + * t + * a + * l + * o + * g + * s + * + * un + * l + * e + * s + * s + * + * th + * e + * + * in + * p + * u + * t + * - + * s + * o + * - + * f + * a + * r + * + * be + * g + * i + * n + * s + * + * wi + * t + * h + * + * "p + * g + * _ + * " + * . + * + * Th + * i + * s + * + * is + * + * a + * co + * m + * p + * r + * o + * m + * i + * s + * e + * + * be + * t + * w + * e + * e + * n + * + * no + * t + * + * of + * f + * e + * r + * i + * n + * g + * + * sy + * s + * t + * e + * m + * + * ca + * t + * a + * l + * o + * g + * s + * + * fo + * r + * + * co + * m + * p + * l + * e + * t + * i + * o + * n + * + * at + * + * al + * l + * , + * + * an + * d + * + * ha + * v + * i + * n + * g + * + * th + * e + * m + * + * sw + * a + * m + * p + * + * th + * e + * + * re + * s + * u + * l + * t + * + * wh + * e + * n + * + * th + * e + * + * in + * p + * u + * t + * + * is + * + * ju + * s + * t + * + * "p + * " + * . + * */ + if (strcmp(completion_squery->catname, + "pg_catalog.pg_class c") == 0 && + strncmp(text, "pg_", 3) !=0) + { + appendPQExpBuffer(&query_buffer, + " AND c.relnamespace <> (SELECT oid FROM" + " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); + } + + /* + * + * Ad + * d + * + * in + * + * ma + * t + * c + * h + * i + * n + * g + * + * sc + * h + * e + * m + * a + * + * na + * m + * e + * s + * , + * + * bu + * t + * + * on + * l + * y + * + * if + * + * th + * e + * r + * e + * + * is + * + * mo + * r + * e + * + * th + * a + * n + * + * on + * e + * + * po + * t + * e + * n + * t + * i + * a + * l + * + * ma + * t + * c + * h + * + * am + * o + * n + * g + * + * sc + * h + * e + * m + * a + * + * na + * m + * e + * s + * . + * */ + appendPQExpBuffer(&query_buffer, "\nUNION\n" + "SELECT pg_catalog.quote_ident(n.nspname) || '.' " + "FROM pg_catalog.pg_namespace n " + "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", + string_length, e_text); + appendPQExpBuffer(&query_buffer, + " AND (SELECT pg_catalog.count(*)" + " FROM pg_catalog.pg_namespace" + " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" + " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", + string_length, e_text); + + /* + * + * Ad + * d + * + * in + * + * ma + * t + * c + * h + * i + * n + * g + * + * qu + * a + * l + * i + * f + * i + * e + * d + * + * na + * m + * e + * s + * , + * + * bu + * t + * + * on + * l + * y + * + * if + * + * th + * e + * r + * e + * + * is + * + * ex + * a + * c + * t + * l + * y + * + * on + * e + * + * sc + * h + * e + * m + * a + * + * ma + * t + * c + * h + * i + * n + * g + * + * th + * e + * + * in + * p + * u + * t + * - + * s + * o + * - + * f + * a + * r + * . + * */ + appendPQExpBuffer(&query_buffer, "\nUNION\n" + "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " + "FROM %s, pg_catalog.pg_namespace n " + "WHERE %s = n.oid AND ", + qualresult, + completion_squery->catname, + completion_squery->namespace); + if (completion_squery->selcondition) + appendPQExpBuffer(&query_buffer, "%s AND ", + completion_squery->selcondition); + appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", + qualresult, + string_length, e_text); + + /* + * + * Th + * i + * s + * + * co + * n + * d + * i + * t + * i + * o + * n + * + * ex + * p + * l + * o + * i + * t + * s + * + * th + * e + * + * si + * n + * g + * l + * e + * - + * m + * a + * t + * c + * h + * i + * n + * g + * - + * s + * c + * h + * e + * m + * a + * + * ru + * l + * e + * + * to + * + * sp + * e + * e + * d + * + * up + * + * th + * e + * + * qu + * e + * r + * y + * */ + appendPQExpBuffer(&query_buffer, + " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" + " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", + string_length, e_text); + appendPQExpBuffer(&query_buffer, + " AND (SELECT pg_catalog.count(*)" + " FROM pg_catalog.pg_namespace" + " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" + " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", + string_length, e_text); + + /* + * I + * f + * + * an + * + * ad + * d + * o + * n + * + * qu + * e + * r + * y + * + * wa + * s + * + * pr + * o + * v + * i + * d + * e + * d + * , + * + * us + * e + * + * it + * + */ + if (completion_charp) + appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); + } + else + { + /* + * c + * o + * m + * p + * l + * e + * t + * i + * o + * n + * _ + * c + * h + * a + * r + * p + * + * is + * + * an + * + * sp + * r + * i + * n + * t + * f + * - + * s + * t + * y + * l + * e + * + * fo + * r + * m + * a + * t + * + * st + * r + * i + * n + * g + * + */ + appendPQExpBuffer(&query_buffer, completion_charp, + string_length, e_text, e_info_charp); + } + + /* + * L + * i + * m + * i + * t + * + * th + * e + * + * nu + * m + * b + * e + * r + * + * of + * + * re + * c + * o + * r + * d + * s + * + * in + * + * th + * e + * + * re + * s + * u + * l + * t + * + */ + appendPQExpBuffer(&query_buffer, "\nLIMIT %d", + completion_max_records); + + result = exec_query(query_buffer.data); + + termPQExpBuffer(&query_buffer); + free(e_text); + if (e_info_charp) + free(e_info_charp); + } + + /* + * F + * i + * n + * d + * + * so + * m + * e + * t + * h + * i + * n + * g + * + * th + * a + * t + * + * ma + * t + * c + * h + * e + * s + * + */ + if (result && PQresultStatus(result) == PGRES_TUPLES_OK) + { + const char *item; + + while (list_index < PQntuples(result) && + (item = PQgetvalue(result, list_index++, 0))) + if (pg_strncasecmp(text, item, string_length) == 0) + return pg_strdup(item); + } + + /* + * I + * f + * + * no + * t + * h + * i + * n + * g + * + * ma + * t + * c + * h + * e + * s + * , + * + * fr + * e + * e + * + * th + * e + * + * db + * + * st + * r + * u + * c + * t + * u + * r + * e + * + * an + * d + * + * re + * t + * u + * r + * n + * + * nu + * l + * l + * + */ + PQclear(result); + result = NULL; + return NULL; + } /* This function returns in order one of a fixed, NULL pointer terminated list of strings (if matching). This can be used if there are only a fixed number SQL words that can appear at certain spot. */ -static char * -complete_from_list(const char *text, int state) -{ - static int string_length, - list_index, - matches; - static bool casesensitive; - const char *item; - - /* need to have a list */ - psql_assert(completion_charpp); - - /* Initialization */ - if (state == 0) - { - list_index = 0; - string_length = strlen(text); - casesensitive = true; - matches = 0; - } - - while ((item = completion_charpp[list_index++])) - { - /* First pass is case sensitive */ - if (casesensitive && strncmp(text, item, string_length) == 0) - { - matches++; - return pg_strdup(item); - } - - /* Second pass is case insensitive, don't bother counting matches */ - if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0) - return pg_strdup(item); - } - - /* - * No matches found. If we're not case insensitive already, lets - * switch to being case insensitive and try again - */ - if (casesensitive && matches == 0) - { - casesensitive = false; - list_index = 0; - state++; - return (complete_from_list(text, state)); - } - - /* If no more matches, return null. */ - return NULL; -} + static char * + complete_from_list(const char *text, int state) + { + static int string_length, + list_index, + matches; + static bool casesensitive; + const char *item; + + /* + * n + * e + * e + * d + * + * to + * + * ha + * v + * e + * + * a + * li + * s + * t + * + */ + psql_assert(completion_charpp); + + /* + * I + * n + * i + * t + * i + * a + * l + * i + * z + * a + * t + * i + * o + * n + * + */ + if (state == 0) + { + list_index = 0; + string_length = strlen(text); + casesensitive = true; + matches = 0; + } + + while ((item = completion_charpp[list_index++])) + { + /* + * F + * i + * r + * s + * t + * + * pa + * s + * s + * + * is + * + * ca + * s + * e + * + * se + * n + * s + * i + * t + * i + * v + * e + * + */ + if (casesensitive && strncmp(text, item, string_length) == 0) + { + matches++; + return pg_strdup(item); + } + + /* + * S + * e + * c + * o + * n + * d + * + * pa + * s + * s + * + * is + * + * ca + * s + * e + * + * in + * s + * e + * n + * s + * i + * t + * i + * v + * e + * , + * + * do + * n + * ' + * t + * + * bo + * t + * h + * e + * r + * + * co + * u + * n + * t + * i + * n + * g + * + * ma + * t + * c + * h + * e + * s + * + */ + if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0) + return pg_strdup(item); + } + + /* + * + * No + * + * ma + * t + * c + * h + * e + * s + * + * fo + * u + * n + * d + * . + * + * If + * + * we + * ' + * r + * e + * + * no + * t + * + * ca + * s + * e + * + * in + * s + * e + * n + * s + * i + * t + * i + * v + * e + * + * al + * r + * e + * a + * d + * y + * , + * + * le + * t + * s + * + * sw + * i + * t + * c + * h + * + * to + * + * be + * i + * n + * g + * + * ca + * s + * e + * + * in + * s + * e + * n + * s + * i + * t + * i + * v + * e + * + * an + * d + * + * tr + * y + * + * ag + * a + * i + * n + * */ + if (casesensitive && matches == 0) + { + casesensitive = false; + list_index = 0; + state++; + return (complete_from_list(text, state)); + } + + /* + * I + * f + * + * no + * + * mo + * r + * e + * + * ma + * t + * c + * h + * e + * s + * , + * + * re + * t + * u + * r + * n + * + * nu + * l + * l + * . + * + */ + return NULL; + } /* This function returns one fixed string the first time even if it doesn't @@ -2121,18 +3654,19 @@ complete_from_list(const char *text, int state) will be overwritten. The string to be passed must be in completion_charp. */ -static char * -complete_from_const(const char *text, int state) -{ - (void) text; /* We don't care about what was entered - * already. */ + static char * + complete_from_const(const char *text, int state) + { + (void) text; /* We don't care about + * what was entered + * already. */ - psql_assert(completion_charp); - if (state == 0) - return pg_strdup(completion_charp); - else - return NULL; -} + psql_assert(completion_charp); + if (state == 0) + return pg_strdup(completion_charp); + else + return NULL; + } @@ -2143,28 +3677,28 @@ complete_from_const(const char *text, int state) * Execute a query and report any errors. This should be the preferred way of * talking to the database in this file. */ -static PGresult * -exec_query(const char *query) -{ - PGresult *result; + static PGresult * + exec_query(const char *query) + { + PGresult *result; - if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK) - return NULL; + if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK) + return NULL; - result = PQexec(pset.db, query); + result = PQexec(pset.db, query); - if (result != NULL && PQresultStatus(result) != PGRES_TUPLES_OK) - { + if (result != NULL && PQresultStatus(result) != PGRES_TUPLES_OK) + { #if 0 - psql_error("tab completion: %s failed - %s\n", - query, PQresStatus(PQresultStatus(result))); + psql_error("tab completion: %s failed - %s\n", + query, PQresStatus(PQresultStatus(result))); #endif - PQclear(result); - result = NULL; - } + PQclear(result); + result = NULL; + } - return result; -} + return result; + } @@ -2173,61 +3707,344 @@ exec_query(const char *query) * skip that many words; e.g. skip=1 finds the word before the * previous one. Return value is NULL or a malloc'ed string. */ -static char * -previous_word(int point, int skip) -{ - int i, - start = 0, - end = -1, - 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; - } - - /* - * If no end found we return null, because there is no word before - * the point - */ - 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 */ - s = pg_malloc(end - start + 2); - - strncpy(s, &rl_line_buffer[start], end - start + 1); - s[end - start + 1] = '\0'; - - return s; -} + static char * + previous_word(int point, int skip) + { + int i, + start = 0, + end = -1, + inquotes = 0; + char *s; + + while (skip-- >= 0) + { + /* + * f + * i + * r + * s + * t + * + * we + * + * lo + * o + * k + * + * fo + * r + * + * a + * sp + * a + * c + * e + * + * be + * f + * o + * r + * e + * + * th + * e + * + * cu + * r + * r + * e + * n + * t + * + * wo + * r + * d + * + */ + for (i = point; i >= 0; i--) + if (rl_line_buffer[i] == ' ') + break; + + /* + * n + * o + * w + * + * fi + * n + * d + * + * th + * e + * + * fi + * r + * s + * t + * + * no + * n + * - + * s + * p + * a + * c + * e + * + * wh + * i + * c + * h + * + * th + * e + * n + * + * co + * n + * s + * t + * i + * t + * u + * t + * e + * s + * + * th + * e + * + * en + * d + * + */ + for (; i >= 0; i--) + if (rl_line_buffer[i] != ' ') + { + end = i; + break; + } + + /* + * + * If + * + * no + * + * en + * d + * + * fo + * u + * n + * d + * + * we + * + * re + * t + * u + * r + * n + * + * nu + * l + * l + * , + * + * be + * c + * a + * u + * s + * e + * + * th + * e + * r + * e + * + * is + * + * no + * + * wo + * r + * d + * + * be + * f + * o + * r + * e + * + * th + * e + * + * po + * i + * n + * t + * */ + if (end == -1) + return NULL; + + /* + * + * Ot + * h + * e + * r + * w + * i + * s + * e + * + * we + * + * no + * w + * + * lo + * o + * k + * + * fo + * r + * + * th + * e + * + * st + * a + * r + * t + * . + * + * Th + * e + * + * st + * a + * r + * t + * + * is + * + * ei + * t + * h + * e + * r + * + * th + * e + * + * la + * s + * t + * + * ch + * a + * r + * a + * c + * t + * e + * r + * + * be + * f + * o + * r + * e + * + * an + * y + * + * sp + * a + * c + * e + * + * go + * i + * n + * g + * + * ba + * c + * k + * w + * a + * r + * d + * s + * + * fr + * o + * m + * + * th + * e + * + * en + * d + * , + * + * or + * + * it + * ' + * s + * + * si + * m + * p + * l + * y + * + * ch + * a + * r + * a + * c + * t + * e + * r + * + * 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; + } + + /* + * m + * a + * k + * e + * + * a + * co + * p + * y + * + */ + s = pg_malloc(end - start + 2); + + strncpy(s, &rl_line_buffer[start], end - start + 1); + s[end - start + 1] = '\0'; + + return s; + } @@ -2238,42 +4055,42 @@ previous_word(int point, int skip) * psql internal. Currently disabled because it is reported not to * cooperate with certain versions of readline. */ -static char * -quote_file_name(char *text, int match_type, char *quote_pointer) -{ - char *s; - size_t length; - - (void) quote_pointer; /* not used */ - - length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2); - s = pg_malloc(length); - s[0] = '\''; - strcpy(s + 1, text); - if (match_type == SINGLE_MATCH) - s[length - 2] = '\''; - s[length - 1] = '\0'; - return s; -} - - - -static char * -dequote_file_name(char *text, char quote_char) -{ - char *s; - size_t length; - - if (!quote_char) - return pg_strdup(text); - - length = strlen(text); - s = pg_malloc(length - 2 + 1); - strncpy(s, text +1, length - 2); - s[length] = '\0'; - - return s; -} + static char * + quote_file_name(char *text, int match_type, char *quote_pointer) + { + char *s; + size_t length; + + (void) quote_pointer; /* not used */ + + length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2); + s = pg_malloc(length); + s[0] = '\''; + strcpy(s + 1, text); + if (match_type == SINGLE_MATCH) + s[length - 2] = '\''; + s[length - 1] = '\0'; + return s; + } + + + + static char * + dequote_file_name(char *text, char quote_char) + { + char *s; + size_t length; + + if (!quote_char) + return pg_strdup(text); + + length = strlen(text); + s = pg_malloc(length - 2 + 1); + strncpy(s, text +1, length - 2); + s[length] = '\0'; + + return s; + } #endif /* 0 */ #endif /* USE_READLINE */ |