psql: Generic tab completion support for enum and bool GUCs.

Author: Pavel Stehule
Reviewed-By: Andres Freund
Discussion: 5594FE7A.5050205@iki.fi
This commit is contained in:
Andres Freund 2015-09-08 20:57:35 +02:00
parent 043113e798
commit 3ae16798f0
1 changed files with 97 additions and 35 deletions

View File

@ -759,6 +759,15 @@ static const SchemaQuery Query_for_list_of_matviews = {
" (SELECT polrelid FROM pg_catalog.pg_policy "\
" WHERE pg_catalog.quote_ident(polname)='%s')"
#define Query_for_enum \
" SELECT name FROM ( "\
" SELECT pg_catalog.quote_ident(pg_catalog.unnest(enumvals)) AS name "\
" FROM pg_catalog.pg_settings "\
" WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\
" UNION ALL " \
" SELECT 'DEFAULT' ) ss "\
" WHERE pg_catalog.substring(name,1,%%d)='%%s'"
/*
* This is a list of all "things" in Pgsql, which can show up after CREATE or
* DROP; and there is also a query to get a list of them.
@ -845,10 +854,13 @@ static char **complete_from_variables(const char *text,
static char *complete_from_files(const char *text, int state);
static char *pg_strdup_keyword_case(const char *s, const char *ref);
static char *escape_string(const char *text);
static PGresult *exec_query(const char *query);
static void get_previous_words(int point, char **previous_words, int nwords);
static char *get_guctype(const char *varname);
#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);
@ -3684,6 +3696,7 @@ psql_completion(const char *text, int start, int end)
else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
(pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
{
/* special cased code for individual GUCs */
if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
{
static const char *const my_list[] =
@ -3694,20 +3707,6 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_LIST(my_list);
}
else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
{
static const char *const my_list[] =
{"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
COMPLETE_WITH_LIST(my_list);
}
else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
{
static const char *const my_list[] =
{"ON", "OFF", "DEFAULT", NULL};
COMPLETE_WITH_LIST(my_list);
}
else if (pg_strcasecmp(prev2_wd, "search_path") == 0)
{
COMPLETE_WITH_QUERY(Query_for_list_of_schemas
@ -3717,10 +3716,34 @@ psql_completion(const char *text, int start, int end)
}
else
{
static const char *const my_list[] =
{"DEFAULT", NULL};
/* generic, type based, GUC support */
COMPLETE_WITH_LIST(my_list);
char *guctype = get_guctype(prev2_wd);
if (guctype && strcmp(guctype, "enum") == 0)
{
char querybuf[1024];
snprintf(querybuf, 1024, Query_for_enum, prev2_wd);
COMPLETE_WITH_QUERY(querybuf);
}
else if (guctype && strcmp(guctype, "bool") == 0)
{
static const char *const my_list[] =
{"on", "off", "true", "false", "yes", "no", "1", "0", "DEFAULT", NULL};
COMPLETE_WITH_LIST(my_list);
}
else
{
static const char *const my_list[] =
{"DEFAULT", NULL};
COMPLETE_WITH_LIST(my_list);
}
if (guctype)
free(guctype);
}
}
@ -4263,30 +4286,15 @@ _complete_from_query(int is_schema_query, const char *text, int state)
result = NULL;
/* Set up suitably-escaped copies of textual inputs */
e_text = pg_malloc(string_length * 2 + 1);
PQescapeString(e_text, text, string_length);
e_text = escape_string(text);
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);
}
e_info_charp = escape_string(completion_info_charp);
else
e_info_charp = NULL;
if (completion_info_charp2)
{
size_t charp_len;
charp_len = strlen(completion_info_charp2);
e_info_charp2 = pg_malloc(charp_len * 2 + 1);
PQescapeString(e_info_charp2, completion_info_charp2,
charp_len);
}
e_info_charp2 = escape_string(completion_info_charp2);
else
e_info_charp2 = NULL;
@ -4677,6 +4685,26 @@ pg_strdup_keyword_case(const char *s, const char *ref)
}
/*
* escape_string - Escape argument for use as string literal.
*
* The returned value has to be freed.
*/
static char *
escape_string(const char *text)
{
size_t text_length;
char *result;
text_length = strlen(text);
result = pg_malloc(text_length * 2 + 1);
PQescapeStringConn(pset.db, result, text, text_length, NULL);
return result;
}
/*
* Execute a query and report any errors. This should be the preferred way of
* talking to the database in this file.
@ -4790,6 +4818,40 @@ get_previous_words(int point, char **previous_words, int nwords)
}
}
/*
* Look up the type for the GUC variable with the passed name.
*
* Returns NULL if the variable is unknown. Otherwise the returned string,
* containing the type, has to be freed.
*/
static char *
get_guctype(const char *varname)
{
PQExpBufferData query_buffer;
char *e_varname;
PGresult *result;
char *guctype = NULL;
e_varname = escape_string(varname);
initPQExpBuffer(&query_buffer);
appendPQExpBuffer(&query_buffer,
"SELECT vartype FROM pg_catalog.pg_settings "
"WHERE pg_catalog.lower(name) = pg_catalog.lower('%s')",
e_varname);
result = exec_query(query_buffer.data);
termPQExpBuffer(&query_buffer);
free(e_varname);
if (PQresultStatus(result) == PGRES_TUPLES_OK && PQntuples(result) > 0)
guctype = pg_strdup(PQgetvalue(result, 0, 0));
PQclear(result);
return guctype;
}
#ifdef NOT_USED
/*