Re: PATCH: psql tab completion for SELECT - Mailing list pgsql-hackers

From Tom Lane
Subject Re: PATCH: psql tab completion for SELECT
Date
Msg-id 24314.1520190408@sss.pgh.pa.us
Whole thread Raw
In response to Re: PATCH: psql tab completion for SELECT  (Edmund Horner <ejrh00@gmail.com>)
Responses Re: PATCH: psql tab completion for SELECT  (Vik Fearing <vik.fearing@2ndquadrant.com>)
Re: PATCH: psql tab completion for SELECT  (Edmund Horner <ejrh00@gmail.com>)
Re: PATCH: psql tab completion for SELECT  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
Edmund Horner <ejrh00@gmail.com> writes:
> On 26 January 2018 at 13:44, Vik Fearing <vik.fearing@2ndquadrant.com> wrote:
>> On 01/26/2018 01:28 AM, Edmund Horner wrote:
>>> The patch mentioned attempts to put savepoints around the tab
>>> completion query where appropriate.

>> I am -1 on this idea.

> May I ask why?  It doesn't stop psql working against older versions,
> as it checks that the server supports savepoints.

I looked into this patch and was disappointed to discover that it had
only a very ad-hoc solution to the problem of version-dependent tab
completion queries.  We need something better --- in particular, the
recent prokind changes mean that there needs to be a way to make
SchemaQuery queries version-dependent.

So ... here is a modest proposal.  It invents a VersionedQuery concept
and also extends the SchemaQuery infrastructure to allow those to be
versioned.  I have not taken this nearly as far as it could be taken,
since it's mostly just proposing mechanism.  To illustrate the
VersionedQuery infrastructure, I fixed it so it wouldn't send
publication/subscription queries to pre-v10 servers, and to illustrate
the versioned SchemaQuery infrastructure, I fixed the prokind problems.

If people like this approach, I propose to commit this more or less
as-is.  The select-tab-completion patch would then need to be rewritten
to use this infrastructure, but I think that should be straightforward.
As a separate line of work, the infrastructure could be applied to fix
the pre-existing places where tab completion fails against old servers.
But that's probably work for v12 or beyond, unless somebody's really
motivated to do it right now.

            regards, tom lane

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 47909ed..9d0d45b 100644
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** extern char *filename_completion_functio
*** 71,85 ****
--- 71,112 ----
  PQExpBuffer tab_completion_query_buf = NULL;

  /*
+  * In some situations, the query to find out what names are available to
+  * complete with must vary depending on server version.  We handle this by
+  * storing a list of queries, each tagged with the minimum server version
+  * it will work for.  Each list must be stored in descending server version
+  * order, so that the first satisfactory query is the one to use.
+  *
+  * When the query string is otherwise constant, an array of VersionedQuery
+  * suffices.  Terminate the array with 0/NULL.  (If the search reaches that
+  * entry, we give up and do no completion.)
+  */
+ typedef struct VersionedQuery
+ {
+     int            min_server_version;
+     const char *query;
+ } VersionedQuery;
+
+ /*
   * This struct is used to define "schema queries", which are custom-built
   * to obtain possibly-schema-qualified names of database objects.  There is
   * enough similarity in the structure that we don't want to repeat it each
   * time.  So we put the components of each query into this struct and
   * assemble them with the common boilerplate in _complete_from_query().
+  *
+  * As with VersionedQuery, we can use an array of these if the query details
+  * must vary across versions.
   */
  typedef struct SchemaQuery
  {
      /*
+      * If not zero, minimum server version this struct applies to.  If not
+      * zero, there should be a following struct with a smaller minimum server
+      * version; use catname == NULL in the last entry if it should do nothing.
+      */
+     int            min_server_version;
+
+     /*
       * Name of catalog or catalogs to be queried, with alias, eg.
       * "pg_catalog.pg_class c".  Note that "pg_namespace n" will be added.
       */
*************** static const char *completion_charp;    /*
*** 133,138 ****
--- 160,166 ----
  static const char *const *completion_charpp;    /* to pass a list of strings */
  static const char *completion_info_charp;    /* to pass a second string */
  static const char *completion_info_charp2;    /* to pass a third string */
+ static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */
  static const SchemaQuery *completion_squery;    /* to pass a SchemaQuery */
  static bool completion_case_sensitive;    /* completion is case sensitive */

*************** static bool completion_case_sensitive;    /
*** 140,146 ****
--- 168,176 ----
   * A few macros to ease typing. You can use these to complete the given
   * string with
   * 1) The results from a query you pass it. (Perhaps one of those below?)
