Hi, On 2015-09-02 22:58:21 +0200, Pavel Stehule wrote: > > Won't that mean that enum variables don't complete to default anymore?
> no, it does > > #define Query_for_enum \ > " SELECT name FROM ( "\ > " SELECT unnest(enumvals) AS name "\ > " FROM pg_catalog.pg_settings "\ > " WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\ > " UNION SELECT 'DEFAULT' ) ss "\ > **************** > " WHERE pg_catalog.substring(name,1,%%d)='%%s'" Ah. I've added a quote_ident() around unnest() - otherwise the auto-completions for default_transaction_isolation will mostly be wrong due to spaces. I also renamed get_vartype into get_guctype, changed the comment as I found the reference to the pg type system confusing, and more importantly made it not return a static buffer. The spellings for boolean values were a relatively small subset of what the backend accepts - it's now on,off,true,false,yes,no,1,0. I'm not sure whether that's a good idea. Comments? Andres
>From 279cdbdaed568a9dd95e18b4bb5c3098a0791008 Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Mon, 7 Sep 2015 21:28:10 +0200 Subject: [PATCH] psql: Generic tab completion support for enum and bool GUCs. Author: Pavel Stehule Reviewed-By: Andres Freund Discussion: 5594fe7a.5050...@iki.fi --- src/bin/psql/tab-complete.c | 132 ++++++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 35 deletions(-) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 9303f6a..85207cc 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -757,6 +757,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. @@ -843,10 +852,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); @@ -3594,6 +3606,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[] = @@ -3604,20 +3617,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 @@ -3627,10 +3626,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); } } @@ -4173,30 +4196,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; @@ -4588,6 +4596,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. */ @@ -4700,6 +4728,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 /* -- 2.5.0.400.gff86faf
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers