/*
* psql - the PostgreSQL interactive terminal
*
* Copyright ( c ) 2000 - 2025 , PostgreSQL Global Development Group
*
* src / bin / psql / tab - complete . in . c
*
* Note : this will compile and work as - is if SWITCH_CONVERSION_APPLIED
* is not defined . However , the expected usage is that it ' s first run
* through gen_tabcomplete . pl , which will # define that symbol , fill in the
* tcpatterns [ ] array , and convert the else - if chain in match_previous_words ( )
* into a switch . See comments for match_previous_words ( ) and the header
* comment in gen_tabcomplete . pl for more detail .
*/
/*----------------------------------------------------------------------
* This file implements a somewhat more sophisticated readline " TAB
* completion " in psql . It is not intended to be AI , to replace
* learning SQL , or to relieve you from thinking about what you ' re
* doing . Also it does not always give you all the syntactically legal
* completions , only those that are the most common or the ones that
* the programmer felt most like implementing .
*
* CAVEAT : Tab completion causes queries to be sent to the backend .
* The number of tuples returned gets limited , in most default
* installations to 1000 , but if you still don ' t like this prospect ,
* you can turn off tab completion in your ~ / . inputrc ( or else
* $ { INPUTRC } ) file so :
*
* $ if psql
* set disable - completion on
* $ endif
*
* See ` man 3 readline ' or ` info readline ' for the full details .
*
* BUGS :
* - Quotes , parentheses , and other funny characters are not handled
* all that gracefully .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres_fe.h"
#include "input.h"
#include "tab-complete.h"
/* If we don't have this, we might as well forget about the whole thing: */
#ifdef USE_READLINE
#include <ctype.h>
#include <sys/stat.h>
#include "catalog/pg_am_d.h"
#include "catalog/pg_class_d.h"
#include "common.h"
#include "common/keywords.h"
#include "libpq-fe.h"
#include "mb/pg_wchar.h"
#include "pqexpbuffer.h"
#include "settings.h"
#include "stringutils.h"
/*
* Ancient versions of libedit provide filename_completion_function ( )
* instead of rl_filename_completion_function ( ) . Likewise for
* [ rl_ ] completion_matches ( ) .
*/
#ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
#define rl_filename_completion_function filename_completion_function
#endif
#ifndef HAVE_RL_COMPLETION_MATCHES
#define rl_completion_matches completion_matches
#endif
/*
* Currently we assume that rl_filename_dequoting_function exists if
* rl_filename_quoting_function does . If that proves not to be the case ,
* we ' d need to test for the former , or possibly both , in configure .
*/
#ifdef HAVE_RL_FILENAME_QUOTING_FUNCTION
#define USE_FILENAME_QUOTING_FUNCTIONS 1
#endif
/* word break characters */
#define WORD_BREAKS "\t\n@><=;|&() "
/*
* Since readline doesn ' t let us pass any state through to the tab completion
* callback , we have to use this global variable to let get_previous_words ( )
* get at the previous lines of the current command . Ick .
*/
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 an entry having min_server_version = 0 .
* That entry ' s query string can be a query that works in all supported older
* server versions , or NULL to 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 ( ) .
*
* We also use this struct to define queries that use completion_ref_object ,
* which is some object related to the one ( s ) we want to get the names of
* ( for example , the table we want the indexes of ) . In that usage the
* objects we ' re completing might not have a schema of their own , but the
* reference object almost always does ( passed in completion_ref_schema ) .
*
* 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 we should do nothing .
*/
int min_server_version;
/*
* Name of catalog or catalogs to be queried , with alias ( es ) , eg .
* " pg_catalog . pg_class c " . Note that " pg_namespace n " and / or
* " pg_namespace nr " will be added automatically when needed .
*/
const char *catname;
/*
* Selection condition - - - only rows meeting this condition are candidates
* to display . If catname mentions multiple tables , include the necessary
* join condition here . For example , this might look like " c . relkind = "
* CppAsString2 ( RELKIND_RELATION ) . Write NULL ( not an empty string ) if
* not needed .
*/
const char *selcondition;
/*
* Visibility condition - - - which rows are visible without schema
* qualification ? For example , " pg_catalog . pg_table_is_visible ( c . oid ) " .
* NULL if not needed .
*/
const char *viscondition;
/*
* Namespace - - - name of field to join to pg_namespace . oid when there is
* schema qualification . For example , " c . relnamespace " . NULL if we don ' t
* want to join to pg_namespace ( then any schema part in the input word
* will be ignored ) .
*/
const char *namespace ;
/*
* Result - - - the base object name to return . For example , " c . relname " .
*/
const char *result;
/*
* In some cases , it ' s difficult to keep the query from returning the same
* object multiple times . Specify use_distinct to filter out duplicates .
*/
bool use_distinct;
/*
* Additional literal strings ( usually keywords ) to be offered along with
* the query results . Provide a NULL - terminated array of constant
* strings , or NULL if none .
*/
const char *const *keywords;
/*
* If this query uses completion_ref_object / completion_ref_schema ,
* populate the remaining fields , else leave them NULL . When using this
* capability , catname must include the catalog that defines the
* completion_ref_object , and selcondition must include the join condition
* that connects it to the result ' s catalog .
*
* refname is the field that should be equated to completion_ref_object ,
* for example " cr . relname " .
*/
const char *refname;
/*
* Visibility condition to use when completion_ref_schema is not set . For
* example , " pg_catalog . pg_table_is_visible ( cr . oid ) " . NULL if not needed .
*/
const char *refviscondition;
/*
* Name of field to join to pg_namespace . oid when completion_ref_schema is
* set . For example , " cr . relnamespace " . NULL if we don ' t want to
* consider completion_ref_schema .
*/
const char *refnamespace;
} SchemaQuery;
/* Store maximum number of records we want from database queries
* ( implemented via SELECT . . . LIMIT xx ) .
*/
static int completion_max_records;
/*
* Communication variables set by psql_completion ( mostly in COMPLETE_WITH_FOO
* macros ) and then used by the completion callback functions . Ugly but there
* is no better way .
*/
static char completion_last_char; /* last char of input word */
static const char *completion_charp; /* to pass a string */
static const char *const *completion_charpp; /* to pass a list of strings */
static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */
static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */
static char *completion_ref_object; /* name of reference object */
static char *completion_ref_schema; /* schema name of reference object */
static bool completion_case_sensitive; /* completion is case sensitive */
static bool completion_verbatim; /* completion is verbatim */
static bool completion_force_quote; /* true to force-quote filenames */
/*
* A few macros to ease typing . You can use these to complete the given
* string with
* 1 ) The result from a query you pass it . ( Perhaps one of those below ? )
* We support both simple and versioned queries .
* 2 ) The result 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 ) ; if the list is constant you can build it
* with COMPLETE_WITH ( ) or COMPLETE_WITH_CS ( ) . The QUERY_LIST and
* QUERY_PLUS forms combine such literal lists with a query result .
* 4 ) The list of attributes of the given table ( possibly schema - qualified ) .
* 5 ) The list of arguments to the given function ( possibly schema - qualified ) .
*
* The query is generally expected to return raw SQL identifiers ; matching
* to what the user typed is done in a quoting - aware fashion . If what is
* returned is not SQL identifiers , use one of the VERBATIM forms , in which
* case the query results are matched to the user ' s text without double - quote
* processing ( so if quoting is needed , you must provide it in the query
* results ) .
*/
#define COMPLETE_WITH_QUERY(query) \
COMPLETE_WITH_QUERY_LIST(query, NULL)
#define COMPLETE_WITH_QUERY_LIST(query, list) \
do { \
completion_charp = query; \
completion_charpp = list; \
completion_verbatim = false ; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0 )
#define COMPLETE_WITH_QUERY_PLUS(query, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_QUERY_LIST(query, list); \
} while (0 )
#define COMPLETE_WITH_QUERY_VERBATIM(query) \
COMPLETE_WITH_QUERY_VERBATIM_LIST(query, NULL)
#define COMPLETE_WITH_QUERY_VERBATIM_LIST(query, list) \
do { \
completion_charp = query; \
completion_charpp = list; \
completion_verbatim = true ; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0 )
#define COMPLETE_WITH_QUERY_VERBATIM_PLUS(query, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_QUERY_VERBATIM_LIST(query, list); \
} while (0 )
#define COMPLETE_WITH_VERSIONED_QUERY(query) \
COMPLETE_WITH_VERSIONED_QUERY_LIST(query, NULL)
#define COMPLETE_WITH_VERSIONED_QUERY_LIST(query, list) \
do { \
completion_vquery = query; \
completion_charpp = list; \
completion_verbatim = false ; \
matches = rl_completion_matches(text, complete_from_versioned_query); \
} while (0 )
#define COMPLETE_WITH_VERSIONED_QUERY_PLUS(query, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_VERSIONED_QUERY_LIST(query, list); \
} while (0 )
#define COMPLETE_WITH_SCHEMA_QUERY(query) \
COMPLETE_WITH_SCHEMA_QUERY_LIST(query, NULL)
#define COMPLETE_WITH_SCHEMA_QUERY_LIST(query, list) \
do { \
completion_squery = &(query); \
completion_charpp = list; \
completion_verbatim = false ; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0 )
#define COMPLETE_WITH_SCHEMA_QUERY_PLUS(query, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_SCHEMA_QUERY_LIST(query, list); \
} while (0 )
#define COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(query) \
do { \
completion_squery = &(query); \
completion_charpp = NULL; \
completion_verbatim = true ; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0 )
#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query) \
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, NULL)
#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, list) \
do { \
completion_squery = query; \
completion_charpp = list; \
completion_verbatim = false ; \
matches = rl_completion_matches(text, complete_from_versioned_schema_query); \
} while (0 )
#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_PLUS(query, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, list); \
} while (0 )
/*
* Caution : COMPLETE_WITH_CONST is not for general - purpose use ; you probably
* want COMPLETE_WITH ( ) with one element , instead .
*/
#define COMPLETE_WITH_CONST(cs, con) \
do { \
completion_case_sensitive = (cs); \
completion_charp = (con); \
matches = rl_completion_matches(text, complete_from_const); \
} while (0 )
#define COMPLETE_WITH_LIST_INT(cs, list) \
do { \
completion_case_sensitive = (cs); \
completion_charpp = (list); \
matches = rl_completion_matches(text, complete_from_list); \
} while (0 )
#define COMPLETE_WITH_LIST(list) COMPLETE_WITH_LIST_INT(false , list)
#define COMPLETE_WITH_LIST_CS(list) COMPLETE_WITH_LIST_INT(true , list)
#define COMPLETE_WITH(...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_LIST(list); \
} while (0 )
#define COMPLETE_WITH_CS(...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_LIST_CS(list); \
} while (0 )
#define COMPLETE_WITH_ATTR(relation) \
COMPLETE_WITH_ATTR_LIST(relation, NULL)
#define COMPLETE_WITH_ATTR_LIST(relation, list) \
do { \
set_completion_reference(relation); \
completion_squery = &(Query_for_list_of_attributes); \
completion_charpp = list; \
completion_verbatim = false ; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0 )
#define COMPLETE_WITH_ATTR_PLUS(relation, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_ATTR_LIST(relation, list); \
} while (0 )
/*
* libedit will typically include the literal ' s leading single quote in
* " text " , while readline will not . Adapt our offered strings to fit .
* But include a quote if there ' s not one just before " text " , to get the
* user off to the right start .
*/
#define COMPLETE_WITH_ENUM_VALUE(type) \
do { \
set_completion_reference(type); \
if (text[0 ] == '\' ' || \
start == 0 || rl_line_buffer[start - 1 ] != '\' ') \
completion_squery = &(Query_for_list_of_enum_values_quoted); \
else \
completion_squery = &(Query_for_list_of_enum_values_unquoted); \
completion_charpp = NULL; \
completion_verbatim = true ; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0 )
/*
* Timezone completion is mostly like enum label completion , but we work
* a little harder since this is a more common use - case .
*/
#define COMPLETE_WITH_TIMEZONE_NAME() \
do { \
static const char *const list[] = { "DEFAULT" , NULL }; \
if (text[0 ] == '\' ') \
completion_charp = Query_for_list_of_timezone_names_quoted_in; \
else if (start == 0 || rl_line_buffer[start - 1 ] != '\' ') \
completion_charp = Query_for_list_of_timezone_names_quoted_out; \
else \
completion_charp = Query_for_list_of_timezone_names_unquoted; \
completion_charpp = list; \
completion_verbatim = true ; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0 )
#define COMPLETE_WITH_FUNCTION_ARG(function) \
do { \
set_completion_reference(function); \
completion_squery = &(Query_for_list_of_arguments); \
completion_charpp = NULL; \
completion_verbatim = true ; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0 )
/*
* Assembly instructions for schema queries
*
* Note that toast tables are not included in those queries to avoid
* unnecessary bloat in the completions generated .
*/
static const SchemaQuery Query_for_constraint_of_table = {
.catname = "pg_catalog.pg_constraint con, pg_catalog.pg_class c1" ,
.selcondition = "con.conrelid=c1.oid" ,
.result = "con.conname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
static const SchemaQuery Query_for_constraint_of_table_not_validated = {
.catname = "pg_catalog.pg_constraint con, pg_catalog.pg_class c1" ,
.selcondition = "con.conrelid=c1.oid and not con.convalidated" ,
.result = "con.conname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
static const SchemaQuery Query_for_constraint_of_type = {
.catname = "pg_catalog.pg_constraint con, pg_catalog.pg_type t" ,
.selcondition = "con.contypid=t.oid" ,
.result = "con.conname" ,
.refname = "t.typname" ,
.refviscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.refnamespace = "t.typnamespace" ,
};
static const SchemaQuery Query_for_index_of_table = {
.catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" ,
.selcondition = "c1.oid=i.indrelid and i.indexrelid=c2.oid" ,
.result = "c2.relname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
static const SchemaQuery Query_for_unique_index_of_table = {
.catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" ,
.selcondition = "c1.oid=i.indrelid and i.indexrelid=c2.oid and i.indisunique" ,
.result = "c2.relname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
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 = "p.proname" ,
},
{
.catname = "pg_catalog.pg_proc p" ,
.selcondition = "p.proisagg" ,
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)" ,
.namespace = "p.pronamespace" ,
.result = "p.proname" ,
}
};
static const SchemaQuery Query_for_list_of_arguments = {
.catname = "pg_catalog.pg_proc p" ,
.result = "pg_catalog.oidvectortypes(p.proargtypes)||')'" ,
.refname = "p.proname" ,
.refviscondition = "pg_catalog.pg_function_is_visible(p.oid)" ,
.refnamespace = "p.pronamespace" ,
};
static const SchemaQuery Query_for_list_of_attributes = {
.catname = "pg_catalog.pg_attribute a, pg_catalog.pg_class c" ,
.selcondition = "c.oid = a.attrelid and a.attnum > 0 and not a.attisdropped" ,
.result = "a.attname" ,
.refname = "c.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.refnamespace = "c.relnamespace" ,
};
static const SchemaQuery Query_for_list_of_attribute_numbers = {
.catname = "pg_catalog.pg_attribute a, pg_catalog.pg_class c" ,
.selcondition = "c.oid = a.attrelid and a.attnum > 0 and not a.attisdropped" ,
.result = "a.attnum::pg_catalog.text" ,
.refname = "c.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.refnamespace = "c.relnamespace" ,
};
static const char *const Keywords_for_list_of_datatypes[] = {
"bigint" ,
"boolean" ,
"character" ,
"double precision" ,
"integer" ,
"real" ,
"smallint" ,
/*
* Note : currently there ' s no value in offering the following multiword
* type names , because tab completion cannot succeed for them : we can ' t
* disambiguate until somewhere in the second word , at which point we
* won ' t have the first word as context . ( " double precision " does work ,
* as long as no other type name begins with " double " . ) Leave them out to
* encourage users to use the PG - specific aliases , which we can complete .
*/
#ifdef NOT_USED
"bit varying" ,
"character varying" ,
"time with time zone" ,
"time without time zone" ,
"timestamp with time zone" ,
"timestamp without time zone" ,
#endif
NULL
};
static const SchemaQuery Query_for_list_of_datatypes = {
.catname = "pg_catalog.pg_type t" ,
/* selcondition --- ignore table rowtypes and array types */
.selcondition = "(t.typrelid = 0 "
" OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
" FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
"AND t.typname !~ '^_'" ,
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.namespace = "t.typnamespace" ,
.result = "t.typname" ,
.keywords = Keywords_for_list_of_datatypes,
};
static const SchemaQuery Query_for_list_of_composite_datatypes = {
.catname = "pg_catalog.pg_type t" ,
/* selcondition --- only get composite types */
.selcondition = "(SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
" FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid) "
"AND t.typname !~ '^_'" ,
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.namespace = "t.typnamespace" ,
.result = "t.typname" ,
};
static const SchemaQuery Query_for_list_of_domains = {
.catname = "pg_catalog.pg_type t" ,
.selcondition = "t.typtype = 'd'" ,
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.namespace = "t.typnamespace" ,
.result = "t.typname" ,
};
static const SchemaQuery Query_for_list_of_enum_values_quoted = {
.catname = "pg_catalog.pg_enum e, pg_catalog.pg_type t" ,
.selcondition = "t.oid = e.enumtypid" ,
.result = "pg_catalog.quote_literal(enumlabel)" ,
.refname = "t.typname" ,
.refviscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.refnamespace = "t.typnamespace" ,
};
static const SchemaQuery Query_for_list_of_enum_values_unquoted = {
.catname = "pg_catalog.pg_enum e, pg_catalog.pg_type t" ,
.selcondition = "t.oid = e.enumtypid" ,
.result = "e.enumlabel" ,
.refname = "t.typname" ,
.refviscondition = "pg_catalog.pg_type_is_visible(t.oid)" ,
.refnamespace = "t.typnamespace" ,
};
/* Note: this intentionally accepts aggregates as well as plain functions */
static const SchemaQuery Query_for_list_of_functions[] = {
{
.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 = "p.proname" ,
},
{
.catname = "pg_catalog.pg_proc p" ,
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)" ,
.namespace = "p.pronamespace" ,
.result = "p.proname" ,
}
};
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 = "p.proname" ,
},
{
/* not supported in older versions */
.catname = NULL,
}
};
static const SchemaQuery Query_for_list_of_routines = {
.catname = "pg_catalog.pg_proc p" ,
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)" ,
.namespace = "p.pronamespace" ,
.result = "p.proname" ,
};
static const SchemaQuery Query_for_list_of_sequences = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_SEQUENCE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_foreign_tables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_FOREIGN_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_tables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_partitioned_tables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_tables_for_constraint = {
.catname = "pg_catalog.pg_class c, pg_catalog.pg_constraint con" ,
.selcondition = "c.oid=con.conrelid and c.relkind IN ("
CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
.use_distinct = true ,
.refname = "con.conname" ,
};
static const SchemaQuery Query_for_list_of_tables_for_policy = {
.catname = "pg_catalog.pg_class c, pg_catalog.pg_policy p" ,
.selcondition = "c.oid=p.polrelid" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
.use_distinct = true ,
.refname = "p.polname" ,
};
static const SchemaQuery Query_for_list_of_tables_for_rule = {
.catname = "pg_catalog.pg_class c, pg_catalog.pg_rewrite r" ,
.selcondition = "c.oid=r.ev_class" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
.use_distinct = true ,
.refname = "r.rulename" ,
};
static const SchemaQuery Query_for_list_of_tables_for_trigger = {
.catname = "pg_catalog.pg_class c, pg_catalog.pg_trigger t" ,
.selcondition = "c.oid=t.tgrelid" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
.use_distinct = true ,
.refname = "t.tgname" ,
};
static const SchemaQuery Query_for_list_of_ts_configurations = {
.catname = "pg_catalog.pg_ts_config c" ,
.viscondition = "pg_catalog.pg_ts_config_is_visible(c.oid)" ,
.namespace = "c.cfgnamespace" ,
.result = "c.cfgname" ,
};
static const SchemaQuery Query_for_list_of_ts_dictionaries = {
.catname = "pg_catalog.pg_ts_dict d" ,
.viscondition = "pg_catalog.pg_ts_dict_is_visible(d.oid)" ,
.namespace = "d.dictnamespace" ,
.result = "d.dictname" ,
};
static const SchemaQuery Query_for_list_of_ts_parsers = {
.catname = "pg_catalog.pg_ts_parser p" ,
.viscondition = "pg_catalog.pg_ts_parser_is_visible(p.oid)" ,
.namespace = "p.prsnamespace" ,
.result = "p.prsname" ,
};
static const SchemaQuery Query_for_list_of_ts_templates = {
.catname = "pg_catalog.pg_ts_template t" ,
.viscondition = "pg_catalog.pg_ts_template_is_visible(t.oid)" ,
.namespace = "t.tmplnamespace" ,
.result = "t.tmplname" ,
};
static const SchemaQuery Query_for_list_of_views = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_matviews = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_MATVIEW) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_indexes = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_INDEX) ", "
CppAsString2(RELKIND_PARTITIONED_INDEX) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_partitioned_indexes = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* All relations */
static const SchemaQuery Query_for_list_of_relations = {
.catname = "pg_catalog.pg_class c" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* partitioned relations */
static const SchemaQuery Query_for_list_of_partitioned_relations = {
.catname = "pg_catalog.pg_class c" ,
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE)
", " CppAsString2(RELKIND_PARTITIONED_INDEX) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_operator_families = {
.catname = "pg_catalog.pg_opfamily c" ,
.viscondition = "pg_catalog.pg_opfamily_is_visible(c.oid)" ,
.namespace = "c.opfnamespace" ,
.result = "c.opfname" ,
};
/* Relations supporting INSERT, UPDATE or DELETE */
static const SchemaQuery Query_for_list_of_updatables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ", "
CppAsString2(RELKIND_VIEW) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* Relations supporting MERGE */
static const SchemaQuery Query_for_list_of_mergetargets = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_VIEW) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ") " ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* Relations supporting SELECT */
static const SchemaQuery Query_for_list_of_selectables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_SEQUENCE) ", "
CppAsString2(RELKIND_VIEW) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* Relations supporting TRUNCATE */
static const SchemaQuery Query_for_list_of_truncatables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* Relations supporting GRANT are currently same as those supporting SELECT */
#define Query_for_list_of_grantables Query_for_list_of_selectables
/* Relations supporting ANALYZE */
static const SchemaQuery Query_for_list_of_analyzables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/* Relations supporting index creation */
static const SchemaQuery Query_for_list_of_indexables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
CppAsString2(RELKIND_MATVIEW) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
/*
* Relations supporting VACUUM are currently same as those supporting
* indexing .
*/
#define Query_for_list_of_vacuumables Query_for_list_of_indexables
/* Relations supporting CLUSTER */
static const SchemaQuery Query_for_list_of_clusterables = {
.catname = "pg_catalog.pg_class c" ,
.selcondition =
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
CppAsString2(RELKIND_MATVIEW) ")" ,
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)" ,
.namespace = "c.relnamespace" ,
.result = "c.relname" ,
};
static const SchemaQuery Query_for_list_of_constraints_with_schema = {
.catname = "pg_catalog.pg_constraint c" ,
.selcondition = "c.conrelid <> 0" ,
.namespace = "c.connamespace" ,
.result = "c.conname" ,
};
static const SchemaQuery Query_for_list_of_statistics = {
.catname = "pg_catalog.pg_statistic_ext s" ,
.viscondition = "pg_catalog.pg_statistics_obj_is_visible(s.oid)" ,
.namespace = "s.stxnamespace" ,
.result = "s.stxname" ,
};
static const SchemaQuery Query_for_list_of_collations = {
.catname = "pg_catalog.pg_collation c" ,
.selcondition = "c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))" ,
.viscondition = "pg_catalog.pg_collation_is_visible(c.oid)" ,
.namespace = "c.collnamespace" ,
.result = "c.collname" ,
};
static const SchemaQuery Query_for_partition_of_table = {
.catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i" ,
.selcondition = "c1.oid=i.inhparent and i.inhrelid=c2.oid and c2.relispartition" ,
.viscondition = "pg_catalog.pg_table_is_visible(c2.oid)" ,
.namespace = "c2.relnamespace" ,
.result = "c2.relname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
static const SchemaQuery Query_for_rule_of_table = {
.catname = "pg_catalog.pg_rewrite r, pg_catalog.pg_class c1" ,
.selcondition = "r.ev_class=c1.oid" ,
.result = "r.rulename" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
static const SchemaQuery Query_for_trigger_of_table = {
.catname = "pg_catalog.pg_trigger t, pg_catalog.pg_class c1" ,
.selcondition = "t.tgrelid=c1.oid and not t.tgisinternal" ,
.result = "t.tgname" ,
.refname = "c1.relname" ,
.refviscondition = "pg_catalog.pg_table_is_visible(c1.oid)" ,
.refnamespace = "c1.relnamespace" ,
};
/*
* Queries to get lists of names of various kinds of things , possibly
* restricted to names matching a partially entered name . Don ' t use
* this method where the user might wish to enter a schema - qualified
* name ; make a SchemaQuery instead .
*
* In these queries , there must be a restriction clause of the form
* output LIKE ' % s '
* where " output " is the same string that the query returns . The % s
* will be replaced by a LIKE pattern to match the already - typed text .
*
* There can be a second ' % s ' , which will be replaced by a suitably - escaped
* version of the string provided in completion_ref_object . If there is a
* third ' % s ' , it will be replaced by a suitably - escaped version of the string
* provided in completion_ref_schema . NOTE : using completion_ref_object
* that way is usually the wrong thing , and using completion_ref_schema
* that way is always the wrong thing . Make a SchemaQuery instead .
*/
#define Query_for_list_of_template_databases \
"SELECT d.datname " \
" FROM pg_catalog.pg_database d " \
" WHERE d.datname LIKE '%s' " \
" AND (d.datistemplate OR pg_catalog.pg_has_role(d.datdba, 'USAGE'))"
#define Query_for_list_of_databases \
"SELECT datname FROM pg_catalog.pg_database " \
" WHERE datname LIKE '%s'"
#define Query_for_list_of_database_vars \
"SELECT conf FROM (" \
" SELECT setdatabase, pg_catalog.split_part(pg_catalog.unnest(setconfig),'=',1) conf" \
" FROM pg_db_role_setting " \
" ) s, pg_database d " \
" WHERE s.setdatabase = d.oid " \
" AND conf LIKE '%s'" \
" AND d.datname LIKE '%s'"
#define Query_for_list_of_tablespaces \
"SELECT spcname FROM pg_catalog.pg_tablespace " \
" WHERE spcname LIKE '%s'"
#define Query_for_list_of_encodings \
" SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) " \
" FROM pg_catalog.pg_conversion " \
" WHERE pg_catalog.pg_encoding_to_char(conforencoding) LIKE pg_catalog.upper('%s')"
#define Query_for_list_of_languages \
"SELECT lanname " \
" FROM pg_catalog.pg_language " \
" WHERE lanname != 'internal' " \
" AND lanname LIKE '%s'"
#define Query_for_list_of_schemas \
"SELECT nspname FROM pg_catalog.pg_namespace " \
" WHERE nspname LIKE '%s'"
/* Use COMPLETE_WITH_QUERY_VERBATIM with these queries for GUC names: */
#define Query_for_list_of_alter_system_set_vars \
"SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings " \
" WHERE context != 'internal' " \
" AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')"
#define Query_for_list_of_set_vars \
"SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings " \
" WHERE context IN ('user', 'superuser') " \
" AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')"
#define Query_for_list_of_show_vars \
"SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings " \
" WHERE pg_catalog.lower(name) LIKE pg_catalog.lower('%s')"
#define Query_for_list_of_roles \
" SELECT rolname " \
" FROM pg_catalog.pg_roles " \
" WHERE rolname LIKE '%s'"
/* add these to Query_for_list_of_roles in OWNER contexts */
#define Keywords_for_list_of_owner_roles \
"CURRENT_ROLE" , "CURRENT_USER" , "SESSION_USER"
/* add these to Query_for_list_of_roles in GRANT contexts */
#define Keywords_for_list_of_grant_roles \
Keywords_for_list_of_owner_roles, "PUBLIC"
#define Query_for_all_table_constraints \
"SELECT conname " \
" FROM pg_catalog.pg_constraint c " \
" WHERE c.conrelid <> 0 " \
" and conname LIKE '%s'"
#define Query_for_list_of_fdws \
" SELECT fdwname " \
" FROM pg_catalog.pg_foreign_data_wrapper " \
" WHERE fdwname LIKE '%s'"
#define Query_for_list_of_servers \
" SELECT srvname " \
" FROM pg_catalog.pg_foreign_server " \
" WHERE srvname LIKE '%s'"
#define Query_for_list_of_user_mappings \
" SELECT usename " \
" FROM pg_catalog.pg_user_mappings " \
" WHERE usename LIKE '%s'"
#define Query_for_list_of_user_vars \
"SELECT conf FROM (" \
" SELECT rolname, pg_catalog.split_part(pg_catalog.unnest(rolconfig),'=',1) conf" \
" FROM pg_catalog.pg_roles" \
" ) s" \
" WHERE s.conf like '%s' " \
" AND s.rolname LIKE '%s'"
#define Query_for_list_of_access_methods \
" SELECT amname " \
" FROM pg_catalog.pg_am " \
" WHERE amname LIKE '%s'"
#define Query_for_list_of_index_access_methods \
" SELECT amname " \
" FROM pg_catalog.pg_am " \
" WHERE amname LIKE '%s' AND " \
" amtype=" CppAsString2(AMTYPE_INDEX)
#define Query_for_list_of_table_access_methods \
" SELECT amname " \
" FROM pg_catalog.pg_am " \
" WHERE amname LIKE '%s' AND " \
" amtype=" CppAsString2(AMTYPE_TABLE)
#define Query_for_list_of_extensions \
" SELECT extname " \
" FROM pg_catalog.pg_extension " \
" WHERE extname LIKE '%s'"
#define Query_for_list_of_available_extensions \
" SELECT name " \
" FROM pg_catalog.pg_available_extensions " \
" WHERE name LIKE '%s' AND installed_version IS NULL"
#define Query_for_list_of_available_extension_versions \
" SELECT version " \
" FROM pg_catalog.pg_available_extension_versions " \
" WHERE version LIKE '%s' AND name='%s'"
#define Query_for_list_of_prepared_statements \
" SELECT name " \
" FROM pg_catalog.pg_prepared_statements " \
" WHERE name LIKE '%s'"
#define Query_for_list_of_event_triggers \
" SELECT evtname " \
" FROM pg_catalog.pg_event_trigger " \
" WHERE evtname LIKE '%s'"
#define Query_for_list_of_tablesample_methods \
" SELECT proname " \
" FROM pg_catalog.pg_proc " \
" WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND " \
" proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND " \
" proname LIKE '%s'"
#define Query_for_list_of_policies \
" SELECT polname " \
" FROM pg_catalog.pg_policy " \
" WHERE polname LIKE '%s'"
#define Query_for_values_of_enum_GUC \
" SELECT val FROM ( " \
" SELECT name, pg_catalog.unnest(enumvals) AS val " \
" FROM pg_catalog.pg_settings " \
" ) ss " \
" WHERE val LIKE '%s'" \
" and pg_catalog.lower(name)=pg_catalog.lower('%s')"
#define Query_for_list_of_channels \
" SELECT channel " \
" FROM pg_catalog.pg_listening_channels() AS channel " \
" WHERE channel LIKE '%s'"
#define Query_for_list_of_cursors \
" SELECT name " \
" FROM pg_catalog.pg_cursors " \
" WHERE name LIKE '%s'"
#define Query_for_list_of_timezone_names_unquoted \
" SELECT name " \
" FROM pg_catalog.pg_timezone_names() " \
" WHERE pg_catalog.lower(name) LIKE pg_catalog.lower('%s')"
#define Query_for_list_of_timezone_names_quoted_out \
"SELECT pg_catalog.quote_literal(name) AS name " \
" FROM pg_catalog.pg_timezone_names() " \
" WHERE pg_catalog.lower(name) LIKE pg_catalog.lower('%s')"
#define Query_for_list_of_timezone_names_quoted_in \
"SELECT pg_catalog.quote_literal(name) AS name " \
" FROM pg_catalog.pg_timezone_names() " \
" WHERE pg_catalog.quote_literal(pg_catalog.lower(name)) LIKE pg_catalog.lower('%s')"
/* Privilege options shared between GRANT and REVOKE */
#define Privilege_options_of_grant_and_revoke \
"SELECT" , "INSERT" , "UPDATE" , "DELETE" , "TRUNCATE" , "REFERENCES" , "TRIGGER" , \
"CREATE" , "CONNECT" , "TEMPORARY" , "EXECUTE" , "USAGE" , "SET" , "ALTER SYSTEM" , \
"MAINTAIN" , "ALL"
/* ALTER PROCEDURE options */
#define Alter_procedure_options \
"DEPENDS ON EXTENSION" , "EXTERNAL SECURITY" , "NO DEPENDS ON EXTENSION" , \
"OWNER TO" , "RENAME TO" , "RESET" , "SECURITY" , "SET"
/* ALTER ROUTINE options */
#define Alter_routine_options \
Alter_procedure_options, "COST" , "IMMUTABLE" , "LEAKPROOF" , "NOT LEAKPROOF" , \
"PARALLEL" , "ROWS" , "STABLE" , "VOLATILE"
/* ALTER FUNCTION options */
#define Alter_function_options \
Alter_routine_options, "CALLED ON NULL INPUT" , "RETURNS NULL ON NULL INPUT" , \
"STRICT" , "SUPPORT"
/* COPY options shared between FROM and TO */
#define Copy_common_options \
"DELIMITER" , "ENCODING" , "ESCAPE" , "FORMAT" , "HEADER" , "NULL" , "QUOTE"
/* COPY FROM options */
#define Copy_from_options \
Copy_common_options, "DEFAULT" , "FORCE_NOT_NULL" , "FORCE_NULL" , "FREEZE" , \
"LOG_VERBOSITY" , "ON_ERROR" , "REJECT_LIMIT"
/* COPY TO options */
#define Copy_to_options \
Copy_common_options, "FORCE_QUOTE"
/*
* These object types were introduced later than our support cutoff of
* server version 9 . 2 . 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 pubname "
" FROM pg_catalog.pg_publication "
" WHERE pubname LIKE '%s'"
},
{0 , NULL}
};
static const VersionedQuery Query_for_list_of_subscriptions[] = {
{100000 ,
" SELECT s.subname "
" FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
" WHERE s.subname LIKE '%s' "
" AND d.datname = pg_catalog.current_database() "
" AND s.subdbid = d.oid"
},
{0 , NULL}
};
/* Known command-starting keywords. */
static const char *const sql_commands[] = {
"ABORT" , "ALTER" , "ANALYZE" , "BEGIN" , "CALL" , "CHECKPOINT" , "CLOSE" , "CLUSTER" ,
"COMMENT" , "COMMIT" , "COPY" , "CREATE" , "DEALLOCATE" , "DECLARE" ,
"DELETE FROM" , "DISCARD" , "DO" , "DROP" , "END" , "EXECUTE" , "EXPLAIN" ,
"FETCH" , "GRANT" , "IMPORT FOREIGN SCHEMA" , "INSERT INTO" , "LISTEN" , "LOAD" , "LOCK" ,
"MERGE INTO" , "MOVE" , "NOTIFY" , "PREPARE" ,
"REASSIGN" , "REFRESH MATERIALIZED VIEW" , "REINDEX" , "RELEASE" ,
"RESET" , "REVOKE" , "ROLLBACK" ,
"SAVEPOINT" , "SECURITY LABEL" , "SELECT" , "SET" , "SHOW" , "START" ,
"TABLE" , "TRUNCATE" , "UNLISTEN" , "UPDATE" , "VACUUM" , "VALUES" , "WITH" ,
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
{
const char *name;
/* Provide at most one of these three types of query: */
const char *query; /* simple query, or NULL */
const VersionedQuery *vquery; /* versioned query, or NULL */
const SchemaQuery *squery; /* schema query, or NULL */
const char *const *keywords; /* keywords to be offered as well */
const bits32 flags; /* visibility flags, see below */
} pgsql_thing_t;
#define THING_NO_CREATE (1 << 0 ) /* should not show up after CREATE */
#define THING_NO_DROP (1 << 1 ) /* should not show up after DROP */
#define THING_NO_ALTER (1 << 2 ) /* should not show up after ALTER */
#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)
/* When we have DROP USER etc, also offer MAPPING FOR */
static const char *const Keywords_for_user_thing[] = {
"MAPPING FOR" ,
NULL
};
static const pgsql_thing_t words_after_create[] = {
{"ACCESS METHOD" , NULL, 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" , NULL, NULL, &Query_for_list_of_collations},
/*
* CREATE CONSTRAINT TRIGGER is not supported here because it is designed
* to be used only by pg_dump .
*/
{"CONFIGURATION" , NULL, NULL, &Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"CONVERSION" , "SELECT conname FROM pg_catalog.pg_conversion WHERE conname LIKE '%s'" },
{"DATABASE" , Query_for_list_of_databases},
{"DEFAULT PRIVILEGES" , NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
{"DICTIONARY" , NULL, NULL, &Query_for_list_of_ts_dictionaries, 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, 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. */
{"OR REPLACE" , NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},
{"OWNED" , NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */
{"PARSER" , NULL, NULL, &Query_for_list_of_ts_parsers, 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, NULL, THING_NO_CREATE},
{"RULE" , "SELECT rulename FROM pg_catalog.pg_rules WHERE rulename LIKE '%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, 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, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE
* ... */
{"TEMPLATE" , NULL, NULL, &Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
{"TEMPORARY" , NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY
* TABLE ... */
{"TEXT SEARCH" , NULL, NULL, NULL},
{"TRANSFORM" , NULL, NULL, NULL, NULL, THING_NO_ALTER},
{"TRIGGER" , "SELECT tgname FROM pg_catalog.pg_trigger WHERE tgname LIKE '%s' AND NOT tgisinternal" },
{"TYPE" , NULL, NULL, &Query_for_list_of_datatypes},
{"UNIQUE" , NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
* INDEX ... */
{"UNLOGGED" , NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED
* TABLE ... */
{"USER" , Query_for_list_of_roles, NULL, NULL, Keywords_for_user_thing},
{"USER MAPPING FOR" , NULL, NULL, NULL},
{"VIEW" , NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
/*
* The tcpatterns [ ] table provides the initial pattern - match rule for each
* switch case in match_previous_words ( ) . The contents of the table
* are constructed by gen_tabcomplete . pl .
*/
/* Basic match rules appearing in tcpatterns[].kind */
enum TCPatternKind
{
Match,
MatchCS,
HeadMatch,
HeadMatchCS,
TailMatch,
TailMatchCS,
};
/* Things besides string literals that can appear in tcpatterns[].words */
#define MatchAny NULL
#define MatchAnyExcept(pattern) ("!" pattern)
#define MatchAnyN ""
/* One entry in tcpatterns[] */
typedef struct
{
int id; /* case label used in match_previous_words */
enum TCPatternKind kind; /* match kind, see above */
int nwords; /* length of words[] array */
const char *const *words; /* array of match words */
} TCPattern;
/* Macro emitted by gen_tabcomplete.pl to fill a tcpatterns[] entry */
#define TCPAT(id, kind, ...) \
{ (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
(const char * const []) { __VA_ARGS__ } }
#ifdef SWITCH_CONVERSION_APPLIED
static const TCPattern tcpatterns[] =
{
/* Insert tab-completion pattern data here. */
};
#endif /* SWITCH_CONVERSION_APPLIED */
/* Storage parameters for CREATE TABLE and ALTER TABLE */
static const char *const table_storage_parameters[] = {
"autovacuum_analyze_scale_factor" ,
"autovacuum_analyze_threshold" ,
"autovacuum_enabled" ,
"autovacuum_freeze_max_age" ,
"autovacuum_freeze_min_age" ,
"autovacuum_freeze_table_age" ,
"autovacuum_multixact_freeze_max_age" ,
"autovacuum_multixact_freeze_min_age" ,
"autovacuum_multixact_freeze_table_age" ,
"autovacuum_vacuum_cost_delay" ,
"autovacuum_vacuum_cost_limit" ,
"autovacuum_vacuum_insert_scale_factor" ,
"autovacuum_vacuum_insert_threshold" ,
"autovacuum_vacuum_max_threshold" ,
"autovacuum_vacuum_scale_factor" ,
"autovacuum_vacuum_threshold" ,
"fillfactor" ,
"log_autovacuum_min_duration" ,
"parallel_workers" ,
"toast.autovacuum_enabled" ,
"toast.autovacuum_freeze_max_age" ,
"toast.autovacuum_freeze_min_age" ,
"toast.autovacuum_freeze_table_age" ,
"toast.autovacuum_multixact_freeze_max_age" ,
"toast.autovacuum_multixact_freeze_min_age" ,
"toast.autovacuum_multixact_freeze_table_age" ,
"toast.autovacuum_vacuum_cost_delay" ,
"toast.autovacuum_vacuum_cost_limit" ,
"toast.autovacuum_vacuum_insert_scale_factor" ,
"toast.autovacuum_vacuum_insert_threshold" ,
"toast.autovacuum_vacuum_max_threshold" ,
"toast.autovacuum_vacuum_scale_factor" ,
"toast.autovacuum_vacuum_threshold" ,
"toast.log_autovacuum_min_duration" ,
"toast.vacuum_index_cleanup" ,
"toast.vacuum_max_eager_freeze_failure_rate" ,
"toast.vacuum_truncate" ,
"toast_tuple_target" ,
"user_catalog_table" ,
"vacuum_index_cleanup" ,
"vacuum_max_eager_freeze_failure_rate" ,
"vacuum_truncate" ,
NULL
};
/* Optional parameters for CREATE VIEW and ALTER VIEW */
static const char *const view_optional_parameters[] = {
"check_option" ,
"security_barrier" ,
"security_invoker" ,
NULL
};
/* Forward declaration of functions */
static char **psql_completion(const char *text, int start, int end);
static char **match_previous_words(int pattern_id,
const char *text, int start, int end,
char **previous_words,
int previous_words_count);
static char *create_command_generator(const char *text, int state);
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 *const *keywords,
bool verbatim,
const char *text, int state);
static void set_completion_reference(const char *word);
static void set_completion_reference_verbatim(const char *word);
static char *complete_from_list(const char *text, int state);
static char *complete_from_const(const char *text, int state);
static void append_variable_names(char ***varnames, int *nvars,
int *maxvars, const char *varname,
const char *prefix, const char *suffix);
static char **complete_from_variables(const char *text,
const char *prefix, const char *suffix, bool need_value);
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 char *make_like_pattern(const char *word);
static void parse_identifier(const char *ident,
char **schemaname, char **objectname,
bool *schemaquoted, bool *objectquoted);
static char *requote_identifier(const char *schemaname, const char *objectname,
bool quote_schema, bool quote_object);
static bool identifier_needs_quotes(const char *ident);
static PGresult *exec_query(const char *query);
static char **get_previous_words(int point, char **buffer, int *nwords);
static char *get_guctype(const char *varname);
#ifdef USE_FILENAME_QUOTING_FUNCTIONS
static char *quote_file_name(char *fname, int match_type, char *quote_pointer);
static char *dequote_file_name(char *fname, int quote_char);
#endif
/*
* Initialize the readline library for our purposes .
*/
void
initialize_readline(void )
{
rl_readline_name = (char *) pset.progname;
rl_attempted_completion_function = psql_completion;
#ifdef USE_FILENAME_QUOTING_FUNCTIONS
rl_filename_quoting_function = quote_file_name;
rl_filename_dequoting_function = dequote_file_name;
#endif
rl_basic_word_break_characters = WORD_BREAKS;
/*
* Ideally we ' d include ' " ' in rl_completer_quote_characters too , which
* should allow us to complete quoted identifiers that include spaces .
* However , the library support for rl_completer_quote_characters is
* presently too inconsistent to want to mess with that . ( Note in
* particular that libedit has this variable but completely ignores it . )
*/
rl_completer_quote_characters = "'" ;
/*
* Set rl_filename_quote_characters to " all possible characters " ,
* otherwise Readline will skip filename quoting if it thinks a filename
* doesn ' t need quoting . Readline actually interprets this as bytes , so
* there are no encoding considerations here .
*/
#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
{
unsigned char *fqc = (unsigned char *) pg_malloc(256 );
for (int i = 0 ; i < 255 ; i++)
fqc[i] = (unsigned char ) (i + 1 );
fqc[255 ] = '\0' ;
rl_filename_quote_characters = (const char *) fqc;
}
#endif
completion_max_records = 1000 ;
/*
* There is a variable rl_completion_query_items for this but apparently
* it ' s not defined everywhere .
*/
}
/*
* Check if ' word ' matches any of the ' | ' - separated strings in ' pattern ' ,
* using case - insensitive or case - sensitive comparisons .
*
* If pattern is NULL , it ' s a wild card that matches any word .
* If pattern begins with ' ! ' , the result is negated , ie we check that ' word '
* does * not * match any alternative appearing in the rest of ' pattern ' .
* Any alternative can contain ' * ' which is a wild card , i . e . , it can match
* any substring ; however , we allow at most one ' * ' per alternative .
*
* For readability , callers should use the macros MatchAny and MatchAnyExcept
* to invoke those two special cases for ' pattern ' . ( But ' | ' and ' * ' must
* just be written directly in patterns . ) There is also MatchAnyN , but that
* is supported only in Matches / MatchesCS and is not handled here .
*/
static bool
word_matches(const char *pattern,
const char *word,
bool case_sensitive)
{
size_t wordlen;
#define cimatch(s1, s2, n) \
(case_sensitive ? strncmp(s1, s2, n) == 0 : pg_strncasecmp(s1, s2, n) == 0 )
/* NULL pattern matches anything. */
if (pattern == NULL)
return true ;
/* Handle negated patterns from the MatchAnyExcept macro. */
if (*pattern == '!' )
return !word_matches(pattern + 1 , word, case_sensitive);
/* Else consider each alternative in the pattern. */
wordlen = strlen(word);
for (;;)
{
const char *star = NULL;
const char *c;
/* Find end of current alternative, and locate any wild card. */
c = pattern;
while (*c != '\0' && *c != '|' )
{
if (*c == '*' )
star = c;
c++;
}
/* Was there a wild card? */
if (star)
{
/* Yes, wildcard match? */
size_t beforelen = star - pattern,
afterlen = c - star - 1 ;
if (wordlen >= (beforelen + afterlen) &&
cimatch(word, pattern, beforelen) &&
cimatch(word + wordlen - afterlen, star + 1 , afterlen))
return true ;
}
else
{
/* No, plain match? */
if (wordlen == (c - pattern) &&
cimatch(word, pattern, wordlen))
return true ;
}
/* Out of alternatives? */
if (*c == '\0' )
break ;
/* Nope, try next alternative. */
pattern = c + 1 ;
}
return false ;
}
/*
* Implementation of TailMatches and TailMatchesCS tests : do the last N words
* in previous_words match the pattern arguments ?
*
* The array indexing might look backwards , but remember that
* previous_words [ 0 ] contains the * last * word on the line , not the first .
*/
static bool
TailMatchesArray(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg, const char *const *args)
{
if (previous_words_count < narg)
return false ;
for (int argno = 0 ; argno < narg; argno++)
{
const char *arg = args[argno];
if (!word_matches(arg, previous_words[narg - argno - 1 ],
case_sensitive))
return false ;
}
return true ;
}
/*
* As above , but the pattern is passed as a variadic argument list .
*/
static bool
TailMatchesImpl(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg,...)
{
const char *argarray[64 ];
va_list args;
Assert(narg <= lengthof(argarray));
if (previous_words_count < narg)
return false ;
va_start(args, narg);
for (int argno = 0 ; argno < narg; argno++)
argarray[argno] = va_arg(args, const char *);
va_end(args);
return TailMatchesArray(case_sensitive,
previous_words_count, previous_words,
narg, argarray);
}
/*
* Implementation of HeadMatches and HeadMatchesCS tests : do the first N
* words in previous_words match the pattern arguments ?
*/
static bool
HeadMatchesArray(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg, const char *const *args)
{
if (previous_words_count < narg)
return false ;
for (int argno = 0 ; argno < narg; argno++)
{
const char *arg = args[argno];
if (!word_matches(arg, previous_words[previous_words_count - argno - 1 ],
case_sensitive))
return false ;
}
return true ;
}
/*
* As above , but the pattern is passed as a variadic argument list .
*/
static bool
HeadMatchesImpl(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg,...)
{
const char *argarray[64 ];
va_list args;
Assert(narg <= lengthof(argarray));
if (previous_words_count < narg)
return false ;
va_start(args, narg);
for (int argno = 0 ; argno < narg; argno++)
argarray[argno] = va_arg(args, const char *);
va_end(args);
return HeadMatchesArray(case_sensitive,
previous_words_count, previous_words,
narg, argarray);
}
/*
* Implementation of Matches and MatchesCS tests : do all of the words
* in previous_words match the pattern arguments ?
*
* This supports an additional kind of wildcard : MatchAnyN ( represented as " " )
* can match any number of words , including zero , in the middle of the list .
*/
static bool
MatchesArray(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg, const char *const *args)
{
int match_any_pos = -1 ;
/* Even with MatchAnyN, there must be at least N-1 words */
if (previous_words_count < narg - 1 )
return false ;
/* Check for MatchAnyN */
for (int argno = 0 ; argno < narg; argno++)
{
const char *arg = args[argno];
if (arg != NULL && arg[0 ] == '\0' )
{
match_any_pos = argno;
break ;
}
}
if (match_any_pos < 0 )
{
/* Standard case without MatchAnyN */
if (previous_words_count != narg)
return false ;
/* Either Head or Tail match will do for the rest */
if (!HeadMatchesArray(case_sensitive,
previous_words_count, previous_words,
narg, args))
return false ;
}
else
{
/* Match against head */
if (!HeadMatchesArray(case_sensitive,
previous_words_count, previous_words,
match_any_pos, args))
return false ;
/* Match against tail */
if (!TailMatchesArray(case_sensitive,
previous_words_count, previous_words,
narg - match_any_pos - 1 ,
args + match_any_pos + 1 ))
return false ;
}
return true ;
}
/*
* As above , but the pattern is passed as a variadic argument list .
*/
static bool
MatchesImpl(bool case_sensitive,
int previous_words_count, char **previous_words,
int narg,...)
{
const char *argarray[64 ];
va_list args;
Assert(narg <= lengthof(argarray));
/* Even with MatchAnyN, there must be at least N-1 words */
if (previous_words_count < narg - 1 )
return false ;
va_start(args, narg);
for (int argno = 0 ; argno < narg; argno++)
argarray[argno] = va_arg(args, const char *);
va_end(args);
return MatchesArray(case_sensitive,
previous_words_count, previous_words,
narg, argarray);
}
/*
* Check if the final character of ' s ' is ' c ' .
*/
static bool
ends_with(const char *s, char c)
{
size_t length = strlen(s);
return (length > 0 && s[length - 1 ] == c);
}
/*
* 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
* rl_completion_matches ( ) function , so we don ' t have to worry about it .
*/
static char **
psql_completion(const char *text, int start, int end)
{
/* This is the variable we'll return. */
char **matches = NULL;
/* Workspace for parsed words. */
char *words_buffer;
/* This array will contain pointers to parsed words. */
char **previous_words;
/* The number of words found on the input line. */
int previous_words_count;
/*
* For compactness , we use these macros to reference previous_words [ ] .
* Caution : do not access a previous_words [ ] entry without having checked
* previous_words_count to be sure it ' s valid . In most cases below , that
* check is implicit in a TailMatches ( ) or similar macro , but in some
* places we have to check it explicitly .
*/
#define prev_wd (previous_words[0 ])
#define prev2_wd (previous_words[1 ])
#define prev3_wd (previous_words[2 ])
#define prev4_wd (previous_words[3 ])
#define prev5_wd (previous_words[4 ])
#define prev6_wd (previous_words[5 ])
#define prev7_wd (previous_words[6 ])
#define prev8_wd (previous_words[7 ])
#define prev9_wd (previous_words[8 ])
/* Match the last N words before point, case-insensitively. */
#define TailMatches(...) \
TailMatchesImpl(false , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Match the last N words before point, case-sensitively. */
#define TailMatchesCS(...) \
TailMatchesImpl(true , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Match N words representing all of the line, case-insensitively. */
#define Matches(...) \
MatchesImpl(false , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Match N words representing all of the line, case-sensitively. */
#define MatchesCS(...) \
MatchesImpl(true , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Match the first N words on the line, case-insensitively. */
#define HeadMatches(...) \
HeadMatchesImpl(false , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Match the first N words on the line, case-sensitively. */
#define HeadMatchesCS(...) \
HeadMatchesImpl(true , previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
/* psql's backslash commands. */
static const char *const backslash_commands[] = {
"\\a" ,
"\\bind" , "\\bind_named" ,
"\\connect" , "\\conninfo" , "\\C" , "\\cd" , "\\close_prepared" , "\\copy" ,
"\\copyright" , "\\crosstabview" ,
"\\d" , "\\da" , "\\dA" , "\\dAc" , "\\dAf" , "\\dAo" , "\\dAp" ,
"\\db" , "\\dc" , "\\dconfig" , "\\dC" , "\\dd" , "\\ddp" , "\\dD" ,
"\\des" , "\\det" , "\\deu" , "\\dew" , "\\dE" , "\\df" ,
"\\dF" , "\\dFd" , "\\dFp" , "\\dFt" , "\\dg" , "\\di" , "\\dl" , "\\dL" ,
"\\dm" , "\\dn" , "\\do" , "\\dO" , "\\dp" , "\\dP" , "\\dPi" , "\\dPt" ,
"\\drds" , "\\drg" , "\\dRs" , "\\dRp" , "\\ds" ,
"\\dt" , "\\dT" , "\\dv" , "\\du" , "\\dx" , "\\dX" , "\\dy" ,
"\\echo" , "\\edit" , "\\ef" , "\\elif" , "\\else" , "\\encoding" ,
"\\endif" , "\\endpipeline" , "\\errverbose" , "\\ev" ,
"\\f" , "\\flush" , "\\flushrequest" ,
"\\g" , "\\gdesc" , "\\getenv" , "\\getresults" , "\\gexec" , "\\gset" , "\\gx" ,
"\\help" , "\\html" ,
"\\if" , "\\include" , "\\include_relative" , "\\ir" ,
"\\list" , "\\lo_import" , "\\lo_export" , "\\lo_list" , "\\lo_unlink" ,
"\\out" ,
"\\parse" , "\\password" , "\\print" , "\\prompt" , "\\pset" ,
"\\qecho" , "\\quit" ,
"\\reset" , "\\restrict" ,
"\\s" , "\\sendpipeline" , "\\set" , "\\setenv" , "\\sf" ,
"\\startpipeline" , "\\sv" , "\\syncpipeline" ,
"\\t" , "\\T" , "\\timing" ,
"\\unrestrict" , "\\unset" ,
"\\x" ,
"\\warn" , "\\watch" , "\\write" ,
"\\z" ,
"\\!" , "\\?" ,
NULL
};
/*
* Temporary workaround for a bug in recent ( 2019 ) libedit : it incorrectly
* de - escapes the input " text " , causing us to fail to recognize backslash
* commands . So get the string to look at from rl_line_buffer instead .
*/
char *text_copy = pnstrdup(rl_line_buffer + start, end - start);
text = text_copy;
/* Remember last char of the given input word. */
completion_last_char = (end > start) ? text[end - start - 1 ] : '\0' ;
/* We usually want the append character to be a space. */
rl_completion_append_character = ' ' ;
/* Clear a few things. */
completion_charp = NULL;
completion_charpp = NULL;
completion_vquery = NULL;
completion_squery = NULL;
completion_ref_object = NULL;
completion_ref_schema = NULL;
/*
* Scan the input line to extract the words before our current position .
* According to those we ' ll make some smart decisions on what the user is
* probably intending to type .
*/
previous_words = get_previous_words(start,
&words_buffer,
&previous_words_count);
/* If current word is a backslash command, offer completions for that */
if (text[0 ] == '\\' )
COMPLETE_WITH_LIST_CS(backslash_commands);
/* If current word is a variable interpolation, handle that case */
else if (text[0 ] == ':' && text[1 ] != ':' )
{
if (text[1 ] == '\' ')
matches = complete_from_variables(text, ":'" , "'" , true );
else if (text[1 ] == '"' )
matches = complete_from_variables(text, ":\" ", " \"" , true );
else if (text[1 ] == '{' && text[2 ] == '?' )
matches = complete_from_variables(text, ":{?" , "}" , true );
else
matches = complete_from_variables(text, ":" , "" , true );
}
/* If no previous word, suggest one of the basic sql commands */
else if (previous_words_count == 0 )
COMPLETE_WITH_LIST(sql_commands);
/* Else try completions based on matching patterns of previous words */
else
{
#ifdef SWITCH_CONVERSION_APPLIED
/*
* If we have transformed match_previous_words into a switch , iterate
* through tcpatterns [ ] to see which pattern ids match .
*
* For now , we have to try the patterns in the order they are stored
* ( matching the order of switch cases in match_previous_words ) ,
* because some of the logic in match_previous_words assumes that
* previous matches have been eliminated . This is fairly
* unprincipled , and it is likely that there are undesirable as well
* as desirable interactions hidden in the order of the pattern
* checks . TODO : think about a better way to manage that .
*/
for (int tindx = 0 ; tindx < lengthof(tcpatterns); tindx++)
{
const TCPattern *tcpat = tcpatterns + tindx;
bool match = false ;
switch (tcpat->kind)
{
case Match:
match = MatchesArray(false ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
case MatchCS:
match = MatchesArray(true ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
case HeadMatch:
match = HeadMatchesArray(false ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
case HeadMatchCS:
match = HeadMatchesArray(true ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
case TailMatch:
match = TailMatchesArray(false ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
case TailMatchCS:
match = TailMatchesArray(true ,
previous_words_count,
previous_words,
tcpat->nwords, tcpat->words);
break ;
}
if (match)
{
matches = match_previous_words(tcpat->id, text, start, end,
previous_words,
previous_words_count);
if (matches != NULL)
break ;
}
}
#else /* !SWITCH_CONVERSION_APPLIED */
/*
* If gen_tabcomplete . pl hasn ' t been applied to this code , just let
* match_previous_words scan through all its patterns .
*/
matches = match_previous_words(0 , text, start, end,
previous_words,
previous_words_count);
#endif /* SWITCH_CONVERSION_APPLIED */
}
/*
* Finally , we look through the list of " things " , such as TABLE , INDEX and
* check if that was the previous word . If so , execute the query to get a
* list of them .
*/
if (matches == NULL && previous_words_count > 0 )
{
const pgsql_thing_t *wac;
for (wac = words_after_create; wac->name != NULL; wac++)
{
if (pg_strcasecmp(prev_wd, wac->name) == 0 )
{
if (wac->query)
COMPLETE_WITH_QUERY_LIST(wac->query,
wac->keywords);
else if (wac->vquery)
COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
wac->keywords);
else if (wac->squery)
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
wac->keywords);
break ;
}
}
}
/*
* If we still don ' t have anything to match we have to fabricate some sort
* of default list . If we were to just return NULL , readline automatically
* attempts filename completion , and that ' s usually no good .
*/
if (matches == NULL)
{
COMPLETE_WITH_CONST(true , "" );
/* Also, prevent Readline from appending stuff to the non-match */
rl_completion_append_character = '\0' ;
#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
rl_completion_suppress_quote = 1 ;
#endif
}
/* free storage */
free(previous_words);
free(words_buffer);
free(text_copy);
free(completion_ref_object);
completion_ref_object = NULL;
free(completion_ref_schema);
completion_ref_schema = NULL;
/* Return our Grand List O' Matches */
return matches;
}
/*
* Subroutine to try matches based on previous_words .
*
* This can operate in one of two modes . As presented , the body of the
* function is a long if - else - if chain that sequentially tries each known
* match rule . That works , but some C compilers have trouble with such a long
* else - if chain , either taking extra time to compile or failing altogether .
* Therefore , we prefer to transform the else - if chain into a switch , and then
* each call of this function considers just one match rule ( under control of
* a loop in psql_completion ( ) ) . Compilers tend to be more ready to deal
* with many - arm switches than many - arm else - if chains .
*
* Each if - condition in this function must begin with a call of one of the
* functions Matches , HeadMatches , TailMatches , MatchesCS , HeadMatchesCS , or
* TailMatchesCS . The preprocessor gen_tabcomplete . pl strips out those
* calls and converts them into entries in tcpatterns [ ] , which are evaluated
* by the calling loop in psql_completion ( ) . Successful matches result in
* calls to this function with the appropriate pattern_id , causing just the
* corresponding switch case to be executed .
*
* If - conditions in this function can be more complex than a single * Matches
* function call in one of two ways ( but not both ! ) . They can be OR ' s
* of * Matches calls , such as
* else if ( Matches ( " ALTER " , " VIEW " , MatchAny , " ALTER " , MatchAny ) | |
* Matches ( " ALTER " , " VIEW " , MatchAny , " ALTER " , " COLUMN " , MatchAny ) )
* or they can be a * Matches call AND ' ed with some other condition , e . g .
* else if ( Matches ( " CREATE " , " PUBLICATION " , MatchAny , " FOR " , " TABLE " , MatchAny ) & &an>
* ! ends_with ( prev_wd , ' , ' ) )
* The former case is transformed into multiple tcpatterns [ ] entries and
* multiple case labels for the same bit of code . The latter case is
* transformed into a case label and a contained if - statement .
*
* This is split out of psql_completion ( ) primarily to separate code that
* gen_tabcomplete . pl should process from code that it should not , although
* doing so also helps to avoid extra indentation of this code .
*
* Returns a matches list , or NULL if no match .
*/
static char **
match_previous_words(int pattern_id,
const char *text, int start, int end,
char **previous_words, int previous_words_count)
{
/* This is the variable we'll return. */
char **matches = NULL;
/* Dummy statement, allowing all the match rules to look like "else if" */
if (0 )
{
/* skip */
}
/* gen_tabcomplete.pl begins special processing here */
/* BEGIN GEN_TABCOMPLETE */
/* CREATE */
/* complete with something you can create */
else if (TailMatches("CREATE" ))
{
/* only some object types can be created as part of CREATE SCHEMA */
if (HeadMatches("CREATE" , "SCHEMA" ))
COMPLETE_WITH("TABLE" , "VIEW" , "INDEX" , "SEQUENCE" , "TRIGGER" ,
/* for INDEX and TABLE/SEQUENCE, respectively */
"UNIQUE" , "UNLOGGED" );
else
matches = rl_completion_matches(text, create_command_generator);
}
/* complete with something you can create or replace */
else if (TailMatches("CREATE" , "OR" , "REPLACE" ))
COMPLETE_WITH("FUNCTION" , "PROCEDURE" , "LANGUAGE" , "RULE" , "VIEW" ,
"AGGREGATE" , "TRANSFORM" , "TRIGGER" );
/* DROP, but not DROP embedded in other commands */
/* complete with something you can drop */
else if (Matches("DROP" ))
matches = rl_completion_matches(text, drop_command_generator);
/* ALTER */
/* ALTER TABLE */
else if (Matches("ALTER" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
"ALL IN TABLESPACE" );
/* ALTER something */
else if (Matches("ALTER" ))
matches = rl_completion_matches(text, alter_command_generator);
/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
else if (TailMatches("ALL" , "IN" , "TABLESPACE" , MatchAny))
COMPLETE_WITH("SET TABLESPACE" , "OWNED BY" );
/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */
else if (TailMatches("ALL" , "IN" , "TABLESPACE" , MatchAny, "OWNED" , "BY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */
else if (TailMatches("ALL" , "IN" , "TABLESPACE" , MatchAny, "OWNED" , "BY" , MatchAny))
COMPLETE_WITH("SET TABLESPACE" );
/* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */
else if (Matches("ALTER" , "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE" , MatchAny))
COMPLETE_WITH("(" );
/* ALTER AGGREGATE <name> (...) */
else if (Matches("ALTER" , "AGGREGATE" , MatchAny, MatchAny))
{
if (ends_with(prev_wd, ')' ))
COMPLETE_WITH("OWNER TO" , "RENAME TO" , "SET SCHEMA" );
else
COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
/* ALTER FUNCTION <name> (...) */
else if (Matches("ALTER" , "FUNCTION" , MatchAny, MatchAny))
{
if (ends_with(prev_wd, ')' ))
COMPLETE_WITH(Alter_function_options);
else
COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
/* ALTER PROCEDURE <name> (...) */
else if (Matches("ALTER" , "PROCEDURE" , MatchAny, MatchAny))
{
if (ends_with(prev_wd, ')' ))
COMPLETE_WITH(Alter_procedure_options);
else
COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
/* ALTER ROUTINE <name> (...) */
else if (Matches("ALTER" , "ROUTINE" , MatchAny, MatchAny))
{
if (ends_with(prev_wd, ')' ))
COMPLETE_WITH(Alter_routine_options);
else
COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
/* ALTER FUNCTION|ROUTINE <name> (...) PARALLEL */
else if (Matches("ALTER" , "FUNCTION|ROUTINE" , MatchAny, MatchAny, "PARALLEL" ))
COMPLETE_WITH("RESTRICTED" , "SAFE" , "UNSAFE" );
/* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) [EXTERNAL] SECURITY */
else if (Matches("ALTER" , "FUNCTION|PROCEDURE|ROUTINE" , MatchAny, MatchAny, "SECURITY" ) ||
Matches("ALTER" , "FUNCTION|PROCEDURE|ROUTINE" , MatchAny, MatchAny, "EXTERNAL" , "SECURITY" ))
COMPLETE_WITH("DEFINER" , "INVOKER" );
/* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) RESET */
else if (Matches("ALTER" , "FUNCTION|PROCEDURE|ROUTINE" , MatchAny, MatchAny, "RESET" ))
COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
"ALL" );
/* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) SET */
else if (Matches("ALTER" , "FUNCTION|PROCEDURE|ROUTINE" , MatchAny, MatchAny, "SET" ))
COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
"SCHEMA" );
/* ALTER PUBLICATION <name> */
else if (Matches("ALTER" , "PUBLICATION" , MatchAny))
COMPLETE_WITH("ADD" , "DROP" , "OWNER TO" , "RENAME TO" , "SET" );
/* ALTER PUBLICATION <name> ADD */
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, "ADD" ))
COMPLETE_WITH("TABLES IN SCHEMA" , "TABLE" );
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, "ADD|SET" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (HeadMatches("ALTER" , "PUBLICATION" , MatchAny, "ADD|SET" , "TABLE" ) &&
ends_with(prev_wd, ',' ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/*
* " ALTER PUBLICATION < name > SET TABLE < name > WHERE ( " - complete with
* table attributes
*
* " ALTER PUBLICATION < name > ADD TABLE < name > WHERE ( " - complete with
* table attributes
*/
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, MatchAnyN, "WHERE" ))
COMPLETE_WITH("(" );
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, MatchAnyN, "WHERE" , "(" ))
COMPLETE_WITH_ATTR(prev3_wd);
else if (HeadMatches("ALTER" , "PUBLICATION" , MatchAny, "ADD|SET" , "TABLE" ) &&
!TailMatches("WHERE" , "(*)" ))
COMPLETE_WITH("," , "WHERE (" );
else if (HeadMatches("ALTER" , "PUBLICATION" , MatchAny, "ADD|SET" , "TABLE" ))
COMPLETE_WITH("," );
/* ALTER PUBLICATION <name> DROP */
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, "DROP" ))
COMPLETE_WITH("TABLES IN SCHEMA" , "TABLE" );
/* ALTER PUBLICATION <name> SET */
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "TABLES IN SCHEMA" , "TABLE" );
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, "ADD|DROP|SET" , "TABLES" , "IN" , "SCHEMA" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'" ,
"CURRENT_SCHEMA" );
/* ALTER PUBLICATION <name> SET ( */
else if (Matches("ALTER" , "PUBLICATION" , MatchAny, MatchAnyN, "SET" , "(" ))
COMPLETE_WITH("publish" , "publish_generated_columns" , "publish_via_partition_root" );
/* ALTER SUBSCRIPTION <name> */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny))
COMPLETE_WITH("CONNECTION" , "ENABLE" , "DISABLE" , "OWNER TO" ,
"RENAME TO" , "REFRESH PUBLICATION" , "SET" , "SKIP (" ,
"ADD PUBLICATION" , "DROP PUBLICATION" );
/* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN, "REFRESH" , "PUBLICATION" ))
COMPLETE_WITH("WITH (" );
/* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN, "REFRESH" , "PUBLICATION" , "WITH" , "(" ))
COMPLETE_WITH("copy_data" );
/* ALTER SUBSCRIPTION <name> SET */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "PUBLICATION" );
/* ALTER SUBSCRIPTION <name> SET ( */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN, "SET" , "(" ))
COMPLETE_WITH("binary" , "disable_on_error" , "failover" , "origin" ,
"password_required" , "run_as_owner" , "slot_name" ,
"streaming" , "synchronous_commit" , "two_phase" );
/* ALTER SUBSCRIPTION <name> SKIP ( */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN, "SKIP" , "(" ))
COMPLETE_WITH("lsn" );
/* ALTER SUBSCRIPTION <name> SET PUBLICATION */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN, "SET" , "PUBLICATION" ))
{
/* complete with nothing here as this refers to remote publications */
}
/* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN,
"ADD|DROP|SET" , "PUBLICATION" , MatchAny))
COMPLETE_WITH("WITH (" );
/* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
else if (Matches("ALTER" , "SUBSCRIPTION" , MatchAny, MatchAnyN,
"ADD|DROP|SET" , "PUBLICATION" , MatchAny, "WITH" , "(" ))
COMPLETE_WITH("copy_data" , "refresh" );
/* ALTER SCHEMA <name> */
else if (Matches("ALTER" , "SCHEMA" , MatchAny))
COMPLETE_WITH("OWNER TO" , "RENAME TO" );
/* ALTER COLLATION <name> */
else if (Matches("ALTER" , "COLLATION" , MatchAny))
COMPLETE_WITH("OWNER TO" , "REFRESH VERSION" , "RENAME TO" , "SET SCHEMA" );
/* ALTER CONVERSION <name> */
else if (Matches("ALTER" , "CONVERSION" , MatchAny))
COMPLETE_WITH("OWNER TO" , "RENAME TO" , "SET SCHEMA" );
/* ALTER DATABASE <name> */
else if (Matches("ALTER" , "DATABASE" , MatchAny))
COMPLETE_WITH("RESET" , "SET" , "OWNER TO" , "REFRESH COLLATION VERSION" , "RENAME TO" ,
"IS_TEMPLATE" , "ALLOW_CONNECTIONS" ,
"CONNECTION LIMIT" );
/* ALTER DATABASE <name> RESET */
else if (Matches("ALTER" , "DATABASE" , MatchAny, "RESET" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_database_vars, "ALL" );
}
/* ALTER DATABASE <name> SET TABLESPACE */
else if (Matches("ALTER" , "DATABASE" , MatchAny, "SET" , "TABLESPACE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
/* ALTER EVENT TRIGGER */
else if (Matches("ALTER" , "EVENT" , "TRIGGER" ))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
/* ALTER EVENT TRIGGER <name> */
else if (Matches("ALTER" , "EVENT" , "TRIGGER" , MatchAny))
COMPLETE_WITH("DISABLE" , "ENABLE" , "OWNER TO" , "RENAME TO" );
/* ALTER EVENT TRIGGER <name> ENABLE */
else if (Matches("ALTER" , "EVENT" , "TRIGGER" , MatchAny, "ENABLE" ))
COMPLETE_WITH("REPLICA" , "ALWAYS" );
/* ALTER EXTENSION <name> */
else if (Matches("ALTER" , "EXTENSION" , MatchAny))
COMPLETE_WITH("ADD" , "DROP" , "UPDATE" , "SET SCHEMA" );
/* ALTER EXTENSION <name> ADD|DROP */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "ADD|DROP" ))
COMPLETE_WITH("ACCESS METHOD" , "AGGREGATE" , "CAST" , "COLLATION" ,
"CONVERSION" , "DOMAIN" , "EVENT TRIGGER" , "FOREIGN" ,
"FUNCTION" , "MATERIALIZED VIEW" , "OPERATOR" ,
"LANGUAGE" , "PROCEDURE" , "ROUTINE" , "SCHEMA" ,
"SEQUENCE" , "SERVER" , "TABLE" , "TEXT SEARCH" ,
"TRANSFORM FOR" , "TYPE" , "VIEW" );
/* ALTER EXTENSION <name> ADD|DROP FOREIGN */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "ADD|DROP" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
/* ALTER EXTENSION <name> ADD|DROP OPERATOR */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "ADD|DROP" , "OPERATOR" ))
COMPLETE_WITH("CLASS" , "FAMILY" );
/* ALTER EXTENSION <name> ADD|DROP TEXT SEARCH */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "ADD|DROP" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
/* ALTER EXTENSION <name> UPDATE */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "UPDATE" ))
COMPLETE_WITH("TO" );
/* ALTER EXTENSION <name> UPDATE TO */
else if (Matches("ALTER" , "EXTENSION" , MatchAny, "UPDATE" , "TO" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
}
/* ALTER FOREIGN */
else if (Matches("ALTER" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
/* ALTER FOREIGN DATA WRAPPER <name> */
else if (Matches("ALTER" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny))
COMPLETE_WITH("HANDLER" , "VALIDATOR" , "NO" ,
"OPTIONS" , "OWNER TO" , "RENAME TO" );
else if (Matches("ALTER" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny, "NO" ))
COMPLETE_WITH("HANDLER" , "VALIDATOR" );
/* ALTER FOREIGN TABLE <name> */
else if (Matches("ALTER" , "FOREIGN" , "TABLE" , MatchAny))
COMPLETE_WITH("ADD" , "ALTER" , "DISABLE TRIGGER" , "DROP" , "ENABLE" ,
"INHERIT" , "NO INHERIT" , "OPTIONS" , "OWNER TO" ,
"RENAME" , "SET" , "VALIDATE CONSTRAINT" );
/* ALTER INDEX */
else if (Matches("ALTER" , "INDEX" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
"ALL IN TABLESPACE" );
/* ALTER INDEX <name> */
else if (Matches("ALTER" , "INDEX" , MatchAny))
COMPLETE_WITH("ALTER COLUMN" , "OWNER TO" , "RENAME TO" , "SET" ,
"RESET" , "ATTACH PARTITION" ,
"DEPENDS ON EXTENSION" , "NO DEPENDS ON EXTENSION" );
else if (Matches("ALTER" , "INDEX" , MatchAny, "ATTACH" ))
COMPLETE_WITH("PARTITION" );
else if (Matches("ALTER" , "INDEX" , MatchAny, "ATTACH" , "PARTITION" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
/* ALTER INDEX <name> ALTER */
else if (Matches("ALTER" , "INDEX" , MatchAny, "ALTER" ))
COMPLETE_WITH("COLUMN" );
/* ALTER INDEX <name> ALTER COLUMN */
else if (Matches("ALTER" , "INDEX" , MatchAny, "ALTER" , "COLUMN" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(Query_for_list_of_attribute_numbers);
}
/* ALTER INDEX <name> ALTER COLUMN <colnum> */
else if (Matches("ALTER" , "INDEX" , MatchAny, "ALTER" , "COLUMN" , MatchAny))
COMPLETE_WITH("SET STATISTICS" );
/* ALTER INDEX <name> ALTER COLUMN <colnum> SET */
else if (Matches("ALTER" , "INDEX" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" ))
COMPLETE_WITH("STATISTICS" );
/* ALTER INDEX <name> ALTER COLUMN <colnum> SET STATISTICS */
else if (Matches("ALTER" , "INDEX" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "STATISTICS" ))
{
/* Enforce no completion here, as an integer has to be specified */
}
/* ALTER INDEX <name> SET */
else if (Matches("ALTER" , "INDEX" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "TABLESPACE" );
/* ALTER INDEX <name> RESET */
else if (Matches("ALTER" , "INDEX" , MatchAny, "RESET" ))
COMPLETE_WITH("(" );
/* ALTER INDEX <foo> SET|RESET ( */
else if (Matches("ALTER" , "INDEX" , MatchAny, "RESET" , "(" ))
COMPLETE_WITH("fillfactor" ,
"deduplicate_items" , /* BTREE */
"fastupdate" , "gin_pending_list_limit" , /* GIN */
"buffering" , /* GiST */
"pages_per_range" , "autosummarize" /* BRIN */
);
else if (Matches("ALTER" , "INDEX" , MatchAny, "SET" , "(" ))
COMPLETE_WITH("fillfactor =" ,
"deduplicate_items =" , /* BTREE */
"fastupdate =" , "gin_pending_list_limit =" , /* GIN */
"buffering =" , /* GiST */
"pages_per_range =" , "autosummarize =" /* BRIN */
);
else if (Matches("ALTER" , "INDEX" , MatchAny, "NO" , "DEPENDS" ))
COMPLETE_WITH("ON EXTENSION" );
else if (Matches("ALTER" , "INDEX" , MatchAny, "DEPENDS" ))
COMPLETE_WITH("ON EXTENSION" );
/* ALTER LANGUAGE <name> */
else if (Matches("ALTER" , "LANGUAGE" , MatchAny))
COMPLETE_WITH("OWNER TO" , "RENAME TO" );
/* ALTER LARGE OBJECT <oid> */
else if (Matches("ALTER" , "LARGE" , "OBJECT" , MatchAny))
COMPLETE_WITH("OWNER TO" );
/* ALTER MATERIALIZED VIEW */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
"ALL IN TABLESPACE" );
/* ALTER USER,ROLE <name> */
else if (Matches("ALTER" , "USER|ROLE" , MatchAny) &&
!TailMatches("USER" , "MAPPING" ))
COMPLETE_WITH("BYPASSRLS" , "CONNECTION LIMIT" , "CREATEDB" , "CREATEROLE" ,
"ENCRYPTED PASSWORD" , "INHERIT" , "LOGIN" , "NOBYPASSRLS" ,
"NOCREATEDB" , "NOCREATEROLE" , "NOINHERIT" ,
"NOLOGIN" , "NOREPLICATION" , "NOSUPERUSER" , "PASSWORD" ,
"RENAME TO" , "REPLICATION" , "RESET" , "SET" , "SUPERUSER" ,
"VALID UNTIL" , "WITH" );
/* ALTER USER,ROLE <name> RESET */
else if (Matches("ALTER" , "USER|ROLE" , MatchAny, "RESET" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_user_vars, "ALL" );
}
/* ALTER USER,ROLE <name> WITH */
else if (Matches("ALTER" , "USER|ROLE" , MatchAny, "WITH" ))
/* Similar to the above, but don't complete "WITH" again. */
COMPLETE_WITH("BYPASSRLS" , "CONNECTION LIMIT" , "CREATEDB" , "CREATEROLE" ,
"ENCRYPTED PASSWORD" , "INHERIT" , "LOGIN" , "NOBYPASSRLS" ,
"NOCREATEDB" , "NOCREATEROLE" , "NOINHERIT" ,
"NOLOGIN" , "NOREPLICATION" , "NOSUPERUSER" , "PASSWORD" ,
"RENAME TO" , "REPLICATION" , "RESET" , "SET" , "SUPERUSER" ,
"VALID UNTIL" );
/* ALTER DEFAULT PRIVILEGES */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" ))
COMPLETE_WITH("FOR" , "GRANT" , "IN SCHEMA" , "REVOKE" );
/* ALTER DEFAULT PRIVILEGES FOR */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "FOR" ))
COMPLETE_WITH("ROLE" );
/* ALTER DEFAULT PRIVILEGES IN */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "IN" ))
COMPLETE_WITH("SCHEMA" );
/* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "FOR" , "ROLE|USER" ,
MatchAny))
COMPLETE_WITH("GRANT" , "REVOKE" , "IN SCHEMA" );
/* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "IN" , "SCHEMA" ,
MatchAny))
COMPLETE_WITH("GRANT" , "REVOKE" , "FOR ROLE" );
/* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "IN" , "SCHEMA" ,
MatchAny, "FOR" ))
COMPLETE_WITH("ROLE" );
/* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */
/* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "FOR" , "ROLE|USER" ,
MatchAny, "IN" , "SCHEMA" , MatchAny) ||
Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , "IN" , "SCHEMA" ,
MatchAny, "FOR" , "ROLE|USER" , MatchAny))
COMPLETE_WITH("GRANT" , "REVOKE" );
/* ALTER DOMAIN <name> */
else if (Matches("ALTER" , "DOMAIN" , MatchAny))
COMPLETE_WITH("ADD" , "DROP" , "OWNER TO" , "RENAME" , "SET" ,
"VALIDATE CONSTRAINT" );
/* ALTER DOMAIN <sth> ADD */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "ADD" ))
COMPLETE_WITH("CONSTRAINT" , "NOT NULL" , "CHECK (" );
/* ALTER DOMAIN <sth> ADD CONSTRAINT <sth> */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny))
COMPLETE_WITH("NOT NULL" , "CHECK (" );
/* ALTER DOMAIN <sth> DROP */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "DROP" ))
COMPLETE_WITH("CONSTRAINT" , "DEFAULT" , "NOT NULL" );
/* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "DROP|RENAME|VALIDATE" , "CONSTRAINT" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_type);
}
/* ALTER DOMAIN <sth> RENAME */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "RENAME" ))
COMPLETE_WITH("CONSTRAINT" , "TO" );
/* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "RENAME" , "CONSTRAINT" , MatchAny))
COMPLETE_WITH("TO" );
/* ALTER DOMAIN <sth> SET */
else if (Matches("ALTER" , "DOMAIN" , MatchAny, "SET" ))
COMPLETE_WITH("DEFAULT" , "NOT NULL" , "SCHEMA" );
/* ALTER SEQUENCE <name> */
else if (Matches("ALTER" , "SEQUENCE" , MatchAny))
COMPLETE_WITH("AS" , "INCREMENT" , "MINVALUE" , "MAXVALUE" , "RESTART" ,
"START" , "NO" , "CACHE" , "CYCLE" , "SET" , "OWNED BY" ,
"OWNER TO" , "RENAME TO" );
/* ALTER SEQUENCE <name> AS */
else if (TailMatches("ALTER" , "SEQUENCE" , MatchAny, "AS" ))
COMPLETE_WITH_CS("smallint" , "integer" , "bigint" );
/* ALTER SEQUENCE <name> NO */
else if (Matches("ALTER" , "SEQUENCE" , MatchAny, "NO" ))
COMPLETE_WITH("MINVALUE" , "MAXVALUE" , "CYCLE" );
/* ALTER SEQUENCE <name> SET */
else if (Matches("ALTER" , "SEQUENCE" , MatchAny, "SET" ))
COMPLETE_WITH("SCHEMA" , "LOGGED" , "UNLOGGED" );
/* ALTER SERVER <name> */
else if (Matches("ALTER" , "SERVER" , MatchAny))
COMPLETE_WITH("VERSION" , "OPTIONS" , "OWNER TO" , "RENAME TO" );
/* ALTER SERVER <name> VERSION <version> */
else if (Matches("ALTER" , "SERVER" , MatchAny, "VERSION" , MatchAny))
COMPLETE_WITH("OPTIONS" );
/* ALTER SYSTEM SET, RESET, RESET ALL */
else if (Matches("ALTER" , "SYSTEM" ))
COMPLETE_WITH("SET" , "RESET" );
else if (Matches("ALTER" , "SYSTEM" , "SET|RESET" ))
COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_set_vars,
"ALL" );
else if (Matches("ALTER" , "SYSTEM" , "SET" , MatchAny))
COMPLETE_WITH("TO" );
/* ALTER VIEW <name> */
else if (Matches("ALTER" , "VIEW" , MatchAny))
COMPLETE_WITH("ALTER COLUMN" , "OWNER TO" , "RENAME" , "RESET" , "SET" );
/* ALTER VIEW xxx RENAME */
else if (Matches("ALTER" , "VIEW" , MatchAny, "RENAME" ))
COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN" , "TO" );
else if (Matches("ALTER" , "VIEW" , MatchAny, "ALTER|RENAME" , "COLUMN" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
else if (Matches("ALTER" , "VIEW" , MatchAny, "ALTER" , MatchAny) ||
Matches("ALTER" , "VIEW" , MatchAny, "ALTER" , "COLUMN" , MatchAny))
COMPLETE_WITH("SET DEFAULT" , "DROP DEFAULT" );
/* ALTER VIEW xxx RENAME yyy */
else if (Matches("ALTER" , "VIEW" , MatchAny, "RENAME" , MatchAnyExcept("TO" )))
COMPLETE_WITH("TO" );
/* ALTER VIEW xxx RENAME COLUMN yyy */
else if (Matches("ALTER" , "VIEW" , MatchAny, "RENAME" , "COLUMN" , MatchAnyExcept("TO" )))
COMPLETE_WITH("TO" );
/* ALTER VIEW xxx RESET ( */
else if (Matches("ALTER" , "VIEW" , MatchAny, "RESET" ))
COMPLETE_WITH("(" );
/* Complete ALTER VIEW xxx SET with "(" or "SCHEMA" */
else if (Matches("ALTER" , "VIEW" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "SCHEMA" );
/* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */
else if (Matches("ALTER" , "VIEW" , MatchAny, "SET|RESET" , "(" ))
COMPLETE_WITH_LIST(view_optional_parameters);
else if (Matches("ALTER" , "VIEW" , MatchAny, "SET" , "(" , MatchAny))
COMPLETE_WITH("=" );
else if (Matches("ALTER" , "VIEW" , MatchAny, "SET" , "(" , "check_option" , "=" ))
COMPLETE_WITH("local" , "cascaded" );
else if (Matches("ALTER" , "VIEW" , MatchAny, "SET" , "(" , "security_barrier|security_invoker" , "=" ))
COMPLETE_WITH("true" , "false" );
/* ALTER MATERIALIZED VIEW <name> */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny))
COMPLETE_WITH("ALTER COLUMN" , "CLUSTER ON" , "DEPENDS ON EXTENSION" ,
"NO DEPENDS ON EXTENSION" , "OWNER TO" , "RENAME" ,
"RESET (" , "SET" );
/* ALTER MATERIALIZED VIEW xxx RENAME */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "RENAME" ))
COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN" , "TO" );
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "ALTER|RENAME" , "COLUMN" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER MATERIALIZED VIEW xxx RENAME yyy */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "RENAME" , MatchAnyExcept("TO" )))
COMPLETE_WITH("TO" );
/* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "RENAME" , "COLUMN" , MatchAnyExcept("TO" )))
COMPLETE_WITH("TO" );
/* ALTER MATERIALIZED VIEW xxx SET */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "ACCESS METHOD" , "SCHEMA" , "TABLESPACE" , "WITHOUT CLUSTER" );
/* ALTER MATERIALIZED VIEW xxx SET ACCESS METHOD */
else if (Matches("ALTER" , "MATERIALIZED" , "VIEW" , MatchAny, "SET" , "ACCESS" , "METHOD" ))
COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
/* ALTER POLICY <name> */
else if (Matches("ALTER" , "POLICY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_policies);
/* ALTER POLICY <name> ON */
else if (Matches("ALTER" , "POLICY" , MatchAny))
COMPLETE_WITH("ON" );
/* ALTER POLICY <name> ON <table> */
else if (Matches("ALTER" , "POLICY" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
/* ALTER POLICY <name> ON <table> - show options */
else if (Matches("ALTER" , "POLICY" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("RENAME TO" , "TO" , "USING (" , "WITH CHECK (" );
/* ALTER POLICY <name> ON <table> TO <role> */
else if (Matches("ALTER" , "POLICY" , MatchAny, "ON" , MatchAny, "TO" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/* ALTER POLICY <name> ON <table> USING ( */
else if (Matches("ALTER" , "POLICY" , MatchAny, "ON" , MatchAny, "USING" ))
COMPLETE_WITH("(" );
/* ALTER POLICY <name> ON <table> WITH CHECK ( */
else if (Matches("ALTER" , "POLICY" , MatchAny, "ON" , MatchAny, "WITH" , "CHECK" ))
COMPLETE_WITH("(" );
/* ALTER RULE <name>, add ON */
else if (Matches("ALTER" , "RULE" , MatchAny))
COMPLETE_WITH("ON" );
/* If we have ALTER RULE <name> ON, then add the correct tablename */
else if (Matches("ALTER" , "RULE" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
/* ALTER RULE <name> ON <name> */
else if (Matches("ALTER" , "RULE" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("RENAME TO" );
/* ALTER STATISTICS <name> */
else if (Matches("ALTER" , "STATISTICS" , MatchAny))
COMPLETE_WITH("OWNER TO" , "RENAME TO" , "SET SCHEMA" , "SET STATISTICS" );
/* ALTER STATISTICS <name> SET */
else if (Matches("ALTER" , "STATISTICS" , MatchAny, "SET" ))
COMPLETE_WITH("SCHEMA" , "STATISTICS" );
/* ALTER TRIGGER <name>, add ON */
else if (Matches("ALTER" , "TRIGGER" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("ALTER" , "TRIGGER" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
/* ALTER TRIGGER <name> ON <name> */
else if (Matches("ALTER" , "TRIGGER" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("RENAME TO" , "DEPENDS ON EXTENSION" ,
"NO DEPENDS ON EXTENSION" );
/*
* If we detect ALTER TABLE < name > , suggest sub commands
*/
else if (Matches("ALTER" , "TABLE" , MatchAny))
COMPLETE_WITH("ADD" , "ALTER" , "CLUSTER ON" , "DISABLE" , "DROP" ,
"ENABLE" , "INHERIT" , "NO" , "RENAME" , "RESET" ,
"OWNER TO" , "SET" , "VALIDATE CONSTRAINT" ,
"REPLICA IDENTITY" , "ATTACH PARTITION" ,
"DETACH PARTITION" , "FORCE ROW LEVEL SECURITY" ,
"OF" , "NOT OF" );
/* ALTER TABLE xxx ADD */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" ))
{
/* make sure to keep this list and the !Matches() below in sync */
COMPLETE_WITH("COLUMN" , "CONSTRAINT" , "CHECK" , "UNIQUE" , "PRIMARY KEY" ,
"EXCLUDE" , "FOREIGN KEY" );
}
/* ALTER TABLE xxx ADD [COLUMN] yyy */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "COLUMN" , MatchAny) ||
Matches("ALTER" , "TABLE" , MatchAny, "ADD" , MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN" )))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
/* ALTER TABLE xxx ADD CONSTRAINT yyy */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny))
COMPLETE_WITH("CHECK" , "UNIQUE" , "PRIMARY KEY" , "EXCLUDE" , "FOREIGN KEY" );
/* ALTER TABLE xxx ADD [CONSTRAINT yyy] (PRIMARY KEY|UNIQUE) */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "PRIMARY" , "KEY" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "UNIQUE" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny, "PRIMARY" , "KEY" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny, "UNIQUE" ))
COMPLETE_WITH("(" , "USING INDEX" );
/* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "PRIMARY" , "KEY" , "USING" , "INDEX" ))
{
set_completion_reference(prev6_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD UNIQUE USING INDEX */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "UNIQUE" , "USING" , "INDEX" ))
{
set_completion_reference(prev5_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny,
"PRIMARY" , "KEY" , "USING" , "INDEX" ))
{
set_completion_reference(prev8_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ADD" , "CONSTRAINT" , MatchAny,
"UNIQUE" , "USING" , "INDEX" ))
{
set_completion_reference(prev7_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ENABLE */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" ))
COMPLETE_WITH("ALWAYS" , "REPLICA" , "ROW LEVEL SECURITY" , "RULE" ,
"TRIGGER" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" , "REPLICA|ALWAYS" ))
COMPLETE_WITH("RULE" , "TRIGGER" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" , "RULE" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" , MatchAny, "RULE" ))
{
set_completion_reference(prev4_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" , "TRIGGER" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "ENABLE" , MatchAny, "TRIGGER" ))
{
set_completion_reference(prev4_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
/* ALTER TABLE xxx INHERIT */
else if (Matches("ALTER" , "TABLE" , MatchAny, "INHERIT" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* ALTER TABLE xxx NO */
else if (Matches("ALTER" , "TABLE" , MatchAny, "NO" ))
COMPLETE_WITH("FORCE ROW LEVEL SECURITY" , "INHERIT" );
/* ALTER TABLE xxx NO INHERIT */
else if (Matches("ALTER" , "TABLE" , MatchAny, "NO" , "INHERIT" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* ALTER TABLE xxx DISABLE */
else if (Matches("ALTER" , "TABLE" , MatchAny, "DISABLE" ))
COMPLETE_WITH("ROW LEVEL SECURITY" , "RULE" , "TRIGGER" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "DISABLE" , "RULE" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "DISABLE" , "TRIGGER" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
/* ALTER TABLE xxx ALTER */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" ))
COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN" , "CONSTRAINT" );
/* ALTER TABLE xxx RENAME */
else if (Matches("ALTER" , "TABLE" , MatchAny, "RENAME" ))
COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN" , "CONSTRAINT" , "TO" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER|RENAME" , "COLUMN" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TABLE xxx RENAME yyy */
else if (Matches("ALTER" , "TABLE" , MatchAny, "RENAME" , MatchAnyExcept("CONSTRAINT|TO" )))
COMPLETE_WITH("TO" );
/* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
else if (Matches("ALTER" , "TABLE" , MatchAny, "RENAME" , "COLUMN|CONSTRAINT" , MatchAnyExcept("TO" )))
COMPLETE_WITH("TO" );
/* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
else if (Matches("ALTER" , "TABLE" , MatchAny, "DROP" ))
COMPLETE_WITH("COLUMN" , "CONSTRAINT" );
/* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
else if (Matches("ALTER" , "TABLE" , MatchAny, "DROP" , "COLUMN" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER|DROP|RENAME" , "CONSTRAINT" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table);
}
/* ALTER TABLE <sth> VALIDATE CONSTRAINT <non-validated constraint> */
else if (Matches("ALTER" , "TABLE" , MatchAny, "VALIDATE" , "CONSTRAINT" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table_not_validated);
}
/* ALTER TABLE ALTER [COLUMN] <foo> */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny))
COMPLETE_WITH("TYPE" , "SET" , "RESET" , "RESTART" , "ADD" , "DROP" );
/* ALTER TABLE ALTER [COLUMN] <foo> ADD */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "ADD" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "ADD" ))
COMPLETE_WITH("GENERATED" );
/* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "ADD" , "GENERATED" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "ADD" , "GENERATED" ))
COMPLETE_WITH("ALWAYS" , "BY DEFAULT" );
/* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "ADD" , "GENERATED" , "ALWAYS" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "ADD" , "GENERATED" , "ALWAYS" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "ADD" , "GENERATED" , "BY" , "DEFAULT" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "ADD" , "GENERATED" , "BY" , "DEFAULT" ))
COMPLETE_WITH("AS IDENTITY" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "COMPRESSION" , "DATA TYPE" , "DEFAULT" , "EXPRESSION" , "GENERATED" , "NOT NULL" ,
"STATISTICS" , "STORAGE" ,
/* a subset of ALTER SEQUENCE options */
"INCREMENT" , "MINVALUE" , "MAXVALUE" , "START" , "NO" , "CACHE" , "CYCLE" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "(" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "(" ))
COMPLETE_WITH("n_distinct" , "n_distinct_inherited" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET COMPRESSION */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "COMPRESSION" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "COMPRESSION" ))
COMPLETE_WITH("DEFAULT" , "PGLZ" , "LZ4" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "EXPRESSION" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "EXPRESSION" ))
COMPLETE_WITH("AS" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION AS */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "EXPRESSION" , "AS" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "EXPRESSION" , "AS" ))
COMPLETE_WITH("(" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "GENERATED" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "GENERATED" ))
COMPLETE_WITH("ALWAYS" , "BY DEFAULT" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET NO */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "NO" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "NO" ))
COMPLETE_WITH("MINVALUE" , "MAXVALUE" , "CYCLE" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "STORAGE" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "STORAGE" ))
COMPLETE_WITH("DEFAULT" , "PLAIN" , "EXTERNAL" , "EXTENDED" , "MAIN" );
/* ALTER TABLE ALTER [COLUMN] <foo> SET STATISTICS */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "SET" , "STATISTICS" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "SET" , "STATISTICS" ))
{
/* Enforce no completion here, as an integer has to be specified */
}
/* ALTER TABLE ALTER [COLUMN] <foo> DROP */
else if (Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , "COLUMN" , MatchAny, "DROP" ) ||
Matches("ALTER" , "TABLE" , MatchAny, "ALTER" , MatchAny, "DROP" ))
COMPLETE_WITH("DEFAULT" , "EXPRESSION" , "IDENTITY" , "NOT NULL" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "CLUSTER" ))
COMPLETE_WITH("ON" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "CLUSTER" , "ON" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
/* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
else if (Matches("ALTER" , "TABLE" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "ACCESS METHOD" , "LOGGED" , "SCHEMA" ,
"TABLESPACE" , "UNLOGGED" , "WITH" , "WITHOUT" );
/*
* If we have ALTER TABLE < sth > SET ACCESS METHOD provide a list of table
* AMs .
*/
else if (Matches("ALTER" , "TABLE" , MatchAny, "SET" , "ACCESS" , "METHOD" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_table_access_methods,
"DEFAULT" );
/*
* If we have ALTER TABLE < sth > SET TABLESPACE provide a list of
* tablespaces
*/
else if (Matches("ALTER" , "TABLE" , MatchAny, "SET" , "TABLESPACE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
/* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
else if (Matches("ALTER" , "TABLE" , MatchAny, "SET" , "WITHOUT" ))
COMPLETE_WITH("CLUSTER" , "OIDS" );
/* ALTER TABLE <foo> RESET */
else if (Matches("ALTER" , "TABLE" , MatchAny, "RESET" ))
COMPLETE_WITH("(" );
/* ALTER TABLE <foo> SET|RESET ( */
else if (Matches("ALTER" , "TABLE" , MatchAny, "SET|RESET" , "(" ))
COMPLETE_WITH_LIST(table_storage_parameters);
else if (Matches("ALTER" , "TABLE" , MatchAny, "REPLICA" , "IDENTITY" , "USING" , "INDEX" ))
{
set_completion_reference(prev5_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "REPLICA" , "IDENTITY" , "USING" ))
COMPLETE_WITH("INDEX" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "REPLICA" , "IDENTITY" ))
COMPLETE_WITH("FULL" , "NOTHING" , "DEFAULT" , "USING" );
else if (Matches("ALTER" , "TABLE" , MatchAny, "REPLICA" ))
COMPLETE_WITH("IDENTITY" );
/*
* If we have ALTER TABLE < foo > ATTACH PARTITION , provide a list of
* tables .
*/
else if (Matches("ALTER" , "TABLE" , MatchAny, "ATTACH" , "PARTITION" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* Limited completion support for partition bound specification */
else if (TailMatches("ATTACH" , "PARTITION" , MatchAny))
COMPLETE_WITH("FOR VALUES" , "DEFAULT" );
else if (TailMatches("FOR" , "VALUES" ))
COMPLETE_WITH("FROM (" , "IN (" , "WITH (" );
/*
* If we have ALTER TABLE < foo > DETACH PARTITION , provide a list of
* partitions of < foo > .
*/
else if (Matches("ALTER" , "TABLE" , MatchAny, "DETACH" , "PARTITION" ))
{
set_completion_reference(prev3_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
}
else if (Matches("ALTER" , "TABLE" , MatchAny, "DETACH" , "PARTITION" , MatchAny))
COMPLETE_WITH("CONCURRENTLY" , "FINALIZE" );
/* ALTER TABLE <name> OF */
else if (Matches("ALTER" , "TABLE" , MatchAny, "OF" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
/* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
else if (Matches("ALTER" , "TABLESPACE" , MatchAny))
COMPLETE_WITH("RENAME TO" , "OWNER TO" , "SET" , "RESET" );
/* ALTER TABLESPACE <foo> SET|RESET */
else if (Matches("ALTER" , "TABLESPACE" , MatchAny, "SET|RESET" ))
COMPLETE_WITH("(" );
/* ALTER TABLESPACE <foo> SET|RESET ( */
else if (Matches("ALTER" , "TABLESPACE" , MatchAny, "SET|RESET" , "(" ))
COMPLETE_WITH("seq_page_cost" , "random_page_cost" ,
"effective_io_concurrency" , "maintenance_io_concurrency" );
/* ALTER TEXT SEARCH */
else if (Matches("ALTER" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
else if (Matches("ALTER" , "TEXT" , "SEARCH" , "TEMPLATE|PARSER" , MatchAny))
COMPLETE_WITH("RENAME TO" , "SET SCHEMA" );
else if (Matches("ALTER" , "TEXT" , "SEARCH" , "DICTIONARY" , MatchAny))
COMPLETE_WITH("(" , "OWNER TO" , "RENAME TO" , "SET SCHEMA" );
else if (Matches("ALTER" , "TEXT" , "SEARCH" , "CONFIGURATION" , MatchAny))
COMPLETE_WITH("ADD MAPPING FOR" , "ALTER MAPPING" ,
"DROP MAPPING FOR" ,
"OWNER TO" , "RENAME TO" , "SET SCHEMA" );
/* complete ALTER TYPE <foo> with actions */
else if (Matches("ALTER" , "TYPE" , MatchAny))
COMPLETE_WITH("ADD ATTRIBUTE" , "ADD VALUE" , "ALTER ATTRIBUTE" ,
"DROP ATTRIBUTE" ,
"OWNER TO" , "RENAME" , "SET SCHEMA" , "SET (" );
/* complete ALTER TYPE <foo> ADD with actions */
else if (Matches("ALTER" , "TYPE" , MatchAny, "ADD" ))
COMPLETE_WITH("ATTRIBUTE" , "VALUE" );
/* ALTER TYPE <foo> RENAME */
else if (Matches("ALTER" , "TYPE" , MatchAny, "RENAME" ))
COMPLETE_WITH("ATTRIBUTE" , "TO" , "VALUE" );
/* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */
else if (Matches("ALTER" , "TYPE" , MatchAny, "RENAME" , "ATTRIBUTE|VALUE" , MatchAny))
COMPLETE_WITH("TO" );
/* ALTER TYPE xxx RENAME ATTRIBUTE yyy TO zzz */
else if (Matches("ALTER" , "TYPE" , MatchAny, "RENAME" , "ATTRIBUTE" , MatchAny, "TO" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/*
* If we have ALTER TYPE < sth > ALTER / DROP / RENAME ATTRIBUTE , provide list
* of attributes
*/
else if (Matches("ALTER" , "TYPE" , MatchAny, "ALTER|DROP|RENAME" , "ATTRIBUTE" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* complete ALTER TYPE ADD ATTRIBUTE <foo> with list of types */
else if (Matches("ALTER" , "TYPE" , MatchAny, "ADD" , "ATTRIBUTE" , MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
/* complete ALTER TYPE ADD ATTRIBUTE <foo> <footype> with CASCADE/RESTRICT */
else if (Matches("ALTER" , "TYPE" , MatchAny, "ADD" , "ATTRIBUTE" , MatchAny, MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* complete ALTER TYPE DROP ATTRIBUTE <foo> with CASCADE/RESTRICT */
else if (Matches("ALTER" , "TYPE" , MatchAny, "DROP" , "ATTRIBUTE" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* ALTER TYPE ALTER ATTRIBUTE <foo> */
else if (Matches("ALTER" , "TYPE" , MatchAny, "ALTER" , "ATTRIBUTE" , MatchAny))
COMPLETE_WITH("TYPE" );
/* ALTER TYPE ALTER ATTRIBUTE <foo> TYPE <footype> */
else if (Matches("ALTER" , "TYPE" , MatchAny, "ALTER" , "ATTRIBUTE" , MatchAny, "TYPE" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* complete ALTER TYPE <sth> RENAME VALUE with list of enum values */
else if (Matches("ALTER" , "TYPE" , MatchAny, "RENAME" , "VALUE" ))
COMPLETE_WITH_ENUM_VALUE(prev3_wd);
/* ALTER TYPE <foo> SET */
else if (Matches("ALTER" , "TYPE" , MatchAny, "SET" ))
COMPLETE_WITH("(" , "SCHEMA" );
/* complete ALTER TYPE <foo> SET ( with settable properties */
else if (Matches("ALTER" , "TYPE" , MatchAny, "SET" , "(" ))
COMPLETE_WITH("ANALYZE" , "RECEIVE" , "SEND" , "STORAGE" , "SUBSCRIPT" ,
"TYPMOD_IN" , "TYPMOD_OUT" );
/* complete ALTER GROUP <foo> */
else if (Matches("ALTER" , "GROUP" , MatchAny))
COMPLETE_WITH("ADD USER" , "DROP USER" , "RENAME TO" );
/* complete ALTER GROUP <foo> ADD|DROP with USER */
else if (Matches("ALTER" , "GROUP" , MatchAny, "ADD|DROP" ))
COMPLETE_WITH("USER" );
/* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
else if (Matches("ALTER" , "GROUP" , MatchAny, "ADD|DROP" , "USER" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/*
* ANALYZE [ ( option [ , . . . ] ) ] [ [ ONLY ] table_and_columns [ , . . . ] ]
* ANALYZE [ VERBOSE ] [ [ ONLY ] table_and_columns [ , . . . ] ]
*/
else if (Matches("ANALYZE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
"(" , "VERBOSE" , "ONLY" );
else if (Matches("ANALYZE" , "VERBOSE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
"ONLY" );
else if (HeadMatches("ANALYZE" , "(*" ) &&
!HeadMatches("ANALYZE" , "(*)" ))
{
/*
* This fires if we ' re in an unfinished parenthesized option list .
* get_previous_words treats a completed parenthesized option list as
* one word , so the above test is correct .
*/
if (ends_with(prev_wd, '(' ) || ends_with(prev_wd, ',' ))
COMPLETE_WITH("VERBOSE" , "SKIP_LOCKED" , "BUFFER_USAGE_LIMIT" );
else if (TailMatches("VERBOSE|SKIP_LOCKED" ))
COMPLETE_WITH("ON" , "OFF" );
}
else if (Matches("ANALYZE" , "(*)" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
"ONLY" );
else if (Matches("ANALYZE" , MatchAnyN, "(" ))
/* "ANALYZE (" should be caught above, so assume we want columns */
COMPLETE_WITH_ATTR(prev2_wd);
else if (HeadMatches("ANALYZE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables);
/* BEGIN */
else if (Matches("BEGIN" ))
COMPLETE_WITH("WORK" , "TRANSACTION" , "ISOLATION LEVEL" , "READ" , "DEFERRABLE" , "NOT DEFERRABLE" );
/* END, ABORT */
else if (Matches("END|ABORT" ))
COMPLETE_WITH("AND" , "WORK" , "TRANSACTION" );
/* COMMIT */
else if (Matches("COMMIT" ))
COMPLETE_WITH("AND" , "WORK" , "TRANSACTION" , "PREPARED" );
/* RELEASE SAVEPOINT */
else if (Matches("RELEASE" ))
COMPLETE_WITH("SAVEPOINT" );
/* ROLLBACK */
else if (Matches("ROLLBACK" ))
COMPLETE_WITH("AND" , "WORK" , "TRANSACTION" , "TO SAVEPOINT" , "PREPARED" );
else if (Matches("ABORT|END|COMMIT|ROLLBACK" , "AND" ))
COMPLETE_WITH("CHAIN" );
/* CALL */
else if (Matches("CALL" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
else if (Matches("CALL" , MatchAny))
COMPLETE_WITH("(" );
/* CLOSE */
else if (Matches("CLOSE" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
"ALL" );
/* CLUSTER */
else if (Matches("CLUSTER" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
"VERBOSE" );
else if (Matches("CLUSTER" , "VERBOSE" ) ||
Matches("CLUSTER" , "(*)" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables);
/* If we have CLUSTER <sth>, then add "USING" */
else if (Matches("CLUSTER" , MatchAnyExcept("VERBOSE|ON|(|(*)" )))
COMPLETE_WITH("USING" );
/* If we have CLUSTER VERBOSE <sth>, then add "USING" */
else if (Matches("CLUSTER" , "VERBOSE|(*)" , MatchAny))
COMPLETE_WITH("USING" );
/* If we have CLUSTER <sth> USING, then add the index as well */
else if (Matches("CLUSTER" , MatchAny, "USING" ) ||
Matches("CLUSTER" , "VERBOSE|(*)" , MatchAny, "USING" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
else if (HeadMatches("CLUSTER" , "(*" ) &&
!HeadMatches("CLUSTER" , "(*)" ))
{
/*
* This fires if we ' re in an unfinished parenthesized option list .
* get_previous_words treats a completed parenthesized option list as
* one word , so the above test is correct .
*/
if (ends_with(prev_wd, '(' ) || ends_with(prev_wd, ',' ))
COMPLETE_WITH("VERBOSE" );
}
/* COMMENT */
else if (Matches("COMMENT" ))
COMPLETE_WITH("ON" );
else if (Matches("COMMENT" , "ON" ))
COMPLETE_WITH("ACCESS METHOD" , "AGGREGATE" , "CAST" , "COLLATION" ,
"COLUMN" , "CONSTRAINT" , "CONVERSION" , "DATABASE" ,
"DOMAIN" , "EXTENSION" , "EVENT TRIGGER" ,
"FOREIGN DATA WRAPPER" , "FOREIGN TABLE" ,
"FUNCTION" , "INDEX" , "LANGUAGE" , "LARGE OBJECT" ,
"MATERIALIZED VIEW" , "OPERATOR" , "POLICY" ,
"PROCEDURE" , "PROCEDURAL LANGUAGE" , "PUBLICATION" , "ROLE" ,
"ROUTINE" , "RULE" , "SCHEMA" , "SEQUENCE" , "SERVER" ,
"STATISTICS" , "SUBSCRIPTION" , "TABLE" ,
"TABLESPACE" , "TEXT SEARCH" , "TRANSFORM FOR" ,
"TRIGGER" , "TYPE" , "VIEW" );
else if (Matches("COMMENT" , "ON" , "ACCESS" , "METHOD" ))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (Matches("COMMENT" , "ON" , "CONSTRAINT" ))
COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
else if (Matches("COMMENT" , "ON" , "CONSTRAINT" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("COMMENT" , "ON" , "CONSTRAINT" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables_for_constraint,
"DOMAIN" );
}
else if (Matches("COMMENT" , "ON" , "CONSTRAINT" , MatchAny, "ON" , "DOMAIN" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (Matches("COMMENT" , "ON" , "EVENT" , "TRIGGER" ))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
else if (Matches("COMMENT" , "ON" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
else if (Matches("COMMENT" , "ON" , "FOREIGN" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
else if (Matches("COMMENT" , "ON" , "MATERIALIZED" , "VIEW" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("COMMENT" , "ON" , "POLICY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_policies);
else if (Matches("COMMENT" , "ON" , "POLICY" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("COMMENT" , "ON" , "POLICY" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
else if (Matches("COMMENT" , "ON" , "PROCEDURAL" , "LANGUAGE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (Matches("COMMENT" , "ON" , "RULE" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("COMMENT" , "ON" , "RULE" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
else if (Matches("COMMENT" , "ON" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
else if (Matches("COMMENT" , "ON" , "TEXT" , "SEARCH" , "CONFIGURATION" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
else if (Matches("COMMENT" , "ON" , "TEXT" , "SEARCH" , "DICTIONARY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
else if (Matches("COMMENT" , "ON" , "TEXT" , "SEARCH" , "PARSER" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
else if (Matches("COMMENT" , "ON" , "TEXT" , "SEARCH" , "TEMPLATE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
else if (Matches("COMMENT" , "ON" , "TRANSFORM" , "FOR" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("COMMENT" , "ON" , "TRANSFORM" , "FOR" , MatchAny))
COMPLETE_WITH("LANGUAGE" );
else if (Matches("COMMENT" , "ON" , "TRANSFORM" , "FOR" , MatchAny, "LANGUAGE" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
else if (Matches("COMMENT" , "ON" , "TRIGGER" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("COMMENT" , "ON" , "TRIGGER" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
else if (Matches("COMMENT" , "ON" , MatchAny, MatchAnyExcept("IS" )) ||
Matches("COMMENT" , "ON" , MatchAny, MatchAny, MatchAnyExcept("IS" )) ||
Matches("COMMENT" , "ON" , MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS" )) ||
Matches("COMMENT" , "ON" , MatchAny, MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS" )))
COMPLETE_WITH("IS" );
/* COPY */
/*
* If we have COPY , offer list of tables or " ( " ( Also cover the analogous
* backslash command ) .
*/
else if (Matches("COPY|\\copy" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables, "(" );
/* Complete COPY ( with legal query commands */
else if (Matches("COPY|\\copy" , "(" ))
COMPLETE_WITH("SELECT" , "TABLE" , "VALUES" , "INSERT INTO" , "UPDATE" , "DELETE FROM" , "MERGE INTO" , "WITH" );
/* Complete COPY <sth> */
else if (Matches("COPY|\\copy" , MatchAny))
COMPLETE_WITH("FROM" , "TO" );
/* Complete COPY <sth> FROM|TO with filename */
else if (Matches("COPY" , MatchAny, "FROM|TO" ))
{
completion_charp = "" ;
completion_force_quote = true ; /* COPY requires quoted filename */
matches = rl_completion_matches(text, complete_from_files);
}
else if (Matches("\\copy" , MatchAny, "FROM|TO" ))
{
completion_charp = "" ;
completion_force_quote = false ;
matches = rl_completion_matches(text, complete_from_files);
}
/* Complete COPY <sth> TO <sth> */
else if (Matches("COPY|\\copy" , MatchAny, "TO" , MatchAny))
COMPLETE_WITH("WITH (" );
/* Complete COPY <sth> FROM <sth> */
else if (Matches("COPY|\\copy" , MatchAny, "FROM" , MatchAny))
COMPLETE_WITH("WITH (" , "WHERE" );
/* Complete COPY <sth> FROM filename WITH ( */
else if (Matches("COPY|\\copy" , MatchAny, "FROM" , MatchAny, "WITH" , "(" ))
COMPLETE_WITH(Copy_from_options);
/* Complete COPY <sth> TO filename WITH ( */
else if (Matches("COPY|\\copy" , MatchAny, "TO" , MatchAny, "WITH" , "(" ))
COMPLETE_WITH(Copy_to_options);
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
else if (Matches("COPY|\\copy" , MatchAny, "FROM|TO" , MatchAny, "WITH" , "(" , "FORMAT" ))
COMPLETE_WITH("binary" , "csv" , "text" );
/* Complete COPY <sth> FROM filename WITH (ON_ERROR */
else if (Matches("COPY|\\copy" , MatchAny, "FROM" , MatchAny, "WITH" , "(" , "ON_ERROR" ))
COMPLETE_WITH("stop" , "ignore" );
/* Complete COPY <sth> FROM filename WITH (LOG_VERBOSITY */
else if (Matches("COPY|\\copy" , MatchAny, "FROM" , MatchAny, "WITH" , "(" , "LOG_VERBOSITY" ))
COMPLETE_WITH("silent" , "default" , "verbose" );
/* Complete COPY <sth> FROM <sth> WITH (<options>) */
else if (Matches("COPY|\\copy" , MatchAny, "FROM" , MatchAny, "WITH" , MatchAny))
COMPLETE_WITH("WHERE" );
/* CREATE ACCESS METHOD */
/* Complete "CREATE ACCESS METHOD <name>" */
else if (Matches("CREATE" , "ACCESS" , "METHOD" , MatchAny))
COMPLETE_WITH("TYPE" );
/* Complete "CREATE ACCESS METHOD <name> TYPE" */
else if (Matches("CREATE" , "ACCESS" , "METHOD" , MatchAny, "TYPE" ))
COMPLETE_WITH("INDEX" , "TABLE" );
/* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
else if (Matches("CREATE" , "ACCESS" , "METHOD" , MatchAny, "TYPE" , MatchAny))
COMPLETE_WITH("HANDLER" );
/* CREATE COLLATION */
else if (Matches("CREATE" , "COLLATION" , MatchAny))
COMPLETE_WITH("(" , "FROM" );
else if (Matches("CREATE" , "COLLATION" , MatchAny, "FROM" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
else if (HeadMatches("CREATE" , "COLLATION" , MatchAny, "(*" ))
{
if (TailMatches("(|*," ))
COMPLETE_WITH("LOCALE =" , "LC_COLLATE =" , "LC_CTYPE =" ,
"PROVIDER =" , "DETERMINISTIC =" );
else if (TailMatches("PROVIDER" , "=" ))
COMPLETE_WITH("libc" , "icu" );
else if (TailMatches("DETERMINISTIC" , "=" ))
COMPLETE_WITH("true" , "false" );
}
/* CREATE DATABASE */
else if (Matches("CREATE" , "DATABASE" , MatchAny))
COMPLETE_WITH("OWNER" , "TEMPLATE" , "ENCODING" , "TABLESPACE" ,
"IS_TEMPLATE" , "STRATEGY" ,
"ALLOW_CONNECTIONS" , "CONNECTION LIMIT" ,
"LC_COLLATE" , "LC_CTYPE" , "LOCALE" , "OID" ,
"LOCALE_PROVIDER" , "ICU_LOCALE" );
else if (Matches("CREATE" , "DATABASE" , MatchAny, "TEMPLATE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
else if (Matches("CREATE" , "DATABASE" , MatchAny, "STRATEGY" ))
COMPLETE_WITH("WAL_LOG" , "FILE_COPY" );
/* CREATE DOMAIN */
else if (Matches("CREATE" , "DOMAIN" , MatchAny))
COMPLETE_WITH("AS" );
else if (Matches("CREATE" , "DOMAIN" , MatchAny, "AS" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("CREATE" , "DOMAIN" , MatchAny, "AS" , MatchAny))
COMPLETE_WITH("COLLATE" , "DEFAULT" , "CONSTRAINT" ,
"NOT NULL" , "NULL" , "CHECK (" );
else if (Matches("CREATE" , "DOMAIN" , MatchAny, "COLLATE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
/* CREATE EXTENSION */
/* Complete with available extensions rather than installed ones. */
else if (Matches("CREATE" , "EXTENSION" ))
COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
/* CREATE EXTENSION <name> */
else if (Matches("CREATE" , "EXTENSION" , MatchAny))
COMPLETE_WITH("WITH SCHEMA" , "CASCADE" , "VERSION" );
/* CREATE EXTENSION <name> VERSION */
else if (Matches("CREATE" , "EXTENSION" , MatchAny, "VERSION" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
}
/* CREATE FOREIGN */
else if (Matches("CREATE" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
/* CREATE FOREIGN DATA WRAPPER */
else if (Matches("CREATE" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny))
COMPLETE_WITH("HANDLER" , "VALIDATOR" , "OPTIONS" );
/* CREATE FOREIGN TABLE */
else if (Matches("CREATE" , "FOREIGN" , "TABLE" , MatchAny))
COMPLETE_WITH("(" , "PARTITION OF" );
/* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* First off we complete CREATE UNIQUE with "INDEX" */
else if (TailMatches("CREATE" , "UNIQUE" ))
COMPLETE_WITH("INDEX" );
/*
* If we have CREATE | UNIQUE INDEX , then add " ON " , " CONCURRENTLY " , and
* existing indexes
*/
else if (TailMatches("CREATE|UNIQUE" , "INDEX" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
"ON" , "CONCURRENTLY" );
/*
* Complete . . . INDEX | CONCURRENTLY [ < name > ] ON with a list of relations
* that indexes can be created on
*/
else if (TailMatches("INDEX|CONCURRENTLY" , MatchAny, "ON" ) ||
TailMatches("INDEX|CONCURRENTLY" , "ON" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
/*
* Complete CREATE | UNIQUE INDEX CONCURRENTLY with " ON " and existing
* indexes
*/
else if (TailMatches("CREATE|UNIQUE" , "INDEX" , "CONCURRENTLY" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
"ON" );
/* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
else if (TailMatches("CREATE|UNIQUE" , "INDEX" , MatchAny) ||
TailMatches("CREATE|UNIQUE" , "INDEX" , "CONCURRENTLY" , MatchAny))
COMPLETE_WITH("ON" );
/*
* Complete INDEX < name > ON < table > with a list of table columns ( which
* should really be in parens )
*/
else if (TailMatches("INDEX" , MatchAny, "ON" , MatchAny) ||
TailMatches("INDEX|CONCURRENTLY" , "ON" , MatchAny))
COMPLETE_WITH("(" , "USING" );
else if (TailMatches("INDEX" , MatchAny, "ON" , MatchAny, "(" ) ||
TailMatches("INDEX|CONCURRENTLY" , "ON" , MatchAny, "(" ))
COMPLETE_WITH_ATTR(prev2_wd);
/* same if you put in USING */
else if (TailMatches("ON" , MatchAny, "USING" , MatchAny, "(" ))
COMPLETE_WITH_ATTR(prev4_wd);
/* Complete USING with an index method */
else if (TailMatches("INDEX" , MatchAny, MatchAny, "ON" , MatchAny, "USING" ) ||
TailMatches("INDEX" , MatchAny, "ON" , MatchAny, "USING" ) ||
TailMatches("INDEX" , "ON" , MatchAny, "USING" ))
COMPLETE_WITH_QUERY(Query_for_list_of_index_access_methods);
else if (TailMatches("ON" , MatchAny, "USING" , MatchAny) &&
!TailMatches("POLICY" , MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
!TailMatches("FOR" , MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("(" );
/* CREATE OR REPLACE */
else if (Matches("CREATE" , "OR" ))
COMPLETE_WITH("REPLACE" );
/* CREATE POLICY */
/* Complete "CREATE POLICY <name> ON" */
else if (Matches("CREATE" , "POLICY" , MatchAny))
COMPLETE_WITH("ON" );
/* Complete "CREATE POLICY <name> ON <table>" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("AS" , "FOR" , "TO" , "USING (" , "WITH CHECK (" );
/* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" ))
COMPLETE_WITH("PERMISSIVE" , "RESTRICTIVE" );
/*
* CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE
* FOR | TO | USING | WITH CHECK
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny))
COMPLETE_WITH("FOR" , "TO" , "USING" , "WITH CHECK" );
/* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "FOR" ))
COMPLETE_WITH("ALL" , "SELECT" , "INSERT" , "UPDATE" , "DELETE" );
/* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "FOR" , "INSERT" ))
COMPLETE_WITH("TO" , "WITH CHECK (" );
/* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "FOR" , "SELECT|DELETE" ))
COMPLETE_WITH("TO" , "USING (" );
/* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "FOR" , "ALL|UPDATE" ))
COMPLETE_WITH("TO" , "USING (" , "WITH CHECK (" );
/* Complete "CREATE POLICY <name> ON <table> TO <role>" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "TO" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/* Complete "CREATE POLICY <name> ON <table> USING (" */
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "USING" ))
COMPLETE_WITH("(" );
/*
* CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE FOR
* ALL | SELECT | INSERT | UPDATE | DELETE
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "FOR" ))
COMPLETE_WITH("ALL" , "SELECT" , "INSERT" , "UPDATE" , "DELETE" );
/*
* Complete " CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE FOR
* INSERT TO | WITH CHECK "
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "FOR" , "INSERT" ))
COMPLETE_WITH("TO" , "WITH CHECK (" );
/*
* Complete " CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE FOR
* SELECT | DELETE TO | USING "
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "FOR" , "SELECT|DELETE" ))
COMPLETE_WITH("TO" , "USING (" );
/*
* CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE FOR
* ALL | UPDATE TO | USING | WITH CHECK
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "FOR" , "ALL|UPDATE" ))
COMPLETE_WITH("TO" , "USING (" , "WITH CHECK (" );
/*
* Complete " CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE TO
* < role > "
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "TO" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/*
* Complete " CREATE POLICY < name > ON < table > AS PERMISSIVE | RESTRICTIVE
* USING ( "
*/
else if (Matches("CREATE" , "POLICY" , MatchAny, "ON" , MatchAny, "AS" , MatchAny, "USING" ))
COMPLETE_WITH("(" );
/* CREATE PUBLICATION */
else if (Matches("CREATE" , "PUBLICATION" , MatchAny))
COMPLETE_WITH("FOR TABLE" , "FOR ALL TABLES" , "FOR TABLES IN SCHEMA" , "WITH (" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" ))
COMPLETE_WITH("TABLE" , "ALL TABLES" , "TABLES IN SCHEMA" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "ALL" ))
COMPLETE_WITH("TABLES" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "ALL" , "TABLES" ))
COMPLETE_WITH("WITH (" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "TABLES" ))
COMPLETE_WITH("IN SCHEMA" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "TABLE" , MatchAny) && !ends_with(prev_wd, ',' ))
COMPLETE_WITH("WHERE (" , "WITH (" );
/* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/*
* " CREATE PUBLICATION < name > FOR TABLE < name > WHERE ( " - complete with
* table attributes
*/
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, MatchAnyN, "WHERE" ))
COMPLETE_WITH("(" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, MatchAnyN, "WHERE" , "(" ))
COMPLETE_WITH_ATTR(prev3_wd);
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, MatchAnyN, "WHERE" , "(*)" ))
COMPLETE_WITH(" WITH (" );
/*
* Complete " CREATE PUBLICATION < name > FOR TABLES IN SCHEMA < schema > , . . . "
*/
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "TABLES" , "IN" , "SCHEMA" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_%%'" ,
"CURRENT_SCHEMA" );
else if (Matches("CREATE" , "PUBLICATION" , MatchAny, "FOR" , "TABLES" , "IN" , "SCHEMA" , MatchAny) && (!ends_with(prev_wd, ',' )))
COMPLETE_WITH("WITH (" );
/* Complete "CREATE PUBLICATION <name> [...] WITH" */
else if (Matches("CREATE" , "PUBLICATION" , MatchAnyN, "WITH" , "(" ))
COMPLETE_WITH("publish" , "publish_generated_columns" , "publish_via_partition_root" );
/* CREATE RULE */
/* Complete "CREATE [ OR REPLACE ] RULE <sth>" with "AS ON" */
else if (Matches("CREATE" , "RULE" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "RULE" , MatchAny))
COMPLETE_WITH("AS ON" );
/* Complete "CREATE [ OR REPLACE ] RULE <sth> AS" with "ON" */
else if (Matches("CREATE" , "RULE" , MatchAny, "AS" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "RULE" , MatchAny, "AS" ))
COMPLETE_WITH("ON" );
/*
* Complete " CREATE [ OR REPLACE ] RULE < sth > AS ON " with
* SELECT | UPDATE | INSERT | DELETE
*/
else if (Matches("CREATE" , "RULE" , MatchAny, "AS" , "ON" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "RULE" , MatchAny, "AS" , "ON" ))
COMPLETE_WITH("SELECT" , "UPDATE" , "INSERT" , "DELETE" );
/* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
else if (TailMatches("AS" , "ON" , "SELECT|UPDATE|INSERT|DELETE" ))
COMPLETE_WITH("TO" );
/* Complete "AS ON <sth> TO" with a table name */
else if (TailMatches("AS" , "ON" , "SELECT|UPDATE|INSERT|DELETE" , "TO" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* CREATE SCHEMA [ <name> ] [ AUTHORIZATION ] */
else if (Matches("CREATE" , "SCHEMA" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
"AUTHORIZATION" );
else if (Matches("CREATE" , "SCHEMA" , "AUTHORIZATION" ) ||
Matches("CREATE" , "SCHEMA" , MatchAny, "AUTHORIZATION" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_owner_roles);
else if (Matches("CREATE" , "SCHEMA" , "AUTHORIZATION" , MatchAny) ||
Matches("CREATE" , "SCHEMA" , MatchAny, "AUTHORIZATION" , MatchAny))
COMPLETE_WITH("CREATE" , "GRANT" );
else if (Matches("CREATE" , "SCHEMA" , MatchAny))
COMPLETE_WITH("AUTHORIZATION" , "CREATE" , "GRANT" );
/* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
else if (TailMatches("CREATE" , "SEQUENCE" , MatchAny) ||
TailMatches("CREATE" , "TEMP|TEMPORARY" , "SEQUENCE" , MatchAny))
COMPLETE_WITH("AS" , "INCREMENT BY" , "MINVALUE" , "MAXVALUE" , "NO" ,
"CACHE" , "CYCLE" , "OWNED BY" , "START WITH" );
else if (TailMatches("CREATE" , "SEQUENCE" , MatchAny, "AS" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY" , "SEQUENCE" , MatchAny, "AS" ))
COMPLETE_WITH_CS("smallint" , "integer" , "bigint" );
else if (TailMatches("CREATE" , "SEQUENCE" , MatchAny, "NO" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY" , "SEQUENCE" , MatchAny, "NO" ))
COMPLETE_WITH("MINVALUE" , "MAXVALUE" , "CYCLE" );
/* CREATE SERVER <name> */
else if (Matches("CREATE" , "SERVER" , MatchAny))
COMPLETE_WITH("TYPE" , "VERSION" , "FOREIGN DATA WRAPPER" );
/* CREATE STATISTICS <name> */
else if (Matches("CREATE" , "STATISTICS" , MatchAny))
COMPLETE_WITH("(" , "ON" );
else if (Matches("CREATE" , "STATISTICS" , MatchAny, "(" ))
COMPLETE_WITH("ndistinct" , "dependencies" , "mcv" );
else if (Matches("CREATE" , "STATISTICS" , MatchAny, "(*)" ))
COMPLETE_WITH("ON" );
else if (Matches("CREATE" , "STATISTICS" , MatchAny, MatchAnyN, "FROM" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
else if (TailMatches("CREATE" , "TEMP|TEMPORARY" ))
COMPLETE_WITH("SEQUENCE" , "TABLE" , "VIEW" );
/* Complete "CREATE UNLOGGED" with TABLE or SEQUENCE */
else if (TailMatches("CREATE" , "UNLOGGED" ))
COMPLETE_WITH("TABLE" , "SEQUENCE" );
/* Complete PARTITION BY with RANGE ( or LIST ( or ... */
else if (TailMatches("PARTITION" , "BY" ))
COMPLETE_WITH("RANGE (" , "LIST (" , "HASH (" );
/* If we have xxx PARTITION OF, provide a list of partitioned tables */
else if (TailMatches("PARTITION" , "OF" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
/* Limited completion support for partition bound specification */
else if (TailMatches("PARTITION" , "OF" , MatchAny))
COMPLETE_WITH("FOR VALUES" , "DEFAULT" );
/* Complete CREATE TABLE <name> with '(', AS, OF or PARTITION OF */
else if (TailMatches("CREATE" , "TABLE" , MatchAny) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny))
COMPLETE_WITH("(" , "AS" , "OF" , "PARTITION OF" );
/* Complete CREATE TABLE <name> OF with list of composite types */
else if (TailMatches("CREATE" , "TABLE" , MatchAny, "OF" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny, "OF" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
/* Complete CREATE TABLE <name> [ (...) ] AS with list of keywords */
else if (TailMatches("CREATE" , "TABLE" , MatchAny, "AS" ) ||
TailMatches("CREATE" , "TABLE" , MatchAny, "(*)" , "AS" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny, "AS" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny, "(*)" , "AS" ))
COMPLETE_WITH("EXECUTE" , "SELECT" , "TABLE" , "VALUES" , "WITH" );
/* Complete CREATE TABLE name (...) with supported options */
else if (TailMatches("CREATE" , "TABLE" , MatchAny, "(*)" ))
COMPLETE_WITH("AS" , "INHERITS (" , "PARTITION BY" , "USING" , "TABLESPACE" , "WITH (" );
else if (TailMatches("CREATE" , "UNLOGGED" , "TABLE" , MatchAny, "(*)" ))
COMPLETE_WITH("AS" , "INHERITS (" , "USING" , "TABLESPACE" , "WITH (" );
else if (TailMatches("CREATE" , "TEMP|TEMPORARY" , "TABLE" , MatchAny, "(*)" ))
COMPLETE_WITH("AS" , "INHERITS (" , "ON COMMIT" , "PARTITION BY" , "USING" ,
"TABLESPACE" , "WITH (" );
/* Complete CREATE TABLE (...) USING with table access methods */
else if (TailMatches("CREATE" , "TABLE" , MatchAny, "(*)" , "USING" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny, "(*)" , "USING" ))
COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
/* Complete CREATE TABLE (...) WITH with storage parameters */
else if (TailMatches("CREATE" , "TABLE" , MatchAny, "(*)" , "WITH" , "(" ) ||
TailMatches("CREATE" , "TEMP|TEMPORARY|UNLOGGED" , "TABLE" , MatchAny, "(*)" , "WITH" , "(" ))
COMPLETE_WITH_LIST(table_storage_parameters);
/* Complete CREATE TABLE ON COMMIT with actions */
else if (TailMatches("CREATE" , "TEMP|TEMPORARY" , "TABLE" , MatchAny, "(*)" , "ON" , "COMMIT" ))
COMPLETE_WITH("DELETE ROWS" , "DROP" , "PRESERVE ROWS" );
/* CREATE TABLESPACE */
else if (Matches("CREATE" , "TABLESPACE" , MatchAny))
COMPLETE_WITH("OWNER" , "LOCATION" );
/* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
else if (Matches("CREATE" , "TABLESPACE" , MatchAny, "OWNER" , MatchAny))
COMPLETE_WITH("LOCATION" );
/* CREATE TEXT SEARCH */
else if (Matches("CREATE" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
else if (Matches("CREATE" , "TEXT" , "SEARCH" , "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE" , MatchAny))
COMPLETE_WITH("(" );
/* CREATE TRANSFORM */
else if (Matches("CREATE" , "TRANSFORM" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRANSFORM" ))
COMPLETE_WITH("FOR" );
else if (Matches("CREATE" , "TRANSFORM" , "FOR" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRANSFORM" , "FOR" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("CREATE" , "TRANSFORM" , "FOR" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRANSFORM" , "FOR" , MatchAny))
COMPLETE_WITH("LANGUAGE" );
else if (Matches("CREATE" , "TRANSFORM" , "FOR" , MatchAny, "LANGUAGE" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRANSFORM" , "FOR" , MatchAny, "LANGUAGE" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
/* CREATE SUBSCRIPTION */
else if (Matches("CREATE" , "SUBSCRIPTION" , MatchAny))
COMPLETE_WITH("CONNECTION" );
else if (Matches("CREATE" , "SUBSCRIPTION" , MatchAny, "CONNECTION" , MatchAny))
COMPLETE_WITH("PUBLICATION" );
else if (Matches("CREATE" , "SUBSCRIPTION" , MatchAny, "CONNECTION" ,
MatchAny, "PUBLICATION" ))
{
/* complete with nothing here as this refers to remote publications */
}
else if (Matches("CREATE" , "SUBSCRIPTION" , MatchAnyN, "PUBLICATION" , MatchAny))
COMPLETE_WITH("WITH (" );
/* Complete "CREATE SUBSCRIPTION <name> ... WITH ( <opt>" */
else if (Matches("CREATE" , "SUBSCRIPTION" , MatchAnyN, "WITH" , "(" ))
COMPLETE_WITH("binary" , "connect" , "copy_data" , "create_slot" ,
"disable_on_error" , "enabled" , "failover" , "origin" ,
"password_required" , "run_as_owner" , "slot_name" ,
"streaming" , "synchronous_commit" , "two_phase" );
/* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */
/*
* Complete CREATE [ OR REPLACE ] TRIGGER < name > with BEFORE | AFTER | INSTEAD
* OF .
*/
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny))
COMPLETE_WITH("BEFORE" , "AFTER" , "INSTEAD OF" );
/*
* Complete CREATE [ OR REPLACE ] TRIGGER < name > BEFORE , AFTER with an
* event .
*/
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" ))
COMPLETE_WITH("INSERT" , "DELETE" , "UPDATE" , "TRUNCATE" );
/* Complete CREATE [ OR REPLACE ] TRIGGER <name> INSTEAD OF with an event */
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" ))
COMPLETE_WITH("INSERT" , "DELETE" , "UPDATE" );
/*
* Complete CREATE [ OR REPLACE ] TRIGGER < name > BEFORE , AFTER sth with
* OR | ON .
*/
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" , MatchAny) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" , MatchAny) ||
TailMatches("CREATE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" , MatchAny) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" , MatchAny))
COMPLETE_WITH("ON" , "OR" );
/*
* Complete CREATE [ OR REPLACE ] TRIGGER < name > BEFORE , AFTER event ON
* with a list of tables . EXECUTE FUNCTION is the recommended grammar
* instead of EXECUTE PROCEDURE in version 11 and upwards .
*/
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" , MatchAny, "ON" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "BEFORE|AFTER" , MatchAny, "ON" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/*
* Complete CREATE [ OR REPLACE ] TRIGGER . . . INSTEAD OF event ON with a
* list of views .
*/
else if (TailMatches("CREATE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" , MatchAny, "ON" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAny, "INSTEAD" , "OF" , MatchAny, "ON" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"ON" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"ON" , MatchAny))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("NOT DEFERRABLE" , "DEFERRABLE" , "INITIALLY" ,
"REFERENCING" , "FOR" , "WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("NOT DEFERRABLE" , "DEFERRABLE" , "INITIALLY" ,
"REFERENCING" , "FOR" , "WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"DEFERRABLE" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"DEFERRABLE" ) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"INITIALLY" , "IMMEDIATE|DEFERRED" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"INITIALLY" , "IMMEDIATE|DEFERRED" ))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("REFERENCING" , "FOR" , "WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("REFERENCING" , "FOR" , "WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" ))
COMPLETE_WITH("OLD TABLE" , "NEW TABLE" );
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"OLD|NEW" , "TABLE" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"OLD|NEW" , "TABLE" ))
COMPLETE_WITH("AS" );
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD" , "TABLE" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD" , "TABLE" , MatchAny))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("NEW TABLE" , "FOR" , "WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("NEW TABLE" , "FOR" , "WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "NEW" , "TABLE" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "NEW" , "TABLE" , MatchAny))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("OLD TABLE" , "FOR" , "WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("OLD TABLE" , "FOR" , "WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , "AS" , MatchAny, "OLD|NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , "AS" , MatchAny, "OLD|NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , MatchAny, "OLD|NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , MatchAny, "OLD|NEW" , "TABLE" , "AS" , MatchAny) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , "AS" , MatchAny, "OLD|NEW" , "TABLE" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , "AS" , MatchAny, "OLD|NEW" , "TABLE" , MatchAny) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , MatchAny, "OLD|NEW" , "TABLE" , MatchAny) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"REFERENCING" , "OLD|NEW" , "TABLE" , MatchAny, "OLD|NEW" , "TABLE" , MatchAny))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("FOR" , "WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("FOR" , "WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"FOR" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"FOR" ))
COMPLETE_WITH("EACH" , "ROW" , "STATEMENT" );
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"FOR" , "EACH" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"FOR" , "EACH" ))
COMPLETE_WITH("ROW" , "STATEMENT" );
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"FOR" , "EACH" , "ROW|STATEMENT" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"FOR" , "EACH" , "ROW|STATEMENT" ) ||
Matches("CREATE" , "TRIGGER" , MatchAnyN,
"FOR" , "ROW|STATEMENT" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"FOR" , "ROW|STATEMENT" ))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("WHEN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("WHEN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"WHEN" , "(*)" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"WHEN" , "(*)" ))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("EXECUTE FUNCTION" );
else
COMPLETE_WITH("EXECUTE PROCEDURE" );
}
/*
* Complete CREATE [ OR REPLACE ] TRIGGER . . . EXECUTE with
* PROCEDURE | FUNCTION .
*/
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"EXECUTE" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"EXECUTE" ))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("FUNCTION" );
else
COMPLETE_WITH("PROCEDURE" );
}
else if (Matches("CREATE" , "TRIGGER" , MatchAnyN,
"EXECUTE" , "FUNCTION|PROCEDURE" ) ||
Matches("CREATE" , "OR" , "REPLACE" , "TRIGGER" , MatchAnyN,
"EXECUTE" , "FUNCTION|PROCEDURE" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
/* CREATE ROLE,USER,GROUP <name> */
else if (Matches("CREATE" , "ROLE|GROUP|USER" , MatchAny) &&
!TailMatches("USER" , "MAPPING" ))
COMPLETE_WITH("ADMIN" , "BYPASSRLS" , "CONNECTION LIMIT" , "CREATEDB" ,
"CREATEROLE" , "ENCRYPTED PASSWORD" , "IN" , "INHERIT" ,
"LOGIN" , "NOBYPASSRLS" ,
"NOCREATEDB" , "NOCREATEROLE" , "NOINHERIT" ,
"NOLOGIN" , "NOREPLICATION" , "NOSUPERUSER" , "PASSWORD" ,
"REPLICATION" , "ROLE" , "SUPERUSER" , "SYSID" ,
"VALID UNTIL" , "WITH" );
/* CREATE ROLE,USER,GROUP <name> WITH */
else if (Matches("CREATE" , "ROLE|GROUP|USER" , MatchAny, "WITH" ))
/* Similar to the above, but don't complete "WITH" again. */
COMPLETE_WITH("ADMIN" , "BYPASSRLS" , "CONNECTION LIMIT" , "CREATEDB" ,
"CREATEROLE" , "ENCRYPTED PASSWORD" , "IN" , "INHERIT" ,
"LOGIN" , "NOBYPASSRLS" ,
"NOCREATEDB" , "NOCREATEROLE" , "NOINHERIT" ,
"NOLOGIN" , "NOREPLICATION" , "NOSUPERUSER" , "PASSWORD" ,
"REPLICATION" , "ROLE" , "SUPERUSER" , "SYSID" ,
"VALID UNTIL" );
/* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
else if (Matches("CREATE" , "ROLE|USER|GROUP" , MatchAny, "IN" ))
COMPLETE_WITH("GROUP" , "ROLE" );
/* CREATE TYPE */
else if (Matches("CREATE" , "TYPE" , MatchAny))
COMPLETE_WITH("(" , "AS" );
else if (Matches("CREATE" , "TYPE" , MatchAny, "AS" ))
COMPLETE_WITH("ENUM" , "RANGE" , "(" );
else if (HeadMatches("CREATE" , "TYPE" , MatchAny, "AS" , "(" ))
{
if (TailMatches("(|*," , MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatches("(|*," , MatchAny, MatchAnyExcept("*)" )))
COMPLETE_WITH("COLLATE" , "," , ")" );
}
else if (Matches("CREATE" , "TYPE" , MatchAny, "AS" , "ENUM|RANGE" ))
COMPLETE_WITH("(" );
else if (HeadMatches("CREATE" , "TYPE" , MatchAny, "(" ))
{
if (TailMatches("(|*," ))
COMPLETE_WITH("INPUT" , "OUTPUT" , "RECEIVE" , "SEND" ,
"TYPMOD_IN" , "TYPMOD_OUT" , "ANALYZE" , "SUBSCRIPT" ,
"INTERNALLENGTH" , "PASSEDBYVALUE" , "ALIGNMENT" ,
"STORAGE" , "LIKE" , "CATEGORY" , "PREFERRED" ,
"DEFAULT" , "ELEMENT" , "DELIMITER" ,
"COLLATABLE" );
else if (TailMatches("(*|*," , MatchAnyExcept("*=" )))
COMPLETE_WITH("=" );
else if (TailMatches("=" , MatchAnyExcept("*)" )))
COMPLETE_WITH("," , ")" );
}
else if (HeadMatches("CREATE" , "TYPE" , MatchAny, "AS" , "RANGE" , "(" ))
{
if (TailMatches("(|*," ))
COMPLETE_WITH("SUBTYPE" , "SUBTYPE_OPCLASS" , "COLLATION" ,
"CANONICAL" , "SUBTYPE_DIFF" ,
"MULTIRANGE_TYPE_NAME" );
else if (TailMatches("(*|*," , MatchAnyExcept("*=" )))
COMPLETE_WITH("=" );
else if (TailMatches("=" , MatchAnyExcept("*)" )))
COMPLETE_WITH("," , ")" );
}
/* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH */
else if (TailMatches("CREATE" , "VIEW" , MatchAny) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny))
COMPLETE_WITH("AS" , "WITH" );
/* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "AS" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "AS" ))
COMPLETE_WITH("SELECT" );
/* CREATE [ OR REPLACE ] VIEW <name> WITH ( yyy [= zzz] ) */
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" ))
COMPLETE_WITH("(" );
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" , "(" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" , "(" ))
COMPLETE_WITH_LIST(view_optional_parameters);
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" , "(" , "check_option" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" , "(" , "check_option" ))
COMPLETE_WITH("=" );
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" , "(" , "check_option" , "=" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" , "(" , "check_option" , "=" ))
COMPLETE_WITH("local" , "cascaded" );
/* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS */
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" , "(*)" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" , "(*)" ))
COMPLETE_WITH("AS" );
/* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS SELECT */
else if (TailMatches("CREATE" , "VIEW" , MatchAny, "WITH" , "(*)" , "AS" ) ||
TailMatches("CREATE" , "OR" , "REPLACE" , "VIEW" , MatchAny, "WITH" , "(*)" , "AS" ))
COMPLETE_WITH("SELECT" );
/* CREATE MATERIALIZED VIEW */
else if (Matches("CREATE" , "MATERIALIZED" ))
COMPLETE_WITH("VIEW" );
/* Complete CREATE MATERIALIZED VIEW <name> with AS or USING */
else if (Matches("CREATE" , "MATERIALIZED" , "VIEW" , MatchAny))
COMPLETE_WITH("AS" , "USING" );
/*
* Complete CREATE MATERIALIZED VIEW < name > USING with list of access
* methods
*/
else if (Matches("CREATE" , "MATERIALIZED" , "VIEW" , MatchAny, "USING" ))
COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
/* Complete CREATE MATERIALIZED VIEW <name> USING <access method> with AS */
else if (Matches("CREATE" , "MATERIALIZED" , "VIEW" , MatchAny, "USING" , MatchAny))
COMPLETE_WITH("AS" );
/*
* Complete CREATE MATERIALIZED VIEW < name > [ USING < access method > ] AS
* with " SELECT "
*/
else if (Matches("CREATE" , "MATERIALIZED" , "VIEW" , MatchAny, "AS" ) ||
Matches("CREATE" , "MATERIALIZED" , "VIEW" , MatchAny, "USING" , MatchAny, "AS" ))
COMPLETE_WITH("SELECT" );
/* CREATE EVENT TRIGGER */
else if (Matches("CREATE" , "EVENT" ))
COMPLETE_WITH("TRIGGER" );
/* Complete CREATE EVENT TRIGGER <name> with ON */
else if (Matches("CREATE" , "EVENT" , "TRIGGER" , MatchAny))
COMPLETE_WITH("ON" );
/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
else if (Matches("CREATE" , "EVENT" , "TRIGGER" , MatchAny, "ON" ))
COMPLETE_WITH("ddl_command_start" , "ddl_command_end" , "login" ,
"sql_drop" , "table_rewrite" );
/*
* Complete CREATE EVENT TRIGGER < name > ON < event_type > . EXECUTE FUNCTION
* is the recommended grammar instead of EXECUTE PROCEDURE in version 11
* and upwards .
*/
else if (Matches("CREATE" , "EVENT" , "TRIGGER" , MatchAny, "ON" , MatchAny))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("WHEN TAG IN (" , "EXECUTE FUNCTION" );
else
COMPLETE_WITH("WHEN TAG IN (" , "EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "EVENT" , "TRIGGER" , MatchAnyN, "WHEN|AND" , MatchAny, "IN" , "(*)" ))
{
if (pset.sversion >= 110000 )
COMPLETE_WITH("EXECUTE FUNCTION" );
else
COMPLETE_WITH("EXECUTE PROCEDURE" );
}
else if (Matches("CREATE" , "EVENT" , "TRIGGER" , MatchAnyN, "EXECUTE" , "FUNCTION|PROCEDURE" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
/* DEALLOCATE */
else if (Matches("DEALLOCATE" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_prepared_statements,
"ALL" );
/* DECLARE */
/*
* Complete DECLARE < name > with one of BINARY , ASENSITIVE , INSENSITIVE ,
* SCROLL , NO SCROLL , and CURSOR .
*/
else if (Matches("DECLARE" , MatchAny))
COMPLETE_WITH("BINARY" , "ASENSITIVE" , "INSENSITIVE" , "SCROLL" , "NO SCROLL" ,
"CURSOR" );
/*
* Complete DECLARE . . . < option > with other options . The PostgreSQL parser
* allows DECLARE options to be specified in any order . But the
* tab - completion follows the ordering of them that the SQL standard
* provides , like the syntax of DECLARE command in the documentation
* indicates .
*/
else if (Matches("DECLARE" , MatchAnyN, "BINARY" ))
COMPLETE_WITH("ASENSITIVE" , "INSENSITIVE" , "SCROLL" , "NO SCROLL" , "CURSOR" );
else if (Matches("DECLARE" , MatchAnyN, "ASENSITIVE|INSENSITIVE" ))
COMPLETE_WITH("SCROLL" , "NO SCROLL" , "CURSOR" );
else if (Matches("DECLARE" , MatchAnyN, "SCROLL" ))
COMPLETE_WITH("CURSOR" );
/* Complete DECLARE ... [options] NO with SCROLL */
else if (Matches("DECLARE" , MatchAnyN, "NO" ))
COMPLETE_WITH("SCROLL" );
/*
* Complete DECLARE . . . CURSOR with one of WITH HOLD , WITHOUT HOLD , and
* FOR
*/
else if (Matches("DECLARE" , MatchAnyN, "CURSOR" ))
COMPLETE_WITH("WITH HOLD" , "WITHOUT HOLD" , "FOR" );
/* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
else if (Matches("DECLARE" , MatchAnyN, "CURSOR" , "WITH|WITHOUT" ))
COMPLETE_WITH("HOLD" );
/* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
else if (Matches("DECLARE" , MatchAnyN, "CURSOR" , "WITH|WITHOUT" , "HOLD" ))
COMPLETE_WITH("FOR" );
/* DELETE --- can be inside EXPLAIN, RULE, etc */
/* Complete DELETE with "FROM" */
else if (Matches("DELETE" ))
COMPLETE_WITH("FROM" );
/* Complete DELETE FROM with a list of tables */
else if (TailMatches("DELETE" , "FROM" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete DELETE FROM <table> */
else if (TailMatches("DELETE" , "FROM" , MatchAny))
COMPLETE_WITH("USING" , "WHERE" );
/* XXX: implement tab completion for DELETE ... USING */
/* DISCARD */
else if (Matches("DISCARD" ))
COMPLETE_WITH("ALL" , "PLANS" , "SEQUENCES" , "TEMP" );
/* DO */
else if (Matches("DO" ))
COMPLETE_WITH("LANGUAGE" );
/* DROP */
/* Complete DROP object with CASCADE / RESTRICT */
else if (Matches("DROP" ,
"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW" ,
MatchAny) ||
Matches("DROP" , "ACCESS" , "METHOD" , MatchAny) ||
Matches("DROP" , "EVENT" , "TRIGGER" , MatchAny) ||
Matches("DROP" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny) ||
Matches("DROP" , "FOREIGN" , "TABLE" , MatchAny) ||
Matches("DROP" , "TEXT" , "SEARCH" , "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
else if (Matches("DROP" , "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE" , MatchAny, MatchAny) &&
ends_with(prev_wd, ')' ))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* help completing some of the variants */
else if (Matches("DROP" , "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE" , MatchAny))
COMPLETE_WITH("(" );
else if (Matches("DROP" , "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE" , MatchAny, "(" ))
COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
else if (Matches("DROP" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
else if (Matches("DROP" , "DATABASE" , MatchAny))
COMPLETE_WITH("WITH (" );
else if (HeadMatches("DROP" , "DATABASE" ) && (ends_with(prev_wd, '(' )))
COMPLETE_WITH("FORCE" );
/* DROP INDEX */
else if (Matches("DROP" , "INDEX" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
"CONCURRENTLY" );
else if (Matches("DROP" , "INDEX" , "CONCURRENTLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (Matches("DROP" , "INDEX" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
else if (Matches("DROP" , "INDEX" , "CONCURRENTLY" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP MATERIALIZED VIEW */
else if (Matches("DROP" , "MATERIALIZED" ))
COMPLETE_WITH("VIEW" );
else if (Matches("DROP" , "MATERIALIZED" , "VIEW" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("DROP" , "MATERIALIZED" , "VIEW" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP OWNED BY */
else if (Matches("DROP" , "OWNED" ))
COMPLETE_WITH("BY" );
else if (Matches("DROP" , "OWNED" , "BY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (Matches("DROP" , "OWNED" , "BY" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP TEXT SEARCH */
else if (Matches("DROP" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
/* DROP TRIGGER */
else if (Matches("DROP" , "TRIGGER" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("DROP" , "TRIGGER" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
else if (Matches("DROP" , "TRIGGER" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP ACCESS METHOD */
else if (Matches("DROP" , "ACCESS" ))
COMPLETE_WITH("METHOD" );
else if (Matches("DROP" , "ACCESS" , "METHOD" ))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
/* DROP EVENT TRIGGER */
else if (Matches("DROP" , "EVENT" ))
COMPLETE_WITH("TRIGGER" );
else if (Matches("DROP" , "EVENT" , "TRIGGER" ))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
/* DROP POLICY <name> */
else if (Matches("DROP" , "POLICY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_policies);
/* DROP POLICY <name> ON */
else if (Matches("DROP" , "POLICY" , MatchAny))
COMPLETE_WITH("ON" );
/* DROP POLICY <name> ON <table> */
else if (Matches("DROP" , "POLICY" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
else if (Matches("DROP" , "POLICY" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP RULE */
else if (Matches("DROP" , "RULE" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("DROP" , "RULE" , MatchAny, "ON" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
else if (Matches("DROP" , "RULE" , MatchAny, "ON" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* DROP TRANSFORM */
else if (Matches("DROP" , "TRANSFORM" ))
COMPLETE_WITH("FOR" );
else if (Matches("DROP" , "TRANSFORM" , "FOR" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("DROP" , "TRANSFORM" , "FOR" , MatchAny))
COMPLETE_WITH("LANGUAGE" );
else if (Matches("DROP" , "TRANSFORM" , "FOR" , MatchAny, "LANGUAGE" ))
{
set_completion_reference(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
else if (Matches("DROP" , "TRANSFORM" , "FOR" , MatchAny, "LANGUAGE" , MatchAny))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* EXECUTE */
else if (Matches("EXECUTE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
/*
* EXPLAIN [ ( option [ , . . . ] ) ] statement
* EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
*/
else if (Matches("EXPLAIN" ))
COMPLETE_WITH("SELECT" , "INSERT INTO" , "DELETE FROM" , "UPDATE" , "DECLARE" ,
"MERGE INTO" , "EXECUTE" , "ANALYZE" , "VERBOSE" );
else if (HeadMatches("EXPLAIN" , "(*" ) &&
!HeadMatches("EXPLAIN" , "(*)" ))
{
/*
* This fires if we ' re in an unfinished parenthesized option list .
* get_previous_words treats a completed parenthesized option list as
* one word , so the above test is correct .
*/
if (ends_with(prev_wd, '(' ) || ends_with(prev_wd, ',' ))
COMPLETE_WITH("ANALYZE" , "VERBOSE" , "COSTS" , "SETTINGS" , "GENERIC_PLAN" ,
"BUFFERS" , "SERIALIZE" , "WAL" , "TIMING" , "SUMMARY" ,
"MEMORY" , "FORMAT" );
else if (TailMatches("ANALYZE|VERBOSE|COSTS|SETTINGS|GENERIC_PLAN|BUFFERS|WAL|TIMING|SUMMARY|MEMORY" ))
COMPLETE_WITH("ON" , "OFF" );
else if (TailMatches("SERIALIZE" ))
COMPLETE_WITH("TEXT" , "NONE" , "BINARY" );
else if (TailMatches("FORMAT" ))
COMPLETE_WITH("TEXT" , "XML" , "JSON" , "YAML" );
}
else if (Matches("EXPLAIN" , "ANALYZE" ))
COMPLETE_WITH("SELECT" , "INSERT INTO" , "DELETE FROM" , "UPDATE" , "DECLARE" ,
"MERGE INTO" , "EXECUTE" , "VERBOSE" );
else if (Matches("EXPLAIN" , "(*)" ) ||
Matches("EXPLAIN" , "VERBOSE" ) ||
Matches("EXPLAIN" , "ANALYZE" , "VERBOSE" ))
COMPLETE_WITH("SELECT" , "INSERT INTO" , "DELETE FROM" , "UPDATE" , "DECLARE" ,
"MERGE INTO" , "EXECUTE" );
/* FETCH && MOVE */
/*
* Complete FETCH with one of ABSOLUTE , BACKWARD , FORWARD , RELATIVE , ALL ,
* NEXT , PRIOR , FIRST , LAST , FROM , IN , and a list of cursors
*/
else if (Matches("FETCH|MOVE" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
"ABSOLUTE" ,
"BACKWARD" ,
"FORWARD" ,
"RELATIVE" ,
"ALL" ,
"NEXT" ,
"PRIOR" ,
"FIRST" ,
"LAST" ,
"FROM" ,
"IN" );
/*
* Complete FETCH BACKWARD or FORWARD with one of ALL , FROM , IN , and a
* list of cursors
*/
else if (Matches("FETCH|MOVE" , "BACKWARD|FORWARD" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
"ALL" ,
"FROM" ,
"IN" );
/*
* Complete FETCH < direction > with " FROM " or " IN " . These are equivalent ,
* but we may as well tab - complete both : perhaps some users prefer one
* variant or the other .
*/
else if (Matches("FETCH|MOVE" , "ABSOLUTE|BACKWARD|FORWARD|RELATIVE" ,
MatchAnyExcept("FROM|IN" )) ||
Matches("FETCH|MOVE" , "ALL|NEXT|PRIOR|FIRST|LAST" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
"FROM" ,
"IN" );
/* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
else if (Matches("FETCH|MOVE" , MatchAnyN, "FROM|IN" ))
COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
/* FOREIGN DATA WRAPPER */
/* applies in ALTER/DROP FDW and in CREATE SERVER */
else if (TailMatches("FOREIGN" , "DATA" , "WRAPPER" ) &&
!TailMatches("CREATE" , MatchAny, MatchAny, MatchAny))
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
/* applies in CREATE SERVER */
else if (Matches("CREATE" , "SERVER" , MatchAnyN, "FOREIGN" , "DATA" , "WRAPPER" , MatchAny))
COMPLETE_WITH("OPTIONS" );
/* FOREIGN TABLE */
else if (TailMatches("FOREIGN" , "TABLE" ) &&
!TailMatches("CREATE" , MatchAny, MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
/* FOREIGN SERVER */
else if (TailMatches("FOREIGN" , "SERVER" ))
COMPLETE_WITH_QUERY(Query_for_list_of_servers);
/*
* GRANT and REVOKE are allowed inside CREATE SCHEMA and
* ALTER DEFAULT PRIVILEGES , so use TailMatches
*/
/* Complete GRANT/REVOKE with a list of roles and privileges */
else if (TailMatches("GRANT|REVOKE" ) ||
TailMatches("REVOKE" , "ADMIN|GRANT|INHERIT|SET" , "OPTION" , "FOR" ))
{
/*
* With ALTER DEFAULT PRIVILEGES , restrict completion to grantable
* privileges ( can ' t grant roles )
*/
if (HeadMatches("ALTER" , "DEFAULT" , "PRIVILEGES" ))
{
if (TailMatches("GRANT" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" ))
COMPLETE_WITH("SELECT" , "INSERT" , "UPDATE" ,
"DELETE" , "TRUNCATE" , "REFERENCES" , "TRIGGER" ,
"CREATE" , "EXECUTE" , "USAGE" , "MAINTAIN" , "ALL" );
else if (TailMatches("REVOKE" ))
COMPLETE_WITH("SELECT" , "INSERT" , "UPDATE" ,
"DELETE" , "TRUNCATE" , "REFERENCES" , "TRIGGER" ,
"CREATE" , "EXECUTE" , "USAGE" , "MAINTAIN" , "ALL" ,
"GRANT OPTION FOR" );
}
else if (TailMatches("GRANT" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Privilege_options_of_grant_and_revoke);
else if (TailMatches("REVOKE" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Privilege_options_of_grant_and_revoke,
"GRANT OPTION FOR" ,
"ADMIN OPTION FOR" ,
"INHERIT OPTION FOR" ,
"SET OPTION FOR" );
else if (TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" ))
COMPLETE_WITH(Privilege_options_of_grant_and_revoke);
else if (TailMatches("REVOKE" , "ADMIN|INHERIT|SET" , "OPTION" , "FOR" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
}
else if (TailMatches("GRANT|REVOKE" , "ALTER" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , "ALTER" ))
COMPLETE_WITH("SYSTEM" );
else if (TailMatches("REVOKE" , "SET" ))
COMPLETE_WITH("ON PARAMETER" , "OPTION FOR" );
else if (TailMatches("GRANT" , "SET" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , "SET" ) ||
TailMatches("GRANT|REVOKE" , "ALTER" , "SYSTEM" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , "ALTER" , "SYSTEM" ))
COMPLETE_WITH("ON PARAMETER" );
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "PARAMETER" ) ||
TailMatches("GRANT|REVOKE" , MatchAny, MatchAny, "ON" , "PARAMETER" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "PARAMETER" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, MatchAny, "ON" , "PARAMETER" ))
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars);
else if (TailMatches("GRANT" , MatchAny, "ON" , "PARAMETER" , MatchAny) ||
TailMatches("GRANT" , MatchAny, MatchAny, "ON" , "PARAMETER" , MatchAny))
COMPLETE_WITH("TO" );
else if (TailMatches("REVOKE" , MatchAny, "ON" , "PARAMETER" , MatchAny) ||
TailMatches("REVOKE" , MatchAny, MatchAny, "ON" , "PARAMETER" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "PARAMETER" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, MatchAny, "ON" , "PARAMETER" , MatchAny))
COMPLETE_WITH("FROM" );
/*
* Complete GRANT / REVOKE < privilege > with " ON " , GRANT / REVOKE < role > with
* TO / FROM
*/
else if (TailMatches("GRANT|REVOKE" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny))
{
if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|MAINTAIN|ALL" ))
COMPLETE_WITH("ON" );
else if (TailMatches("GRANT" , MatchAny))
COMPLETE_WITH("TO" );
else
COMPLETE_WITH("FROM" );
}
/*
* Complete GRANT / REVOKE < sth > ON with a list of appropriate relations .
*
* Note : GRANT / REVOKE can get quite complex ; tab - completion as implemented
* here will only work if the privilege list contains exactly one
* privilege .
*/
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" ))
{
/*
* With ALTER DEFAULT PRIVILEGES , restrict completion to the kinds of
* objects supported .
*/
if (HeadMatches("ALTER" , "DEFAULT" , "PRIVILEGES" ))
COMPLETE_WITH("TABLES" , "SEQUENCES" , "FUNCTIONS" , "PROCEDURES" , "ROUTINES" , "TYPES" , "SCHEMAS" , "LARGE OBJECTS" );
else
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_grantables,
"ALL FUNCTIONS IN SCHEMA" ,
"ALL PROCEDURES IN SCHEMA" ,
"ALL ROUTINES IN SCHEMA" ,
"ALL SEQUENCES IN SCHEMA" ,
"ALL TABLES IN SCHEMA" ,
"DATABASE" ,
"DOMAIN" ,
"FOREIGN DATA WRAPPER" ,
"FOREIGN SERVER" ,
"FUNCTION" ,
"LANGUAGE" ,
"LARGE OBJECT" ,
"PARAMETER" ,
"PROCEDURE" ,
"ROUTINE" ,
"SCHEMA" ,
"SEQUENCE" ,
"TABLE" ,
"TABLESPACE" ,
"TYPE" );
}
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "ALL" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "ALL" ))
COMPLETE_WITH("FUNCTIONS IN SCHEMA" ,
"PROCEDURES IN SCHEMA" ,
"ROUTINES IN SCHEMA" ,
"SEQUENCES IN SCHEMA" ,
"TABLES IN SCHEMA" );
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "FOREIGN" ) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "SERVER" );
/*
* Complete " GRANT / REVOKE * ON DATABASE / DOMAIN / . . . " with a list of
* appropriate objects .
*
* Complete " GRANT / REVOKE * ON * " with " TO / FROM " .
*/
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , MatchAny))
{
if (TailMatches("DATABASE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (TailMatches("DOMAIN" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (TailMatches("FUNCTION" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
else if (TailMatches("LANGUAGE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (TailMatches("PROCEDURE" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
else if (TailMatches("ROUTINE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatches("SCHEMA" ))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (TailMatches("SEQUENCE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
else if (TailMatches("TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
else if (TailMatches("TABLESPACE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches("TYPE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatches("GRANT" , MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO" );
else
COMPLETE_WITH("FROM" );
}
/*
* Complete " GRANT / REVOKE . . . TO / FROM " with username , PUBLIC ,
* CURRENT_ROLE , CURRENT_USER , or SESSION_USER .
*/
else if (Matches("GRANT" , MatchAnyN, "TO" ) ||
Matches("REVOKE" , MatchAnyN, "FROM" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/*
* Offer grant options after that .
*/
else if (Matches("GRANT" , MatchAnyN, "TO" , MatchAny))
COMPLETE_WITH("WITH ADMIN" ,
"WITH INHERIT" ,
"WITH SET" ,
"WITH GRANT OPTION" ,
"GRANTED BY" );
else if (Matches("GRANT" , MatchAnyN, "TO" , MatchAny, "WITH" ))
COMPLETE_WITH("ADMIN" ,
"INHERIT" ,
"SET" ,
"GRANT OPTION" );
else if (Matches("GRANT" , MatchAnyN, "TO" , MatchAny, "WITH" , "ADMIN|INHERIT|SET" ))
COMPLETE_WITH("OPTION" , "TRUE" , "FALSE" );
else if (Matches("GRANT" , MatchAnyN, "TO" , MatchAny, "WITH" , MatchAny, "OPTION" ))
COMPLETE_WITH("GRANTED BY" );
else if (Matches("GRANT" , MatchAnyN, "TO" , MatchAny, "WITH" , MatchAny, "OPTION" , "GRANTED" , "BY" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , MatchAnyN, "TO|FROM" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
/* Offer WITH GRANT OPTION after that */
else if (Matches("ALTER" , "DEFAULT" , "PRIVILEGES" , MatchAnyN, "TO" , MatchAny))
COMPLETE_WITH("WITH GRANT OPTION" );
/* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
else if (Matches("GRANT" , MatchAnyN, "ON" , MatchAny, MatchAny))
COMPLETE_WITH("TO" );
else if (Matches("REVOKE" , MatchAnyN, "ON" , MatchAny, MatchAny))
COMPLETE_WITH("FROM" );
/* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "ALL" , MatchAny, "IN" , "SCHEMA" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "ALL" , MatchAny, "IN" , "SCHEMA" , MatchAny))
{
if (TailMatches("GRANT" , MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO" );
else
COMPLETE_WITH("FROM" );
}
/* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "FOREIGN" , "DATA" , "WRAPPER" , MatchAny))
{
if (TailMatches("GRANT" , MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO" );
else
COMPLETE_WITH("FROM" );
}
/* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
else if (TailMatches("GRANT|REVOKE" , MatchAny, "ON" , "FOREIGN" , "SERVER" , MatchAny) ||
TailMatches("REVOKE" , "GRANT" , "OPTION" , "FOR" , MatchAny, "ON" , "FOREIGN" , "SERVER" , MatchAny))
{
if (TailMatches("GRANT" , MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO" );
else
COMPLETE_WITH("FROM" );
}
/* GROUP BY */
else if (TailMatches("FROM" , MatchAny, "GROUP" ))
COMPLETE_WITH("BY" );
/* IMPORT FOREIGN SCHEMA */
else if (Matches("IMPORT" ))
COMPLETE_WITH("FOREIGN SCHEMA" );
else if (Matches("IMPORT" , "FOREIGN" ))
COMPLETE_WITH("SCHEMA" );
else if (Matches("IMPORT" , "FOREIGN" , "SCHEMA" , MatchAny))
COMPLETE_WITH("EXCEPT (" , "FROM SERVER" , "LIMIT TO (" );
else if (TailMatches("LIMIT" , "TO" , "(*)" ) ||
TailMatches("EXCEPT" , "(*)" ))
COMPLETE_WITH("FROM SERVER" );
else if (TailMatches("FROM" , "SERVER" , MatchAny))
COMPLETE_WITH("INTO" );
else if (TailMatches("FROM" , "SERVER" , MatchAny, "INTO" ))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (TailMatches("FROM" , "SERVER" , MatchAny, "INTO" , MatchAny))
COMPLETE_WITH("OPTIONS (" );
/* INSERT --- can be inside EXPLAIN, RULE, etc */
/* Complete NOT MATCHED THEN INSERT */
else if (TailMatches("NOT" , "MATCHED" , "THEN" , "INSERT" ))
COMPLETE_WITH("VALUES" , "(" );
/* Complete INSERT with "INTO" */
else if (TailMatches("INSERT" ))
COMPLETE_WITH("INTO" );
/* Complete INSERT INTO with table names */
else if (TailMatches("INSERT" , "INTO" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete "INSERT INTO <table> (" with attribute names */
else if (TailMatches("INSERT" , "INTO" , MatchAny, "(" ))
COMPLETE_WITH_ATTR(prev2_wd);
/*
* Complete INSERT INTO < table > with " ( " or " VALUES " or " SELECT " or
* " TABLE " or " DEFAULT VALUES " or " OVERRIDING "
*/
else if (TailMatches("INSERT" , "INTO" , MatchAny))
COMPLETE_WITH("(" , "DEFAULT VALUES" , "SELECT" , "TABLE" , "VALUES" , "OVERRIDING" );
/*
* Complete INSERT INTO < table > ( attribs ) with " VALUES " or " SELECT " or
* " TABLE " or " OVERRIDING "
*/
else if (TailMatches("INSERT" , "INTO" , MatchAny, MatchAny) &&
ends_with(prev_wd, ')' ))
COMPLETE_WITH("SELECT" , "TABLE" , "VALUES" , "OVERRIDING" );
/* Complete OVERRIDING */
else if (TailMatches("OVERRIDING" ))
COMPLETE_WITH("SYSTEM VALUE" , "USER VALUE" );
/* Complete after OVERRIDING clause */
else if (TailMatches("OVERRIDING" , MatchAny, "VALUE" ))
COMPLETE_WITH("SELECT" , "TABLE" , "VALUES" );
/* Insert an open parenthesis after "VALUES" */
else if (TailMatches("VALUES" ) && !TailMatches("DEFAULT" , "VALUES" ))
COMPLETE_WITH("(" );
/* LOCK */
/* Complete LOCK [TABLE] [ONLY] with a list of tables */
else if (Matches("LOCK" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
"TABLE" , "ONLY" );
else if (Matches("LOCK" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
"ONLY" );
else if (Matches("LOCK" , "TABLE" , "ONLY" ) || Matches("LOCK" , "ONLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* For the following, handle the case of a single table only for now */
/* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
else if (Matches("LOCK" , MatchAnyExcept("TABLE|ONLY" )) ||
Matches("LOCK" , "TABLE" , MatchAnyExcept("ONLY" )) ||
Matches("LOCK" , "ONLY" , MatchAny) ||
Matches("LOCK" , "TABLE" , "ONLY" , MatchAny))
COMPLETE_WITH("IN" , "NOWAIT" );
/* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
else if (Matches("LOCK" , MatchAnyN, "IN" ))
COMPLETE_WITH("ACCESS SHARE MODE" ,
"ROW SHARE MODE" , "ROW EXCLUSIVE MODE" ,
"SHARE UPDATE EXCLUSIVE MODE" , "SHARE MODE" ,
"SHARE ROW EXCLUSIVE MODE" ,
"EXCLUSIVE MODE" , "ACCESS EXCLUSIVE MODE" );
/*
* Complete LOCK [ TABLE ] [ ONLY ] < table > IN ACCESS | ROW with rest of lock
* mode
*/
else if (Matches("LOCK" , MatchAnyN, "IN" , "ACCESS|ROW" ))
COMPLETE_WITH("EXCLUSIVE MODE" , "SHARE MODE" );
/* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
else if (Matches("LOCK" , MatchAnyN, "IN" , "SHARE" ))
COMPLETE_WITH("MODE" , "ROW EXCLUSIVE MODE" ,
"UPDATE EXCLUSIVE MODE" );
/* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
else if (Matches("LOCK" , MatchAnyN, "MODE" ))
COMPLETE_WITH("NOWAIT" );
/* MERGE --- can be inside EXPLAIN */
else if (TailMatches("MERGE" ))
COMPLETE_WITH("INTO" );
else if (TailMatches("MERGE" , "INTO" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_mergetargets);
/* Complete MERGE INTO <table> [[AS] <alias>] with USING */
else if (TailMatches("MERGE" , "INTO" , MatchAny))
COMPLETE_WITH("USING" , "AS" );
else if (TailMatches("MERGE" , "INTO" , MatchAny, "AS" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, MatchAnyExcept("USING|AS" )))
COMPLETE_WITH("USING" );
/*
* Complete MERGE INTO . . . USING with a list of relations supporting
* SELECT
*/
else if (TailMatches("MERGE" , "INTO" , MatchAny, "USING" ) ||
TailMatches("MERGE" , "INTO" , MatchAny, "AS" , MatchAny, "USING" ) ||
TailMatches("MERGE" , "INTO" , MatchAny, MatchAny, "USING" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/*
* Complete MERGE INTO < table > [ [ AS ] < alias > ] USING < relations > [ [ AS ]
* alias ] with ON
*/
else if (TailMatches("MERGE" , "INTO" , MatchAny, "USING" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, "AS" , MatchAny, "USING" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, MatchAny, "USING" , MatchAny))
COMPLETE_WITH("AS" , "ON" );
else if (TailMatches("MERGE" , "INTO" , MatchAny, "USING" , MatchAny, "AS" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, "AS" , MatchAny, "USING" , MatchAny, "AS" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, MatchAny, "USING" , MatchAny, "AS" , MatchAny) ||
TailMatches("MERGE" , "INTO" , MatchAny, "USING" , MatchAny, MatchAnyExcept("ON|AS" )) ||
TailMatches("MERGE" , "INTO" , MatchAny, "AS" , MatchAny, "USING" , MatchAny, MatchAnyExcept("ON|AS" )) ||
TailMatches("MERGE" , "INTO" , MatchAny, MatchAny, "USING" , MatchAny, MatchAnyExcept("ON|AS" )))
COMPLETE_WITH("ON" );
/* Complete MERGE INTO ... ON with target table attributes */
else if (TailMatches("INTO" , MatchAny, "USING" , MatchAny, "ON" ))
COMPLETE_WITH_ATTR(prev4_wd);
else if (TailMatches("INTO" , MatchAny, "AS" , MatchAny, "USING" , MatchAny, "AS" , MatchAny, "ON" ))
COMPLETE_WITH_ATTR(prev8_wd);
else if (TailMatches("INTO" , MatchAny, MatchAny, "USING" , MatchAny, MatchAny, "ON" ))
COMPLETE_WITH_ATTR(prev6_wd);
/*
* Complete . . . USING < relation > [ [ AS ] alias ] ON join condition
* ( consisting of one or three words typically used ) with WHEN [ NOT ]
* MATCHED
*/
else if (TailMatches("USING" , MatchAny, "ON" , MatchAny) ||
TailMatches("USING" , MatchAny, "AS" , MatchAny, "ON" , MatchAny) ||
TailMatches("USING" , MatchAny, MatchAny, "ON" , MatchAny) ||
TailMatches("USING" , MatchAny, "ON" , MatchAny, MatchAnyExcept("WHEN" ), MatchAnyExcept("WHEN" )) ||
TailMatches("USING" , MatchAny, "AS" , MatchAny, "ON" , MatchAny, MatchAnyExcept("WHEN" ), MatchAnyExcept("WHEN" )) ||
TailMatches("USING" , MatchAny, MatchAny, "ON" , MatchAny, MatchAnyExcept("WHEN" ), MatchAnyExcept("WHEN" )))
COMPLETE_WITH("WHEN MATCHED" , "WHEN NOT MATCHED" );
else if (TailMatches("USING" , MatchAny, "ON" , MatchAny, "WHEN" ) ||
TailMatches("USING" , MatchAny, "AS" , MatchAny, "ON" , MatchAny, "WHEN" ) ||
TailMatches("USING" , MatchAny, MatchAny, "ON" , MatchAny, "WHEN" ) ||
TailMatches("USING" , MatchAny, "ON" , MatchAny, MatchAny, MatchAny, "WHEN" ) ||
TailMatches("USING" , MatchAny, "AS" , MatchAny, "ON" , MatchAny, MatchAny, MatchAny, "WHEN" ) ||
TailMatches("USING" , MatchAny, MatchAny, "ON" , MatchAny, MatchAny, MatchAny, "WHEN" ))
COMPLETE_WITH("MATCHED" , "NOT MATCHED" );
/*
* Complete . . . WHEN MATCHED and WHEN NOT MATCHED BY SOURCE | TARGET with
* THEN / AND
*/
else if (TailMatches("WHEN" , "MATCHED" ) ||
TailMatches("WHEN" , "NOT" , "MATCHED" , "BY" , "SOURCE|TARGET" ))
COMPLETE_WITH("THEN" , "AND" );
/* Complete ... WHEN NOT MATCHED with BY/THEN/AND */
else if (TailMatches("WHEN" , "NOT" , "MATCHED" ))
COMPLETE_WITH("BY" , "THEN" , "AND" );
/* Complete ... WHEN NOT MATCHED BY with SOURCE/TARGET */
else if (TailMatches("WHEN" , "NOT" , "MATCHED" , "BY" ))
COMPLETE_WITH("SOURCE" , "TARGET" );
/*
* Complete . . . WHEN MATCHED THEN and WHEN NOT MATCHED BY SOURCE THEN with
* UPDATE SET / DELETE / DO NOTHING
*/
else if (TailMatches("WHEN" , "MATCHED" , "THEN" ) ||
TailMatches("WHEN" , "NOT" , "MATCHED" , "BY" , "SOURCE" , "THEN" ))
COMPLETE_WITH("UPDATE SET" , "DELETE" , "DO NOTHING" );
/*
* Complete . . . WHEN NOT MATCHED [ BY TARGET ] THEN with INSERT / DO NOTHING
*/
else if (TailMatches("WHEN" , "NOT" , "MATCHED" , "THEN" ) ||
TailMatches("WHEN" , "NOT" , "MATCHED" , "BY" , "TARGET" , "THEN" ))
COMPLETE_WITH("INSERT" , "DO NOTHING" );
/* NOTIFY --- can be inside EXPLAIN, RULE, etc */
else if (TailMatches("NOTIFY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_channels);
/* OPTIONS */
else if (TailMatches("OPTIONS" ))
COMPLETE_WITH("(" );
/* OWNER TO - complete with available roles */
else if (TailMatches("OWNER" , "TO" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_owner_roles);
/* ORDER BY */
else if (TailMatches("FROM" , MatchAny, "ORDER" ))
COMPLETE_WITH("BY" );
else if (TailMatches("FROM" , MatchAny, "ORDER" , "BY" ))
COMPLETE_WITH_ATTR(prev3_wd);
/* PREPARE xx AS */
else if (Matches("PREPARE" , MatchAny, "AS" ))
COMPLETE_WITH("SELECT" , "UPDATE" , "INSERT INTO" , "DELETE FROM" );
/*
* PREPARE TRANSACTION is missing on purpose . It ' s intended for transaction
* managers , not for manual use in interactive sessions .
*/
/* REASSIGN OWNED BY xxx TO yyy */
else if (Matches("REASSIGN" ))
COMPLETE_WITH("OWNED BY" );
else if (Matches("REASSIGN" , "OWNED" ))
COMPLETE_WITH("BY" );
else if (Matches("REASSIGN" , "OWNED" , "BY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (Matches("REASSIGN" , "OWNED" , "BY" , MatchAny))
COMPLETE_WITH("TO" );
else if (Matches("REASSIGN" , "OWNED" , "BY" , MatchAny, "TO" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* REFRESH MATERIALIZED VIEW */
else if (Matches("REFRESH" ))
COMPLETE_WITH("MATERIALIZED VIEW" );
else if (Matches("REFRESH" , "MATERIALIZED" ))
COMPLETE_WITH("VIEW" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
"CONCURRENTLY" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , "CONCURRENTLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , MatchAny))
COMPLETE_WITH("WITH" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , "CONCURRENTLY" , MatchAny))
COMPLETE_WITH("WITH" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , MatchAny, "WITH" ))
COMPLETE_WITH("NO DATA" , "DATA" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , "CONCURRENTLY" , MatchAny, "WITH" ))
COMPLETE_WITH("NO DATA" , "DATA" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , MatchAny, "WITH" , "NO" ))
COMPLETE_WITH("DATA" );
else if (Matches("REFRESH" , "MATERIALIZED" , "VIEW" , "CONCURRENTLY" , MatchAny, "WITH" , "NO" ))
COMPLETE_WITH("DATA" );
/* REINDEX */
else if (Matches("REINDEX" ) ||
Matches("REINDEX" , "(*)" ))
COMPLETE_WITH("TABLE" , "INDEX" , "SYSTEM" , "SCHEMA" , "DATABASE" );
else if (Matches("REINDEX" , "TABLE" ) ||
Matches("REINDEX" , "(*)" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexables,
"CONCURRENTLY" );
else if (Matches("REINDEX" , "INDEX" ) ||
Matches("REINDEX" , "(*)" , "INDEX" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
"CONCURRENTLY" );
else if (Matches("REINDEX" , "SCHEMA" ) ||
Matches("REINDEX" , "(*)" , "SCHEMA" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
"CONCURRENTLY" );
else if (Matches("REINDEX" , "SYSTEM|DATABASE" ) ||
Matches("REINDEX" , "(*)" , "SYSTEM|DATABASE" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_databases,
"CONCURRENTLY" );
else if (Matches("REINDEX" , "TABLE" , "CONCURRENTLY" ) ||
Matches("REINDEX" , "(*)" , "TABLE" , "CONCURRENTLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
else if (Matches("REINDEX" , "INDEX" , "CONCURRENTLY" ) ||
Matches("REINDEX" , "(*)" , "INDEX" , "CONCURRENTLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (Matches("REINDEX" , "SCHEMA" , "CONCURRENTLY" ) ||
Matches("REINDEX" , "(*)" , "SCHEMA" , "CONCURRENTLY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (Matches("REINDEX" , "SYSTEM|DATABASE" , "CONCURRENTLY" ) ||
Matches("REINDEX" , "(*)" , "SYSTEM|DATABASE" , "CONCURRENTLY" ))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (HeadMatches("REINDEX" , "(*" ) &&
!HeadMatches("REINDEX" , "(*)" ))
{
/*
* This fires if we ' re in an unfinished parenthesized option list .
* get_previous_words treats a completed parenthesized option list as
* one word , so the above test is correct .
*/
if (ends_with(prev_wd, '(' ) || ends_with(prev_wd, ',' ))
COMPLETE_WITH("CONCURRENTLY" , "TABLESPACE" , "VERBOSE" );
else if (TailMatches("TABLESPACE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
}
/* SECURITY LABEL */
else if (Matches("SECURITY" ))
COMPLETE_WITH("LABEL" );
else if (Matches("SECURITY" , "LABEL" ))
COMPLETE_WITH("ON" , "FOR" );
else if (Matches("SECURITY" , "LABEL" , "FOR" , MatchAny))
COMPLETE_WITH("ON" );
else if (Matches("SECURITY" , "LABEL" , "ON" ) ||
Matches("SECURITY" , "LABEL" , "FOR" , MatchAny, "ON" ))
COMPLETE_WITH("TABLE" , "COLUMN" , "AGGREGATE" , "DATABASE" , "DOMAIN" ,
"EVENT TRIGGER" , "FOREIGN TABLE" , "FUNCTION" ,
"LARGE OBJECT" , "MATERIALIZED VIEW" , "LANGUAGE" ,
"PUBLICATION" , "PROCEDURE" , "ROLE" , "ROUTINE" , "SCHEMA" ,
"SEQUENCE" , "SUBSCRIPTION" , "TABLESPACE" , "TYPE" , "VIEW" );
else if (Matches("SECURITY" , "LABEL" , "ON" , MatchAny, MatchAny))
COMPLETE_WITH("IS" );
/* SELECT */
/* naah . . . */
/* SET, RESET, SHOW */
/* Complete with a variable name */
else if (TailMatches("SET|RESET" ) &&
!TailMatches("UPDATE" , MatchAny, "SET" ) &&
!TailMatches("ALTER" , "DATABASE|USER|ROLE" , MatchAny, "RESET" ))
COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
"CONSTRAINTS" ,
"TRANSACTION" ,
"SESSION" ,
"ROLE" ,
"TABLESPACE" ,
"ALL" );
else if (Matches("SHOW" ))
COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_show_vars,
"SESSION AUTHORIZATION" ,
"ALL" );
else if (Matches("SHOW" , "SESSION" ))
COMPLETE_WITH("AUTHORIZATION" );
/* Complete "SET TRANSACTION" */
else if (Matches("SET" , "TRANSACTION" ))
COMPLETE_WITH("SNAPSHOT" , "ISOLATION LEVEL" , "READ" , "DEFERRABLE" , "NOT DEFERRABLE" );
else if (Matches("BEGIN|START" , "TRANSACTION" ) ||
Matches("BEGIN" , "WORK" ) ||
Matches("BEGIN" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" ))
COMPLETE_WITH("ISOLATION LEVEL" , "READ" , "DEFERRABLE" , "NOT DEFERRABLE" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "NOT" ) ||
Matches("BEGIN" , "NOT" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "NOT" ))
COMPLETE_WITH("DEFERRABLE" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "ISOLATION" ) ||
Matches("BEGIN" , "ISOLATION" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "ISOLATION" ))
COMPLETE_WITH("LEVEL" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "ISOLATION" , "LEVEL" ) ||
Matches("BEGIN" , "ISOLATION" , "LEVEL" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "ISOLATION" , "LEVEL" ))
COMPLETE_WITH("READ" , "REPEATABLE READ" , "SERIALIZABLE" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "ISOLATION" , "LEVEL" , "READ" ) ||
Matches("BEGIN" , "ISOLATION" , "LEVEL" , "READ" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "ISOLATION" , "LEVEL" , "READ" ))
COMPLETE_WITH("UNCOMMITTED" , "COMMITTED" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "ISOLATION" , "LEVEL" , "REPEATABLE" ) ||
Matches("BEGIN" , "ISOLATION" , "LEVEL" , "REPEATABLE" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "ISOLATION" , "LEVEL" , "REPEATABLE" ))
COMPLETE_WITH("READ" );
else if (Matches("SET|BEGIN|START" , "TRANSACTION|WORK" , "READ" ) ||
Matches("BEGIN" , "READ" ) ||
Matches("SET" , "SESSION" , "CHARACTERISTICS" , "AS" , "TRANSACTION" , "READ" ))
COMPLETE_WITH("ONLY" , "WRITE" );
/* SET CONSTRAINTS */
else if (Matches("SET" , "CONSTRAINTS" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_constraints_with_schema,
"ALL" );
/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
else if (Matches("SET" , "CONSTRAINTS" , MatchAny))
COMPLETE_WITH("DEFERRED" , "IMMEDIATE" );
/* Complete SET ROLE */
else if (Matches("SET" , "ROLE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
else if (Matches("SET" , "SESSION" ))
COMPLETE_WITH("AUTHORIZATION" , "CHARACTERISTICS AS TRANSACTION" );
/* Complete SET SESSION AUTHORIZATION with username */
else if (Matches("SET" , "SESSION" , "AUTHORIZATION" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
"DEFAULT" );
/* Complete RESET SESSION with AUTHORIZATION */
else if (Matches("RESET" , "SESSION" ))
COMPLETE_WITH("AUTHORIZATION" );
/* Complete SET <var> with "TO" */
else if (Matches("SET" , MatchAny))
COMPLETE_WITH("TO" );
/*
* Complete ALTER DATABASE | FUNCTION | PROCEDURE | ROLE | ROUTINE | USER . . . SET
* < name >
*/
else if (Matches("ALTER" , "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER" , MatchAnyN, "SET" , MatchAnyExcept("SCHEMA" )))
COMPLETE_WITH("FROM CURRENT" , "TO" );
/*
* Suggest possible variable values in SET variable TO | = , along with the
* preceding ALTER syntaxes .
*/
else if (TailMatches("SET" , MatchAny, "TO|=" ) &&
!TailMatches("UPDATE" , MatchAny, "SET" , MatchAny, "TO|=" ))
{
/* special cased code for individual GUCs */
if (TailMatches("DateStyle" , "TO|=" ))
COMPLETE_WITH("ISO" , "SQL" , "Postgres" , "German" ,
"YMD" , "DMY" , "MDY" ,
"US" , "European" , "NonEuropean" ,
"DEFAULT" );
else if (TailMatches("search_path" , "TO|=" ))
{
/* Here, we want to allow pg_catalog, so use narrower exclusion */
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
" AND nspname NOT LIKE E'pg\\\\_toast%%'"
" AND nspname NOT LIKE E'pg\\\\_temp%%'" ,
"DEFAULT" );
}
else if (TailMatches("TimeZone" , "TO|=" ))
COMPLETE_WITH_TIMEZONE_NAME();
else
{
/* generic, type based, GUC support */
char *guctype = get_guctype(prev2_wd);
/*
* Note : if we don ' t recognize the GUC name , it ' s important to not
* offer any completions , as most likely we ' ve misinterpreted the
* context and this isn ' t a GUC - setting command at all .
*/
if (guctype)
{
if (strcmp(guctype, "enum" ) == 0 )
{
set_completion_reference_verbatim(prev2_wd);
COMPLETE_WITH_QUERY_PLUS(Query_for_values_of_enum_GUC,
"DEFAULT" );
}
else if (strcmp(guctype, "bool" ) == 0 )
COMPLETE_WITH("on" , "off" , "true" , "false" , "yes" , "no" ,
"1" , "0" , "DEFAULT" );
else
COMPLETE_WITH("DEFAULT" );
free(guctype);
}
}
}
/* START TRANSACTION */
else if (Matches("START" ))
COMPLETE_WITH("TRANSACTION" );
/* TABLE, but not TABLE embedded in other commands */
else if (Matches("TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/* TABLESAMPLE */
else if (TailMatches("TABLESAMPLE" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods);
else if (TailMatches("TABLESAMPLE" , MatchAny))
COMPLETE_WITH("(" );
/* TRUNCATE */
else if (Matches("TRUNCATE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
"TABLE" , "ONLY" );
else if (Matches("TRUNCATE" , "TABLE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
"ONLY" );
else if (Matches("TRUNCATE" , MatchAnyN, "ONLY" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
else if (Matches("TRUNCATE" , MatchAny) ||
Matches("TRUNCATE" , "TABLE|ONLY" , MatchAny) ||
Matches("TRUNCATE" , "TABLE" , "ONLY" , MatchAny))
COMPLETE_WITH("RESTART IDENTITY" , "CONTINUE IDENTITY" , "CASCADE" , "RESTRICT" );
else if (Matches("TRUNCATE" , MatchAnyN, "IDENTITY" ))
COMPLETE_WITH("CASCADE" , "RESTRICT" );
/* UNLISTEN */
else if (Matches("UNLISTEN" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_channels, "*" );
/* UPDATE --- can be inside EXPLAIN, RULE, etc */
/* If prev. word is UPDATE suggest a list of tables */
else if (TailMatches("UPDATE" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete UPDATE <table> with "SET" */
else if (TailMatches("UPDATE" , MatchAny))
COMPLETE_WITH("SET" );
/* Complete UPDATE <table> SET with list of attributes */
else if (TailMatches("UPDATE" , MatchAny, "SET" ))
COMPLETE_WITH_ATTR(prev2_wd);
/* UPDATE <table> SET <attr> = */
else if (TailMatches("UPDATE" , MatchAny, "SET" , MatchAnyExcept("*=" )))
COMPLETE_WITH("=" );
/* USER MAPPING */
else if (Matches("ALTER|CREATE|DROP" , "USER" , "MAPPING" ))
COMPLETE_WITH("FOR" );
else if (Matches("CREATE" , "USER" , "MAPPING" , "FOR" ))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
"CURRENT_ROLE" ,
"CURRENT_USER" ,
"PUBLIC" ,
"USER" );
else if (Matches("ALTER|DROP" , "USER" , "MAPPING" , "FOR" ))
COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
else if (Matches("CREATE|ALTER|DROP" , "USER" , "MAPPING" , "FOR" , MatchAny))
COMPLETE_WITH("SERVER" );
else if (Matches("CREATE|ALTER" , "USER" , "MAPPING" , "FOR" , MatchAny, "SERVER" , MatchAny))
COMPLETE_WITH("OPTIONS" );
/*
* VACUUM [ ( option [ , . . . ] ) ] [ [ ONLY ] table_and_columns [ , . . . ] ]
* VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ [ ONLY ] table_and_columns [ , . . . ] ]
*/
else if (Matches("VACUUM" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"(" ,
"FULL" ,
"FREEZE" ,
"VERBOSE" ,
"ANALYZE" ,
"ONLY" );
else if (HeadMatches("VACUUM" , "(*" ) &&
!HeadMatches("VACUUM" , "(*)" ))
{
/*
* This fires if we ' re in an unfinished parenthesized option list .
* get_previous_words treats a completed parenthesized option list as
* one word , so the above test is correct .
*/
if (ends_with(prev_wd, '(' ) || ends_with(prev_wd, ',' ))
COMPLETE_WITH("FULL" , "FREEZE" , "ANALYZE" , "VERBOSE" ,
"DISABLE_PAGE_SKIPPING" , "SKIP_LOCKED" ,
"INDEX_CLEANUP" , "PROCESS_MAIN" , "PROCESS_TOAST" ,
"TRUNCATE" , "PARALLEL" , "SKIP_DATABASE_STATS" ,
"ONLY_DATABASE_STATS" , "BUFFER_USAGE_LIMIT" );
else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_MAIN|PROCESS_TOAST|TRUNCATE|SKIP_DATABASE_STATS|ONLY_DATABASE_STATS" ))
COMPLETE_WITH("ON" , "OFF" );
else if (TailMatches("INDEX_CLEANUP" ))
COMPLETE_WITH("AUTO" , "ON" , "OFF" );
}
else if (Matches("VACUUM" , "(*)" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"ONLY" );
else if (Matches("VACUUM" , "FULL" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"FREEZE" ,
"VERBOSE" ,
"ANALYZE" ,
"ONLY" );
else if (Matches("VACUUM" , MatchAnyN, "FREEZE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"VERBOSE" ,
"ANALYZE" ,
"ONLY" );
else if (Matches("VACUUM" , MatchAnyN, "VERBOSE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"ANALYZE" ,
"ONLY" );
else if (Matches("VACUUM" , MatchAnyN, "ANALYZE" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
"ONLY" );
else if (Matches("VACUUM" , MatchAnyN, "(" ))
/* "VACUUM (" should be caught above, so assume we want columns */
COMPLETE_WITH_ATTR(prev2_wd);
else if (HeadMatches("VACUUM" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
/* WITH [RECURSIVE] */
/*
* Only match when WITH is the first word , as WITH may appear in many
* other contexts .
*/
else if (Matches("WITH" ))
COMPLETE_WITH("RECURSIVE" );
/* WHERE */
/* Simple case of the word before the where being the table name */
else if (TailMatches(MatchAny, "WHERE" ))
COMPLETE_WITH_ATTR(prev2_wd);
/* ... FROM ... */
/* TODO: also include SRF ? */
else if (TailMatches("FROM" ) && !Matches("COPY|\\copy" , MatchAny, "FROM" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/* ... JOIN ... */
else if (TailMatches("JOIN" ))
COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_selectables, "LATERAL" );
else if (TailMatches("JOIN" , MatchAny) && !TailMatches("CROSS|NATURAL" , "JOIN" , MatchAny))
COMPLETE_WITH("ON" , "USING (" );
else if (TailMatches("JOIN" , MatchAny, MatchAny) &&
!TailMatches("CROSS|NATURAL" , "JOIN" , MatchAny, MatchAny) && !TailMatches("ON|USING" ))
COMPLETE_WITH("ON" , "USING (" );
else if (TailMatches("JOIN" , "LATERAL" , MatchAny, MatchAny) &&
!TailMatches("CROSS|NATURAL" , "JOIN" , "LATERAL" , MatchAny, MatchAny) && !TailMatches("ON|USING" ))
COMPLETE_WITH("ON" , "USING (" );
else if (TailMatches("JOIN" , MatchAny, "USING" ) ||
TailMatches("JOIN" , MatchAny, MatchAny, "USING" ) ||
TailMatches("JOIN" , "LATERAL" , MatchAny, MatchAny, "USING" ))
COMPLETE_WITH("(" );
else if (TailMatches("JOIN" , MatchAny, "USING" , "(" ))
COMPLETE_WITH_ATTR(prev3_wd);
else if (TailMatches("JOIN" , MatchAny, MatchAny, "USING" , "(" ))
COMPLETE_WITH_ATTR(prev4_wd);
/* ... AT [ LOCAL | TIME ZONE ] ... */
else if (TailMatches("AT" ))
COMPLETE_WITH("LOCAL" , "TIME ZONE" );
else if (TailMatches("AT" , "TIME" , "ZONE" ))
COMPLETE_WITH_TIMEZONE_NAME();
/* Backslash commands */
/* TODO: \dc \dd \dl */
else if (TailMatchesCS("\\?" ))
COMPLETE_WITH_CS("commands" , "options" , "variables" );
else if (TailMatchesCS("\\connect|\\c" ))
{
if (!recognized_connection_string(text))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
}
else if (TailMatchesCS("\\connect|\\c" , MatchAny))
{
if (!recognized_connection_string(prev_wd))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
}
else if (TailMatchesCS("\\da*" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates);
else if (TailMatchesCS("\\dAc*" , MatchAny) ||
TailMatchesCS("\\dAf*" , MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dAo*" , MatchAny) ||
TailMatchesCS("\\dAp*" , MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families);
else if (TailMatchesCS("\\dA*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS("\\db*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatchesCS("\\dconfig*" ))
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_show_vars);
else if (TailMatchesCS("\\dD*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (TailMatchesCS("\\des*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_servers);
else if (TailMatchesCS("\\deu*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
else if (TailMatchesCS("\\dew*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
else if (TailMatchesCS("\\df*" ))
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
else if (HeadMatchesCS("\\df*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dFd*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
else if (TailMatchesCS("\\dFp*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
else if (TailMatchesCS("\\dFt*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
/* must be at end of \dF alternatives: */
else if (TailMatchesCS("\\dF*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
else if (TailMatchesCS("\\di*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (TailMatchesCS("\\dL*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (TailMatchesCS("\\dn*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
/* no support for completing operators, but we can complete types: */
else if (HeadMatchesCS("\\do*" , MatchAny))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dp" ) || TailMatchesCS("\\z" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
else if (TailMatchesCS("\\dPi*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes);
else if (TailMatchesCS("\\dPt*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
else if (TailMatchesCS("\\dP*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations);
else if (TailMatchesCS("\\dRp*" ))
COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_publications);
else if (TailMatchesCS("\\dRs*" ))
COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_subscriptions);
else if (TailMatchesCS("\\ds*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
else if (TailMatchesCS("\\dt*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (TailMatchesCS("\\dT*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\du*" ) ||
TailMatchesCS("\\dg*" ) ||
TailMatchesCS("\\drg*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (TailMatchesCS("\\dv*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\dx*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
else if (TailMatchesCS("\\dX*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics);
else if (TailMatchesCS("\\dm*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (TailMatchesCS("\\dE*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
else if (TailMatchesCS("\\dy*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
/* must be at end of \d alternatives: */
else if (TailMatchesCS("\\d*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
else if (TailMatchesCS("\\ef" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatchesCS("\\ev" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\encoding" ))
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_encodings);
else if (TailMatchesCS("\\h|\\help" ))
COMPLETE_WITH_LIST(sql_commands);
else if (TailMatchesCS("\\h|\\help" , MatchAny))
{
if (TailMatches("DROP" ))
matches = rl_completion_matches(text, drop_command_generator);
else if (TailMatches("ALTER" ))
matches = rl_completion_matches(text, alter_command_generator);
/*
* CREATE is recognized by tail match elsewhere , so doesn ' t need to be
* repeated here
*/
}
else if (TailMatchesCS("\\h|\\help" , MatchAny, MatchAny))
{
if (TailMatches("CREATE|DROP" , "ACCESS" ))
COMPLETE_WITH("METHOD" );
else if (TailMatches("ALTER" , "DEFAULT" ))
COMPLETE_WITH("PRIVILEGES" );
else if (TailMatches("CREATE|ALTER|DROP" , "EVENT" ))
COMPLETE_WITH("TRIGGER" );
else if (TailMatches("CREATE|ALTER|DROP" , "FOREIGN" ))
COMPLETE_WITH("DATA WRAPPER" , "TABLE" );
else if (TailMatches("ALTER" , "LARGE" ))
COMPLETE_WITH("OBJECT" );
else if (TailMatches("CREATE|ALTER|DROP" , "MATERIALIZED" ))
COMPLETE_WITH("VIEW" );
else if (TailMatches("CREATE|ALTER|DROP" , "TEXT" ))
COMPLETE_WITH("SEARCH" );
else if (TailMatches("CREATE|ALTER|DROP" , "USER" ))
COMPLETE_WITH("MAPPING FOR" );
}
else if (TailMatchesCS("\\h|\\help" , MatchAny, MatchAny, MatchAny))
{
if (TailMatches("CREATE|ALTER|DROP" , "FOREIGN" , "DATA" ))
COMPLETE_WITH("WRAPPER" );
else if (TailMatches("CREATE|ALTER|DROP" , "TEXT" , "SEARCH" ))
COMPLETE_WITH("CONFIGURATION" , "DICTIONARY" , "PARSER" , "TEMPLATE" );
else if (TailMatches("CREATE|ALTER|DROP" , "USER" , "MAPPING" ))
COMPLETE_WITH("FOR" );
}
else if (TailMatchesCS("\\l*" ) && !TailMatchesCS("\\lo*" ))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (TailMatchesCS("\\password" ))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (TailMatchesCS("\\pset" ))
COMPLETE_WITH_CS("border" , "columns" , "csv_fieldsep" , "expanded" ,
"fieldsep" , "fieldsep_zero" , "footer" , "format" ,
"linestyle" , "null" , "numericlocale" ,
"pager" , "pager_min_lines" ,
"recordsep" , "recordsep_zero" ,
"tableattr" , "title" , "tuples_only" ,
"unicode_border_linestyle" ,
"unicode_column_linestyle" ,
"unicode_header_linestyle" ,
"xheader_width" );
else if (TailMatchesCS("\\pset" , MatchAny))
{
if (TailMatchesCS("format" ))
COMPLETE_WITH_CS("aligned" , "asciidoc" , "csv" , "html" , "latex" ,
"latex-longtable" , "troff-ms" , "unaligned" ,
"wrapped" );
else if (TailMatchesCS("xheader_width" ))
COMPLETE_WITH_CS("full" , "column" , "page" );
else if (TailMatchesCS("linestyle" ))
COMPLETE_WITH_CS("ascii" , "old-ascii" , "unicode" );
else if (TailMatchesCS("pager" ))
COMPLETE_WITH_CS("on" , "off" , "always" );
else if (TailMatchesCS("unicode_border_linestyle|"
"unicode_column_linestyle|"
"unicode_header_linestyle" ))
COMPLETE_WITH_CS("single" , "double" );
}
else if (TailMatchesCS("\\unset" ))
matches = complete_from_variables(text, "" , "" , true );
else if (TailMatchesCS("\\set" ))
matches = complete_from_variables(text, "" , "" , false );
else if (TailMatchesCS("\\set" , MatchAny))
{
if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|SHOW_ALL_RESULTS|"
"SINGLELINE|SINGLESTEP" ))
COMPLETE_WITH_CS("on" , "off" );
else if (TailMatchesCS("COMP_KEYWORD_CASE" ))
COMPLETE_WITH_CS("lower" , "upper" ,
"preserve-lower" , "preserve-upper" );
else if (TailMatchesCS("ECHO" ))
COMPLETE_WITH_CS("errors" , "queries" , "all" , "none" );
else if (TailMatchesCS("ECHO_HIDDEN" ))
COMPLETE_WITH_CS("noexec" , "off" , "on" );
else if (TailMatchesCS("HISTCONTROL" ))
COMPLETE_WITH_CS("ignorespace" , "ignoredups" ,
"ignoreboth" , "none" );
else if (TailMatchesCS("ON_ERROR_ROLLBACK" ))
COMPLETE_WITH_CS("on" , "off" , "interactive" );
else if (TailMatchesCS("SHOW_CONTEXT" ))
COMPLETE_WITH_CS("never" , "errors" , "always" );
else if (TailMatchesCS("VERBOSITY" ))
COMPLETE_WITH_CS("default" , "verbose" , "terse" , "sqlstate" );
}
else if (TailMatchesCS("\\sf*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatchesCS("\\sv*" ))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|"
"\\ir|\\include_relative|\\o|\\out|"
"\\s|\\w|\\write|\\lo_import" ))
{
completion_charp = "\\" ;
completion_force_quote = false ;
matches = rl_completion_matches(text, complete_from_files);
}
/* gen_tabcomplete.pl ends special processing here */
/* END GEN_TABCOMPLETE */
return matches;
}
/*
* 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 .
*/
/*
* Common routine for create_command_generator and drop_command_generator .
* Entries that have ' excluded ' flags are not returned .
*/
static char *
create_or_drop_command_generator(const char *text, int state, bits32 excluded)
{
static int list_index,
string_length;
const char *name;
/* If this is the first time for this completion, init some values */
if (state == 0 )
{
list_index = 0 ;
string_length = strlen(text);
}
/* 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 ].flags & excluded))
return pg_strdup_keyword_case(name, text);
}
/* if nothing matches, return NULL */
return NULL;
}
/*
* 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)
{
return create_or_drop_command_generator(text, state, THING_NO_CREATE);
}
/*
* This function gives you a list of things you can put after a DROP command .
*/
static char *
drop_command_generator(const char *text, int state)
{
return create_or_drop_command_generator(text, state, THING_NO_DROP);
}
/*
* This function gives you a list of things you can put after an ALTER command .
*/
static char *
alter_command_generator(const char *text, int state)
{
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, completion_charpp,
completion_verbatim, 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, completion_charpp,
completion_verbatim, 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(NULL, completion_squery, completion_charpp,
completion_verbatim, text, state);
}
static char *
complete_from_versioned_schema_query(const char *text, int state)
{
const SchemaQuery *squery = completion_squery;
/* 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;
return _complete_from_query(NULL, squery, completion_charpp,
completion_verbatim, 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 restriction clause of the form
* output LIKE ' % s '
* where " output " is the same string that the query returns . The % s
* will be replaced by a LIKE pattern to match the already - typed text .
* There can be a second ' % s ' , which will be replaced by a suitably - escaped
* version of the string provided in completion_ref_object . If there is a
* third ' % s ' , it will be replaced by a suitably - escaped version of the string
* provided in completion_ref_schema . Those strings should be set up
* by calling set_completion_reference or set_completion_reference_verbatim .
* Simple queries should return a single column of matches . If " verbatim "
* is true , the matches are returned as - is ; otherwise , they are taken to
* be SQL identifiers and quoted if necessary .
*
* 2 . A schema query used for completion of both schema and relation names .
* This is represented by a SchemaQuery object ; see that typedef for details .
*
* See top of file for examples of both kinds of query .
*
* In addition to the query itself , we accept a null - terminated array of
* literal keywords , which will be returned if they match the input - so - far
* ( case insensitively ) . ( These are in addition to keywords specified
* within the schema_query , if any . )
*
* If " verbatim " is true , then we use the given text as - is to match the
* query results ; otherwise we parse it as a possibly - qualified identifier ,
* and reconstruct suitable quoting afterward .
*
* " text " and " state " are supplied by Readline . " text " is the word we are
* trying to complete . " state " is zero on first call , nonzero later .
*
* readline will call this repeatedly with the same text and varying
* state . On each call , we are supposed to return a malloc ' d string
* that is a candidate completion . Return NULL when done .
*/
static char *
_complete_from_query(const char *simple_query,
const SchemaQuery *schema_query,
const char *const *keywords,
bool verbatim,
const char *text, int state)
{
static int list_index,
num_schema_only,
num_query_other,
num_keywords;
static PGresult *result = NULL;
static bool non_empty_object;
static bool schemaquoted;
static bool objectquoted;
/*
* If this is the first time for this completion , we fetch a list of our
* " things " from the backend .
*/
if (state == 0 )
{
PQExpBufferData query_buffer;
char *schemaname;
char *objectname;
char *e_object_like;
char *e_schemaname;
char *e_ref_object;
char *e_ref_schema;
/* Reset static state, ensuring no memory leaks */
list_index = 0 ;
num_schema_only = 0 ;
num_query_other = 0 ;
num_keywords = 0 ;
PQclear(result);
result = NULL;
/* Parse text, splitting into schema and object name if needed */
if (verbatim)
{
objectname = pg_strdup(text);
schemaname = NULL;
}
else
{
parse_identifier(text,
&schemaname, &objectname,
&schemaquoted, &objectquoted);
}
/* Remember whether the user has typed anything in the object part */
non_empty_object = (*objectname != '\0' );
/*
* Convert objectname to a LIKE prefix pattern ( e . g . ' foo % ' ) , and set
* up suitably - escaped copies of all the strings we need .
*/
e_object_like = make_like_pattern(objectname);
if (schemaname)
e_schemaname = escape_string(schemaname);
else
e_schemaname = NULL;
if (completion_ref_object)
e_ref_object = escape_string(completion_ref_object);
else
e_ref_object = NULL;
if (completion_ref_schema)
e_ref_schema = escape_string(completion_ref_schema);
else
e_ref_schema = NULL;
initPQExpBuffer(&query_buffer);
if (schema_query)
{
Assert(simple_query == NULL);
/*
* We issue different queries depending on whether the input is
* already qualified or not . schema_query gives us the pieces to
* assemble .
*/
if (schemaname == NULL || schema_query->namespace == NULL)
{
/* Get unqualified names matching the input-so-far */
appendPQExpBufferStr(&query_buffer, "SELECT " );
if (schema_query->use_distinct)
appendPQExpBufferStr(&query_buffer, "DISTINCT " );
appendPQExpBuffer(&query_buffer,
"%s, NULL::pg_catalog.text FROM %s" ,
schema_query->result,
schema_query->catname);
if (schema_query->refnamespace && completion_ref_schema)
appendPQExpBufferStr(&query_buffer,
", pg_catalog.pg_namespace nr" );
appendPQExpBufferStr(&query_buffer, " WHERE " );
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND " ,
schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "(%s) LIKE '%s'" ,
schema_query->result,
e_object_like);
if (schema_query->viscondition)
appendPQExpBuffer(&query_buffer, " AND %s" ,
schema_query->viscondition);
if (schema_query->refname)
{
Assert(completion_ref_object);
appendPQExpBuffer(&query_buffer, " AND %s = '%s'" ,
schema_query->refname, e_ref_object);
if (schema_query->refnamespace && completion_ref_schema)
appendPQExpBuffer(&query_buffer,
" AND %s = nr.oid AND nr.nspname = '%s'" ,
schema_query->refnamespace,
e_ref_schema);
else if (schema_query->refviscondition)
appendPQExpBuffer(&query_buffer,
" AND %s" ,
schema_query->refviscondition);
}
/*
* When fetching relation names , suppress system catalogs
* unless the input - so - far begins with " pg_ " . This is a
* compromise 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(objectname, "pg_" , 3 ) != 0 )
{
appendPQExpBufferStr(&query_buffer,
" AND c.relnamespace <> (SELECT oid FROM"
" pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')" );
}
/*
* If the target object type can be schema - qualified , add in
* schema names matching the input - so - far .
*/
if (schema_query->namespace )
{
appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"
"SELECT NULL::pg_catalog.text, n.nspname "
"FROM pg_catalog.pg_namespace n "
"WHERE n.nspname LIKE '%s'" ,
e_object_like);
/*
* Likewise , suppress system schemas unless the
* input - so - far begins with " pg_ " .
*/
if (strncmp(objectname, "pg_" , 3 ) != 0 )
appendPQExpBufferStr(&query_buffer,
" AND n.nspname NOT LIKE E'pg\\\\_%'" );
/*
* Since we ' re matching these schema names to the object
* name , handle their quoting using the object name ' s
* quoting state .
*/
schemaquoted = objectquoted;
}
}
else
{
/* Input is qualified, so produce only qualified names */
appendPQExpBufferStr(&query_buffer, "SELECT " );
if (schema_query->use_distinct)
appendPQExpBufferStr(&query_buffer, "DISTINCT " );
appendPQExpBuffer(&query_buffer, "%s, n.nspname "
"FROM %s, pg_catalog.pg_namespace n" ,
schema_query->result,
schema_query->catname);
if (schema_query->refnamespace && completion_ref_schema)
appendPQExpBufferStr(&query_buffer,
", pg_catalog.pg_namespace nr" );
appendPQExpBuffer(&query_buffer, " WHERE %s = n.oid AND " ,
schema_query->namespace );
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND " ,
schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "(%s) LIKE '%s' AND " ,
schema_query->result,
e_object_like);
appendPQExpBuffer(&query_buffer, "n.nspname = '%s'" ,
e_schemaname);
if (schema_query->refname)
{
Assert(completion_ref_object);
appendPQExpBuffer(&query_buffer, " AND %s = '%s'" ,
schema_query->refname, e_ref_object);
if (schema_query->refnamespace && completion_ref_schema)
appendPQExpBuffer(&query_buffer,
" AND %s = nr.oid AND nr.nspname = '%s'" ,
schema_query->refnamespace,
e_ref_schema);
else if (schema_query->refviscondition)
appendPQExpBuffer(&query_buffer,
" AND %s" ,
schema_query->refviscondition);
}
}
}
else
{
Assert(simple_query);
/* simple_query is an sprintf-style format string */
appendPQExpBuffer(&query_buffer, simple_query,
e_object_like,
e_ref_object, e_ref_schema);
}
/* Limit the number of records in the result */
appendPQExpBuffer(&query_buffer, "\nLIMIT %d" ,
completion_max_records);
/* Finally, we can issue the query */
result = exec_query(query_buffer.data);
/* Clean up */
termPQExpBuffer(&query_buffer);
free(schemaname);
free(objectname);
free(e_object_like);
free(e_schemaname);
free(e_ref_object);
free(e_ref_schema);
}
/* Return the next result, if any, but not if the query failed */
if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
{
int nskip;
while (list_index < PQntuples(result))
{
const char *item = NULL;
const char *nsp = NULL;
if (!PQgetisnull(result, list_index, 0 ))
item = PQgetvalue(result, list_index, 0 );
if (PQnfields(result) > 1 &&
!PQgetisnull(result, list_index, 1 ))
nsp = PQgetvalue(result, list_index, 1 );
list_index++;
/* In verbatim mode, we return all the items as-is */
if (verbatim)
{
num_query_other++;
return pg_strdup(item);
}
/*
* In normal mode , a name requiring quoting will be returned only
* if the input was empty or quoted . Otherwise the user might see
* completion inserting a quote she didn ' t type , which is
* surprising . This restriction also dodges some odd behaviors of
* some versions of readline / libedit .
*/
if (non_empty_object)
{
if (item && !objectquoted && identifier_needs_quotes(item))
continue ;
if (nsp && !schemaquoted && identifier_needs_quotes(nsp))
continue ;
}
/* Count schema-only results for hack below */
if (item == NULL && nsp != NULL)
num_schema_only++;
else
num_query_other++;
return requote_identifier(nsp, item, schemaquoted, objectquoted);
}
/*
* When the query result is exhausted , check for hard - wired keywords .
* These will only be returned if they match the input - so - far ,
* ignoring case .
*/
nskip = list_index - PQntuples(result);
if (schema_query && schema_query->keywords)
{
const char *const *itemp = schema_query->keywords;
while (*itemp)
{
const char *item = *itemp++;
if (nskip-- > 0 )
continue ;
list_index++;
if (pg_strncasecmp(text, item, strlen(text)) == 0 )
{
num_keywords++;
return pg_strdup_keyword_case(item, text);
}
}
}
if (keywords)
{
const char *const *itemp = keywords;
while (*itemp)
{
const char *item = *itemp++;
if (nskip-- > 0 )
continue ;
list_index++;
if (pg_strncasecmp(text, item, strlen(text)) == 0 )
{
num_keywords++;
return pg_strdup_keyword_case(item, text);
}
}
}
}
/*
* Hack : if we returned only bare schema names , don ' t let Readline add a
* space afterwards . Otherwise the schema will stop being part of the
* completion subject text , which is not what we want .
*/
if (num_schema_only > 0 && num_query_other == 0 && num_keywords == 0 )
rl_completion_append_character = '\0' ;
/* No more matches, so free the result structure and return null */
PQclear(result);
result = NULL;
return NULL;
}
/*
* Set up completion_ref_object and completion_ref_schema
* by parsing the given word . These variables can then be
* used in a query passed to _ complete_from_query .
*/
static void
set_completion_reference(const char *word)
{
bool schemaquoted,
objectquoted;
parse_identifier(word,
&completion_ref_schema, &completion_ref_object,
&schemaquoted, &objectquoted);
}
/*
* Set up completion_ref_object when it should just be
* the given word verbatim .
*/
static void
set_completion_reference_verbatim(const char *word)
{
completion_ref_schema = NULL;
completion_ref_object = pg_strdup(word);
}
/*
* 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)
{
static int string_length,
list_index,
matches;
static bool casesensitive;
const char *item;
/* need to have a list */
Assert(completion_charpp != NULL);
/* Initialization */
if (state == 0 )
{
list_index = 0 ;
string_length = strlen(text);
casesensitive = completion_case_sensitive;
matches = 0 ;
}
while ((item = completion_charpp[list_index++]))
{
/* First pass is case sensitive */
if (casesensitive && strncmp(text, item, string_length) == 0 )
{
matches++;
return pg_strdup(item);
}
/* Second pass is case insensitive, don't bother counting matches */
if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0 )
{
if (completion_case_sensitive)
return pg_strdup(item);
else
/*
* If case insensitive matching was requested initially ,
* adjust the case according to setting .
*/
return pg_strdup_keyword_case(item, text);
}
}
/*
* No matches found . If we ' re not case insensitive already , lets switch to
* being case insensitive and try again
*/
if (casesensitive && matches == 0 )
{
casesensitive = false ;
list_index = 0 ;
state++;
return complete_from_list(text, state);
}
/* If no more matches, return null. */
return NULL;
}
/*
* This function returns one fixed string the first time even if it doesn ' t
* match what ' s there , and nothing the second time . The string
* to be used must be in completion_charp .
*
* If the given string is " " , this has the effect of preventing readline
* from doing any completion . ( Without this , readline tries to do filename
* completion which is seldom the right thing . )
*
* If the given string is not empty , readline will replace whatever the
* user typed with that string . This behavior might be useful if it ' s
* completely certain that we know what must appear at a certain spot ,
* so that it ' s okay to overwrite misspellings . In practice , given the
* relatively lame parsing technology used in this file , the level of
* certainty is seldom that high , so that you probably don ' t want to
* use this . Use complete_from_list with a one - element list instead ;
* that won ' t try to auto - correct " misspellings " .
*/
static char *
complete_from_const(const char *text, int state)
{
Assert(completion_charp != NULL);
if (state == 0 )
{
if (completion_case_sensitive)
return pg_strdup(completion_charp);
else
/*
* If case insensitive matching was requested initially , adjust
* the case according to setting .
*/
return pg_strdup_keyword_case(completion_charp, text);
}
else
return NULL;
}
/*
* This function appends the variable name with prefix and suffix to
* the variable names array .
*/
static void
append_variable_names(char ***varnames, int *nvars,
int *maxvars, const char *varname,
const char *prefix, const char *suffix)
{
if (*nvars >= *maxvars)
{
*maxvars *= 2 ;
*varnames = (char **) pg_realloc(*varnames,
((*maxvars) + 1 ) * sizeof (char *));
}
(*varnames)[(*nvars)++] = psprintf("%s%s%s" , prefix, varname, suffix);
}
/*
* 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 . If need_value is true , only variables
* that are currently set are included ; otherwise , special variables
* ( those that have hooks ) are included even if currently unset .
*/
static char **
complete_from_variables(const char *text, const char *prefix, const char *suffix,
bool need_value)
{
char **matches;
char **varnames;
int nvars = 0 ;
int maxvars = 100 ;
int i;
struct _variable *ptr;
varnames = (char **) pg_malloc((maxvars + 1 ) * sizeof (char *));
for (ptr = pset.vars->next; ptr; ptr = ptr->next)
{
if (need_value && !(ptr->value))
continue ;
append_variable_names(&varnames, &nvars, &maxvars, ptr->name,
prefix, suffix);
}
varnames[nvars] = NULL;
COMPLETE_WITH_LIST_CS((const char *const *) varnames);
for (i = 0 ; i < nvars; i++)
free(varnames[i]);
free(varnames);
return matches;
}
/*
* This function wraps rl_filename_completion_function ( ) to strip quotes from
* the input before searching for matches and to quote any matches for which
* the consuming command will require it .
*
* Caller must set completion_charp to a zero - or one - character string
* containing the escape character . This is necessary since \ copy has no
* escape character , but every other backslash command recognizes " \ " as an
* escape character .
*
* Caller must also set completion_force_quote to indicate whether to force
* quotes around the result . ( The SQL COPY command requires that . )
*/
static char *
complete_from_files(const char *text, int state)
{
#ifdef USE_FILENAME_QUOTING_FUNCTIONS
/*
* If we ' re using a version of Readline that supports filename quoting
* hooks , rely on those , and invoke rl_filename_completion_function ( )
* without messing with its arguments . Readline does stuff internally
* that does not work well at all if we try to handle dequoting here .
* Instead , Readline will call quote_file_name ( ) and dequote_file_name ( )
* ( see below ) at appropriate times .
*
* . . . or at least , mostly it will . There are some paths involving
* unmatched file names in which Readline never calls quote_file_name ( ) ,
* and if left to its own devices it will incorrectly append a quote
* anyway . Set rl_completion_suppress_quote to prevent that . If we do
* get to quote_file_name ( ) , we ' ll clear this again . ( Yes , this seems
* like it ' s working around Readline bugs . )
*/
#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
rl_completion_suppress_quote = 1 ;
#endif
/* If user typed a quote, force quoting (never remove user's quote) */
if (*text == '\' ')
completion_force_quote = true ;
return rl_filename_completion_function(text, state);
#else
/*
* Otherwise , we have to do the best we can .
*/
static const char *unquoted_text;
char *unquoted_match;
char *ret = NULL;
/* If user typed a quote, force quoting (never remove user's quote) */
if (*text == '\' ')
completion_force_quote = true ;
if (state == 0 )
{
/* Initialization: stash the unquoted input. */
unquoted_text = strtokx(text, "" , NULL, "'" , *completion_charp,
false , true , pset.encoding);
/* expect a NULL return for the empty string only */
if (!unquoted_text)
{
Assert(*text == '\0' );
unquoted_text = text;
}
}
unquoted_match = rl_filename_completion_function(unquoted_text, state);
if (unquoted_match)
{
struct stat statbuf;
bool is_dir = (stat(unquoted_match, &statbuf) == 0 &&
S_ISDIR(statbuf.st_mode) != 0 );
/* Re-quote the result, if needed. */
ret = quote_if_needed(unquoted_match, " \t\r\n\" `",
'\' ', *completion_charp,
completion_force_quote,
pset.encoding);
if (ret)
free(unquoted_match);
else
ret = unquoted_match;
/*
* If it ' s a directory , replace trailing quote with a slash ; this is
* usually more convenient . ( If we didn ' t quote , leave this to
* libedit . )
*/
if (*ret == '\' ' && is_dir)
{
char *retend = ret + strlen(ret) - 1 ;
Assert(*retend == '\' ');
*retend = '/' ;
/* Prevent libedit from adding a space, too */
rl_completion_append_character = '\0' ;
}
}
return ret;
#endif /* USE_FILENAME_QUOTING_FUNCTIONS */
}
/* HELPER FUNCTIONS */
/*
* Make a pg_strdup copy of s and convert the case according to
* COMP_KEYWORD_CASE setting , using ref as the text that was already entered .
*/
static char *
pg_strdup_keyword_case(const char *s, const char *ref)
{
char *ret,
*p;
unsigned char first = ref[0 ];
ret = pg_strdup(s);
if (pset.comp_case == PSQL_COMP_CASE_LOWER ||
((pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER ||
pset.comp_case == PSQL_COMP_CASE_PRESERVE_UPPER) && islower(first)) ||
(pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER && !isalpha(first)))
{
for (p = ret; *p; p++)
*p = pg_tolower((unsigned char ) *p);
}
else
{
for (p = ret; *p; p++)
*p = pg_toupper((unsigned char ) *p);
}
return ret;
}
/*
* 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;
}
/*
* make_like_pattern - Convert argument to a LIKE prefix pattern .
*
* We escape _ and % in the given text by backslashing , append a % to
* represent " any subsequent characters " , and then pass the string through
* escape_string ( ) so it ' s ready to insert in a query . The result needs
* to be freed .
*/
static char *
make_like_pattern(const char *word)
{
char *result;
char *buffer = pg_malloc(strlen(word) * 2 + 2 );
char *bptr = buffer;
while (*word)
{
if (*word == '_' || *word == '%' )
*bptr++ = '\\' ;
if (IS_HIGHBIT_SET(*word))
{
/*
* Transfer multibyte characters without further processing , to
* avoid getting confused in unsafe client encodings .
*/
int chlen = PQmblenBounded(word, pset.encoding);
while (chlen-- > 0 )
*bptr++ = *word++;
}
else
*bptr++ = *word++;
}
*bptr++ = '%' ;
*bptr = '\0' ;
result = escape_string(buffer);
free(buffer);
return result;
}
/*
* parse_identifier - Parse a possibly - schema - qualified SQL identifier .
*
* This involves splitting off the schema name if present , de - quoting ,
* and downcasing any unquoted text . We are a bit laxer than the backend
* in that we allow just portions of a name to be quoted - - - that ' s because
* psql metacommands have traditionally behaved that way .
*
* Outputs are a malloc ' d schema name ( NULL if none ) , malloc ' d object name ,
* and booleans telling whether any part of the schema and object name was
* double - quoted .
*/
static void
parse_identifier(const char *ident,
char **schemaname, char **objectname,
bool *schemaquoted, bool *objectquoted)
{
size_t buflen = strlen(ident) + 1 ;
bool enc_is_single_byte = (pg_encoding_max_length(pset.encoding) == 1 );
char *sname;
char *oname;
char *optr;
bool inquotes;
/* Initialize, making a certainly-large-enough output buffer */
sname = NULL;
oname = pg_malloc(buflen);
*schemaquoted = *objectquoted = false ;
/* Scan */
optr = oname;
inquotes = false ;
while (*ident)
{
unsigned char ch = (unsigned char ) *ident++;
if (ch == '"' )
{
if (inquotes && *ident == '"' )
{
/* two quote marks within a quoted identifier = emit quote */
*optr++ = '"' ;
ident++;
}
else
{
inquotes = !inquotes;
*objectquoted = true ;
}
}
else if (ch == '.' && !inquotes)
{
/* Found a schema name, transfer it to sname / *schemaquoted */
*optr = '\0' ;
free(sname); /* drop any catalog name */
sname = oname;
oname = pg_malloc(buflen);
optr = oname;
*schemaquoted = *objectquoted;
*objectquoted = false ;
}
else if (!enc_is_single_byte && IS_HIGHBIT_SET(ch))
{
/*
* Transfer multibyte characters without further processing . They
* wouldn ' t be affected by our downcasing rule anyway , and this
* avoids possibly doing the wrong thing in unsafe client
* encodings .
*/
int chlen = PQmblenBounded(ident - 1 , pset.encoding);
*optr++ = (char ) ch;
while (--chlen > 0 )
*optr++ = *ident++;
}
else
{
if (!inquotes)
{
/*
* This downcasing transformation should match the backend ' s
* downcase_identifier ( ) as best we can . We do not know the
* backend ' s locale , though , so it ' s necessarily approximate .
* We assume that psql is operating in the same locale and
* encoding as the backend .
*/
if (ch >= 'A' && ch <= 'Z' )
ch += 'a' - 'A' ;
else if (enc_is_single_byte && IS_HIGHBIT_SET(ch) && isupper(ch))
ch = tolower(ch);
}
*optr++ = (char ) ch;
}
}
*optr = '\0' ;
*schemaname = sname;
*objectname = oname;
}
/*
* requote_identifier - Reconstruct a possibly - schema - qualified SQL identifier .
*
* Build a malloc ' d string containing the identifier , with quoting applied
* as necessary . This is more or less the inverse of parse_identifier ;
* in particular , if an input component was quoted , we ' ll quote the output
* even when that isn ' t strictly required .
*
* Unlike parse_identifier , we handle the case where a schema and no
* object name is provided , producing just " schema . " .
*/
static char *
requote_identifier(const char *schemaname, const char *objectname,
bool quote_schema, bool quote_object)
{
char *result;
size_t buflen = 1 ; /* count the trailing \0 */
char *ptr;
/*
* We could use PQescapeIdentifier for some of this , but not all , and it
* adds more notational cruft than it seems worth .
*/
if (schemaname)
{
buflen += strlen(schemaname) + 1 ; /* +1 for the dot */
if (!quote_schema)
quote_schema = identifier_needs_quotes(schemaname);
if (quote_schema)
{
buflen += 2 ; /* account for quote marks */
for (const char *p = schemaname; *p; p++)
{
if (*p == '"' )
buflen++;
}
}
}
if (objectname)
{
buflen += strlen(objectname);
if (!quote_object)
quote_object = identifier_needs_quotes(objectname);
if (quote_object)
{
buflen += 2 ; /* account for quote marks */
for (const char *p = objectname; *p; p++)
{
if (*p == '"' )
buflen++;
}
}
}
result = pg_malloc(buflen);
ptr = result;
if (schemaname)
{
if (quote_schema)
*ptr++ = '"' ;
for (const char *p = schemaname; *p; p++)
{
*ptr++ = *p;
if (*p == '"' )
*ptr++ = '"' ;
}
if (quote_schema)
*ptr++ = '"' ;
*ptr++ = '.' ;
}
if (objectname)
{
if (quote_object)
*ptr++ = '"' ;
for (const char *p = objectname; *p; p++)
{
*ptr++ = *p;
if (*p == '"' )
*ptr++ = '"' ;
}
if (quote_object)
*ptr++ = '"' ;
}
*ptr = '\0' ;
return result;
}
/*
* Detect whether an identifier must be double - quoted .
*
* Note we ' ll quote anything that ' s not ASCII ; the backend ' s quote_ident ( )
* does the same . Perhaps this could be relaxed in future .
*/
static bool
identifier_needs_quotes(const char *ident)
{
int kwnum;
/* Check syntax. */
if (!((ident[0 ] >= 'a' && ident[0 ] <= 'z' ) || ident[0 ] == '_' ))
return true ;
if (strspn(ident, "abcdefghijklmnopqrstuvwxyz0123456789_$" ) != strlen(ident))
return true ;
/*
* Check for keyword . We quote keywords except for unreserved ones .
*
* It is possible that our keyword list doesn ' t quite agree with the
* server ' s , but this should be close enough for tab - completion purposes .
*
* Note : ScanKeywordLookup ( ) does case - insensitive comparison , but that ' s
* fine , since we already know we have all - lower - case .
*/
kwnum = ScanKeywordLookup(ident, &ScanKeywords);
if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
return true ;
return false ;
}
/*
* Execute a query , returning NULL if there was any error .
* This should be the preferred way of talking to the database in this file .
*/
static PGresult *
exec_query(const char *query)
{
PGresult *result;
if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
return NULL;
result = PQexec(pset.db, query);
if (PQresultStatus(result) != PGRES_TUPLES_OK)
{
/*
* Printing an error while the user is typing would be quite annoying ,
* so we don ' t . This does complicate debugging of this code ; but you
* can look in the server log instead .
*/
#ifdef NOT_USED
pg_log_error("tab completion query failed: %s\nQuery was:\n%s" ,
PQerrorMessage(pset.db), query);
#endif
PQclear(result);
result = NULL;
}
return result;
}
/*
* Parse all the word ( s ) before point .
*
* Returns a malloc ' d array of character pointers that point into the malloc ' d
* data array returned to * buffer ; caller must free ( ) both of these when done .
* * nwords receives the number of words found , ie , the valid length of the
* return array .
*
* Words are returned right to left , that is , previous_words [ 0 ] gets the last
* word before point , previous_words [ 1 ] the next - to - last , etc .
*/
static char **
get_previous_words(int point, char **buffer, int *nwords)
{
char **previous_words;
char *buf;
char *outptr;
int words_found = 0 ;
int i;
/*
* If we have anything in tab_completion_query_buf , paste it together with
* rl_line_buffer to construct the full query . Otherwise we can just use
* rl_line_buffer as the input string .
*/
if (tab_completion_query_buf && tab_completion_query_buf->len > 0 )
{
i = tab_completion_query_buf->len;
buf = pg_malloc(point + i + 2 );
memcpy(buf, tab_completion_query_buf->data, i);
buf[i++] = '\n' ;
memcpy(buf + i, rl_line_buffer, point);
i += point;
buf[i] = '\0' ;
/* Readjust point to reference appropriate offset in buf */
point = i;
}
else
buf = rl_line_buffer;
/*
* Allocate an array of string pointers and a buffer to hold the strings
* themselves . The worst case is that the line contains only
* non - whitespace WORD_BREAKS characters , making each one a separate word .
* This is usually much more space than we need , but it ' s cheaper than
* doing a separate malloc ( ) for each word .
*/
previous_words = (char **) pg_malloc(point * sizeof (char *));
*buffer = outptr = (char *) pg_malloc(point * 2 );
/*
* First we look for a non - word char before the current point . ( This is
* probably useless , if readline is on the same page as we are about what
* is a word , but if so it ' s cheap . )
*/
for (i = point - 1 ; i >= 0 ; i--)
{
if (strchr(WORD_BREAKS, buf[i]))
break ;
}
point = i;
/*
* Now parse words , working backwards , until we hit start of line . The
* backwards scan has some interesting but intentional properties
* concerning parenthesis handling .
*/
while (point >= 0 )
{
int start,
end;
bool inquotes = false ;
int parentheses = 0 ;
/* now find the first non-space which then constitutes the end */
end = -1 ;
for (i = point; i >= 0 ; i--)
{
if (!isspace((unsigned char ) buf[i]))
{
end = i;
break ;
}
}
/* if no end found, we're done */
if (end < 0 )
break ;
/*
* Otherwise we now look for the start . The start is either the last
* character before any word - break character going backwards from the
* end , or it ' s simply character 0 . We also handle open quotes and
* parentheses .
*/
for (start = end; start > 0 ; start--)
{
if (buf[start] == '"' )
inquotes = !inquotes;
if (!inquotes)
{
if (buf[start] == ')' )
parentheses++;
else if (buf[start] == '(' )
{
if (--parentheses <= 0 )
break ;
}
else if (parentheses == 0 &&
strchr(WORD_BREAKS, buf[start - 1 ]))
break ;
}
}
/* Return the word located at start to end inclusive */
previous_words[words_found++] = outptr;
i = end - start + 1 ;
memcpy(outptr, &buf[start], i);
outptr += i;
*outptr++ = '\0' ;
/* Continue searching */
point = start - 1 ;
}
/* Release parsing input workspace, if we made one above */
if (buf != rl_line_buffer)
free(buf);
*nwords = words_found;
return previous_words;
}
/*
* 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 USE_FILENAME_QUOTING_FUNCTIONS
/*
* Quote a filename according to SQL rules , returning a malloc ' d string .
* completion_charp must point to escape character or ' \ 0 ' , and
* completion_force_quote must be set correctly , as per comments for
* complete_from_files ( ) .
*/
static char *
quote_file_name(char *fname, int match_type, char *quote_pointer)
{
char *s;
struct stat statbuf;
/* Quote if needed. */
s = quote_if_needed(fname, " \t\r\n\" `",
'\' ', *completion_charp,
completion_force_quote,
pset.encoding);
if (!s)
s = pg_strdup(fname);
/*
* However , some of the time we have to strip the trailing quote from what
* we send back . Never strip the trailing quote if the user already typed
* one ; otherwise , suppress the trailing quote if we have multiple / no
* matches ( because we don ' t want to add a quote if the input is seemingly
* unfinished ) , or if the input was already quoted ( because Readline will
* do arguably - buggy things otherwise ) , or if the file does not exist , or
* if it ' s a directory .
*/
if (*s == '\' ' &&
completion_last_char != '\' ' &&
(match_type != SINGLE_MATCH ||
(quote_pointer && *quote_pointer == '\' ') ||
stat(fname, &statbuf) != 0 ||
S_ISDIR(statbuf.st_mode)))
{
char *send = s + strlen(s) - 1 ;
Assert(*send == '\' ');
*send = '\0' ;
}
/*
* And now we can let Readline do its thing with possibly adding a quote
* on its own accord . ( This covers some additional cases beyond those
* dealt with above . )
*/
#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
rl_completion_suppress_quote = 0 ;
#endif
/*
* If user typed a leading quote character other than single quote ( i . e . ,
* double quote ) , zap it , so that we replace it with the correct single
* quote .
*/
if (quote_pointer && *quote_pointer != '\' ')
*quote_pointer = '\0' ;
return s;
}
/*
* Dequote a filename , if it ' s quoted .
* completion_charp must point to escape character or ' \ 0 ' , as per
* comments for complete_from_files ( ) .
*/
static char *
dequote_file_name(char *fname, int quote_char)
{
char *unquoted_fname;
/*
* If quote_char is set , it ' s not included in " fname " . We have to add it
* or strtokx will not interpret the string correctly ( notably , it won ' t
* recognize escapes ) .
*/
if (quote_char == '\' ')
{
char *workspace = (char *) pg_malloc(strlen(fname) + 2 );
workspace[0 ] = quote_char;
strcpy(workspace + 1 , fname);
unquoted_fname = strtokx(workspace, "" , NULL, "'" , *completion_charp,
false , true , pset.encoding);
free(workspace);
}
else
unquoted_fname = strtokx(fname, "" , NULL, "'" , *completion_charp,
false , true , pset.encoding);
/* expect a NULL return for the empty string only */
if (!unquoted_fname)
{
Assert(*fname == '\0' );
unquoted_fname = fname;
}
/* readline expects a malloc'd result that it is to free */
return pg_strdup(unquoted_fname);
}
#endif /* USE_FILENAME_QUOTING_FUNCTIONS */
#endif /* USE_READLINE */
Messung V0.5 in Prozent C=92 H=95 G=93
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.490Bemerkung:
(vorverarbeitet am 2026-08-08)
¤
*Bot Zugriff