+  *      We support both simple and versioned queries.
   * 2) The results from a schema query you pass it.
+  *      We support both simple and versioned schema queries.
   * 3) The items from a null-pointer-terminated list (with or without
   *      case-sensitive comparison; see also COMPLETE_WITH_LISTn, below).
   * 4) A string constant.
*************** do { \
*** 153,158 ****
--- 183,194 ----
      matches = completion_matches(text, complete_from_query); \
  } while (0)

+ #define COMPLETE_WITH_VERSIONED_QUERY(query) \
+ do { \
+     completion_vquery = query; \
+     matches = completion_matches(text, complete_from_versioned_query); \
+ } while (0)
+
  #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
  do { \
      completion_squery = &(query); \
*************** do { \
*** 160,165 ****
--- 196,208 ----
      matches = completion_matches(text, complete_from_schema_query); \
  } while (0)

+ #define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \
+ do { \
+     completion_squery = query; \
+     completion_vquery = addon; \
+     matches = completion_matches(text, complete_from_versioned_schema_query); \
+ } while (0)
+
  #define COMPLETE_WITH_LIST_CS(list) \
  do { \
      completion_charpp = list; \
*************** do { \
*** 345,366 ****
   * Assembly instructions for schema queries
   */

! static const SchemaQuery Query_for_list_of_aggregates = {
!     /* catname */
!     "pg_catalog.pg_proc p",
!     /* selcondition */
!     "p.prokind = 'a'",
!     /* viscondition */
!     "pg_catalog.pg_function_is_visible(p.oid)",
!     /* namespace */
!     "p.pronamespace",
!     /* result */
!     "pg_catalog.quote_ident(p.proname)",
!     /* qualresult */
!     NULL
  };

  static const SchemaQuery Query_for_list_of_datatypes = {
      /* catname */
      "pg_catalog.pg_type t",
      /* selcondition --- ignore table rowtypes and array types */
--- 388,431 ----
   * Assembly instructions for schema queries
   */

! static const SchemaQuery Query_for_list_of_aggregates[] = {
!     {
!         /* min_server_version */
!         110000,
!         /* catname */
!         "pg_catalog.pg_proc p",
!         /* selcondition */
!         "p.prokind = 'a'",
!         /* viscondition */
!         "pg_catalog.pg_function_is_visible(p.oid)",
!         /* namespace */
!         "p.pronamespace",
!         /* result */
!         "pg_catalog.quote_ident(p.proname)",
!         /* qualresult */
!         NULL
!     },
!     {
!         /* min_server_version */
!         0,
!         /* catname */
!         "pg_catalog.pg_proc p",
!         /* selcondition */
!         "p.proisagg",
!         /* viscondition */
!         "pg_catalog.pg_function_is_visible(p.oid)",
!         /* namespace */
!         "p.pronamespace",
!         /* result */
!         "pg_catalog.quote_ident(p.proname)",
!         /* qualresult */
!         NULL
!     }
  };

  static const SchemaQuery Query_for_list_of_datatypes = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_type t",
      /* selcondition --- ignore table rowtypes and array types */
*************** static const SchemaQuery Query_for_list_
*** 379,384 ****
--- 444,451 ----
  };

  static const SchemaQuery Query_for_list_of_domains = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_type t",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 393,414 ****
      NULL
  };

! static const SchemaQuery Query_for_list_of_functions = {
!     /* catname */
!     "pg_catalog.pg_proc p",
!     /* selcondition */
!     "p.prokind IN ('f', 'w')",
!     /* viscondition */
!     "pg_catalog.pg_function_is_visible(p.oid)",
!     /* namespace */
!     "p.pronamespace",
!     /* result */
!     "pg_catalog.quote_ident(p.proname)",
!     /* qualresult */
!     NULL
  };

  static const SchemaQuery Query_for_list_of_indexes = {
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
--- 460,503 ----
      NULL
  };

! static const SchemaQuery Query_for_list_of_functions[] = {
!     {
!         /* min_server_version */
!         110000,
!         /* catname */
!         "pg_catalog.pg_proc p",
!         /* selcondition */
!         "p.prokind IN ('f', 'w')",
!         /* viscondition */
!         "pg_catalog.pg_function_is_visible(p.oid)",
!         /* namespace */
!         "p.pronamespace",
!         /* result */
!         "pg_catalog.quote_ident(p.proname)",
!         /* qualresult */
!         NULL
!     },
!     {
!         /* min_server_version */
!         0,
!         /* catname */
!         "pg_catalog.pg_proc p",
!         /* selcondition */
!         NULL,
!         /* viscondition */
!         "pg_catalog.pg_function_is_visible(p.oid)",
!         /* namespace */
!         "p.pronamespace",
!         /* result */
!         "pg_catalog.quote_ident(p.proname)",
!         /* qualresult */
!         NULL
!     }
  };

  static const SchemaQuery Query_for_list_of_indexes = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 424,445 ****
      NULL
  };

! static const SchemaQuery Query_for_list_of_procedures = {
!     /* catname */
!     "pg_catalog.pg_proc p",
!     /* selcondition */
!     "p.prokind = 'p'",
!     /* viscondition */
!     "pg_catalog.pg_function_is_visible(p.oid)",
!     /* namespace */
!     "p.pronamespace",
!     /* result */
!     "pg_catalog.quote_ident(p.proname)",
!     /* qualresult */
!     NULL
  };

  static const SchemaQuery Query_for_list_of_routines = {
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
--- 513,541 ----
      NULL
  };

! static const SchemaQuery Query_for_list_of_procedures[] = {
!     {
!         /* min_server_version */
!         110000,
!         /* catname */
!         "pg_catalog.pg_proc p",
!         /* selcondition */
!         "p.prokind = 'p'",
!         /* viscondition */
!         "pg_catalog.pg_function_is_visible(p.oid)",
!         /* namespace */
!         "p.pronamespace",
!         /* result */
!         "pg_catalog.quote_ident(p.proname)",
!         /* qualresult */
!         NULL
!     },
!     {0, NULL}
  };

  static const SchemaQuery Query_for_list_of_routines = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 455,460 ****
--- 551,558 ----
  };

  static const SchemaQuery Query_for_list_of_sequences = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 470,475 ****
