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

Reply via email to