/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright (c) 2000-2005, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.138 2005/10/15 02:49:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.161 2007/04/08 00:26:34 momjian Exp $
*/
/*----------------------------------------------------------------------
#include "common.h"
#include "settings.h"
-
#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
#define filename_completion_function rl_filename_completion_function
#else
*/
#define COMPLETE_WITH_QUERY(query) \
do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
-#define COMPLETE_WITH_SCHEMA_QUERY(query,addon) \
+#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
do { completion_squery = &(query); completion_charp = addon; matches = completion_matches(text, complete_from_schema_query); } while(0)
#define COMPLETE_WITH_LIST(list) \
do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
#define COMPLETE_WITH_CONST(string) \
do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0)
-#define COMPLETE_WITH_ATTR(table) \
-do {completion_charp = Query_for_list_of_attributes; completion_info_charp = table; matches = completion_matches(text, complete_from_query); } while(0)
+#define COMPLETE_WITH_ATTR(table, addon) \
+do {completion_charp = Query_for_list_of_attributes addon; completion_info_charp = table; matches = completion_matches(text, complete_from_query); } while(0)
/*
* Assembly instructions for schema queries
" UNION ALL SELECT 'all') ss "\
" WHERE substring(name,1,%d)='%s'"
+/*
+ * Note: As of Pg 8.2, we no longer use relkind 's', but we keep it here
+ * for compatibility with older servers
+ */
#define Query_for_list_of_system_relations \
"SELECT pg_catalog.quote_ident(relname) "\
" FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
/* Forward declaration of functions */
static char **psql_completion(char *text, int start, int end);
static char *create_command_generator(const char *text, int state);
+static char *drop_command_generator(const char *text, int state);
static char *complete_from_query(const char *text, int state);
static char *complete_from_schema_query(const char *text, int state);
static char *_complete_from_query(int is_schema_query,
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);
*prev5_wd;
static const char *const sql_commands[] = {
- "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
- "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE FROM", "DROP", "END", "EXECUTE",
- "EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
- "PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
- "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
+ "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
+ "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+ "DELETE FROM", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH", "GRANT",
+ "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
+ "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
+ "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN",
+ "UPDATE", "VACUUM", NULL
};
static const char *const backslash_commands[] = {
"\\e", "\\echo", "\\encoding",
"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
- "\\o", "\\p", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T",
+ "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
+ "\\set", "\\t", "\\T",
"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
};
else if (!prev_wd)
COMPLETE_WITH_LIST(sql_commands);
-/* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */
- /* complete with something you can create or drop */
- else if (pg_strcasecmp(prev_wd, "CREATE") == 0 ||
- (pg_strcasecmp(prev_wd, "DROP") == 0 &&
- pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
- pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
- pg_strcasecmp(prev3_wd, "GROUP") != 0))
+/* CREATE */
+ /* complete with something you can create */
+ else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
matches = completion_matches(text, create_command_generator);
+/* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */
+ /* complete with something you can drop */
+ else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
+ pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
+ pg_strcasecmp(prev3_wd, "GROUP") != 0)
+ matches = completion_matches(text, drop_command_generator);
+
/* ALTER */
/*
/* ALTER LANGUAGE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
- COMPLETE_WITH_CONST("RENAME TO");
+ {
+ static const char *const list_ALTERLANGUAGE[] =
+ {"OWNER TO", "RENAME TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
+ }
/* ALTER USER,ROLE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
(pg_strcasecmp(prev_wd, "ALTER") == 0 ||
pg_strcasecmp(prev_wd, "RENAME") == 0))
- COMPLETE_WITH_ATTR(prev2_wd);
+ COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
+
+ /*
+ * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
+ * columns
+ */
+ else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+ (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
+ pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
+ pg_strcasecmp(prev_wd, "COLUMN") == 0)
+ COMPLETE_WITH_ATTR(prev3_wd, "");
/* ALTER TABLE xxx RENAME yyy */
else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "TO") != 0)
COMPLETE_WITH_CONST("TO");
+ /* ALTER TABLE xxx RENAME COLUMN yyy */
+ else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
+ pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") != 0)
+ COMPLETE_WITH_CONST("TO");
+
/* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "DROP") == 0)
else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
pg_strcasecmp(prev2_wd, "DROP") == 0 &&
pg_strcasecmp(prev_wd, "COLUMN") == 0)
- COMPLETE_WITH_ATTR(prev3_wd);
+ COMPLETE_WITH_ATTR(prev3_wd, "");
/* ALTER TABLE ALTER [COLUMN] <foo> */
else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
COMPLETE_WITH_LIST(list_COLUMNALTER);
}
- else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
- pg_strcasecmp(prev_wd, "CLUSTER") == 0)
+ else if (pg_strcasecmp(prev3_wd, "TABLE") == 0)
COMPLETE_WITH_CONST("ON");
else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
- pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
pg_strcasecmp(prev_wd, "ON") == 0)
{
completion_info_charp = prev3_wd;
/*
* If the previous word is CLUSTER and not without produce list of
- * indexes.
+ * tables
*/
else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
- /* If we have CLUSTER <sth>, then add "ON" */
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ /* If we have CLUSTER <sth>, then add "USING" */
else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
- pg_strcasecmp(prev_wd, "ON") != 0)
- COMPLETE_WITH_CONST("ON");
+ pg_strcasecmp(prev_wd, "ON") != 0) {
+ COMPLETE_WITH_CONST("USING");
+ }
/*
- * If we have CLUSTER <sth> ON, then add the correct tablename as well.
+ * If we have CLUSTER <sth> ORDER BY, then add the index as well.
*/
else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
- pg_strcasecmp(prev_wd, "ON") == 0)
+ pg_strcasecmp(prev_wd, "USING") == 0)
{
completion_info_charp = prev2_wd;
- COMPLETE_WITH_QUERY(Query_for_table_owning_index);
+ COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
/* COMMENT */
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",
+ "TABLESPACE", "ROLE", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
*/
else if (pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
pg_strcasecmp(prev2_wd, "ON") == 0)
- COMPLETE_WITH_ATTR(prev_wd);
+ {
+ 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_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", "RTREE", "HASH", "GIST", NULL};
+ {"BTREE", "HASH", "GIST", NULL};
COMPLETE_WITH_LIST(index_mth);
}
/* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
pg_strcasecmp(prev2_wd, "ON") == 0 &&
- (toupper((unsigned char) prev_wd[4]) == 'T' ||
- toupper((unsigned char) prev_wd[5]) == 'T'))
+ (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
+ pg_toupper((unsigned char) prev_wd[5]) == 'T'))
COMPLETE_WITH_CONST("TO");
/* Complete "AS ON <sth> TO" with a table name */
else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
prev_wd[strlen(prev_wd) - 1] == ')'))
{
- static const char *const list_DROPCR[] =
- {"CASCADE", "RESTRICT", NULL};
+ 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
+ {
+ static const char *const list_DROPCR[] =
+ {"CASCADE", "RESTRICT", NULL};
+
+ COMPLETE_WITH_LIST(list_DROPCR);
+ }
+ }
+ else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
+ pg_strcasecmp(prev_wd, "(") == 0)
+ {
+ 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(prev2_wd));
- COMPLETE_WITH_LIST(list_DROPCR);
+ sprintf(tmp_buf, func_args_query, prev2_wd);
+ COMPLETE_WITH_QUERY(tmp_buf);
+ free(tmp_buf);
}
+ /* DROP OWNED BY */
+ else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev_wd, "OWNED") == 0)
+ COMPLETE_WITH_CONST("BY");
+ else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
+ pg_strcasecmp(prev_wd, "BY") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+
+
/* EXPLAIN */
{
static const char *const list_privileg[] =
{"SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "REFERENCES",
- "TRIGGER", "CREATE", "TEMPORARY", "EXECUTE", "USAGE", "ALL", NULL};
+ "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
+ "ALL", NULL};
COMPLETE_WITH_LIST(list_privileg);
}
else if (rl_line_buffer[start - 1] == '(' &&
pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
pg_strcasecmp(prev2_wd, "INTO") == 0)
- COMPLETE_WITH_ATTR(prev_wd);
+ COMPLETE_WITH_ATTR(prev_wd, "");
/*
* Complete INSERT INTO <table> with "VALUES" or "SELECT" or "DEFAULT
/* LOCK */
/* Complete LOCK [TABLE] with a list of tables */
- else if (pg_strcasecmp(prev_wd, "LOCK") == 0 ||
- (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
- pg_strcasecmp(prev2_wd, "LOCK") == 0))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
+ " UNION SELECT 'TABLE'");
+ else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev2_wd, "LOCK") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
/* For the following, handle the case of a single table only for now */
else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'");
-/* OWNER TO - complete with available roles*/
+/* OWNER TO - complete with available roles */
else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
pg_strcasecmp(prev_wd, "TO") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
pg_strcasecmp(prev_wd, "BY") == 0)
- COMPLETE_WITH_ATTR(prev3_wd);
+ COMPLETE_WITH_ATTR(prev3_wd, "");
/* PREPARE xx AS */
else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
COMPLETE_WITH_LIST(list_PREPARE);
}
+/* REASSIGN OWNED BY xxx TO yyy */
+ else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
+ COMPLETE_WITH_CONST("OWNED");
+ else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
+ pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
+ COMPLETE_WITH_CONST("BY");
+ else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
+ pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
+ pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+ else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
+ pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
+ pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
+ COMPLETE_WITH_CONST("TO");
+ else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
+ pg_strcasecmp(prev3_wd, "BY") == 0 &&
+ pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
+ pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* REINDEX */
else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
else if (pg_strcasecmp(prev3_wd, "SET") == 0
&& pg_strcasecmp(prev2_wd, "SESSION") == 0
&& pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
- COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
+ /* Complete RESET SESSION with AUTHORIZATION */
+ else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
+ pg_strcasecmp(prev_wd, "SESSION") == 0)
+ COMPLETE_WITH_CONST("AUTHORIZATION");
/* Complete SET <var> with "TO" */
else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
* make a list of attributes.
*/
else if (pg_strcasecmp(prev_wd, "SET") == 0)
- COMPLETE_WITH_ATTR(prev2_wd);
+ COMPLETE_WITH_ATTR(prev2_wd, "");
/* UPDATE xx SET yy = */
else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
/* WHERE */
/* Simple case of the word before the where being the table name */
else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
- COMPLETE_WITH_ATTR(prev2_wd);
+ COMPLETE_WITH_ATTR(prev2_wd, "");
/* ... FROM ... */
/* TODO: also include SRF ? */
COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
COMPLETE_WITH_LIST(sql_commands);
+ else if (strcmp(prev_wd, "\\password") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (strcmp(prev_wd, "\\pset") == 0)
{
static const char *const my_list[] =
matches = completion_matches(text, filename_completion_function);
- /*
- *
- * 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
- * .
- * */
+ /*
+ * 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.
+ */
else
{
int i;
}
}
- /*
- *
- * 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("");
+ /*
+ * 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("");
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
- rl_completion_append_character = '\0';
+ rl_completion_append_character = '\0';
#endif
- }
-
- /*
- * 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;
- }
+ }
+
+ /* 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;
+}
something of that sort.
*/
-/* This one gives you one from a list of things you can put after CREATE or DROP
+/* This one gives you one from a list of things you can put after CREATE
as defined above.
*/
- 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;
- }
+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;
+}
+
+/*
+ * This function gives you a list of things you can put after a DROP command.
+ * Very similar to create_command_generator, but has an additional entry for
+ * OWNED BY. (We do it this way in order not to duplicate the
+ * words_after_create list.)
+ */
+static char *
+drop_command_generator(const char *text, int state)
+{
+ static int list_index,
+ string_length;
+ const char *name;
+
+ if (state == 0)
+ {
+ /* If this is the first time for this completion, init some values */
+ list_index = 0;
+ string_length = strlen(text);
+
+ /*
+ * DROP can be followed by "OWNED BY", which is not found in the list
+ * for CREATE matches, so make it the first state. (We do not make it
+ * the last state because it would be more difficult to detect when we
+ * have to return NULL instead.)
+ *
+ * Make sure we advance to the next state.
+ */
+ list_index++;
+ if (pg_strncasecmp("OWNED", text, string_length) == 0)
+ return pg_strdup("OWNED");
+ }
+
+ /*
+ * In subsequent attempts, try to complete with the same items we use for
+ * CREATE
+ */
+ while ((name = words_after_create[list_index++ - 1].name))
+ {
+ if (pg_strncasecmp(name, text, string_length) == 0)
+ return pg_strdup(name);
+ }
+
+ /* if nothing matches, return NULL */
+ 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
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
- *
- * 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;
- }
+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;
+}
/* 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;
-
- /*
- * 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;
- }
+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;
+}
/* This function returns one fixed string the first time even if it doesn't
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;
+}
* 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;
+}
* 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)
- {
- /*
- * 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;
- }
+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);
+ strlcpy(s, &rl_line_buffer[start], end - start + 2);
+ 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
* 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);
+ strlcpy(s, text +1, length - 2 + 1);
+
+ return s;
+}
#endif /* 0 */
#endif /* USE_READLINE */