--- 568,575 ----
  };

  static const SchemaQuery Query_for_list_of_foreign_tables = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 485,490 ****
--- 585,592 ----
  };

  static const SchemaQuery Query_for_list_of_tables = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 501,506 ****
--- 603,610 ----
  };

  static const SchemaQuery Query_for_list_of_partitioned_tables = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 516,521 ****
--- 620,627 ----
  };

  static const SchemaQuery Query_for_list_of_constraints_with_schema = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_constraint c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 532,537 ****
--- 638,645 ----

  /* Relations supporting INSERT, UPDATE or DELETE */
  static const SchemaQuery Query_for_list_of_updatables = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 550,555 ****
--- 658,665 ----
  };

  static const SchemaQuery Query_for_list_of_relations = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 565,570 ****
--- 675,682 ----
  };

  static const SchemaQuery Query_for_list_of_tsvmf = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 585,590 ****
--- 697,704 ----
  };

  static const SchemaQuery Query_for_list_of_tmf = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 602,607 ****
--- 716,723 ----
  };

  static const SchemaQuery Query_for_list_of_tpm = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 619,624 ****
--- 735,742 ----
  };

  static const SchemaQuery Query_for_list_of_tm = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 635,640 ****
--- 753,760 ----
  };

  static const SchemaQuery Query_for_list_of_views = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 650,655 ****
--- 770,777 ----
  };

  static const SchemaQuery Query_for_list_of_matviews = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_class c",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 665,670 ****
