static char *complete_from_schema_query(const char *text, int state);
static char *_complete_from_query(int is_schema_query,
const char *text, int state);
-static char *complete_from_const(const char *text, int state);
static char *complete_from_list(const char *text, int state);
+static char *complete_from_const(const char *text, int state);
+static char **complete_from_variables(char *text,
+ const char *prefix, const char *suffix);
static PGresult *exec_query(const char *query);
static char *previous_word(int point, int skip);
-#if 0
+#ifdef NOT_USED
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
static char *dequote_file_name(char *text, char quote_char);
#endif
-/* Initialize the readline library for our purposes. */
+/*
+ * Initialize the readline library for our purposes.
+ */
void
initialize_readline(void)
{
}
-/* The completion function. Acc. to readline spec this gets passed the text
- entered to far and its start and end in the readline buffer. The return value
- is some partially obscure list format that can be generated by the readline
- libraries completion_matches() function, so we don't have to worry about it.
-*/
+/*
+ * The completion function.
+ *
+ * According to readline spec this gets passed the text entered so far and its
+ * start and end positions in the readline buffer. The return value is some
+ * partially obscure list format that can be generated by readline's
+ * completion_matches() function, so we don't have to worry about it.
+ */
static char **
psql_completion(char *text, int start, int end)
{
pg_strcasecmp(prev_wd, "WRAPPER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
-/* GRANT && REVOKE*/
+/* GRANT && REVOKE */
/* Complete GRANT/REVOKE with a list of privileges */
else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
pg_strcasecmp(prev_wd, "REVOKE") == 0)
pg_strcasecmp(prev3_wd, "\\copy") != 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
-
/* Backslash commands */
/* TODO: \dc \dd \dl */
else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
COMPLETE_WITH_LIST(my_list);
}
+ else if (strcmp(prev_wd, "\\set") == 0)
+ {
+ matches = complete_from_variables(text, "", "");
+ }
else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (strcmp(prev_wd, "\\cd") == 0 ||
)
matches = completion_matches(text, filename_completion_function);
+/* Variable interpolation */
+ else if (text[0] == ':' && text[1] != ':')
+ {
+ if (text[1] == '\'')
+ matches = complete_from_variables(text, ":'", "'");
+ else if (text[1] == '"')
+ matches = complete_from_variables(text, ":\"", "\"");
+ else
+ matches = complete_from_variables(text, ":", "");
+ }
/*
* Finally, we look through the list of "things", such as TABLE, INDEX and
}
+/*
+ * GENERATOR FUNCTIONS
+ *
+ * These functions do all the actual work of completing the input. They get
+ * passed the text so far and the count how many times they have been called
+ * so far with the same text.
+ * If you read the above carefully, you'll see that these don't get called
+ * directly but through the readline interface.
+ * The return value is expected to be the full completion of the text, going
+ * through a list each time, or NULL if there are no more matches. The string
+ * will be free()'d by readline, so you must run it through strdup() or
+ * something of that sort.
+ */
-/* GENERATOR FUNCTIONS
-
- These functions do all the actual work of completing the input. They get
- passed the text so far and the count how many times they have been called so
- far with the same text.
- If you read the above carefully, you'll see that these don't get called
- directly but through the readline interface.
- The return value is expected to be the full completion of the text, going
- through a list each time, or NULL if there are no more matches. The string
- will be free()'d by readline, so you must run it through strdup() or
- something of that sort.
-*/
-
-/* This one gives you one from a list of things you can put after CREATE
- as defined above.
-*/
+/*
+ * 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)
{
/* find something that matches */
while ((name = words_after_create[list_index++].name))
{
- if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow)
+ if ((pg_strncasecmp(name, text, string_length) == 0) &&
+ !words_after_create[list_index - 1].noshow)
return pg_strdup(name);
}
/* if nothing matches, return NULL */
}
-/* This creates a list of matching things, according to a query pointed to
- by completion_charp.
- The query can be one of two kinds:
- - A simple query which must contain a %d and a %s, which will be replaced
- by the string length of the text and the text itself. The query may also
- have up to four more %s in it; the first two such will be replaced by the
- value of completion_info_charp, the next two by the value of
- completion_info_charp2.
- or:
- - A schema query used for completion of both schema and relation names;
- these are more complex and must contain in the following order:
- %d %s %d %s %d %s %s %d %s
- where %d is the string length of the text and %s the text itself.
-
- It is assumed that strings should be escaped to become SQL literals
- (that is, what is in the query is actually ... '%s' ...)
-
- See top of file for examples of both kinds of query.
-*/
-
+/*
+ * This creates a list of matching things, according to a query pointed to
+ * by completion_charp.
+ * The query can be one of two kinds:
+ *
+ * 1. A simple query which must contain a %d and a %s, which will be replaced
+ * by the string length of the text and the text itself. The query may also
+ * have up to four more %s in it; the first two such will be replaced by the
+ * value of completion_info_charp, the next two by the value of
+ * completion_info_charp2.
+ *
+ * 2. A schema query used for completion of both schema and relation names.
+ * These are more complex and must contain in the following order:
+ * %d %s %d %s %d %s %s %d %s
+ * where %d is the string length of the text and %s the text itself.
+ *
+ * It is assumed that strings should be escaped to become SQL literals
+ * (that is, what is in the query is actually ... '%s' ...)
+ *
+ * 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)
{
}
-/* 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.
-*/
+/*
+ * 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)
{
}
-/* This function returns one fixed string the first time even if it doesn't
- match what's there, and nothing the second time. This should be used if there
- is only one possibility that can appear at a certain spot, so misspellings
- will be overwritten.
- The string to be passed must be in completion_charp.
-*/
+/*
+ * This function returns one fixed string the first time even if it doesn't
+ * match what's there, and nothing the second time. This should be used if
+ * there is only one possibility that can appear at a certain spot, so
+ * misspellings will be overwritten. The string to be passed must be in
+ * completion_charp.
+ */
static char *
complete_from_const(const char *text, int state)
{
}
+/*
+ * This function supports completion with the name of a psql variable.
+ * The variable names can be prefixed and suffixed with additional text
+ * to support quoting usages.
+ */
+static char **
+complete_from_variables(char *text, const char *prefix, const char *suffix)
+{
+ char **matches;
+ int overhead = strlen(prefix) + strlen(suffix) + 1;
+ const char **varnames;
+ int nvars = 0;
+ int maxvars = 100;
+ int i;
+ struct _variable *ptr;
+
+ varnames = (const char **) pg_malloc((maxvars + 1) * sizeof(char *));
+
+ for (ptr = pset.vars->next; ptr; ptr = ptr->next)
+ {
+ char *buffer;
+
+ if (nvars >= maxvars)
+ {
+ maxvars *= 2;
+ varnames = (const char **) realloc(varnames,
+ (maxvars + 1) * sizeof(char *));
+ if (!varnames)
+ {
+ psql_error("out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ buffer = (char *) pg_malloc(strlen(ptr->name) + overhead);
+ sprintf(buffer, "%s%s%s", prefix, ptr->name, suffix);
+ varnames[nvars++] = buffer;
+ }
+
+ varnames[nvars] = NULL;
+ COMPLETE_WITH_LIST(varnames);
+
+ for (i = 0; i < nvars; i++)
+ free((void *) varnames[i]);
+ free(varnames);
+
+ return matches;
+}
+
/* HELPER FUNCTIONS */
if (PQresultStatus(result) != PGRES_TUPLES_OK)
{
-#if 0
+#ifdef NOT_USED
psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
PQerrorMessage(pset.db), query);
#endif
}
-
/*
* Return the word (space delimited) before point. Set skip > 0 to
* skip that many words; e.g. skip=1 finds the word before the
return s;
}
-#if 0
+#ifdef NOT_USED
/*
* Surround a string with single quotes. This works for both SQL and
return s;
}
-
-
static char *
dequote_file_name(char *text, char quote_char)
{
return s;
}
-#endif /* 0 */
+#endif /* NOT_USED */
#endif /* USE_READLINE */