--- 787,794 ----
  };

  static const SchemaQuery Query_for_list_of_statistics = {
+     /* min_server_version */
+     0,
      /* catname */
      "pg_catalog.pg_statistic_ext s",
      /* selcondition */
*************** static const SchemaQuery Query_for_list_
*** 925,942 ****
  "   FROM pg_catalog.pg_am "\
  "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"

- #define Query_for_list_of_publications \
- " SELECT pg_catalog.quote_ident(pubname) "\
- "   FROM pg_catalog.pg_publication "\
- "  WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"
-
- #define Query_for_list_of_subscriptions \
- " SELECT pg_catalog.quote_ident(s.subname) "\
- "   FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "\
- "  WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "\
- "    AND d.datname = pg_catalog.current_database() "\
- "    AND s.subdbid = d.oid"
-
  /* the silly-looking length condition is just to eat up the current word */
  #define Query_for_list_of_arguments \
  "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
--- 1049,1054 ----
*************** static const SchemaQuery Query_for_list_
*** 1031,1036 ****
--- 1143,1174 ----
  "       and c2.relispartition = 'true'"

  /*
+  * These object types were introduced later than our support cutoff of
+  * server version 7.4.  We use the VersionedQuery infrastructure so that
+  * we don't send certain-to-fail queries to older servers.
+  */
+
+ static const VersionedQuery Query_for_list_of_publications[] = {
+     {100000,
+         " SELECT pg_catalog.quote_ident(pubname) "
+         "   FROM pg_catalog.pg_publication "
+         "  WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"
+     },
+     {0, NULL}
+ };
+
+ static const VersionedQuery Query_for_list_of_subscriptions[] = {
+     {100000,
+         " SELECT pg_catalog.quote_ident(s.subname) "
+         "   FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
+         "  WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "
+         "    AND d.datname = pg_catalog.current_database() "
+         "    AND s.subdbid = d.oid"
+     },
+     {0, NULL}
+ };
+
+ /*
   * 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.
   */
*************** typedef struct
*** 1039,1044 ****
--- 1177,1183 ----
  {
      const char *name;
      const char *query;            /* simple query, or NULL */
+     const VersionedQuery *vquery;    /* versioned query, or NULL */
      const SchemaQuery *squery;    /* schema query, or NULL */
      const bits32 flags;            /* visibility flags, see below */
  } pgsql_thing_t;
*************** typedef struct
*** 1049,1057 ****
  #define THING_NO_SHOW        (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)

  static const pgsql_thing_t words_after_create[] = {
!     {"ACCESS METHOD", NULL, NULL, THING_NO_ALTER},
!     {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
!     {"CAST", NULL, NULL},        /* Casts have complex structures for names, so
                                   * skip it */
      {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1,
pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))AND
substring(pg_catalog.quote_ident(collname),1,%d)='%s'"},

--- 1188,1196 ----
  #define THING_NO_SHOW        (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)

  static const pgsql_thing_t words_after_create[] = {
!     {"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER},
!     {"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates},
!     {"CAST", NULL, NULL, NULL}, /* Casts have complex structures for names, so
                                   * skip it */
      {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1,
pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))AND
substring(pg_catalog.quote_ident(collname),1,%d)='%s'"},

*************** static const pgsql_thing_t words_after_c
*** 1059,1114 ****
       * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
       * to be used only by pg_dump.
       */
!     {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
      {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE
substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
      {"DATABASE", Query_for_list_of_databases},
!     {"DEFAULT PRIVILEGES", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
!     {"DOMAIN", NULL, &Query_for_list_of_domains},
!     {"EVENT TRIGGER", NULL, NULL},
      {"EXTENSION", Query_for_list_of_extensions},
!     {"FOREIGN DATA WRAPPER", NULL, NULL},
!     {"FOREIGN TABLE", NULL, NULL},
!     {"FUNCTION", NULL, &Query_for_list_of_functions},
      {"GROUP", Query_for_list_of_roles},
!     {"INDEX", NULL, &Query_for_list_of_indexes},
      {"LANGUAGE", Query_for_list_of_languages},
!     {"LARGE OBJECT", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"MATERIALIZED VIEW", NULL, &Query_for_list_of_matviews},
!     {"OPERATOR", NULL, NULL},    /* Querying for this is probably not such a
!                                  * good idea. */
!     {"OWNED", NULL, NULL, THING_NO_CREATE | THING_NO_ALTER},    /* for DROP OWNED BY ... */
!     {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
!     {"POLICY", NULL, NULL},
!     {"PROCEDURE", NULL, &Query_for_list_of_procedures},
!     {"PUBLICATION", Query_for_list_of_publications},
      {"ROLE", Query_for_list_of_roles},
!     {"ROUTINE", NULL, &Query_for_list_of_routines, THING_NO_CREATE},
      {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE
substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
      {"SCHEMA", Query_for_list_of_schemas},
!     {"SEQUENCE", NULL, &Query_for_list_of_sequences},
      {"SERVER", Query_for_list_of_servers},
!     {"STATISTICS", NULL, &Query_for_list_of_statistics},
!     {"SUBSCRIPTION", Query_for_list_of_subscriptions},
!     {"SYSTEM", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"TABLE", NULL, &Query_for_list_of_tables},
      {"TABLESPACE", Query_for_list_of_tablespaces},
!     {"TEMP", NULL, NULL, THING_NO_DROP | THING_NO_ALTER},    /* for CREATE TEMP TABLE
!                                                              * ... */
!     {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
!     {"TEMPORARY", NULL, NULL, THING_NO_DROP | THING_NO_ALTER},    /* for CREATE TEMPORARY
!                                                                  * TABLE ... */
!     {"TEXT SEARCH", NULL, NULL},
!     {"TRANSFORM", NULL, NULL},
      {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE
substring(pg_catalog.quote_ident(tgname),1,%d)='%s'AND NOT tgisinternal"}, 
!     {"TYPE", NULL, &Query_for_list_of_datatypes},
!     {"UNIQUE", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
!                                                              * INDEX ... */
!     {"UNLOGGED", NULL, NULL, THING_NO_DROP | THING_NO_ALTER},    /* for CREATE UNLOGGED
!                                                                  * TABLE ... */
      {"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
!     {"USER MAPPING FOR", NULL, NULL},
!     {"VIEW", NULL, &Query_for_list_of_views},
      {NULL}                        /* end of list */
  };

--- 1198,1253 ----
       * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
       * to be used only by pg_dump.
       */
!     {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, NULL, THING_NO_SHOW},
      {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE
substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
      {"DATABASE", Query_for_list_of_databases},
!     {"DEFAULT PRIVILEGES", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, THING_NO_SHOW},
!     {"DOMAIN", NULL, NULL, &Query_for_list_of_domains},
!     {"EVENT TRIGGER", NULL, NULL, NULL},
      {"EXTENSION", Query_for_list_of_extensions},
!     {"FOREIGN DATA WRAPPER", NULL, NULL, NULL},
!     {"FOREIGN TABLE", NULL, NULL, NULL},
!     {"FUNCTION", NULL, NULL, Query_for_list_of_functions},
      {"GROUP", Query_for_list_of_roles},
!     {"INDEX", NULL, NULL, &Query_for_list_of_indexes},
      {"LANGUAGE", Query_for_list_of_languages},
!     {"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"MATERIALIZED VIEW", NULL, NULL, &Query_for_list_of_matviews},
!     {"OPERATOR", NULL, NULL, NULL}, /* Querying for this is probably not such
!                                      * a good idea. */
!     {"OWNED", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER},    /* for DROP OWNED BY ... */
!     {"PARSER", Query_for_list_of_ts_parsers, NULL, NULL, THING_NO_SHOW},
!     {"POLICY", NULL, NULL, NULL},
!     {"PROCEDURE", NULL, NULL, Query_for_list_of_procedures},
!     {"PUBLICATION", NULL, Query_for_list_of_publications},
      {"ROLE", Query_for_list_of_roles},
!     {"ROUTINE", NULL, NULL, &Query_for_list_of_routines, THING_NO_CREATE},
      {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE
substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
      {"SCHEMA", Query_for_list_of_schemas},
!     {"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences},
      {"SERVER", Query_for_list_of_servers},
!     {"STATISTICS", NULL, NULL, &Query_for_list_of_statistics},
!     {"SUBSCRIPTION", NULL, Query_for_list_of_subscriptions},
!     {"SYSTEM", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
!     {"TABLE", NULL, NULL, &Query_for_list_of_tables},
      {"TABLESPACE", Query_for_list_of_tablespaces},
!     {"TEMP", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE
!                                                                  * ... */
!     {"TEMPLATE", Query_for_list_of_ts_templates, NULL, NULL, THING_NO_SHOW},
!     {"TEMPORARY", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},    /* for CREATE TEMPORARY
!                                                                          * TABLE ... */
!     {"TEXT SEARCH", NULL, NULL, NULL},
!     {"TRANSFORM", NULL, NULL, NULL},
      {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE
substring(pg_catalog.quote_ident(tgname),1,%d)='%s'AND NOT tgisinternal"}, 
!     {"TYPE", NULL, NULL, &Query_for_list_of_datatypes},
!     {"UNIQUE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},    /* for CREATE UNIQUE
!                                                                      * INDEX ... */
!     {"UNLOGGED", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED
!                                                                      * TABLE ... */
      {"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
!     {"USER MAPPING FOR", NULL, NULL, NULL},
!     {"VIEW", NULL, NULL, &Query_for_list_of_views},
      {NULL}                        /* end of list */
  };

*************** static char *create_command_generator(co
*** 1119,1126 ****
  static char *drop_command_generator(const char *text, int state);
  static char *alter_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,
                       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);
--- 1258,1268 ----
  static char *drop_command_generator(const char *text, int state);
  static char *alter_command_generator(const char *text, int state);
  static char *complete_from_query(const char *text, int state);
+ static char *complete_from_versioned_query(const char *text, int state);
  static char *complete_from_schema_query(const char *text, int state);
! static char *complete_from_versioned_schema_query(const char *text, int state);
! static char *_complete_from_query(const char *simple_query,
!                      const SchemaQuery *schema_query,
                       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);
*************** psql_completion(const char *text, int st
*** 2208,2214 ****
          COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
  /* CALL */
      else if (Matches1("CALL"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
      else if (Matches2("CALL", MatchAny))
          COMPLETE_WITH_CONST("(");
  /* CLUSTER */
--- 2350,2356 ----
          COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
  /* CALL */
      else if (Matches1("CALL"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
      else if (Matches2("CALL", MatchAny))
          COMPLETE_WITH_CONST("(");
  /* CLUSTER */
*************** psql_completion(const char *text, int st
*** 2654,2660 ****
      else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
          COMPLETE_WITH_CONST("PROCEDURE");
      else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);

  /* CREATE ROLE,USER,GROUP <name> */
      else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
--- 2796,2802 ----
      else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
          COMPLETE_WITH_CONST("PROCEDURE");
      else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);

  /* CREATE ROLE,USER,GROUP <name> */
      else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
*************** psql_completion(const char *text, int st
*** 3008,3018 ****
          else if (TailMatches1("DOMAIN"))
              COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
          else if (TailMatches1("FUNCTION"))
!             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
          else if (TailMatches1("LANGUAGE"))
              COMPLETE_WITH_QUERY(Query_for_list_of_languages);
          else if (TailMatches1("PROCEDURE"))
!             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
          else if (TailMatches1("ROUTINE"))
              COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
          else if (TailMatches1("SCHEMA"))
--- 3150,3160 ----
          else if (TailMatches1("DOMAIN"))
              COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
          else if (TailMatches1("FUNCTION"))
!             COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
          else if (TailMatches1("LANGUAGE"))
              COMPLETE_WITH_QUERY(Query_for_list_of_languages);
          else if (TailMatches1("PROCEDURE"))
!             COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
          else if (TailMatches1("ROUTINE"))
              COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
          else if (TailMatches1("SCHEMA"))
*************** psql_completion(const char *text, int st
*** 3483,3489 ****
              COMPLETE_WITH_QUERY(Query_for_list_of_roles);
      }
      else if (TailMatchesCS1("\\da*"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
      else if (TailMatchesCS1("\\dA*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
      else if (TailMatchesCS1("\\db*"))
--- 3625,3631 ----
              COMPLETE_WITH_QUERY(Query_for_list_of_roles);
      }
      else if (TailMatchesCS1("\\da*"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
      else if (TailMatchesCS1("\\dA*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
      else if (TailMatchesCS1("\\db*"))
*************** psql_completion(const char *text, int st
*** 3497,3503 ****
      else if (TailMatchesCS1("\\dew*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
      else if (TailMatchesCS1("\\df*"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);

      else if (TailMatchesCS1("\\dFd*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
--- 3639,3645 ----
      else if (TailMatchesCS1("\\dew*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
      else if (TailMatchesCS1("\\df*"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);

      else if (TailMatchesCS1("\\dFd*"))
          COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
*************** psql_completion(const char *text, int st
*** 3541,3547 ****
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);

      else if (TailMatchesCS1("\\ef"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
      else if (TailMatchesCS1("\\ev"))
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);

--- 3683,3689 ----
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);

      else if (TailMatchesCS1("\\ef"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
      else if (TailMatchesCS1("\\ev"))
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);

*************** psql_completion(const char *text, int st
*** 3650,3656 ****
              COMPLETE_WITH_LIST_CS3("default", "verbose", "terse");
      }
      else if (TailMatchesCS1("\\sf*"))
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
      else if (TailMatchesCS1("\\sv*"))
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
      else if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
--- 3792,3798 ----
              COMPLETE_WITH_LIST_CS3("default", "verbose", "terse");
      }
      else if (TailMatchesCS1("\\sf*"))
!         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
      else if (TailMatchesCS1("\\sv*"))
          COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
      else if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
*************** psql_completion(const char *text, int st
*** 3676,3684 ****
              {
                  if (words_after_create[i].query)
                      COMPLETE_WITH_QUERY(words_after_create[i].query);
                  else if (words_after_create[i].squery)
!                     COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
!                                                NULL);
                  break;
              }
          }
--- 3818,3828 ----
              {
                  if (words_after_create[i].query)
                      COMPLETE_WITH_QUERY(words_after_create[i].query);
+                 else if (words_after_create[i].vquery)
+                     COMPLETE_WITH_VERSIONED_QUERY(words_after_create[i].vquery);
                  else if (words_after_create[i].squery)
!                     COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(words_after_create[i].squery,
!                                                          NULL);
                  break;
              }
          }
*************** alter_command_generator(const char *text
*** 3777,3800 ****
      return create_or_drop_command_generator(text, state, THING_NO_ALTER);
  }

! /* 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_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
!  * 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
--- 3921,3993 ----
      return create_or_drop_command_generator(text, state, THING_NO_ALTER);
  }

! /*
!  * These functions generate lists using server queries.
!  * They are all wrappers for _complete_from_query.
!  */

  static char *
  complete_from_query(const char *text, int state)
  {
!     /* query is assumed to work for any server version */
!     return _complete_from_query(completion_charp, NULL, text, state);
! }
!
! static char *
! complete_from_versioned_query(const char *text, int state)
! {
!     const VersionedQuery *vquery = completion_vquery;
!
!     /* Find appropriate array element */
!     while (pset.sversion < vquery->min_server_version)
!         vquery++;
!     /* Fail completion if server is too old */
!     if (vquery->query == NULL)
!         return NULL;
!
!     return _complete_from_query(vquery->query, NULL, text, state);
  }

  static char *
  complete_from_schema_query(const char *text, int state)
  {
!     /* query is assumed to work for any server version */
!     return _complete_from_query(completion_charp, completion_squery,
!                                 text, state);
! }
!
! static char *
! complete_from_versioned_schema_query(const char *text, int state)
! {
!     const SchemaQuery *squery = completion_squery;
!     const VersionedQuery *vquery = completion_vquery;
!
!     /* Find appropriate array element */
!     while (pset.sversion < squery->min_server_version)
!         squery++;
!     /* Fail completion if server is too old */
!     if (squery->catname == NULL)
!         return NULL;
!
!     /* Likewise for the add-on text, if any */
!     if (vquery)
!     {
!         while (pset.sversion < vquery->min_server_version)
!             vquery++;
!         if (vquery->query == NULL)
!             return NULL;
!     }
!
!     return _complete_from_query(vquery ? vquery->query : NULL,
!                                 squery, text, state);
  }


  /*
!  * This creates a list of matching things, according to a query described by
!  * the initial arguments.  The caller has already done any work needed to
!  * select the appropriate query for the server's version.
!  *
   * The query can be one of two kinds:
   *
   * 1. A simple query which must contain a %d and a %s, which will be replaced
*************** complete_from_schema_query(const char *t
*** 3808,3820 ****
   * %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)
  {
      static int    list_index,
                  byte_length;
--- 4001,4020 ----
   * %d %s %d %s %d %s %s %d %s
   * where %d is the string length of the text and %s the text itself.
   *
+  * If both simple_query and schema_query are non-NULL, then we construct
+  * a schema query and append the (uninterpreted) string simple_query to it.
+  *
   * 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.
+  *
+  * "text" and "state" are supplied by readline.
   */
  static char *
! _complete_from_query(const char *simple_query,
!                      const SchemaQuery *schema_query,
!                      const char *text, int state)
  {
      static int    list_index,
                  byte_length;
*************** _complete_from_query(int is_schema_query
*** 3865,3890 ****

          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,
                                char_length, e_text);
              appendPQExpBuffer(&query_buffer, " AND %s",
!                               completion_squery->viscondition);

              /*
               * When fetching relation names, suppress system catalogs unless
--- 4065,4090 ----

          initPQExpBuffer(&query_buffer);

!         if (schema_query)
          {
!             /* schema_query gives us the pieces to assemble */
!             const char *qualresult = schema_query->qualresult;

              if (qualresult == NULL)
!                 qualresult = schema_query->result;

              /* Get unqualified names matching the input-so-far */
              appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
!                               schema_query->result,
!                               schema_query->catname);
!             if (schema_query->selcondition)
                  appendPQExpBuffer(&query_buffer, "%s AND ",
!                                   schema_query->selcondition);
              appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
!                               schema_query->result,
                                char_length, e_text);
              appendPQExpBuffer(&query_buffer, " AND %s",
!                               schema_query->viscondition);

              /*
               * When fetching relation names, suppress system catalogs unless
*************** _complete_from_query(int is_schema_query
*** 3892,3898 ****
               * 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)
              {
--- 4092,4098 ----
               * between not offering system catalogs for completion at all, and
               * having them swamp the result when the input is just "p".
               */
!             if (strcmp(schema_query->catname,
                         "pg_catalog.pg_class c") == 0 &&
                  strncmp(text, "pg_", 3) !=0)
              {
*************** _complete_from_query(int is_schema_query
*** 3926,3936 ****
                                "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,
                                char_length, e_text);
--- 4126,4136 ----
                                "FROM %s, pg_catalog.pg_namespace n "
                                "WHERE %s = n.oid AND ",
                                qualresult,
!                               schema_query->catname,
!                               schema_query->namespace);
!             if (schema_query->selcondition)
                  appendPQExpBuffer(&query_buffer, "%s AND ",
!                                   schema_query->selcondition);
              appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
                                qualresult,
                                char_length, e_text);
*************** _complete_from_query(int is_schema_query
*** 3951,3963 ****
                                char_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,
                                char_length, e_text,
                                e_info_charp, e_info_charp,
                                e_info_charp2, e_info_charp2);
--- 4151,4164 ----
                                char_length, e_text);

              /* If an addon query was provided, use it */
!             if (simple_query)
!                 appendPQExpBuffer(&query_buffer, "\n%s", simple_query);
          }
          else
          {
!             Assert(simple_query);
!             /* simple_query is an sprintf-style format string */
!             appendPQExpBuffer(&query_buffer, simple_query,
                                char_length, e_text,
                                e_info_charp, e_info_charp,
                                e_info_charp2, e_info_charp2);

pgsql-hackers by date:

Previous
From: Tomas Vondra
Date:
Subject: Re: [HACKERS] user-defined numeric data types triggering ERROR:unsupported type
Next
From: Tom Lane
Date:
Subject: Re: [HACKERS] user-defined numeric data types triggering ERROR: unsupported type