From 4ecd6b58fb1099d249d85d766a8d8a67954b3094 Mon Sep 17 00:00:00 2001 From: Brian Cloutier Date: Fri, 26 Aug 2016 10:41:59 +0300 Subject: [PATCH] Remove csql, \stage is no longer needed --- Makefile | 14 +- src/bin/csql/.gitignore | 2 - src/bin/csql/Makefile | 45 - src/bin/csql/command.c | 3247 ------------------------ src/bin/csql/command.h | 43 - src/bin/csql/common.c | 1903 -------------- src/bin/csql/common.h | 52 - src/bin/csql/copy.c | 595 ----- src/bin/csql/copy.h | 33 - src/bin/csql/copy_options.c | 315 --- src/bin/csql/copy_options.h | 60 - src/bin/csql/create_help.pl | 214 -- src/bin/csql/describe.c | 4613 ---------------------------------- src/bin/csql/describe.h | 102 - src/bin/csql/dumputils.c | 1243 --------- src/bin/csql/help.c | 572 ----- src/bin/csql/help.h | 21 - src/bin/csql/input.c | 539 ---- src/bin/csql/input.h | 51 - src/bin/csql/keywords.c | 30 - src/bin/csql/kwlookup.c | 89 - src/bin/csql/large_obj.c | 315 --- src/bin/csql/large_obj.h | 16 - src/bin/csql/mainloop.c | 462 ---- src/bin/csql/mainloop.h | 15 - src/bin/csql/mbprint.c | 398 --- src/bin/csql/mbprint.h | 18 - src/bin/csql/print.c | 3495 -------------------------- src/bin/csql/print.h | 206 -- src/bin/csql/prompt.c | 325 --- src/bin/csql/prompt.h | 25 - src/bin/csql/psqlrc.sample | 8 - src/bin/csql/psqlscan.h | 64 - src/bin/csql/psqlscan.l | 1988 --------------- src/bin/csql/settings.h | 149 -- src/bin/csql/sql_help.c | 4052 ------------------------------ src/bin/csql/sql_help.h | 1040 -------- src/bin/csql/stage.c | 1787 ------------- src/bin/csql/stage.h | 131 - src/bin/csql/startup.c | 896 ------- src/bin/csql/stringutils.c | 340 --- src/bin/csql/stringutils.h | 27 - src/bin/csql/tab-complete.c | 4719 ----------------------------------- src/bin/csql/tab-complete.h | 15 - src/bin/csql/variables.c | 305 --- src/bin/csql/variables.h | 56 - 46 files changed, 1 insertion(+), 34634 deletions(-) delete mode 100644 src/bin/csql/.gitignore delete mode 100644 src/bin/csql/Makefile delete mode 100644 src/bin/csql/command.c delete mode 100644 src/bin/csql/command.h delete mode 100644 src/bin/csql/common.c delete mode 100644 src/bin/csql/common.h delete mode 100644 src/bin/csql/copy.c delete mode 100644 src/bin/csql/copy.h delete mode 100644 src/bin/csql/copy_options.c delete mode 100644 src/bin/csql/copy_options.h delete mode 100644 src/bin/csql/create_help.pl delete mode 100644 src/bin/csql/describe.c delete mode 100644 src/bin/csql/describe.h delete mode 100644 src/bin/csql/dumputils.c delete mode 100644 src/bin/csql/help.c delete mode 100644 src/bin/csql/help.h delete mode 100644 src/bin/csql/input.c delete mode 100644 src/bin/csql/input.h delete mode 100644 src/bin/csql/keywords.c delete mode 100644 src/bin/csql/kwlookup.c delete mode 100644 src/bin/csql/large_obj.c delete mode 100644 src/bin/csql/large_obj.h delete mode 100644 src/bin/csql/mainloop.c delete mode 100644 src/bin/csql/mainloop.h delete mode 100644 src/bin/csql/mbprint.c delete mode 100644 src/bin/csql/mbprint.h delete mode 100644 src/bin/csql/print.c delete mode 100644 src/bin/csql/print.h delete mode 100644 src/bin/csql/prompt.c delete mode 100644 src/bin/csql/prompt.h delete mode 100644 src/bin/csql/psqlrc.sample delete mode 100644 src/bin/csql/psqlscan.h delete mode 100644 src/bin/csql/psqlscan.l delete mode 100644 src/bin/csql/settings.h delete mode 100644 src/bin/csql/sql_help.c delete mode 100644 src/bin/csql/sql_help.h delete mode 100644 src/bin/csql/stage.c delete mode 100644 src/bin/csql/stage.h delete mode 100644 src/bin/csql/startup.c delete mode 100644 src/bin/csql/stringutils.c delete mode 100644 src/bin/csql/stringutils.h delete mode 100644 src/bin/csql/tab-complete.c delete mode 100644 src/bin/csql/tab-complete.h delete mode 100644 src/bin/csql/variables.c delete mode 100644 src/bin/csql/variables.h diff --git a/Makefile b/Makefile index 9d8fc8bcc..10841d743 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ endif include Makefile.global -all: extension csql +all: extension # build extension extension: @@ -30,18 +30,6 @@ clean-extension: install: install-extension install-headers clean: clean-extension -# build csql binary -csql: - $(MAKE) -C src/bin/csql/ all -install-csql: csql - $(MAKE) -C src/bin/csql/ install -clean-csql: - $(MAKE) -C src/bin/csql/ clean -.PHONY: csql install-csql clean-csql -# Add to generic targets -install: install-csql -clean: clean-csql - # apply or check style reindent: cd ${citus_abs_top_srcdir} && citus_indent --quiet diff --git a/src/bin/csql/.gitignore b/src/bin/csql/.gitignore deleted file mode 100644 index ea0a3a77a..000000000 --- a/src/bin/csql/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/psqlscan.c -/csql diff --git a/src/bin/csql/Makefile b/src/bin/csql/Makefile deleted file mode 100644 index 470031999..000000000 --- a/src/bin/csql/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -#------------------------------------------------------------------------- -# -# Makefile for src/bin/csql -# -# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 1994, Regents of the University of California -# -# src/bin/csql/Makefile -# -#------------------------------------------------------------------------- - -citus_subdir = src/bin/csql -citus_top_builddir = ../../.. - -PROGRAM = csql - -PGFILEDESC = "csql - the Citus interactive terminal" -PGAPPICON=win32 - -OBJS =command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ - copy_options.o stage.o \ - startup.o prompt.o variables.o large_obj.o print.o describe.o \ - tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \ - sql_help.o \ - $(WIN32RES) - -PG_LIBS = $(libpq) - -include $(citus_top_builddir)/Makefile.global - -# ensure client includes occur before server -client_includes := $(shell $(PG_CONFIG) --includedir)/internal - -override CPPFLAGS := -I$(client_includes) -I$(libpq_srcdir) -I$(citus_abs_top_srcdir)/src/bin/csql $(CPPFLAGS) - -# psqlscan is compiled as part of mainloop -mainloop.o: psqlscan.c -psqlscan.c: FLEXFLAGS = -Cfe -p -p - -psqlscan.c: psqlscan.l - $(FLEX) $(FLEXFLAGS) -o'$@' $< - -clean: csql-clean -csql-clean: - rm -f csql$(X) $(OBJS) psqlscan.c lex.backup diff --git a/src/bin/csql/command.c b/src/bin/csql/command.c deleted file mode 100644 index 52650c62d..000000000 --- a/src/bin/csql/command.c +++ /dev/null @@ -1,3247 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/command.c - */ -#include "postgres_fe.h" -#include "command.h" - -#ifdef __BORLANDC__ /* needed for BCC */ -#undef mkdir -#endif - -#include -#include -#ifdef HAVE_PWD_H -#include -#endif -#ifndef WIN32 -#include /* for umask() */ -#include /* for stat() */ -#include /* open() flags */ -#include /* for geteuid(), getpid(), stat() */ -#else -#include -#include -#include -#include -#include /* for umask() */ -#include /* for stat() */ -#endif - -#include "portability/instr_time.h" - -#include "libpq-fe.h" -#include "pqexpbuffer.h" -#include "dumputils.h" - -#include "common.h" -#include "copy.h" -#include "describe.h" -#include "help.h" -#include "input.h" -#include "large_obj.h" -#include "mainloop.h" -#include "print.h" -#include "psqlscan.h" -#include "settings.h" -#include "stage.h" -#include "variables.h" - - -/* functions for use in this file */ -static backslashResult exec_command(const char *cmd, - PsqlScanState scan_state, - PQExpBuffer query_buf); -static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, - int lineno, bool *edited); -static bool do_connect(char *dbname, char *user, char *host, char *port); -static bool do_shell(const char *command); -static bool do_watch(PQExpBuffer query_buf, long sleep); -static bool lookup_function_oid(const char *desc, Oid *foid); -static bool get_create_function_cmd(Oid oid, PQExpBuffer buf); -static int strip_lineno_from_funcdesc(char *func); -static void minimal_error_message(PGresult *res); - -static void printSSLInfo(void); -static bool printPsetInfo(const char *param, struct printQueryOpt *popt); -static char *pset_value_string(const char *param, struct printQueryOpt *popt); - -#ifdef WIN32 -static void checkWin32Codepage(void); -#endif - - - -/*---------- - * HandleSlashCmds: - * - * Handles all the different commands that start with '\'. - * Ordinarily called by MainLoop(). - * - * scan_state is a lexer working state that is set to continue scanning - * just after the '\'. The lexer is advanced past the command and all - * arguments on return. - * - * 'query_buf' contains the query-so-far, which may be modified by - * execution of the backslash command (for example, \r clears it). - * query_buf can be NULL if there is no query so far. - * - * Returns a status code indicating what action is desired, see command.h. - *---------- - */ - -backslashResult -HandleSlashCmds(PsqlScanState scan_state, - PQExpBuffer query_buf) -{ - backslashResult status = PSQL_CMD_SKIP_LINE; - char *cmd; - char *arg; - - Assert(scan_state != NULL); - - /* Parse off the command name */ - cmd = psql_scan_slash_command(scan_state); - - /* And try to execute it */ - status = exec_command(cmd, scan_state, query_buf); - - if (status == PSQL_CMD_UNKNOWN) - { - if (pset.cur_cmd_interactive) - psql_error("Invalid command \\%s. Try \\? for help.\n", cmd); - else - psql_error("invalid command \\%s\n", cmd); - status = PSQL_CMD_ERROR; - } - - if (status != PSQL_CMD_ERROR) - { - /* eat any remaining arguments after a valid command */ - /* note we suppress evaluation of backticks here */ - while ((arg = psql_scan_slash_option(scan_state, - OT_NO_EVAL, NULL, false))) - { - psql_error("\\%s: extra argument \"%s\" ignored\n", cmd, arg); - free(arg); - } - } - else - { - /* silently throw away rest of line after an erroneous command */ - while ((arg = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, false))) - free(arg); - } - - /* if there is a trailing \\, swallow it */ - psql_scan_slash_command_end(scan_state); - - free(cmd); - - /* some commands write to queryFout, so make sure output is sent */ - fflush(pset.queryFout); - - return status; -} - -/* - * Read and interpret an argument to the \connect slash command. - */ -static char * -read_connect_arg(PsqlScanState scan_state) -{ - char *result; - char quote; - - /* - * Ideally we should treat the arguments as SQL identifiers. But for - * backwards compatibility with 7.2 and older pg_dump files, we have to - * take unquoted arguments verbatim (don't downcase them). For now, - * double-quoted arguments may be stripped of double quotes (as if SQL - * identifiers). By 7.4 or so, pg_dump files can be expected to - * double-quote all mixed-case \connect arguments, and then we can get rid - * of OT_SQLIDHACK. - */ - result = psql_scan_slash_option(scan_state, OT_SQLIDHACK, "e, true); - - if (!result) - return NULL; - - if (quote) - return result; - - if (*result == '\0' || strcmp(result, "-") == 0) - return NULL; - - return result; -} - - -/* - * Subroutine to actually try to execute a backslash command. - */ -static backslashResult -exec_command(const char *cmd, - PsqlScanState scan_state, - PQExpBuffer query_buf) -{ - bool success = true; /* indicate here if the command ran ok or - * failed */ - backslashResult status = PSQL_CMD_SKIP_LINE; - - /* - * \a -- toggle field alignment This makes little sense but we keep it - * around. - */ - if (strcmp(cmd, "a") == 0) - { - if (pset.popt.topt.format != PRINT_ALIGNED) - success = do_pset("format", "aligned", &pset.popt, pset.quiet); - else - success = do_pset("format", "unaligned", &pset.popt, pset.quiet); - } - - /* \C -- override table title (formerly change HTML caption) */ - else if (strcmp(cmd, "C") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - success = do_pset("title", opt, &pset.popt, pset.quiet); - free(opt); - } - - /* - * \c or \connect -- connect to database using the specified parameters. - * - * \c dbname user host port - * - * If any of these parameters are omitted or specified as '-', the current - * value of the parameter will be used instead. If the parameter has no - * current value, the default value for that parameter will be used. Some - * examples: - * - * \c - - hst Connect to current database on current port of host - * "hst" as current user. \c - usr - prt Connect to current database on - * "prt" port of current host as user "usr". \c dbs Connect to - * "dbs" database on current port of current host as current user. - */ - else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) - { - char *opt1, - *opt2, - *opt3, - *opt4; - - opt1 = read_connect_arg(scan_state); - opt2 = read_connect_arg(scan_state); - opt3 = read_connect_arg(scan_state); - opt4 = read_connect_arg(scan_state); - - success = do_connect(opt1, opt2, opt3, opt4); - - free(opt1); - free(opt2); - free(opt3); - free(opt4); - } - - /* \cd */ - else if (strcmp(cmd, "cd") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - char *dir; - - if (opt) - dir = opt; - else - { -#ifndef WIN32 - struct passwd *pw; - uid_t user_id = geteuid(); - - errno = 0; /* clear errno before call */ - pw = getpwuid(user_id); - if (!pw) - { - psql_error("could not get home directory for user ID %ld: %s\n", - (long) user_id, - errno ? strerror(errno) : _("user does not exist")); - exit(EXIT_FAILURE); - } - dir = pw->pw_dir; -#else /* WIN32 */ - - /* - * On Windows, 'cd' without arguments prints the current - * directory, so if someone wants to code this here instead... - */ - dir = "/"; -#endif /* WIN32 */ - } - - if (chdir(dir) == -1) - { - psql_error("\\%s: could not change directory to \"%s\": %s\n", - cmd, dir, strerror(errno)); - success = false; - } - - if (opt) - free(opt); - } - - /* \conninfo -- display information about the current connection */ - else if (strcmp(cmd, "conninfo") == 0) - { - char *db = PQdb(pset.db); - - if (db == NULL) - printf(_("You are currently not connected to a database.\n")); - else - { - char *host; - PQconninfoOption *connOptions; - PQconninfoOption *option; - - host = PQhost(pset.db); - if (host == NULL) - host = DEFAULT_PGSOCKET_DIR; - /* A usable "hostaddr" overrides the basic sense of host. */ - connOptions = PQconninfo(pset.db); - if (connOptions == NULL) - { - psql_error("out of memory\n"); - exit(EXIT_FAILURE); - } - for (option = connOptions; option && option->keyword; option++) - if (strcmp(option->keyword, "hostaddr") == 0) - { - if (option->val != NULL && option->val[0] != '\0') - host = option->val; - break; - } - - /* If the host is an absolute path, the connection is via socket */ - if (is_absolute_path(host)) - printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"), - db, PQuser(pset.db), host, PQport(pset.db)); - else - printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"), - db, PQuser(pset.db), host, PQport(pset.db)); - printSSLInfo(); - - PQconninfoFree(connOptions); - } - } - - /* \copy */ - else if (pg_strcasecmp(cmd, "copy") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, false); - - success = do_copy(opt); - free(opt); - } - - /* \copyright */ - else if (strcmp(cmd, "copyright") == 0) - print_copyright(); - - /* \d* commands */ - else if (cmd[0] == 'd') - { - char *pattern; - bool show_verbose, - show_system; - - /* We don't do SQLID reduction on the pattern yet */ - pattern = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - show_verbose = strchr(cmd, '+') ? true : false; - show_system = strchr(cmd, 'S') ? true : false; - - switch (cmd[1]) - { - case '\0': - case '+': - case 'S': - if (pattern) - success = describeTableDetails(pattern, show_verbose, show_system); - else - /* standard listing of interesting things */ - success = listTables("tvmsE", NULL, show_verbose, show_system); - break; - case 'a': - success = describeAggregates(pattern, show_verbose, show_system); - break; - case 'b': - success = describeTablespaces(pattern, show_verbose); - break; - case 'c': - success = listConversions(pattern, show_verbose, show_system); - break; - case 'C': - success = listCasts(pattern, show_verbose); - break; - case 'd': - if (strncmp(cmd, "ddp", 3) == 0) - success = listDefaultACLs(pattern); - else - success = objectDescription(pattern, show_system); - break; - case 'D': - success = listDomains(pattern, show_verbose, show_system); - break; - case 'f': /* function subsystem */ - switch (cmd[2]) - { - case '\0': - case '+': - case 'S': - case 'a': - case 'n': - case 't': - case 'w': - success = describeFunctions(&cmd[2], pattern, show_verbose, show_system); - break; - default: - status = PSQL_CMD_UNKNOWN; - break; - } - break; - case 'g': - /* no longer distinct from \du */ - success = describeRoles(pattern, show_verbose); - break; - case 'l': - success = do_lo_list(); - break; - case 'L': - success = listLanguages(pattern, show_verbose, show_system); - break; - case 'n': - success = listSchemas(pattern, show_verbose, show_system); - break; - case 'o': - success = describeOperators(pattern, show_verbose, show_system); - break; - case 'O': - success = listCollations(pattern, show_verbose, show_system); - break; - case 'p': - success = permissionsList(pattern); - break; - case 'T': - success = describeTypes(pattern, show_verbose, show_system); - break; - case 't': - case 'v': - case 'm': - case 'i': - case 's': - case 'E': - success = listTables(&cmd[1], pattern, show_verbose, show_system); - break; - case 'r': - if (cmd[2] == 'd' && cmd[3] == 's') - { - char *pattern2 = NULL; - - if (pattern) - pattern2 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - success = listDbRoleSettings(pattern, pattern2); - } - else - success = PSQL_CMD_UNKNOWN; - break; - case 'u': - success = describeRoles(pattern, show_verbose); - break; - case 'F': /* text search subsystem */ - switch (cmd[2]) - { - case '\0': - case '+': - success = listTSConfigs(pattern, show_verbose); - break; - case 'p': - success = listTSParsers(pattern, show_verbose); - break; - case 'd': - success = listTSDictionaries(pattern, show_verbose); - break; - case 't': - success = listTSTemplates(pattern, show_verbose); - break; - default: - status = PSQL_CMD_UNKNOWN; - break; - } - break; - case 'e': /* SQL/MED subsystem */ - switch (cmd[2]) - { - case 's': - success = listForeignServers(pattern, show_verbose); - break; - case 'u': - success = listUserMappings(pattern, show_verbose); - break; - case 'w': - success = listForeignDataWrappers(pattern, show_verbose); - break; - case 't': - success = listForeignTables(pattern, show_verbose); - break; - default: - status = PSQL_CMD_UNKNOWN; - break; - } - break; - case 'x': /* Extensions */ - if (show_verbose) - success = listExtensionContents(pattern); - else - success = listExtensions(pattern); - break; - case 'y': /* Event Triggers */ - success = listEventTriggers(pattern, show_verbose); - break; - default: - status = PSQL_CMD_UNKNOWN; - } - - if (pattern) - free(pattern); - } - - - /* - * \e or \edit -- edit the current query buffer, or edit a file and make - * it the query buffer - */ - else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0) - { - if (!query_buf) - { - psql_error("no query buffer\n"); - status = PSQL_CMD_ERROR; - } - else - { - char *fname; - char *ln = NULL; - int lineno = -1; - - fname = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - if (fname) - { - /* try to get separate lineno arg */ - ln = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - if (ln == NULL) - { - /* only one arg; maybe it is lineno not fname */ - if (fname[0] && - strspn(fname, "0123456789") == strlen(fname)) - { - /* all digits, so assume it is lineno */ - ln = fname; - fname = NULL; - } - } - } - if (ln) - { - lineno = atoi(ln); - if (lineno < 1) - { - psql_error("invalid line number: %s\n", ln); - status = PSQL_CMD_ERROR; - } - } - if (status != PSQL_CMD_ERROR) - { - expand_tilde(&fname); - if (fname) - canonicalize_path(fname); - if (do_edit(fname, query_buf, lineno, NULL)) - status = PSQL_CMD_NEWEDIT; - else - status = PSQL_CMD_ERROR; - } - if (fname) - free(fname); - if (ln) - free(ln); - } - } - - /* - * \ef -- edit the named function, or present a blank CREATE FUNCTION - * template if no argument is given - */ - else if (strcmp(cmd, "ef") == 0) - { - int lineno = -1; - - if (pset.sversion < 80400) - { - psql_error("The server (version %d.%d) does not support editing function source.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - status = PSQL_CMD_ERROR; - } - else if (!query_buf) - { - psql_error("no query buffer\n"); - status = PSQL_CMD_ERROR; - } - else - { - char *func; - Oid foid = InvalidOid; - - func = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, true); - lineno = strip_lineno_from_funcdesc(func); - if (lineno == 0) - { - /* error already reported */ - status = PSQL_CMD_ERROR; - } - else if (!func) - { - /* set up an empty command to fill in */ - printfPQExpBuffer(query_buf, - "CREATE FUNCTION ( )\n" - " RETURNS \n" - " LANGUAGE \n" - " -- common options: IMMUTABLE STABLE STRICT SECURITY DEFINER\n" - "AS $function$\n" - "\n$function$\n"); - } - else if (!lookup_function_oid(func, &foid)) - { - /* error already reported */ - status = PSQL_CMD_ERROR; - } - else if (!get_create_function_cmd(foid, query_buf)) - { - /* error already reported */ - status = PSQL_CMD_ERROR; - } - else if (lineno > 0) - { - /* - * lineno "1" should correspond to the first line of the - * function body. We expect that pg_get_functiondef() will - * emit that on a line beginning with "AS ", and that there - * can be no such line before the real start of the function - * body. Increment lineno by the number of lines before that - * line, so that it becomes relative to the first line of the - * function definition. - */ - const char *lines = query_buf->data; - - while (*lines != '\0') - { - if (strncmp(lines, "AS ", 3) == 0) - break; - lineno++; - /* find start of next line */ - lines = strchr(lines, '\n'); - if (!lines) - break; - lines++; - } - } - - if (func) - free(func); - } - - if (status != PSQL_CMD_ERROR) - { - bool edited = false; - - if (!do_edit(NULL, query_buf, lineno, &edited)) - status = PSQL_CMD_ERROR; - else if (!edited) - puts(_("No changes")); - else - status = PSQL_CMD_NEWEDIT; - } - } - - /* \echo and \qecho */ - else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0) - { - char *value; - char quoted; - bool no_newline = false; - bool first = true; - FILE *fout; - - if (strcmp(cmd, "qecho") == 0) - fout = pset.queryFout; - else - fout = stdout; - - while ((value = psql_scan_slash_option(scan_state, - OT_NORMAL, "ed, false))) - { - if (!quoted && strcmp(value, "-n") == 0) - no_newline = true; - else - { - if (first) - first = false; - else - fputc(' ', fout); - fputs(value, fout); - } - free(value); - } - if (!no_newline) - fputs("\n", fout); - } - - /* \encoding -- set/show client side encoding */ - else if (strcmp(cmd, "encoding") == 0) - { - char *encoding = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!encoding) - { - /* show encoding */ - puts(pg_encoding_to_char(pset.encoding)); - } - else - { - /* set encoding */ - if (PQsetClientEncoding(pset.db, encoding) == -1) - psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding); - else - { - /* save encoding info into psql internal data */ - pset.encoding = PQclientEncoding(pset.db); - pset.popt.topt.encoding = pset.encoding; - SetVariable(pset.vars, "ENCODING", - pg_encoding_to_char(pset.encoding)); - } - free(encoding); - } - } - - /* \f -- change field separator */ - else if (strcmp(cmd, "f") == 0) - { - char *fname = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - success = do_pset("fieldsep", fname, &pset.popt, pset.quiet); - free(fname); - } - - /* \g [filename] -- send query, optionally with output to file/pipe */ - else if (strcmp(cmd, "g") == 0) - { - char *fname = psql_scan_slash_option(scan_state, - OT_FILEPIPE, NULL, false); - - if (!fname) - pset.gfname = NULL; - else - { - expand_tilde(&fname); - pset.gfname = pg_strdup(fname); - } - free(fname); - status = PSQL_CMD_SEND; - } - - /* \gset [prefix] -- send query and store result into variables */ - else if (strcmp(cmd, "gset") == 0) - { - char *prefix = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (prefix) - pset.gset_prefix = prefix; - else - { - /* we must set a non-NULL prefix to trigger storing */ - pset.gset_prefix = pg_strdup(""); - } - /* gset_prefix is freed later */ - status = PSQL_CMD_SEND; - } - - /* help */ - else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, false); - size_t len; - - /* strip any trailing spaces and semicolons */ - if (opt) - { - len = strlen(opt); - while (len > 0 && - (isspace((unsigned char) opt[len - 1]) - || opt[len - 1] == ';')) - opt[--len] = '\0'; - } - - helpSQL(opt, pset.popt.topt.pager); - free(opt); - } - - /* HTML mode */ - else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0) - { - if (pset.popt.topt.format != PRINT_HTML) - success = do_pset("format", "html", &pset.popt, pset.quiet); - else - success = do_pset("format", "aligned", &pset.popt, pset.quiet); - } - - - /* \i and \ir include files */ - else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0 - || strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0) - { - char *fname = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - if (!fname) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - bool include_relative; - - include_relative = (strcmp(cmd, "ir") == 0 - || strcmp(cmd, "include_relative") == 0); - expand_tilde(&fname); - success = (process_file(fname, false, include_relative) == EXIT_SUCCESS); - free(fname); - } - } - - /* \l is list databases */ - else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 || - strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) - { - char *pattern; - bool show_verbose; - - pattern = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - show_verbose = strchr(cmd, '+') ? true : false; - - success = listAllDbs(pattern, show_verbose); - - if (pattern) - free(pattern); - } - - /* - * large object things - */ - else if (strncmp(cmd, "lo_", 3) == 0) - { - char *opt1, - *opt2; - - opt1 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - opt2 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - if (strcmp(cmd + 3, "export") == 0) - { - if (!opt2) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - expand_tilde(&opt2); - success = do_lo_export(opt1, opt2); - } - } - - else if (strcmp(cmd + 3, "import") == 0) - { - if (!opt1) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - expand_tilde(&opt1); - success = do_lo_import(opt1, opt2); - } - } - - else if (strcmp(cmd + 3, "list") == 0) - success = do_lo_list(); - - else if (strcmp(cmd + 3, "unlink") == 0) - { - if (!opt1) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - success = do_lo_unlink(opt1); - } - - else - status = PSQL_CMD_UNKNOWN; - - free(opt1); - free(opt2); - } - - - /* \o -- set query output */ - else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0) - { - char *fname = psql_scan_slash_option(scan_state, - OT_FILEPIPE, NULL, true); - - expand_tilde(&fname); - success = setQFout(fname); - free(fname); - } - - /* \p prints the current query buffer */ - else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0) - { - if (query_buf && query_buf->len > 0) - puts(query_buf->data); - else if (!pset.quiet) - puts(_("Query buffer is empty.")); - fflush(stdout); - } - - /* \password -- set user password */ - else if (strcmp(cmd, "password") == 0) - { - char *pw1; - char *pw2; - - pw1 = simple_prompt("Enter new password: ", 100, false); - pw2 = simple_prompt("Enter it again: ", 100, false); - - if (strcmp(pw1, pw2) != 0) - { - psql_error("Passwords didn't match.\n"); - success = false; - } - else - { - char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true); - char *user; - char *encrypted_password; - - if (opt0) - user = opt0; - else - user = PQuser(pset.db); - - encrypted_password = PQencryptPassword(pw1, user); - - if (!encrypted_password) - { - psql_error("Password encryption failed.\n"); - success = false; - } - else - { - PQExpBufferData buf; - PGresult *res; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ", - fmtId(user)); - appendStringLiteralConn(&buf, encrypted_password, pset.db); - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - success = false; - else - PQclear(res); - PQfreemem(encrypted_password); - } - - if (opt0) - free(opt0); - } - - free(pw1); - free(pw2); - } - - /* \prompt -- prompt and set variable */ - else if (strcmp(cmd, "prompt") == 0) - { - char *opt, - *prompt_text = NULL; - char *arg1, - *arg2; - - arg1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); - arg2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); - - if (!arg1) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - char *result; - - if (arg2) - { - prompt_text = arg1; - opt = arg2; - } - else - opt = arg1; - - if (!pset.inputfile) - result = simple_prompt(prompt_text, 4096, true); - else - { - if (prompt_text) - { - fputs(prompt_text, stdout); - fflush(stdout); - } - result = gets_fromFile(stdin); - } - - if (!SetVariable(pset.vars, opt, result)) - { - psql_error("\\%s: error while setting variable\n", cmd); - success = false; - } - - free(result); - if (prompt_text) - free(prompt_text); - free(opt); - } - } - - /* \pset -- set printing parameters */ - else if (strcmp(cmd, "pset") == 0) - { - char *opt0 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - char *opt1 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!opt0) - { - /* list all variables */ - - int i; - static const char *const my_list[] = { - "border", "columns", "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", - NULL - }; - - for (i = 0; my_list[i] != NULL; i++) - { - char *val = pset_value_string(my_list[i], &pset.popt); - - printf("%-24s %s\n", my_list[i], val); - free(val); - } - - success = true; - } - else - success = do_pset(opt0, opt1, &pset.popt, pset.quiet); - - free(opt0); - free(opt1); - } - - /* \q or \quit */ - else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0) - status = PSQL_CMD_TERMINATE; - - /* reset(clear) the buffer */ - else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) - { - resetPQExpBuffer(query_buf); - psql_scan_reset(scan_state); - if (!pset.quiet) - puts(_("Query buffer reset (cleared).")); - } - - /* \s save history in a file or show it on the screen */ - else if (strcmp(cmd, "s") == 0) - { - char *fname = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - expand_tilde(&fname); - success = printHistory(fname, pset.popt.topt.pager); - if (success && !pset.quiet && fname) - printf(_("Wrote history to file \"%s\".\n"), fname); - if (!fname) - putchar('\n'); - free(fname); - } - - /* \set -- generalized set variable/option command */ - else if (strcmp(cmd, "set") == 0) - { - char *opt0 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!opt0) - { - /* list all variables */ - PrintVariables(pset.vars); - success = true; - } - else - { - /* - * Set variable to the concatenation of the arguments. - */ - char *newval; - char *opt; - - opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - newval = pg_strdup(opt ? opt : ""); - free(opt); - - while ((opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false))) - { - newval = pg_realloc(newval, strlen(newval) + strlen(opt) + 1); - strcat(newval, opt); - free(opt); - } - - if (!SetVariable(pset.vars, opt0, newval)) - { - psql_error("\\%s: error while setting variable\n", cmd); - success = false; - } - free(newval); - } - free(opt0); - } - - - /* \setenv -- set environment command */ - else if (strcmp(cmd, "setenv") == 0) - { - char *envvar = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - char *envval = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!envvar) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else if (strchr(envvar, '=') != NULL) - { - psql_error("\\%s: environment variable name must not contain \"=\"\n", - cmd); - success = false; - } - else if (!envval) - { - /* No argument - unset the environment variable */ - unsetenv(envvar); - success = true; - } - else - { - /* Set variable to the value of the next argument */ - char *newval; - - newval = psprintf("%s=%s", envvar, envval); - putenv(newval); - success = true; - - /* - * Do not free newval here, it will screw up the environment if - * you do. See putenv man page for details. That means we leak a - * bit of memory here, but not enough to worry about. - */ - } - free(envvar); - free(envval); - } - - /* \sf -- show a function's source code */ - else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0) - { - bool show_linenumbers = (strcmp(cmd, "sf+") == 0); - PQExpBuffer func_buf; - char *func; - Oid foid = InvalidOid; - - func_buf = createPQExpBuffer(); - func = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, true); - if (pset.sversion < 80400) - { - psql_error("The server (version %d.%d) does not support showing function source.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - status = PSQL_CMD_ERROR; - } - else if (!func) - { - psql_error("function name is required\n"); - status = PSQL_CMD_ERROR; - } - else if (!lookup_function_oid(func, &foid)) - { - /* error already reported */ - status = PSQL_CMD_ERROR; - } - else if (!get_create_function_cmd(foid, func_buf)) - { - /* error already reported */ - status = PSQL_CMD_ERROR; - } - else - { - FILE *output; - bool is_pager; - - /* Select output stream: stdout, pager, or file */ - if (pset.queryFout == stdout) - { - /* count lines in function to see if pager is needed */ - int lineno = 0; - const char *lines = func_buf->data; - - while (*lines != '\0') - { - lineno++; - /* find start of next line */ - lines = strchr(lines, '\n'); - if (!lines) - break; - lines++; - } - - output = PageOutput(lineno, &(pset.popt.topt)); - is_pager = true; - } - else - { - /* use previously set output file, without pager */ - output = pset.queryFout; - is_pager = false; - } - - if (show_linenumbers) - { - bool in_header = true; - int lineno = 0; - char *lines = func_buf->data; - - /* - * lineno "1" should correspond to the first line of the - * function body. We expect that pg_get_functiondef() will - * emit that on a line beginning with "AS ", and that there - * can be no such line before the real start of the function - * body. - * - * Note that this loop scribbles on func_buf. - */ - while (*lines != '\0') - { - char *eol; - - if (in_header && strncmp(lines, "AS ", 3) == 0) - in_header = false; - /* increment lineno only for body's lines */ - if (!in_header) - lineno++; - - /* find and mark end of current line */ - eol = strchr(lines, '\n'); - if (eol != NULL) - *eol = '\0'; - - /* show current line as appropriate */ - if (in_header) - fprintf(output, " %s\n", lines); - else - fprintf(output, "%-7d %s\n", lineno, lines); - - /* advance to next line, if any */ - if (eol == NULL) - break; - lines = ++eol; - } - } - else - { - /* just send the function definition to output */ - fputs(func_buf->data, output); - } - - if (is_pager) - ClosePager(output); - } - - if (func) - free(func); - destroyPQExpBuffer(func_buf); - } - - /* \stage */ - else if (pg_strcasecmp(cmd, "stage") == 0) - { - /* - * The stage command augments psql's copy command with data loading - * functionality for sharded databases. Specifically, the client first - * contacts the master node and fetches shard and node metadata. Then, - * the client coordinates the loading of data to remote nodes. - */ - instr_time startTime; - char *stageCommand = NULL; - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(startTime); - } - - /* read the rest of line; do not strip away quotes or semicolons */ - stageCommand = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, false); - - success = DoStageData(stageCommand); - - if (pset.timing && success) - { - instr_time endTime; - instr_time elapsedTime; - - INSTR_TIME_SET_CURRENT(endTime); - INSTR_TIME_SET_ZERO(elapsedTime); - - INSTR_TIME_ACCUM_DIFF(elapsedTime, endTime, startTime); - printf(_("Time: %.3f ms\n"), INSTR_TIME_GET_MILLISEC(elapsedTime)); - } - - free(stageCommand); - } - - /* \t -- turn off headers and row count */ - else if (strcmp(cmd, "t") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - success = do_pset("tuples_only", opt, &pset.popt, pset.quiet); - free(opt); - } - - /* \T -- define html attributes */ - else if (strcmp(cmd, "T") == 0) - { - char *value = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - success = do_pset("tableattr", value, &pset.popt, pset.quiet); - free(value); - } - - /* \timing -- toggle timing of queries */ - else if (strcmp(cmd, "timing") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (opt) - pset.timing = ParseVariableBool(opt, "\\timing"); - else - pset.timing = !pset.timing; - if (!pset.quiet) - { - if (pset.timing) - puts(_("Timing is on.")); - else - puts(_("Timing is off.")); - } - free(opt); - } - - /* \unset */ - else if (strcmp(cmd, "unset") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!opt) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else if (!SetVariable(pset.vars, opt, NULL)) - { - psql_error("\\%s: error while setting variable\n", cmd); - success = false; - } - free(opt); - } - - /* \w -- write query buffer to file */ - else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0) - { - FILE *fd = NULL; - bool is_pipe = false; - char *fname = NULL; - - if (!query_buf) - { - psql_error("no query buffer\n"); - status = PSQL_CMD_ERROR; - } - else - { - fname = psql_scan_slash_option(scan_state, - OT_FILEPIPE, NULL, true); - expand_tilde(&fname); - - if (!fname) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - if (fname[0] == '|') - { - is_pipe = true; - disable_sigpipe_trap(); - fd = popen(&fname[1], "w"); - } - else - { - canonicalize_path(fname); - fd = fopen(fname, "w"); - } - if (!fd) - { - psql_error("%s: %s\n", fname, strerror(errno)); - success = false; - } - } - } - - if (fd) - { - int result; - - if (query_buf && query_buf->len > 0) - fprintf(fd, "%s\n", query_buf->data); - - if (is_pipe) - result = pclose(fd); - else - result = fclose(fd); - - if (result == EOF) - { - psql_error("%s: %s\n", fname, strerror(errno)); - success = false; - } - } - - if (is_pipe) - restore_sigpipe_trap(); - - free(fname); - } - - /* \watch -- execute a query every N seconds */ - else if (strcmp(cmd, "watch") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - long sleep = 2; - - /* Convert optional sleep-length argument */ - if (opt) - { - sleep = strtol(opt, NULL, 10); - if (sleep <= 0) - sleep = 1; - free(opt); - } - - success = do_watch(query_buf, sleep); - - /* Reset the query buffer as though for \r */ - resetPQExpBuffer(query_buf); - psql_scan_reset(scan_state); - } - - /* \x -- set or toggle expanded table representation */ - else if (strcmp(cmd, "x") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - success = do_pset("expanded", opt, &pset.popt, pset.quiet); - free(opt); - } - - /* \z -- list table rights (equivalent to \dp) */ - else if (strcmp(cmd, "z") == 0) - { - char *pattern = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true); - - success = permissionsList(pattern); - if (pattern) - free(pattern); - } - - /* \! -- shell escape */ - else if (strcmp(cmd, "!") == 0) - { - char *opt = psql_scan_slash_option(scan_state, - OT_WHOLE_LINE, NULL, false); - - success = do_shell(opt); - free(opt); - } - - /* \? -- slash command help */ - else if (strcmp(cmd, "?") == 0) - { - char *opt0 = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, false); - - if (!opt0 || strcmp(opt0, "commands") == 0) - slashUsage(pset.popt.topt.pager); - else if (strcmp(opt0, "options") == 0) - usage(pset.popt.topt.pager); - else if (strcmp(opt0, "variables") == 0) - helpVariables(pset.popt.topt.pager); - else - slashUsage(pset.popt.topt.pager); - } - -#if 0 - - /* - * These commands don't do anything. I just use them to test the parser. - */ - else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0) - { - int i = 0; - char *value; - - while ((value = psql_scan_slash_option(scan_state, - OT_NORMAL, NULL, true))) - { - psql_error("+ opt(%d) = |%s|\n", i++, value); - free(value); - } - } -#endif - - else - status = PSQL_CMD_UNKNOWN; - - if (!success) - status = PSQL_CMD_ERROR; - - return status; -} - -/* - * Ask the user for a password; 'username' is the username the - * password is for, if one has been explicitly specified. Returns a - * malloc'd string. - */ -static char * -prompt_for_password(const char *username) -{ - char *result; - - if (username == NULL) - result = simple_prompt("Password: ", 100, false); - else - { - char *prompt_text; - - prompt_text = psprintf(_("Password for user %s: "), username); - result = simple_prompt(prompt_text, 100, false); - free(prompt_text); - } - - return result; -} - -static bool -param_is_newly_set(const char *old_val, const char *new_val) -{ - if (new_val == NULL) - return false; - - if (old_val == NULL || strcmp(old_val, new_val) != 0) - return true; - - return false; -} - -/* - * do_connect -- handler for \connect - * - * Connects to a database with given parameters. If there exists an - * established connection, NULL values will be replaced with the ones - * in the current connection. Otherwise NULL will be passed for that - * parameter to PQconnectdbParams(), so the libpq defaults will be used. - * - * In interactive mode, if connection fails with the given parameters, - * the old connection will be kept. - */ -static bool -do_connect(char *dbname, char *user, char *host, char *port) -{ - PGconn *o_conn = pset.db, - *n_conn; - char *password = NULL; - bool keep_password; - bool has_connection_string; - - if (!o_conn && (!dbname || !user || !host || !port)) - { - /* - * We don't know the supplied connection parameters and don't want to - * connect to the wrong database by using defaults, so require all - * parameters to be specified. - */ - psql_error("All connection parameters must be supplied because no " - "database connection exists\n"); - return false; - } - - /* grab values from the old connection, unless supplied by caller */ - if (!user) - user = PQuser(o_conn); - if (!host) - host = PQhost(o_conn); - if (!port) - port = PQport(o_conn); - - has_connection_string = - dbname ? recognized_connection_string(dbname) : false; - - /* - * Any change in the parameters read above makes us discard the password. - * We also discard it if we're to use a conninfo rather than the - * positional syntax. Note that currently, PQhost() can return NULL for a - * default Unix-socket connection, so we have to allow NULL for host. - */ - if (has_connection_string) - keep_password = false; - else - keep_password = - (user && PQuser(o_conn) && strcmp(user, PQuser(o_conn)) == 0) && - ((host && PQhost(o_conn) && strcmp(host, PQhost(o_conn)) == 0) || - (host == NULL && PQhost(o_conn) == NULL)) && - (port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0); - - /* - * Grab dbname from old connection unless supplied by caller. No password - * discard if this changes: passwords aren't (usually) database-specific. - */ - if (!dbname) - dbname = PQdb(o_conn); - - /* - * If the user asked to be prompted for a password, ask for one now. If - * not, use the password from the old connection, provided the username - * etc have not changed. Otherwise, try to connect without a password - * first, and then ask for a password if needed. - * - * XXX: this behavior leads to spurious connection attempts recorded in - * the postmaster's log. But libpq offers no API that would let us obtain - * a password and then continue with the first connection attempt. - */ - if (pset.getPassword == TRI_YES) - { - password = prompt_for_password(user); - } - else if (o_conn && keep_password) - { - password = PQpass(o_conn); - if (password && *password) - password = pg_strdup(password); - else - password = NULL; - } - - while (true) - { -#define PARAMS_ARRAY_SIZE 8 - const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); - const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); - int paramnum = 0; - - keywords[0] = "dbname"; - values[0] = dbname; - - if (!has_connection_string) - { - keywords[++paramnum] = "host"; - values[paramnum] = host; - keywords[++paramnum] = "port"; - values[paramnum] = port; - keywords[++paramnum] = "user"; - values[paramnum] = user; - } - keywords[++paramnum] = "password"; - values[paramnum] = password; - keywords[++paramnum] = "fallback_application_name"; - values[paramnum] = pset.progname; - keywords[++paramnum] = "client_encoding"; - values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - - /* add array terminator */ - keywords[++paramnum] = NULL; - values[paramnum] = NULL; - - n_conn = PQconnectdbParams(keywords, values, true); - - pg_free(keywords); - pg_free(values); - - /* We can immediately discard the password -- no longer needed */ - if (password) - pg_free(password); - - if (PQstatus(n_conn) == CONNECTION_OK) - break; - - /* - * Connection attempt failed; either retry the connection attempt with - * a new password, or give up. - */ - if (!password && PQconnectionNeedsPassword(n_conn) && pset.getPassword != TRI_NO) - { - PQfinish(n_conn); - password = prompt_for_password(user); - continue; - } - - /* - * Failed to connect to the database. In interactive mode, keep the - * previous connection to the DB; in scripting mode, close our - * previous connection as well. - */ - if (pset.cur_cmd_interactive) - { - psql_error("%s", PQerrorMessage(n_conn)); - - /* pset.db is left unmodified */ - if (o_conn) - psql_error("Previous connection kept\n"); - } - else - { - psql_error("\\connect: %s", PQerrorMessage(n_conn)); - if (o_conn) - { - PQfinish(o_conn); - pset.db = NULL; - } - } - - PQfinish(n_conn); - return false; - } - - /* - * Replace the old connection with the new one, and update - * connection-dependent variables. - */ - PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL); - pset.db = n_conn; - SyncVariables(); - connection_warnings(false); /* Must be after SyncVariables */ - - /* Tell the user about the new connection */ - if (!pset.quiet) - { - if (!o_conn || - param_is_newly_set(PQhost(o_conn), PQhost(pset.db)) || - param_is_newly_set(PQport(o_conn), PQport(pset.db))) - { - char *host = PQhost(pset.db); - - if (host == NULL) - host = DEFAULT_PGSOCKET_DIR; - /* If the host is an absolute path, the connection is via socket */ - if (is_absolute_path(host)) - printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"), - PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db)); - else - printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"), - PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db)); - } - else - printf(_("You are now connected to database \"%s\" as user \"%s\".\n"), - PQdb(pset.db), PQuser(pset.db)); - } - - if (o_conn) - PQfinish(o_conn); - return true; -} - - -void -connection_warnings(bool in_startup) -{ - if (!pset.quiet && !pset.notty) - { - int client_ver = PG_VERSION_NUM; - - if (pset.sversion != client_ver) - { - const char *server_version; - char server_ver_str[16]; - - /* Try to get full text form, might include "devel" etc */ - server_version = PQparameterStatus(pset.db, "server_version"); - if (!server_version) - { - snprintf(server_ver_str, sizeof(server_ver_str), - "%d.%d.%d", - pset.sversion / 10000, - (pset.sversion / 100) % 100, - pset.sversion % 100); - server_version = server_ver_str; - } - - printf(_("%s (%s, server %s)\n"), - pset.progname, PG_VERSION, server_version); - } - /* For version match, only print psql banner on startup. */ - else if (in_startup) - printf("%s (%s)\n", pset.progname, PG_VERSION); - - if (pset.sversion / 100 > client_ver / 100) - printf(_("WARNING: %s major version %d.%d, server major version %d.%d.\n" - " Some psql features might not work.\n"), - pset.progname, client_ver / 10000, (client_ver / 100) % 100, - pset.sversion / 10000, (pset.sversion / 100) % 100); - -#ifdef WIN32 - checkWin32Codepage(); -#endif - printSSLInfo(); - } -} - - -/* - * printSSLInfo - * - * Prints information about the current SSL connection, if SSL is in use - */ -static void -printSSLInfo(void) -{ - const char *protocol; - const char *cipher; - const char *bits; - const char *compression; - - if (!PQsslInUse(pset.db)) - return; /* no SSL */ - - protocol = PQsslAttribute(pset.db, "protocol"); - cipher = PQsslAttribute(pset.db, "cipher"); - bits = PQsslAttribute(pset.db, "key_bits"); - compression = PQsslAttribute(pset.db, "compression"); - - printf(_("SSL connection (protocol: %s, cipher: %s, bits: %s, compression: %s)\n"), - protocol ? protocol : _("unknown"), - cipher ? cipher : _("unknown"), - bits ? bits : _("unknown"), - (compression && strcmp(compression, "off") != 0) ? _("on") : _("off")); -} - -/* - * checkWin32Codepage - * - * Prints a warning when win32 console codepage differs from Windows codepage - */ -#ifdef WIN32 -static void -checkWin32Codepage(void) -{ - unsigned int wincp, - concp; - - wincp = GetACP(); - concp = GetConsoleCP(); - if (wincp != concp) - { - printf(_("WARNING: Console code page (%u) differs from Windows code page (%u)\n" - " 8-bit characters might not work correctly. See psql reference\n" - " page \"Notes for Windows users\" for details.\n"), - concp, wincp); - } -} -#endif - - -/* - * SyncVariables - * - * Make psql's internal variables agree with connection state upon - * establishing a new connection. - */ -void -SyncVariables(void) -{ - /* get stuff from connection */ - pset.encoding = PQclientEncoding(pset.db); - pset.popt.topt.encoding = pset.encoding; - pset.sversion = PQserverVersion(pset.db); - - SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); - SetVariable(pset.vars, "USER", PQuser(pset.db)); - SetVariable(pset.vars, "HOST", PQhost(pset.db)); - SetVariable(pset.vars, "PORT", PQport(pset.db)); - SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); - - /* send stuff to it, too */ - PQsetErrorVerbosity(pset.db, pset.verbosity); -} - -/* - * UnsyncVariables - * - * Clear variables that should be not be set when there is no connection. - */ -void -UnsyncVariables(void) -{ - SetVariable(pset.vars, "DBNAME", NULL); - SetVariable(pset.vars, "USER", NULL); - SetVariable(pset.vars, "HOST", NULL); - SetVariable(pset.vars, "PORT", NULL); - SetVariable(pset.vars, "ENCODING", NULL); -} - - -/* - * do_edit -- handler for \e - * - * If you do not specify a filename, the current query buffer will be copied - * into a temporary one. - */ -static bool -editFile(const char *fname, int lineno) -{ - const char *editorName; - const char *editor_lineno_arg = NULL; - char *sys; - int result; - - Assert(fname != NULL); - - /* Find an editor to use */ - editorName = getenv("PSQL_EDITOR"); - if (!editorName) - editorName = getenv("EDITOR"); - if (!editorName) - editorName = getenv("VISUAL"); - if (!editorName) - editorName = DEFAULT_EDITOR; - - /* Get line number argument, if we need it. */ - if (lineno > 0) - { - editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG"); -#ifdef DEFAULT_EDITOR_LINENUMBER_ARG - if (!editor_lineno_arg) - editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG; -#endif - if (!editor_lineno_arg) - { - psql_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number\n"); - return false; - } - } - - /* - * On Unix the EDITOR value should *not* be quoted, since it might include - * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it - * if necessary. But this policy is not very workable on Windows, due to - * severe brain damage in their command shell plus the fact that standard - * program paths include spaces. - */ -#ifndef WIN32 - if (lineno > 0) - sys = psprintf("exec %s %s%d '%s'", - editorName, editor_lineno_arg, lineno, fname); - else - sys = psprintf("exec %s '%s'", - editorName, fname); -#else - if (lineno > 0) - sys = psprintf("\"%s\" %s%d \"%s\"", - editorName, editor_lineno_arg, lineno, fname); - else - sys = psprintf("\"%s\" \"%s\"", - editorName, fname); -#endif - result = system(sys); - if (result == -1) - psql_error("could not start editor \"%s\"\n", editorName); - else if (result == 127) - psql_error("could not start /bin/sh\n"); - free(sys); - - return result == 0; -} - - -/* call this one */ -static bool -do_edit(const char *filename_arg, PQExpBuffer query_buf, - int lineno, bool *edited) -{ - char fnametmp[MAXPGPATH]; - FILE *stream = NULL; - const char *fname; - bool error = false; - int fd; - - struct stat before, - after; - - if (filename_arg) - fname = filename_arg; - else - { - /* make a temp file to edit */ -#ifndef WIN32 - const char *tmpdir = getenv("TMPDIR"); - - if (!tmpdir) - tmpdir = "/tmp"; -#else - char tmpdir[MAXPGPATH]; - int ret; - - ret = GetTempPath(MAXPGPATH, tmpdir); - if (ret == 0 || ret > MAXPGPATH) - { - psql_error("could not locate temporary directory: %s\n", - !ret ? strerror(errno) : ""); - return false; - } - - /* - * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the - * current directory to the supplied path unless we use only - * backslashes, so we do that. - */ -#endif -#ifndef WIN32 - snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir, - "/", (int) getpid()); -#else - snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir, - "" /* trailing separator already present */ , (int) getpid()); -#endif - - fname = (const char *) fnametmp; - - fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd != -1) - stream = fdopen(fd, "w"); - - if (fd == -1 || !stream) - { - psql_error("could not open temporary file \"%s\": %s\n", fname, strerror(errno)); - error = true; - } - else - { - unsigned int ql = query_buf->len; - - if (ql == 0 || query_buf->data[ql - 1] != '\n') - { - appendPQExpBufferChar(query_buf, '\n'); - ql++; - } - - if (fwrite(query_buf->data, 1, ql, stream) != ql) - { - psql_error("%s: %s\n", fname, strerror(errno)); - - if (fclose(stream) != 0) - psql_error("%s: %s\n", fname, strerror(errno)); - - if (remove(fname) != 0) - psql_error("%s: %s\n", fname, strerror(errno)); - - error = true; - } - else if (fclose(stream) != 0) - { - psql_error("%s: %s\n", fname, strerror(errno)); - if (remove(fname) != 0) - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - } - } - - if (!error && stat(fname, &before) != 0) - { - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - - /* call editor */ - if (!error) - error = !editFile(fname, lineno); - - if (!error && stat(fname, &after) != 0) - { - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - - if (!error && before.st_mtime != after.st_mtime) - { - stream = fopen(fname, PG_BINARY_R); - if (!stream) - { - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - else - { - /* read file back into query_buf */ - char line[1024]; - - resetPQExpBuffer(query_buf); - while (fgets(line, sizeof(line), stream) != NULL) - appendPQExpBufferStr(query_buf, line); - - if (ferror(stream)) - { - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - else if (edited) - { - *edited = true; - } - - fclose(stream); - } - } - - /* remove temp file */ - if (!filename_arg) - { - if (remove(fname) == -1) - { - psql_error("%s: %s\n", fname, strerror(errno)); - error = true; - } - } - - return !error; -} - - - -/* - * process_file - * - * Reads commands from filename and passes them to the main processing loop. - * Handler for \i and \ir, but can be used for other things as well. Returns - * MainLoop() error code. - * - * If use_relative_path is true and filename is not an absolute path, then open - * the file from where the currently processed file (if any) is located. - */ -int -process_file(char *filename, bool single_txn, bool use_relative_path) -{ - FILE *fd; - int result; - char *oldfilename; - char relpath[MAXPGPATH]; - PGresult *res; - - if (!filename) - { - fd = stdin; - filename = NULL; - } - else if (strcmp(filename, "-") != 0) - { - canonicalize_path(filename); - - /* - * If we were asked to resolve the pathname relative to the location - * of the currently executing script, and there is one, and this is a - * relative pathname, then prepend all but the last pathname component - * of the current script to this pathname. - */ - if (use_relative_path && pset.inputfile && - !is_absolute_path(filename) && !has_drive_prefix(filename)) - { - strlcpy(relpath, pset.inputfile, sizeof(relpath)); - get_parent_directory(relpath); - join_path_components(relpath, relpath, filename); - canonicalize_path(relpath); - - filename = relpath; - } - - fd = fopen(filename, PG_BINARY_R); - - if (!fd) - { - psql_error("%s: %s\n", filename, strerror(errno)); - return EXIT_FAILURE; - } - } - else - { - fd = stdin; - filename = ""; /* for future error messages */ - } - - oldfilename = pset.inputfile; - pset.inputfile = filename; - - if (single_txn) - { - if ((res = PSQLexec("BEGIN")) == NULL) - { - if (pset.on_error_stop) - { - result = EXIT_USER; - goto error; - } - } - else - PQclear(res); - } - - result = MainLoop(fd); - - if (single_txn) - { - if ((res = PSQLexec("COMMIT")) == NULL) - { - if (pset.on_error_stop) - { - result = EXIT_USER; - goto error; - } - } - else - PQclear(res); - } - -error: - if (fd != stdin) - fclose(fd); - - pset.inputfile = oldfilename; - return result; -} - - - -static const char * -_align2string(enum printFormat in) -{ - switch (in) - { - case PRINT_NOTHING: - return "nothing"; - break; - case PRINT_UNALIGNED: - return "unaligned"; - break; - case PRINT_ALIGNED: - return "aligned"; - break; - case PRINT_WRAPPED: - return "wrapped"; - break; - case PRINT_HTML: - return "html"; - break; - case PRINT_ASCIIDOC: - return "asciidoc"; - break; - case PRINT_LATEX: - return "latex"; - break; - case PRINT_LATEX_LONGTABLE: - return "latex-longtable"; - break; - case PRINT_TROFF_MS: - return "troff-ms"; - break; - } - return "unknown"; -} - -/* - * Parse entered Unicode linestyle. If ok, update *linestyle and return - * true, else return false. - */ -static bool -set_unicode_line_style(const char *value, size_t vallen, - unicode_linestyle *linestyle) -{ - if (pg_strncasecmp("single", value, vallen) == 0) - *linestyle = UNICODE_LINESTYLE_SINGLE; - else if (pg_strncasecmp("double", value, vallen) == 0) - *linestyle = UNICODE_LINESTYLE_DOUBLE; - else - return false; - return true; -} - -static const char * -_unicode_linestyle2string(int linestyle) -{ - switch (linestyle) - { - case UNICODE_LINESTYLE_SINGLE: - return "single"; - break; - case UNICODE_LINESTYLE_DOUBLE: - return "double"; - break; - } - return "unknown"; -} - -/* - * do_pset - * - */ -bool -do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) -{ - size_t vallen = 0; - - Assert(param != NULL); - - if (value) - vallen = strlen(value); - - /* set format */ - if (strcmp(param, "format") == 0) - { - if (!value) - ; - else if (pg_strncasecmp("unaligned", value, vallen) == 0) - popt->topt.format = PRINT_UNALIGNED; - else if (pg_strncasecmp("aligned", value, vallen) == 0) - popt->topt.format = PRINT_ALIGNED; - else if (pg_strncasecmp("wrapped", value, vallen) == 0) - popt->topt.format = PRINT_WRAPPED; - else if (pg_strncasecmp("html", value, vallen) == 0) - popt->topt.format = PRINT_HTML; - else if (pg_strncasecmp("asciidoc", value, vallen) == 0) - popt->topt.format = PRINT_ASCIIDOC; - else if (pg_strncasecmp("latex", value, vallen) == 0) - popt->topt.format = PRINT_LATEX; - else if (pg_strncasecmp("latex-longtable", value, vallen) == 0) - popt->topt.format = PRINT_LATEX_LONGTABLE; - else if (pg_strncasecmp("troff-ms", value, vallen) == 0) - popt->topt.format = PRINT_TROFF_MS; - else - { - psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n"); - return false; - } - - } - - /* set table line style */ - else if (strcmp(param, "linestyle") == 0) - { - if (!value) - ; - else if (pg_strncasecmp("ascii", value, vallen) == 0) - popt->topt.line_style = &pg_asciiformat; - else if (pg_strncasecmp("old-ascii", value, vallen) == 0) - popt->topt.line_style = &pg_asciiformat_old; - else if (pg_strncasecmp("unicode", value, vallen) == 0) - popt->topt.line_style = &pg_utf8format; - else - { - psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n"); - return false; - } - - } - - /* set unicode border line style */ - else if (strcmp(param, "unicode_border_linestyle") == 0) - { - if (!value) - ; - else if (set_unicode_line_style(value, vallen, - &popt->topt.unicode_border_linestyle)) - refresh_utf8format(&(popt->topt)); - else - { - psql_error("\\pset: allowed Unicode border line styles are single, double\n"); - return false; - } - } - - /* set unicode column line style */ - else if (strcmp(param, "unicode_column_linestyle") == 0) - { - if (!value) - ; - else if (set_unicode_line_style(value, vallen, - &popt->topt.unicode_column_linestyle)) - refresh_utf8format(&(popt->topt)); - else - { - psql_error("\\pset: allowed Unicode column line styles are single, double\n"); - return false; - } - } - - /* set unicode header line style */ - else if (strcmp(param, "unicode_header_linestyle") == 0) - { - if (!value) - ; - else if (set_unicode_line_style(value, vallen, - &popt->topt.unicode_header_linestyle)) - refresh_utf8format(&(popt->topt)); - else - { - psql_error("\\pset: allowed Unicode header line styles are single, double\n"); - return false; - } - } - - /* set border style/width */ - else if (strcmp(param, "border") == 0) - { - if (value) - popt->topt.border = atoi(value); - - } - - /* set expanded/vertical mode */ - else if (strcmp(param, "x") == 0 || - strcmp(param, "expanded") == 0 || - strcmp(param, "vertical") == 0) - { - if (value && pg_strcasecmp(value, "auto") == 0) - popt->topt.expanded = 2; - else if (value) - popt->topt.expanded = ParseVariableBool(value, param); - else - popt->topt.expanded = !popt->topt.expanded; - } - - /* locale-aware numeric output */ - else if (strcmp(param, "numericlocale") == 0) - { - if (value) - popt->topt.numericLocale = ParseVariableBool(value, param); - else - popt->topt.numericLocale = !popt->topt.numericLocale; - } - - /* null display */ - else if (strcmp(param, "null") == 0) - { - if (value) - { - free(popt->nullPrint); - popt->nullPrint = pg_strdup(value); - } - } - - /* field separator for unaligned text */ - else if (strcmp(param, "fieldsep") == 0) - { - if (value) - { - free(popt->topt.fieldSep.separator); - popt->topt.fieldSep.separator = pg_strdup(value); - popt->topt.fieldSep.separator_zero = false; - } - } - - else if (strcmp(param, "fieldsep_zero") == 0) - { - free(popt->topt.fieldSep.separator); - popt->topt.fieldSep.separator = NULL; - popt->topt.fieldSep.separator_zero = true; - } - - /* record separator for unaligned text */ - else if (strcmp(param, "recordsep") == 0) - { - if (value) - { - free(popt->topt.recordSep.separator); - popt->topt.recordSep.separator = pg_strdup(value); - popt->topt.recordSep.separator_zero = false; - } - } - - else if (strcmp(param, "recordsep_zero") == 0) - { - free(popt->topt.recordSep.separator); - popt->topt.recordSep.separator = NULL; - popt->topt.recordSep.separator_zero = true; - } - - /* toggle between full and tuples-only format */ - else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0) - { - if (value) - popt->topt.tuples_only = ParseVariableBool(value, param); - else - popt->topt.tuples_only = !popt->topt.tuples_only; - } - - /* set title override */ - else if (strcmp(param, "title") == 0) - { - free(popt->title); - if (!value) - popt->title = NULL; - else - popt->title = pg_strdup(value); - } - - /* set HTML table tag options */ - else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0) - { - free(popt->topt.tableAttr); - if (!value) - popt->topt.tableAttr = NULL; - else - popt->topt.tableAttr = pg_strdup(value); - } - - /* toggle use of pager */ - else if (strcmp(param, "pager") == 0) - { - if (value && pg_strcasecmp(value, "always") == 0) - popt->topt.pager = 2; - else if (value) - { - if (ParseVariableBool(value, param)) - popt->topt.pager = 1; - else - popt->topt.pager = 0; - } - else if (popt->topt.pager == 1) - popt->topt.pager = 0; - else - popt->topt.pager = 1; - } - - /* set minimum lines for pager use */ - else if (strcmp(param, "pager_min_lines") == 0) - { - if (value) - popt->topt.pager_min_lines = atoi(value); - } - - /* disable "(x rows)" footer */ - else if (strcmp(param, "footer") == 0) - { - if (value) - popt->topt.default_footer = ParseVariableBool(value, param); - else - popt->topt.default_footer = !popt->topt.default_footer; - } - - /* set border style/width */ - else if (strcmp(param, "columns") == 0) - { - if (value) - popt->topt.columns = atoi(value); - } - else - { - psql_error("\\pset: unknown option: %s\n", param); - return false; - } - - if (!quiet) - printPsetInfo(param, &pset.popt); - - return true; -} - - -static bool -printPsetInfo(const char *param, struct printQueryOpt *popt) -{ - Assert(param != NULL); - - /* show border style/width */ - if (strcmp(param, "border") == 0) - printf(_("Border style is %d.\n"), popt->topt.border); - - /* show the target width for the wrapped format */ - else if (strcmp(param, "columns") == 0) - { - if (!popt->topt.columns) - printf(_("Target width is unset.\n")); - else - printf(_("Target width is %d.\n"), popt->topt.columns); - } - - /* show expanded/vertical mode */ - else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0) - { - if (popt->topt.expanded == 1) - printf(_("Expanded display is on.\n")); - else if (popt->topt.expanded == 2) - printf(_("Expanded display is used automatically.\n")); - else - printf(_("Expanded display is off.\n")); - } - - /* show field separator for unaligned text */ - else if (strcmp(param, "fieldsep") == 0) - { - if (popt->topt.fieldSep.separator_zero) - printf(_("Field separator is zero byte.\n")); - else - printf(_("Field separator is \"%s\".\n"), - popt->topt.fieldSep.separator); - } - - else if (strcmp(param, "fieldsep_zero") == 0) - { - printf(_("Field separator is zero byte.\n")); - } - - /* show disable "(x rows)" footer */ - else if (strcmp(param, "footer") == 0) - { - if (popt->topt.default_footer) - printf(_("Default footer is on.\n")); - else - printf(_("Default footer is off.\n")); - } - - /* show format */ - else if (strcmp(param, "format") == 0) - { - printf(_("Output format is %s.\n"), _align2string(popt->topt.format)); - } - - /* show table line style */ - else if (strcmp(param, "linestyle") == 0) - { - printf(_("Line style is %s.\n"), - get_line_style(&popt->topt)->name); - } - - /* show null display */ - else if (strcmp(param, "null") == 0) - { - printf(_("Null display is \"%s\".\n"), - popt->nullPrint ? popt->nullPrint : ""); - } - - /* show locale-aware numeric output */ - else if (strcmp(param, "numericlocale") == 0) - { - if (popt->topt.numericLocale) - printf(_("Locale-adjusted numeric output is on.\n")); - else - printf(_("Locale-adjusted numeric output is off.\n")); - } - - /* show toggle use of pager */ - else if (strcmp(param, "pager") == 0) - { - if (popt->topt.pager == 1) - printf(_("Pager is used for long output.\n")); - else if (popt->topt.pager == 2) - printf(_("Pager is always used.\n")); - else - printf(_("Pager usage is off.\n")); - } - - /* show minimum lines for pager use */ - else if (strcmp(param, "pager_min_lines") == 0) - { - printf(ngettext("Pager won't be used for less than %d line.\n", - "Pager won't be used for less than %d lines.\n", - popt->topt.pager_min_lines), - popt->topt.pager_min_lines); - } - - /* show record separator for unaligned text */ - else if (strcmp(param, "recordsep") == 0) - { - if (popt->topt.recordSep.separator_zero) - printf(_("Record separator is zero byte.\n")); - else if (strcmp(popt->topt.recordSep.separator, "\n") == 0) - printf(_("Record separator is .\n")); - else - printf(_("Record separator is \"%s\".\n"), - popt->topt.recordSep.separator); - } - - else if (strcmp(param, "recordsep_zero") == 0) - { - printf(_("Record separator is zero byte.\n")); - } - - /* show HTML table tag options */ - else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0) - { - if (popt->topt.tableAttr) - printf(_("Table attributes are \"%s\".\n"), - popt->topt.tableAttr); - else - printf(_("Table attributes unset.\n")); - } - - /* show title override */ - else if (strcmp(param, "title") == 0) - { - if (popt->title) - printf(_("Title is \"%s\".\n"), popt->title); - else - printf(_("Title is unset.\n")); - } - - /* show toggle between full and tuples-only format */ - else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0) - { - if (popt->topt.tuples_only) - printf(_("Tuples only is on.\n")); - else - printf(_("Tuples only is off.\n")); - } - - /* Unicode style formatting */ - else if (strcmp(param, "unicode_border_linestyle") == 0) - { - printf(_("Unicode border line style is \"%s\".\n"), - _unicode_linestyle2string(popt->topt.unicode_border_linestyle)); - } - - else if (strcmp(param, "unicode_column_linestyle") == 0) - { - printf(_("Unicode column line style is \"%s\".\n"), - _unicode_linestyle2string(popt->topt.unicode_column_linestyle)); - } - - else if (strcmp(param, "unicode_header_linestyle") == 0) - { - printf(_("Unicode header line style is \"%s\".\n"), - _unicode_linestyle2string(popt->topt.unicode_header_linestyle)); - } - - else - { - psql_error("\\pset: unknown option: %s\n", param); - return false; - } - - return true; -} - - -static const char * -pset_bool_string(bool val) -{ - return val ? "on" : "off"; -} - - -static char * -pset_quoted_string(const char *str) -{ - char *ret = pg_malloc(strlen(str) * 2 + 3); - char *r = ret; - - *r++ = '\''; - - for (; *str; str++) - { - if (*str == '\n') - { - *r++ = '\\'; - *r++ = 'n'; - } - else if (*str == '\'') - { - *r++ = '\\'; - *r++ = '\''; - } - else - *r++ = *str; - } - - *r++ = '\''; - *r = '\0'; - - return ret; -} - - -/* - * Return a malloc'ed string for the \pset value. - * - * Note that for some string parameters, print.c distinguishes between unset - * and empty string, but for others it doesn't. This function should produce - * output that produces the correct setting when fed back into \pset. - */ -static char * -pset_value_string(const char *param, struct printQueryOpt *popt) -{ - Assert(param != NULL); - - if (strcmp(param, "border") == 0) - return psprintf("%d", popt->topt.border); - else if (strcmp(param, "columns") == 0) - return psprintf("%d", popt->topt.columns); - else if (strcmp(param, "expanded") == 0) - return pstrdup(popt->topt.expanded == 2 - ? "auto" - : pset_bool_string(popt->topt.expanded)); - else if (strcmp(param, "fieldsep") == 0) - return pset_quoted_string(popt->topt.fieldSep.separator - ? popt->topt.fieldSep.separator - : ""); - else if (strcmp(param, "fieldsep_zero") == 0) - return pstrdup(pset_bool_string(popt->topt.fieldSep.separator_zero)); - else if (strcmp(param, "footer") == 0) - return pstrdup(pset_bool_string(popt->topt.default_footer)); - else if (strcmp(param, "format") == 0) - return psprintf("%s", _align2string(popt->topt.format)); - else if (strcmp(param, "linestyle") == 0) - return psprintf("%s", get_line_style(&popt->topt)->name); - else if (strcmp(param, "null") == 0) - return pset_quoted_string(popt->nullPrint - ? popt->nullPrint - : ""); - else if (strcmp(param, "numericlocale") == 0) - return pstrdup(pset_bool_string(popt->topt.numericLocale)); - else if (strcmp(param, "pager") == 0) - return psprintf("%d", popt->topt.pager); - else if (strcmp(param, "pager_min_lines") == 0) - return psprintf("%d", popt->topt.pager_min_lines); - else if (strcmp(param, "recordsep") == 0) - return pset_quoted_string(popt->topt.recordSep.separator - ? popt->topt.recordSep.separator - : ""); - else if (strcmp(param, "recordsep_zero") == 0) - return pstrdup(pset_bool_string(popt->topt.recordSep.separator_zero)); - else if (strcmp(param, "tableattr") == 0) - return popt->topt.tableAttr ? pset_quoted_string(popt->topt.tableAttr) : pstrdup(""); - else if (strcmp(param, "title") == 0) - return popt->title ? pset_quoted_string(popt->title) : pstrdup(""); - else if (strcmp(param, "tuples_only") == 0) - return pstrdup(pset_bool_string(popt->topt.tuples_only)); - else if (strcmp(param, "unicode_border_linestyle") == 0) - return pstrdup(_unicode_linestyle2string(popt->topt.unicode_border_linestyle)); - else if (strcmp(param, "unicode_column_linestyle") == 0) - return pstrdup(_unicode_linestyle2string(popt->topt.unicode_column_linestyle)); - else if (strcmp(param, "unicode_header_linestyle") == 0) - return pstrdup(_unicode_linestyle2string(popt->topt.unicode_header_linestyle)); - else - return pstrdup("ERROR"); -} - - - -#ifndef WIN32 -#define DEFAULT_SHELL "/bin/sh" -#else -/* - * CMD.EXE is in different places in different Win32 releases so we - * have to rely on the path to find it. - */ -#define DEFAULT_SHELL "cmd.exe" -#endif - -static bool -do_shell(const char *command) -{ - int result; - - if (!command) - { - char *sys; - const char *shellName; - - shellName = getenv("SHELL"); -#ifdef WIN32 - if (shellName == NULL) - shellName = getenv("COMSPEC"); -#endif - if (shellName == NULL) - shellName = DEFAULT_SHELL; - - /* See EDITOR handling comment for an explanation */ -#ifndef WIN32 - sys = psprintf("exec %s", shellName); -#else - sys = psprintf("\"%s\"", shellName); -#endif - result = system(sys); - free(sys); - } - else - result = system(command); - - if (result == 127 || result == -1) - { - psql_error("\\!: failed\n"); - return false; - } - return true; -} - -/* - * do_watch -- handler for \watch - * - * We break this out of exec_command to avoid having to plaster "volatile" - * onto a bunch of exec_command's variables to silence stupider compilers. - */ -static bool -do_watch(PQExpBuffer query_buf, long sleep) -{ - printQueryOpt myopt = pset.popt; - char title[50]; - - if (!query_buf || query_buf->len <= 0) - { - psql_error(_("\\watch cannot be used with an empty query\n")); - return false; - } - - /* - * Set up rendering options, in particular, disable the pager, because - * nobody wants to be prompted while watching the output of 'watch'. - */ - myopt.topt.pager = 0; - - for (;;) - { - int res; - time_t timer; - long i; - - /* - * Prepare title for output. XXX would it be better to use the time - * of completion of the command? - */ - timer = time(NULL); - snprintf(title, sizeof(title), _("Watch every %lds\t%s"), - sleep, asctime(localtime(&timer))); - myopt.title = title; - - /* Run the query and print out the results */ - res = PSQLexecWatch(query_buf->data, &myopt); - - /* - * PSQLexecWatch handles the case where we can no longer repeat the - * query, and returns 0 or -1. - */ - if (res == 0) - break; - if (res == -1) - return false; - - /* - * Set up cancellation of 'watch' via SIGINT. We redo this each time - * through the loop since it's conceivable something inside - * PSQLexecWatch could change sigint_interrupt_jmp. - */ - if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) - break; - - /* - * Enable 'watch' cancellations and wait a while before running the - * query again. Break the sleep into short intervals since pg_usleep - * isn't interruptible on some platforms. - */ - sigint_interrupt_enabled = true; - for (i = 0; i < sleep; i++) - { - pg_usleep(1000000L); - if (cancel_pressed) - break; - } - sigint_interrupt_enabled = false; - } - - return true; -} - -/* - * a little code borrowed from PSQLexec() to manage ECHO_HIDDEN output. - * returns true unless we have ECHO_HIDDEN_NOEXEC. - */ -static bool -lookup_function_echo_hidden(char *query) -{ - if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF) - { - printf(_("********* QUERY **********\n" - "%s\n" - "**************************\n\n"), query); - fflush(stdout); - if (pset.logfile) - { - fprintf(pset.logfile, - _("********* QUERY **********\n" - "%s\n" - "**************************\n\n"), query); - fflush(pset.logfile); - } - - if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC) - return false; - } - return true; -} - -/* - * This function takes a function description, e.g. "x" or "x(int)", and - * issues a query on the given connection to retrieve the function's OID - * using a cast to regproc or regprocedure (as appropriate). The result, - * if there is one, is returned at *foid. Note that we'll fail if the - * function doesn't exist OR if there are multiple matching candidates - * OR if there's something syntactically wrong with the function description; - * unfortunately it can be hard to tell the difference. - */ -static bool -lookup_function_oid(const char *desc, Oid *foid) -{ - bool result = true; - PQExpBuffer query; - PGresult *res; - - query = createPQExpBuffer(); - appendPQExpBufferStr(query, "SELECT "); - appendStringLiteralConn(query, desc, pset.db); - appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid", - strchr(desc, '(') ? "regprocedure" : "regproc"); - if (!lookup_function_echo_hidden(query->data)) - { - destroyPQExpBuffer(query); - return false; - } - res = PQexec(pset.db, query->data); - if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) - *foid = atooid(PQgetvalue(res, 0, 0)); - else - { - minimal_error_message(res); - result = false; - } - - PQclear(res); - destroyPQExpBuffer(query); - - return result; -} - -/* - * Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the - * function with the given OID. If successful, the result is stored in buf. - */ -static bool -get_create_function_cmd(Oid oid, PQExpBuffer buf) -{ - bool result = true; - PQExpBuffer query; - PGresult *res; - - query = createPQExpBuffer(); - printfPQExpBuffer(query, "SELECT pg_catalog.pg_get_functiondef(%u)", oid); - - if (!lookup_function_echo_hidden(query->data)) - { - destroyPQExpBuffer(query); - return false; - } - res = PQexec(pset.db, query->data); - if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) - { - resetPQExpBuffer(buf); - appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0)); - } - else - { - minimal_error_message(res); - result = false; - } - - PQclear(res); - destroyPQExpBuffer(query); - - return result; -} - -/* - * If the given argument of \ef ends with a line number, delete the line - * number from the argument string and return it as an integer. (We need - * this kluge because we're too lazy to parse \ef's function name argument - * carefully --- we just slop it up in OT_WHOLE_LINE mode.) - * - * Returns -1 if no line number is present, 0 on error, or a positive value - * on success. - */ -static int -strip_lineno_from_funcdesc(char *func) -{ - char *c; - int lineno; - - if (!func || func[0] == '\0') - return -1; - - c = func + strlen(func) - 1; - - /* - * This business of parsing backwards is dangerous as can be in a - * multibyte environment: there is no reason to believe that we are - * looking at the first byte of a character, nor are we necessarily - * working in a "safe" encoding. Fortunately the bitpatterns we are - * looking for are unlikely to occur as non-first bytes, but beware of - * trying to expand the set of cases that can be recognized. We must - * guard the macros by using isascii() first, too. - */ - - /* skip trailing whitespace */ - while (c > func && isascii((unsigned char) *c) && isspace((unsigned char) *c)) - c--; - - /* must have a digit as last non-space char */ - if (c == func || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c)) - return -1; - - /* find start of digit string */ - while (c > func && isascii((unsigned char) *c) && isdigit((unsigned char) *c)) - c--; - - /* digits must be separated from func name by space or closing paren */ - /* notice also that we are not allowing an empty func name ... */ - if (c == func || !isascii((unsigned char) *c) || - !(isspace((unsigned char) *c) || *c == ')')) - return -1; - - /* parse digit string */ - c++; - lineno = atoi(c); - if (lineno < 1) - { - psql_error("invalid line number: %s\n", c); - return 0; - } - - /* strip digit string from func */ - *c = '\0'; - - return lineno; -} - -/* - * Report just the primary error; this is to avoid cluttering the output - * with, for instance, a redisplay of the internally generated query - */ -static void -minimal_error_message(PGresult *res) -{ - PQExpBuffer msg; - const char *fld; - - msg = createPQExpBuffer(); - - fld = PQresultErrorField(res, PG_DIAG_SEVERITY); - if (fld) - printfPQExpBuffer(msg, "%s: ", fld); - else - printfPQExpBuffer(msg, "ERROR: "); - fld = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); - if (fld) - appendPQExpBufferStr(msg, fld); - else - appendPQExpBufferStr(msg, "(not available)"); - appendPQExpBufferStr(msg, "\n"); - - psql_error("%s", msg->data); - - destroyPQExpBuffer(msg); -} diff --git a/src/bin/csql/command.h b/src/bin/csql/command.h deleted file mode 100644 index 54385e8f8..000000000 --- a/src/bin/csql/command.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/command.h - */ -#ifndef COMMAND_H -#define COMMAND_H - -#include "print.h" -#include "psqlscan.h" - - -typedef enum _backslashResult -{ - PSQL_CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ - PSQL_CMD_SEND, /* query complete; send off */ - PSQL_CMD_SKIP_LINE, /* keep building query */ - PSQL_CMD_TERMINATE, /* quit program */ - PSQL_CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ - PSQL_CMD_ERROR /* the execution of the backslash command - * resulted in an error */ -} backslashResult; - - -extern backslashResult HandleSlashCmds(PsqlScanState scan_state, - PQExpBuffer query_buf); - -extern int process_file(char *filename, bool single_txn, bool use_relative_path); - -extern bool do_pset(const char *param, - const char *value, - printQueryOpt *popt, - bool quiet); - -extern void connection_warnings(bool in_startup); - -extern void SyncVariables(void); - -extern void UnsyncVariables(void); - -#endif /* COMMAND_H */ diff --git a/src/bin/csql/common.c b/src/bin/csql/common.c deleted file mode 100644 index dc00a3c7f..000000000 --- a/src/bin/csql/common.c +++ /dev/null @@ -1,1903 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/common.c - */ -#include "postgres_fe.h" -#include "common.h" - -#include -#include -#include -#ifndef WIN32 -#include /* for write() */ -#else -#include /* for _write() */ -#include -#endif - -#include "portability/instr_time.h" - -#include "settings.h" -#include "command.h" -#include "copy.h" -#include "mbprint.h" - - -static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec); -static bool command_no_begin(const char *query); -static bool is_select_command(const char *query); - - -/* - * openQueryOutputFile --- attempt to open a query output file - * - * fname == NULL selects stdout, else an initial '|' selects a pipe, - * else plain file. - * - * Returns output file pointer into *fout, and is-a-pipe flag into *is_pipe. - * Caller is responsible for adjusting SIGPIPE state if it's a pipe. - * - * On error, reports suitable error message and returns FALSE. - */ -bool -openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe) -{ - if (!fname || fname[0] == '\0') - { - *fout = stdout; - *is_pipe = false; - } - else if (*fname == '|') - { - *fout = popen(fname + 1, "w"); - *is_pipe = true; - } - else - { - *fout = fopen(fname, "w"); - *is_pipe = false; - } - - if (*fout == NULL) - { - psql_error("%s: %s\n", fname, strerror(errno)); - return false; - } - - return true; -} - -/* - * setQFout - * -- handler for -o command line option and \o command - * - * On success, updates pset with the new output file and returns true. - * On failure, returns false without changing pset state. - */ -bool -setQFout(const char *fname) -{ - FILE *fout; - bool is_pipe; - - /* First make sure we can open the new output file/pipe */ - if (!openQueryOutputFile(fname, &fout, &is_pipe)) - return false; - - /* Close old file/pipe */ - if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr) - { - if (pset.queryFoutPipe) - pclose(pset.queryFout); - else - fclose(pset.queryFout); - } - - pset.queryFout = fout; - pset.queryFoutPipe = is_pipe; - - /* Adjust SIGPIPE handling appropriately: ignore signal if is_pipe */ - set_sigpipe_trap_state(is_pipe); - restore_sigpipe_trap(); - - return true; -} - - -/* - * Error reporting for scripts. Errors should look like - * psql:filename:lineno: message - */ -void -psql_error(const char *fmt,...) -{ - va_list ap; - - fflush(stdout); - if (pset.queryFout && pset.queryFout != stdout) - fflush(pset.queryFout); - - if (pset.inputfile) - fprintf(stderr, "%s:%s:" UINT64_FORMAT ": ", pset.progname, pset.inputfile, pset.lineno); - va_start(ap, fmt); - vfprintf(stderr, _(fmt), ap); - va_end(ap); -} - - - -/* - * for backend Notice messages (INFO, WARNING, etc) - */ -void -NoticeProcessor(void *arg, const char *message) -{ - (void) arg; /* not used */ - psql_error("%s", message); -} - - - -/* - * Code to support query cancellation - * - * Before we start a query, we enable the SIGINT signal catcher to send a - * cancel request to the backend. Note that sending the cancel directly from - * the signal handler is safe because PQcancel() is written to make it - * so. We use write() to report to stderr because it's better to use simple - * facilities in a signal handler. - * - * On win32, the signal canceling happens on a separate thread, because - * that's how SetConsoleCtrlHandler works. The PQcancel function is safe - * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required - * to protect the PGcancel structure against being changed while the signal - * thread is using it. - * - * SIGINT is supposed to abort all long-running psql operations, not only - * database queries. In most places, this is accomplished by checking - * cancel_pressed during long-running loops. However, that won't work when - * blocked on user input (in readline() or fgets()). In those places, we - * set sigint_interrupt_enabled TRUE while blocked, instructing the signal - * catcher to longjmp through sigint_interrupt_jmp. We assume readline and - * fgets are coded to handle possible interruption. (XXX currently this does - * not work on win32, so control-C is less useful there) - */ -volatile bool sigint_interrupt_enabled = false; - -sigjmp_buf sigint_interrupt_jmp; - -static PGcancel *volatile cancelConn = NULL; - -#ifdef WIN32 -static CRITICAL_SECTION cancelConnLock; -#endif - -/* Used from signal handlers, no buffering */ -#define write_stderr(str) write(fileno(stderr), str, strlen(str)) - - -#ifndef WIN32 - -static void -handle_sigint(SIGNAL_ARGS) -{ - int save_errno = errno; - int rc; - char errbuf[256]; - - /* if we are waiting for input, longjmp out of it */ - if (sigint_interrupt_enabled) - { - sigint_interrupt_enabled = false; - siglongjmp(sigint_interrupt_jmp, 1); - } - - /* else, set cancel flag to stop any long-running loops */ - cancel_pressed = true; - - /* and send QueryCancel if we are processing a database query */ - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - { - rc = write_stderr("Cancel request sent\n"); - (void) rc; /* ignore errors, nothing we can do here */ - } - else - { - rc = write_stderr("Could not send cancel request: "); - (void) rc; /* ignore errors, nothing we can do here */ - rc = write_stderr(errbuf); - (void) rc; /* ignore errors, nothing we can do here */ - } - } - - errno = save_errno; /* just in case the write changed it */ -} - -void -setup_cancel_handler(void) -{ - pqsignal(SIGINT, handle_sigint); -} -#else /* WIN32 */ - -static BOOL WINAPI -consoleHandler(DWORD dwCtrlType) -{ - char errbuf[256]; - - if (dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT) - { - /* - * Can't longjmp here, because we are in wrong thread :-( - */ - - /* set cancel flag to stop any long-running loops */ - cancel_pressed = true; - - /* and send QueryCancel if we are processing a database query */ - EnterCriticalSection(&cancelConnLock); - if (cancelConn != NULL) - { - if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) - write_stderr("Cancel request sent\n"); - else - { - write_stderr("Could not send cancel request: "); - write_stderr(errbuf); - } - } - LeaveCriticalSection(&cancelConnLock); - - return TRUE; - } - else - /* Return FALSE for any signals not being handled */ - return FALSE; -} - -void -setup_cancel_handler(void) -{ - InitializeCriticalSection(&cancelConnLock); - - SetConsoleCtrlHandler(consoleHandler, TRUE); -} -#endif /* WIN32 */ - - -/* ConnectionUp - * - * Returns whether our backend connection is still there. - */ -static bool -ConnectionUp(void) -{ - return PQstatus(pset.db) != CONNECTION_BAD; -} - - - -/* CheckConnection - * - * Verify that we still have a good connection to the backend, and if not, - * see if it can be restored. - * - * Returns true if either the connection was still there, or it could be - * restored successfully; false otherwise. If, however, there was no - * connection and the session is non-interactive, this will exit the program - * with a code of EXIT_BADCONN. - */ -static bool -CheckConnection(void) -{ - bool OK; - - OK = ConnectionUp(); - if (!OK) - { - if (!pset.cur_cmd_interactive) - { - psql_error("connection to server was lost\n"); - exit(EXIT_BADCONN); - } - - psql_error("The connection to the server was lost. Attempting reset: "); - PQreset(pset.db); - OK = ConnectionUp(); - if (!OK) - { - psql_error("Failed.\n"); - PQfinish(pset.db); - pset.db = NULL; - ResetCancelConn(); - UnsyncVariables(); - } - else - psql_error("Succeeded.\n"); - } - - return OK; -} - - - -/* - * SetCancelConn - * - * Set cancelConn to point to the current database connection. - */ -void -SetCancelConn(void) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - /* Free the old one if we have one */ - oldCancelConn = cancelConn; - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - - cancelConn = PQgetCancel(pset.db); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - - -/* - * ResetCancelConn - * - * Free the current cancel connection, if any, and set to NULL. - */ -void -ResetCancelConn(void) -{ - PGcancel *oldCancelConn; - -#ifdef WIN32 - EnterCriticalSection(&cancelConnLock); -#endif - - oldCancelConn = cancelConn; - /* be sure handle_sigint doesn't use pointer while freeing */ - cancelConn = NULL; - - if (oldCancelConn != NULL) - PQfreeCancel(oldCancelConn); - -#ifdef WIN32 - LeaveCriticalSection(&cancelConnLock); -#endif -} - - -/* - * AcceptResult - * - * Checks whether a result is valid, giving an error message if necessary; - * and ensures that the connection to the backend is still up. - * - * Returns true for valid result, false for error state. - */ -static bool -AcceptResult(const PGresult *result) -{ - bool OK; - - if (!result) - OK = false; - else - switch (PQresultStatus(result)) - { - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - case PGRES_EMPTY_QUERY: - case PGRES_COPY_IN: - case PGRES_COPY_OUT: - /* Fine, do nothing */ - OK = true; - break; - - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - OK = false; - break; - - default: - OK = false; - psql_error("unexpected PQresultStatus: %d\n", - PQresultStatus(result)); - break; - } - - if (!OK) - { - const char *error = PQerrorMessage(pset.db); - - if (strlen(error)) - psql_error("%s", error); - - CheckConnection(); - } - - return OK; -} - - - -/* - * PSQLexec - * - * This is the way to send "backdoor" queries (those not directly entered - * by the user). It is subject to -E but not -e. - * - * Caller is responsible for handling the ensuing processing if a COPY - * command is sent. - * - * Note: we don't bother to check PQclientEncoding; it is assumed that no - * caller uses this path to issue "SET CLIENT_ENCODING". - */ -PGresult * -PSQLexec(const char *query) -{ - PGresult *res; - - if (!pset.db) - { - psql_error("You are currently not connected to a database.\n"); - return NULL; - } - - if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF) - { - printf(_("********* QUERY **********\n" - "%s\n" - "**************************\n\n"), query); - fflush(stdout); - if (pset.logfile) - { - fprintf(pset.logfile, - _("********* QUERY **********\n" - "%s\n" - "**************************\n\n"), query); - fflush(pset.logfile); - } - - if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC) - return NULL; - } - - SetCancelConn(); - - res = PQexec(pset.db, query); - - ResetCancelConn(); - - if (!AcceptResult(res)) - { - PQclear(res); - res = NULL; - } - - return res; -} - - -/* - * PSQLexecWatch - * - * This function is used for \watch command to send the query to - * the server and print out the results. - * - * Returns 1 if the query executed successfully, 0 if it cannot be repeated, - * e.g., because of the interrupt, -1 on error. - */ -int -PSQLexecWatch(const char *query, const printQueryOpt *opt) -{ - PGresult *res; - double elapsed_msec = 0; - instr_time before; - instr_time after; - - if (!pset.db) - { - psql_error("You are currently not connected to a database.\n"); - return 0; - } - - SetCancelConn(); - - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - res = PQexec(pset.db, query); - - ResetCancelConn(); - - if (!AcceptResult(res)) - { - PQclear(res); - return 0; - } - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(after); - INSTR_TIME_SUBTRACT(after, before); - elapsed_msec = INSTR_TIME_GET_MILLISEC(after); - } - - /* - * If SIGINT is sent while the query is processing, the interrupt will be - * consumed. The user's intention, though, is to cancel the entire watch - * process, so detect a sent cancellation request and exit in this case. - */ - if (cancel_pressed) - { - PQclear(res); - return 0; - } - - switch (PQresultStatus(res)) - { - case PGRES_TUPLES_OK: - printQuery(res, opt, pset.queryFout, false, pset.logfile); - break; - - case PGRES_COMMAND_OK: - fprintf(pset.queryFout, "%s\n%s\n\n", opt->title, PQcmdStatus(res)); - break; - - case PGRES_EMPTY_QUERY: - psql_error(_("\\watch cannot be used with an empty query\n")); - PQclear(res); - return -1; - - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - case PGRES_COPY_BOTH: - psql_error(_("\\watch cannot be used with COPY\n")); - PQclear(res); - return -1; - - default: - psql_error(_("unexpected result status for \\watch\n")); - PQclear(res); - return -1; - } - - PQclear(res); - - fflush(pset.queryFout); - - /* Possible microtiming output */ - if (pset.timing) - printf(_("Time: %.3f ms\n"), elapsed_msec); - - return 1; -} - - -/* - * PrintNotifications: check for asynchronous notifications, and print them out - */ -static void -PrintNotifications(void) -{ - PGnotify *notify; - - while ((notify = PQnotifies(pset.db))) - { - /* for backward compatibility, only show payload if nonempty */ - if (notify->extra[0]) - fprintf(pset.queryFout, _("Asynchronous notification \"%s\" with payload \"%s\" received from server process with PID %d.\n"), - notify->relname, notify->extra, notify->be_pid); - else - fprintf(pset.queryFout, _("Asynchronous notification \"%s\" received from server process with PID %d.\n"), - notify->relname, notify->be_pid); - fflush(pset.queryFout); - PQfreemem(notify); - } -} - - -/* - * PrintQueryTuples: assuming query result is OK, print its tuples - * - * Returns true if successful, false otherwise. - */ -static bool -PrintQueryTuples(const PGresult *results) -{ - printQueryOpt my_popt = pset.popt; - - /* write output to \g argument, if any */ - if (pset.gfname) - { - FILE *fout; - bool is_pipe; - - if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe)) - return false; - if (is_pipe) - disable_sigpipe_trap(); - - printQuery(results, &my_popt, fout, false, pset.logfile); - - if (is_pipe) - { - pclose(fout); - restore_sigpipe_trap(); - } - else - fclose(fout); - } - else - printQuery(results, &my_popt, pset.queryFout, false, pset.logfile); - - return true; -} - - -/* - * StoreQueryTuple: assuming query result is OK, save data into variables - * - * Returns true if successful, false otherwise. - */ -static bool -StoreQueryTuple(const PGresult *result) -{ - bool success = true; - - if (PQntuples(result) < 1) - { - psql_error("no rows returned for \\gset\n"); - success = false; - } - else if (PQntuples(result) > 1) - { - psql_error("more than one row returned for \\gset\n"); - success = false; - } - else - { - int i; - - for (i = 0; i < PQnfields(result); i++) - { - char *colname = PQfname(result, i); - char *varname; - char *value; - - /* concate prefix and column name */ - varname = psprintf("%s%s", pset.gset_prefix, colname); - - if (!PQgetisnull(result, 0, i)) - value = PQgetvalue(result, 0, i); - else - { - /* for NULL value, unset rather than set the variable */ - value = NULL; - } - - if (!SetVariable(pset.vars, varname, value)) - { - psql_error("could not set variable \"%s\"\n", varname); - free(varname); - success = false; - break; - } - - free(varname); - } - } - - return success; -} - - -/* - * ProcessResult: utility function for use by SendQuery() only - * - * When our command string contained a COPY FROM STDIN or COPY TO STDOUT, - * PQexec() has stopped at the PGresult associated with the first such - * command. In that event, we'll marshal data for the COPY and then cycle - * through any subsequent PGresult objects. - * - * When the command string contained no such COPY command, this function - * degenerates to an AcceptResult() call. - * - * Changes its argument to point to the last PGresult of the command string, - * or NULL if that result was for a COPY TO STDOUT. (Returning NULL prevents - * the command status from being printed, which we want in that case so that - * the status line doesn't get taken as part of the COPY data.) - * - * Returns true on complete success, false otherwise. Possible failure modes - * include purely client-side problems; check the transaction status for the - * server-side opinion. - */ -static bool -ProcessResult(PGresult **results) -{ - bool success = true; - bool first_cycle = true; - uint64 copySizeUnlimited = 0; - - for (;;) - { - ExecStatusType result_status; - bool is_copy; - PGresult *next_result; - - if (!AcceptResult(*results)) - { - /* - * Failure at this point is always a server-side failure or a - * failure to submit the command string. Either way, we're - * finished with this command string. - */ - success = false; - break; - } - - result_status = PQresultStatus(*results); - switch (result_status) - { - case PGRES_EMPTY_QUERY: - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - is_copy = false; - break; - - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - is_copy = true; - break; - - default: - /* AcceptResult() should have caught anything else. */ - is_copy = false; - psql_error("unexpected PQresultStatus: %d\n", result_status); - break; - } - - if (is_copy) - { - /* - * Marshal the COPY data. Either subroutine will get the - * connection out of its COPY state, then call PQresultStatus() - * once and report any error. - * - * If pset.copyStream is set, use that as data source/sink, - * otherwise use queryFout or cur_cmd_source as appropriate. - */ - FILE *copystream = pset.copyStream; - PGresult *copy_result; - - SetCancelConn(); - if (result_status == PGRES_COPY_OUT) - { - if (!copystream) - copystream = pset.queryFout; - success = handleCopyOut(pset.db, - copystream, - ©_result) && success; - - /* - * Suppress status printing if the report would go to the same - * place as the COPY data just went. Note this doesn't - * prevent error reporting, since handleCopyOut did that. - */ - if (copystream == pset.queryFout) - { - PQclear(copy_result); - copy_result = NULL; - } - } - else - { - if (!copystream) - copystream = pset.cur_cmd_source; - success = handleCopyIn(pset.db, copystream, - PQbinaryTuples(*results), ©_result, - copySizeUnlimited) && success; - } - ResetCancelConn(); - - /* - * Replace the PGRES_COPY_OUT/IN result with COPY command's exit - * status, or with NULL if we want to suppress printing anything. - */ - PQclear(*results); - *results = copy_result; - } - else if (first_cycle) - { - /* fast path: no COPY commands; PQexec visited all results */ - break; - } - - /* - * Check PQgetResult() again. In the typical case of a single-command - * string, it will return NULL. Otherwise, we'll have other results - * to process that may include other COPYs. We keep the last result. - */ - next_result = PQgetResult(pset.db); - if (!next_result) - break; - - PQclear(*results); - *results = next_result; - first_cycle = false; - } - - /* may need this to recover from conn loss during COPY */ - if (!first_cycle && !CheckConnection()) - return false; - - return success; -} - - -/* - * PrintQueryStatus: report command status as required - * - * Note: Utility function for use by PrintQueryResults() only. - */ -static void -PrintQueryStatus(PGresult *results) -{ - char buf[16]; - - if (!pset.quiet) - { - if (pset.popt.topt.format == PRINT_HTML) - { - fputs("

", pset.queryFout); - html_escaped_print(PQcmdStatus(results), pset.queryFout); - fputs("

\n", pset.queryFout); - } - else - fprintf(pset.queryFout, "%s\n", PQcmdStatus(results)); - } - - if (pset.logfile) - fprintf(pset.logfile, "%s\n", PQcmdStatus(results)); - - snprintf(buf, sizeof(buf), "%u", (unsigned int) PQoidValue(results)); - SetVariable(pset.vars, "LASTOID", buf); -} - - -/* - * PrintQueryResults: print out (or store) query results as required - * - * Note: Utility function for use by SendQuery() only. - * - * Returns true if the query executed successfully, false otherwise. - */ -static bool -PrintQueryResults(PGresult *results) -{ - bool success; - const char *cmdstatus; - - if (!results) - return false; - - switch (PQresultStatus(results)) - { - case PGRES_TUPLES_OK: - /* store or print the data ... */ - if (pset.gset_prefix) - success = StoreQueryTuple(results); - else - success = PrintQueryTuples(results); - /* if it's INSERT/UPDATE/DELETE RETURNING, also print status */ - cmdstatus = PQcmdStatus(results); - if (strncmp(cmdstatus, "INSERT", 6) == 0 || - strncmp(cmdstatus, "UPDATE", 6) == 0 || - strncmp(cmdstatus, "DELETE", 6) == 0) - PrintQueryStatus(results); - break; - - case PGRES_COMMAND_OK: - PrintQueryStatus(results); - success = true; - break; - - case PGRES_EMPTY_QUERY: - success = true; - break; - - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - /* nothing to do here */ - success = true; - break; - - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - success = false; - break; - - default: - success = false; - psql_error("unexpected PQresultStatus: %d\n", - PQresultStatus(results)); - break; - } - - fflush(pset.queryFout); - - return success; -} - - -/* - * SendQuery: send the query string to the backend - * (and print out results) - * - * Note: This is the "front door" way to send a query. That is, use it to - * send queries actually entered by the user. These queries will be subject to - * single step mode. - * To send "back door" queries (generated by slash commands, etc.) in a - * controlled way, use PSQLexec(). - * - * Returns true if the query executed successfully, false otherwise. - */ -bool -SendQuery(const char *query) -{ - PGresult *results; - PGTransactionStatusType transaction_status; - double elapsed_msec = 0; - bool OK = false; - bool on_error_rollback_savepoint = false; - static bool on_error_rollback_warning = false; - - if (!pset.db) - { - psql_error("You are currently not connected to a database.\n"); - goto sendquery_cleanup; - } - - if (pset.singlestep) - { - char buf[3]; - - printf(_("***(Single step mode: verify command)*******************************************\n" - "%s\n" - "***(press return to proceed or enter x and return to cancel)********************\n"), - query); - fflush(stdout); - if (fgets(buf, sizeof(buf), stdin) != NULL) - if (buf[0] == 'x') - goto sendquery_cleanup; - } - else if (pset.echo == PSQL_ECHO_QUERIES) - { - puts(query); - fflush(stdout); - } - - if (pset.logfile) - { - fprintf(pset.logfile, - _("********* QUERY **********\n" - "%s\n" - "**************************\n\n"), query); - fflush(pset.logfile); - } - - SetCancelConn(); - - transaction_status = PQtransactionStatus(pset.db); - - if (transaction_status == PQTRANS_IDLE && - !pset.autocommit && - !command_no_begin(query)) - { - results = PQexec(pset.db, "BEGIN"); - if (PQresultStatus(results) != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(pset.db)); - PQclear(results); - ResetCancelConn(); - goto sendquery_cleanup; - } - PQclear(results); - transaction_status = PQtransactionStatus(pset.db); - } - - if (transaction_status == PQTRANS_INTRANS && - pset.on_error_rollback != PSQL_ERROR_ROLLBACK_OFF && - (pset.cur_cmd_interactive || - pset.on_error_rollback == PSQL_ERROR_ROLLBACK_ON)) - { - if (on_error_rollback_warning == false && pset.sversion < 80000) - { - psql_error("The server (version %d.%d) does not support savepoints for ON_ERROR_ROLLBACK.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - on_error_rollback_warning = true; - } - else - { - results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint"); - if (PQresultStatus(results) != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(pset.db)); - PQclear(results); - ResetCancelConn(); - goto sendquery_cleanup; - } - PQclear(results); - on_error_rollback_savepoint = true; - } - } - - if (pset.fetch_count <= 0 || !is_select_command(query)) - { - /* Default fetch-it-all-and-print mode */ - instr_time before, - after; - - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - results = PQexec(pset.db, query); - - /* these operations are included in the timing result: */ - ResetCancelConn(); - OK = ProcessResult(&results); - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(after); - INSTR_TIME_SUBTRACT(after, before); - elapsed_msec = INSTR_TIME_GET_MILLISEC(after); - } - - /* but printing results isn't: */ - if (OK && results) - OK = PrintQueryResults(results); - } - else - { - /* Fetch-in-segments mode */ - OK = ExecQueryUsingCursor(query, &elapsed_msec); - ResetCancelConn(); - results = NULL; /* PQclear(NULL) does nothing */ - } - - if (!OK && pset.echo == PSQL_ECHO_ERRORS) - psql_error("STATEMENT: %s\n", query); - - /* If we made a temporary savepoint, possibly release/rollback */ - if (on_error_rollback_savepoint) - { - const char *svptcmd = NULL; - - transaction_status = PQtransactionStatus(pset.db); - - switch (transaction_status) - { - case PQTRANS_INERROR: - /* We always rollback on an error */ - svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint"; - break; - - case PQTRANS_IDLE: - /* If they are no longer in a transaction, then do nothing */ - break; - - case PQTRANS_INTRANS: - - /* - * Do nothing if they are messing with savepoints themselves: - * If the user did RELEASE or ROLLBACK, our savepoint is gone. - * If they issued a SAVEPOINT, releasing ours would remove - * theirs. - */ - if (results && - (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 || - strcmp(PQcmdStatus(results), "RELEASE") == 0 || - strcmp(PQcmdStatus(results), "ROLLBACK") == 0)) - svptcmd = NULL; - else - svptcmd = "RELEASE pg_psql_temporary_savepoint"; - break; - - case PQTRANS_ACTIVE: - case PQTRANS_UNKNOWN: - default: - OK = false; - /* PQTRANS_UNKNOWN is expected given a broken connection. */ - if (transaction_status != PQTRANS_UNKNOWN || ConnectionUp()) - psql_error("unexpected transaction status (%d)\n", - transaction_status); - break; - } - - if (svptcmd) - { - PGresult *svptres; - - svptres = PQexec(pset.db, svptcmd); - if (PQresultStatus(svptres) != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(pset.db)); - PQclear(svptres); - OK = false; - - PQclear(results); - ResetCancelConn(); - goto sendquery_cleanup; - } - PQclear(svptres); - } - } - - PQclear(results); - - /* Possible microtiming output */ - if (pset.timing) - printf(_("Time: %.3f ms\n"), elapsed_msec); - - /* check for events that may occur during query execution */ - - if (pset.encoding != PQclientEncoding(pset.db) && - PQclientEncoding(pset.db) >= 0) - { - /* track effects of SET CLIENT_ENCODING */ - pset.encoding = PQclientEncoding(pset.db); - pset.popt.topt.encoding = pset.encoding; - SetVariable(pset.vars, "ENCODING", - pg_encoding_to_char(pset.encoding)); - } - - PrintNotifications(); - - /* perform cleanup that should occur after any attempted query */ - -sendquery_cleanup: - - /* reset \g's output-to-filename trigger */ - if (pset.gfname) - { - free(pset.gfname); - pset.gfname = NULL; - } - - /* reset \gset trigger */ - if (pset.gset_prefix) - { - free(pset.gset_prefix); - pset.gset_prefix = NULL; - } - - return OK; -} - - -/* - * ExecQueryUsingCursor: run a SELECT-like query using a cursor - * - * This feature allows result sets larger than RAM to be dealt with. - * - * Returns true if the query executed successfully, false otherwise. - * - * If pset.timing is on, total query time (exclusive of result-printing) is - * stored into *elapsed_msec. - */ -static bool -ExecQueryUsingCursor(const char *query, double *elapsed_msec) -{ - bool OK = true; - PGresult *results; - PQExpBufferData buf; - printQueryOpt my_popt = pset.popt; - FILE *fout; - bool is_pipe; - bool is_pager = false; - bool started_txn = false; - int ntuples; - int fetch_count; - char fetch_cmd[64]; - instr_time before, - after; - int flush_error; - - *elapsed_msec = 0; - - /* initialize print options for partial table output */ - my_popt.topt.start_table = true; - my_popt.topt.stop_table = false; - my_popt.topt.prior_records = 0; - - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - /* if we're not in a transaction, start one */ - if (PQtransactionStatus(pset.db) == PQTRANS_IDLE) - { - results = PQexec(pset.db, "BEGIN"); - OK = AcceptResult(results) && - (PQresultStatus(results) == PGRES_COMMAND_OK); - PQclear(results); - if (!OK) - return false; - started_txn = true; - } - - /* Send DECLARE CURSOR */ - initPQExpBuffer(&buf); - appendPQExpBuffer(&buf, "DECLARE _psql_cursor NO SCROLL CURSOR FOR\n%s", - query); - - results = PQexec(pset.db, buf.data); - OK = AcceptResult(results) && - (PQresultStatus(results) == PGRES_COMMAND_OK); - PQclear(results); - termPQExpBuffer(&buf); - if (!OK) - goto cleanup; - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(after); - INSTR_TIME_SUBTRACT(after, before); - *elapsed_msec += INSTR_TIME_GET_MILLISEC(after); - } - - /* - * In \gset mode, we force the fetch count to be 2, so that we will throw - * the appropriate error if the query returns more than one row. - */ - if (pset.gset_prefix) - fetch_count = 2; - else - fetch_count = pset.fetch_count; - - snprintf(fetch_cmd, sizeof(fetch_cmd), - "FETCH FORWARD %d FROM _psql_cursor", - fetch_count); - - /* prepare to write output to \g argument, if any */ - if (pset.gfname) - { - if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe)) - { - OK = false; - goto cleanup; - } - if (is_pipe) - disable_sigpipe_trap(); - } - else - { - fout = pset.queryFout; - is_pipe = false; /* doesn't matter */ - } - - /* clear any pre-existing error indication on the output stream */ - clearerr(fout); - - for (;;) - { - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - /* get fetch_count tuples at a time */ - results = PQexec(pset.db, fetch_cmd); - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(after); - INSTR_TIME_SUBTRACT(after, before); - *elapsed_msec += INSTR_TIME_GET_MILLISEC(after); - } - - if (PQresultStatus(results) != PGRES_TUPLES_OK) - { - /* shut down pager before printing error message */ - if (is_pager) - { - ClosePager(fout); - is_pager = false; - } - - OK = AcceptResult(results); - Assert(!OK); - PQclear(results); - break; - } - - if (pset.gset_prefix) - { - /* StoreQueryTuple will complain if not exactly one row */ - OK = StoreQueryTuple(results); - PQclear(results); - break; - } - - ntuples = PQntuples(results); - - if (ntuples < fetch_count) - { - /* this is the last result set, so allow footer decoration */ - my_popt.topt.stop_table = true; - } - else if (fout == stdout && !is_pager) - { - /* - * If query requires multiple result sets, hack to ensure that - * only one pager instance is used for the whole mess - */ - fout = PageOutput(INT_MAX, &(my_popt.topt)); - is_pager = true; - } - - printQuery(results, &my_popt, fout, is_pager, pset.logfile); - - PQclear(results); - - /* after the first result set, disallow header decoration */ - my_popt.topt.start_table = false; - my_popt.topt.prior_records += ntuples; - - /* - * Make sure to flush the output stream, so intermediate results are - * visible to the client immediately. We check the results because if - * the pager dies/exits/etc, there's no sense throwing more data at - * it. - */ - flush_error = fflush(fout); - - /* - * Check if we are at the end, if a cancel was pressed, or if there - * were any errors either trying to flush out the results, or more - * generally on the output stream at all. If we hit any errors - * writing things to the stream, we presume $PAGER has disappeared and - * stop bothering to pull down more data. - */ - if (ntuples < fetch_count || cancel_pressed || flush_error || - ferror(fout)) - break; - } - - if (pset.gfname) - { - /* close \g argument file/pipe */ - if (is_pipe) - { - pclose(fout); - restore_sigpipe_trap(); - } - else - fclose(fout); - } - else if (is_pager) - { - /* close transient pager */ - ClosePager(fout); - } - -cleanup: - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - /* - * We try to close the cursor on either success or failure, but on failure - * ignore the result (it's probably just a bleat about being in an aborted - * transaction) - */ - results = PQexec(pset.db, "CLOSE _psql_cursor"); - if (OK) - { - OK = AcceptResult(results) && - (PQresultStatus(results) == PGRES_COMMAND_OK); - } - PQclear(results); - - if (started_txn) - { - results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK"); - OK &= AcceptResult(results) && - (PQresultStatus(results) == PGRES_COMMAND_OK); - PQclear(results); - } - - if (pset.timing) - { - INSTR_TIME_SET_CURRENT(after); - INSTR_TIME_SUBTRACT(after, before); - *elapsed_msec += INSTR_TIME_GET_MILLISEC(after); - } - - return OK; -} - - -/* - * Advance the given char pointer over white space and SQL comments. - */ -static const char * -skip_white_space(const char *query) -{ - int cnestlevel = 0; /* slash-star comment nest level */ - - while (*query) - { - int mblen = PQmblen(query, pset.encoding); - - /* - * Note: we assume the encoding is a superset of ASCII, so that for - * example "query[0] == '/'" is meaningful. However, we do NOT assume - * that the second and subsequent bytes of a multibyte character - * couldn't look like ASCII characters; so it is critical to advance - * by mblen, not 1, whenever we haven't exactly identified the - * character we are skipping over. - */ - if (isspace((unsigned char) *query)) - query += mblen; - else if (query[0] == '/' && query[1] == '*') - { - cnestlevel++; - query += 2; - } - else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/') - { - cnestlevel--; - query += 2; - } - else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-') - { - query += 2; - - /* - * We have to skip to end of line since any slash-star inside the - * -- comment does NOT start a slash-star comment. - */ - while (*query) - { - if (*query == '\n') - { - query++; - break; - } - query += PQmblen(query, pset.encoding); - } - } - else if (cnestlevel > 0) - query += mblen; - else - break; /* found first token */ - } - - return query; -} - - -/* - * Check whether a command is one of those for which we should NOT start - * a new transaction block (ie, send a preceding BEGIN). - * - * These include the transaction control statements themselves, plus - * certain statements that the backend disallows inside transaction blocks. - */ -static bool -command_no_begin(const char *query) -{ - int wordlen; - - /* - * First we must advance over any whitespace and comments. - */ - query = skip_white_space(query); - - /* - * Check word length (since "beginx" is not "begin"). - */ - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - /* - * Transaction control commands. These should include every keyword that - * gives rise to a TransactionStmt in the backend grammar, except for the - * savepoint-related commands. - * - * (We assume that START must be START TRANSACTION, since there is - * presently no other "START foo" command.) - */ - if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0) - return true; - if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0) - return true; - if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0) - return true; - if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0) - return true; - if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0) - return true; - if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0) - return true; - if (wordlen == 7 && pg_strncasecmp(query, "prepare", 7) == 0) - { - /* PREPARE TRANSACTION is a TC command, PREPARE foo is not */ - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 11 && pg_strncasecmp(query, "transaction", 11) == 0) - return true; - return false; - } - - /* - * Commands not allowed within transactions. The statements checked for - * here should be exactly those that call PreventTransactionChain() in the - * backend. - */ - if (wordlen == 6 && pg_strncasecmp(query, "vacuum", 6) == 0) - return true; - if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0) - { - /* CLUSTER with any arguments is allowed in transactions */ - query += wordlen; - - query = skip_white_space(query); - - if (isalpha((unsigned char) query[0])) - return false; /* has additional words */ - return true; /* it's CLUSTER without arguments */ - } - - if (wordlen == 6 && pg_strncasecmp(query, "create", 6) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0) - return true; - if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0) - return true; - - /* CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts */ - if (wordlen == 6 && pg_strncasecmp(query, "unique", 6) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - } - - if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0) - return true; - } - - return false; - } - - if (wordlen == 5 && pg_strncasecmp(query, "alter", 5) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - /* ALTER SYSTEM isn't allowed in xacts */ - if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0) - return true; - - return false; - } - - /* - * Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which - * aren't really valid commands so we don't care much. The other four - * possible matches are correct. - */ - if ((wordlen == 4 && pg_strncasecmp(query, "drop", 4) == 0) || - (wordlen == 7 && pg_strncasecmp(query, "reindex", 7) == 0)) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0) - return true; - if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0) - return true; - if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0) - return true; - - /* DROP INDEX CONCURRENTLY isn't allowed in xacts */ - if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0) - return true; - - return false; - } - - return false; - } - - /* DISCARD ALL isn't allowed in xacts, but other variants are allowed. */ - if (wordlen == 7 && pg_strncasecmp(query, "discard", 7) == 0) - { - query += wordlen; - - query = skip_white_space(query); - - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 3 && pg_strncasecmp(query, "all", 3) == 0) - return true; - return false; - } - - return false; -} - - -/* - * Check whether the specified command is a SELECT (or VALUES). - */ -static bool -is_select_command(const char *query) -{ - int wordlen; - - /* - * First advance over any whitespace, comments and left parentheses. - */ - for (;;) - { - query = skip_white_space(query); - if (query[0] == '(') - query++; - else - break; - } - - /* - * Check word length (since "selectx" is not "select"). - */ - wordlen = 0; - while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); - - if (wordlen == 6 && pg_strncasecmp(query, "select", 6) == 0) - return true; - - if (wordlen == 6 && pg_strncasecmp(query, "values", 6) == 0) - return true; - - return false; -} - - -/* - * Test if the current user is a database superuser. - * - * Note: this will correctly detect superuserness only with a protocol-3.0 - * or newer backend; otherwise it will always say "false". - */ -bool -is_superuser(void) -{ - const char *val; - - if (!pset.db) - return false; - - val = PQparameterStatus(pset.db, "is_superuser"); - - if (val && strcmp(val, "on") == 0) - return true; - - return false; -} - - -/* - * Test if the current session uses standard string literals. - * - * Note: With a pre-protocol-3.0 connection this will always say "false", - * which should be the right answer. - */ -bool -standard_strings(void) -{ - const char *val; - - if (!pset.db) - return false; - - val = PQparameterStatus(pset.db, "standard_conforming_strings"); - - if (val && strcmp(val, "on") == 0) - return true; - - return false; -} - - -/* - * Return the session user of the current connection. - * - * Note: this will correctly detect the session user only with a - * protocol-3.0 or newer backend; otherwise it will return the - * connection user. - */ -const char * -session_username(void) -{ - const char *val; - - if (!pset.db) - return NULL; - - val = PQparameterStatus(pset.db, "session_authorization"); - if (val) - return val; - else - return PQuser(pset.db); -} - - -/* expand_tilde - * - * substitute '~' with HOME or '~username' with username's home dir - * - */ -void -expand_tilde(char **filename) -{ - if (!filename || !(*filename)) - return; - - /* - * WIN32 doesn't use tilde expansion for file names. Also, it uses tilde - * for short versions of long file names, though the tilde is usually - * toward the end, not at the beginning. - */ -#ifndef WIN32 - - /* try tilde expansion */ - if (**filename == '~') - { - char *fn; - char oldp, - *p; - struct passwd *pw; - char home[MAXPGPATH]; - - fn = *filename; - *home = '\0'; - - p = fn + 1; - while (*p != '/' && *p != '\0') - p++; - - oldp = *p; - *p = '\0'; - - if (*(fn + 1) == '\0') - get_home_path(home); /* ~ or ~/ only */ - else if ((pw = getpwnam(fn + 1)) != NULL) - strlcpy(home, pw->pw_dir, sizeof(home)); /* ~user */ - - *p = oldp; - if (strlen(home) != 0) - { - char *newfn; - - newfn = psprintf("%s%s", home, p); - free(fn); - *filename = newfn; - } - } -#endif - - return; -} - -/* - * Checks if connection string starts with either of the valid URI prefix - * designators. - * - * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. - * - * XXX This is a duplicate of the eponymous libpq function. - */ -static int -uri_prefix_length(const char *connstr) -{ - /* The connection URI must start with either of the following designators: */ - static const char uri_designator[] = "postgresql://"; - static const char short_uri_designator[] = "postgres://"; - - if (strncmp(connstr, uri_designator, - sizeof(uri_designator) - 1) == 0) - return sizeof(uri_designator) - 1; - - if (strncmp(connstr, short_uri_designator, - sizeof(short_uri_designator) - 1) == 0) - return sizeof(short_uri_designator) - 1; - - return 0; -} - -/* - * Recognized connection string either starts with a valid URI prefix or - * contains a "=" in it. - * - * Must be consistent with parse_connection_string: anything for which this - * returns true should at least look like it's parseable by that routine. - * - * XXX This is a duplicate of the eponymous libpq function. - */ -bool -recognized_connection_string(const char *connstr) -{ - return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; -} diff --git a/src/bin/csql/common.h b/src/bin/csql/common.h deleted file mode 100644 index 62a602632..000000000 --- a/src/bin/csql/common.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/common.h - */ -#ifndef COMMON_H -#define COMMON_H - -#include "postgres_fe.h" -#include -#include "libpq-fe.h" - -#include "print.h" - -#define atooid(x) ((Oid) strtoul((x), NULL, 10)) - -extern bool openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe); -extern bool setQFout(const char *fname); - -extern void psql_error(const char *fmt,...) pg_attribute_printf(1, 2); - -extern void NoticeProcessor(void *arg, const char *message); - -extern volatile bool sigint_interrupt_enabled; - -extern sigjmp_buf sigint_interrupt_jmp; - -extern volatile bool cancel_pressed; - -/* Note: cancel_pressed is defined in print.c, see that file for reasons */ - -extern void setup_cancel_handler(void); - -extern void SetCancelConn(void); -extern void ResetCancelConn(void); - -extern PGresult *PSQLexec(const char *query); -extern int PSQLexecWatch(const char *query, const printQueryOpt *opt); - -extern bool SendQuery(const char *query); - -extern bool is_superuser(void); -extern bool standard_strings(void); -extern const char *session_username(void); - -extern void expand_tilde(char **filename); - -extern bool recognized_connection_string(const char *connstr); - -#endif /* COMMON_H */ diff --git a/src/bin/csql/copy.c b/src/bin/csql/copy.c deleted file mode 100644 index 52a4bf8b8..000000000 --- a/src/bin/csql/copy.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/copy.c - */ -#include "postgres_fe.h" -#include "copy.h" - -#include -#include -#ifndef WIN32 -#include /* for isatty */ -#else -#include /* I think */ -#endif - -#include "libpq-fe.h" -#include "pqexpbuffer.h" -#include "dumputils.h" - -#include "settings.h" -#include "common.h" -#include "prompt.h" - - -/* - * Execute a \copy command (frontend copy). We have to open a file (or execute - * a command), then submit a COPY query to the backend and either feed it data - * from the file or route its response into the file. - */ -bool -do_copy(const char *args) -{ - copy_options *options = NULL; - PQExpBufferData query = { NULL, 0, 0 }; - FILE *copystream = NULL; - bool success = false; - bool fileClosed = false; - - /* parse options */ - options = parse_slash_copy(args); - - if (!options) - return false; - - /* open file stream to copy data into or out of */ - copystream = OpenCopyStream(options); - if (copystream == NULL) - { - free_copy_options(options); - - return false; - } - - /* build the command we will send to the backend */ - initPQExpBuffer(&query); - printfPQExpBuffer(&query, "COPY "); - appendPQExpBufferStr(&query, options->before_tofrom); - if (options->from) - appendPQExpBufferStr(&query, " FROM STDIN "); - else - appendPQExpBufferStr(&query, " TO STDOUT "); - if (options->after_tofrom) - appendPQExpBufferStr(&query, options->after_tofrom); - - /* run it like a user command, but with copystream as data source/sink */ - pset.copyStream = copystream; - success = SendQuery(query.data); - pset.copyStream = NULL; - termPQExpBuffer(&query); - - /* close file stream */ - fileClosed = CloseCopyStream(options, copystream); - if (!fileClosed) - { - success = false; - } - - free_copy_options(options); - return success; -} - - -/* - * HandleCopyData executes client-side copy data protocols by dispatching the - * call to the appropriate copy protocol function. On successful execution of - * the protocol, the function returns true. Otherwise, the function returns - * false. - * - * Please note that we refactored this function from a previous version (v9.1) - * of PostgreSQL so that copy.c and stage.c could share the same code path. Now - * that do_copy uses SendQuery(), we should move or re-refactor this function. - */ -bool -HandleCopyData(PGconn *connection, ExecStatusType copyStatus, bool copyIsBinary, - FILE *copyStream, uint64 copySizeLimit) -{ - ExecStatusType drainStatus = 0; - PGresult *drainResult = NULL; - bool copyOK = true; - - if (copyStatus == PGRES_COPY_OUT) - { - SetCancelConn(); - copyOK = handleCopyOut(connection, copyStream, &drainResult); - ResetCancelConn(); - } - else if (copyStatus == PGRES_COPY_IN) - { - SetCancelConn(); - copyOK = handleCopyIn(connection, copyStream, copyIsBinary, - &drainResult, copySizeLimit); - ResetCancelConn(); - } - else if (copyStatus == PGRES_BAD_RESPONSE || - copyStatus == PGRES_NONFATAL_ERROR || - copyStatus == PGRES_FATAL_ERROR) - { - psql_error("\\copy: %s", PQerrorMessage(connection)); - copyOK = false; - } - else - { - psql_error("\\copy: unexpected response (%d)\n", copyStatus); - copyOK = false; - } - - PQclear(drainResult); - - /* - * Make sure we drain all results from libpq. Otherwise, the connection may - * still be in ASYNC_BUSY state, leading to false readings in get_prompt(). - */ - drainResult = PQgetResult(connection); - while (drainResult != NULL) - { - copyOK = false; - - drainStatus = PQresultStatus(drainResult); - psql_error("\\copy: unexpected response (%d)\n", drainStatus); - - /* if we are still in COPY IN state, try to get out of it */ - if (drainStatus == PGRES_COPY_IN) - { - PQputCopyEnd(connection, _("trying to exit copy mode")); - } - - PQclear(drainResult); - drainResult = PQgetResult(connection); - } - - return copyOK; -} - - -/* Opens input or output stream to be used during copy command. */ -FILE * -OpenCopyStream(const copy_options *options) -{ - FILE *copyStream = NULL; - - /* prepare to read or write the target file */ - if (options->file && !options->program) - canonicalize_path(options->file); - - if (options->from) - { - if (options->file) - { - if (options->program) - { - fflush(stdout); - fflush(stderr); - errno = 0; - copyStream = popen(options->file, PG_BINARY_R); - } - else - copyStream = fopen(options->file, PG_BINARY_R); - } - else if (!options->psql_inout) - copyStream = pset.cur_cmd_source; - else - copyStream = stdin; - } - else - { - if (options->file) - { - if (options->program) - { - fflush(stdout); - fflush(stderr); - errno = 0; -#ifndef WIN32 - pqsignal(SIGPIPE, SIG_IGN); -#endif - copyStream = popen(options->file, PG_BINARY_W); - } - else - copyStream = fopen(options->file, PG_BINARY_W); - } - else if (!options->psql_inout) - copyStream = pset.queryFout; - else - copyStream = stdout; - } - - if (!copyStream) - { - if (options->program) - psql_error("could not execute command \"%s\": %s\n", - options->file, strerror(errno)); - else - psql_error("%s: %s\n", - options->file, strerror(errno)); - - return NULL; - } - - if (!options->program) - { - struct stat st; - int result; - - /* make sure the specified file is not a directory */ - if ((result = fstat(fileno(copyStream), &st)) < 0) - psql_error("could not stat file \"%s\": %s\n", - options->file, strerror(errno)); - - if (result == 0 && S_ISDIR(st.st_mode)) - psql_error("%s: cannot copy from/to a directory\n", - options->file); - - if (result < 0 || S_ISDIR(st.st_mode)) - { - fclose(copyStream); - - return NULL; - } - } - - return copyStream; -} - - -/* Closes file stream used during copy command, if any. */ -bool -CloseCopyStream(const copy_options *options, FILE *copyStream) -{ - bool success = true; - - if (options->file != NULL) - { - if (options->program) - { - int pclose_rc = pclose(copyStream); - - if (pclose_rc != 0) - { - if (pclose_rc < 0) - psql_error("could not close pipe to external command: %s\n", - strerror(errno)); - else - { - char *reason = wait_result_to_str(pclose_rc); - - psql_error("%s: %s\n", options->file, - reason ? reason : ""); - if (reason) - free(reason); - } - success = false; - } -#ifndef WIN32 - pqsignal(SIGPIPE, SIG_DFL); -#endif - } - else - { - if (fclose(copyStream) != 0) - { - psql_error("%s: %s\n", options->file, strerror(errno)); - success = false; - } - } - } - - return success; -} - - -/* - * Functions for handling COPY IN/OUT data transfer. - * - * If you want to use COPY TO STDOUT/FROM STDIN in your application, - * this is the code to steal ;) - */ - -/* - * handleCopyOut - * receives data as a result of a COPY ... TO STDOUT command - * - * conn should be a database connection that you just issued COPY TO on - * and got back a PGRES_COPY_OUT result. - * copystream is the file stream for the data to go to. - * The final status for the COPY is returned into *res (but note - * we already reported the error, if it's not a success result). - * - * result is true if successful, false if not. - */ -bool -handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res) -{ - bool OK = true; - char *buf; - int ret; - - for (;;) - { - ret = PQgetCopyData(conn, &buf, 0); - - if (ret < 0) - break; /* done or server/connection error */ - - if (buf) - { - if (OK && fwrite(buf, 1, ret, copystream) != ret) - { - psql_error("could not write COPY data: %s\n", - strerror(errno)); - /* complain only once, keep reading data from server */ - OK = false; - } - PQfreemem(buf); - } - } - - if (OK && fflush(copystream)) - { - psql_error("could not write COPY data: %s\n", - strerror(errno)); - OK = false; - } - - if (ret == -2) - { - psql_error("COPY data transfer failed: %s", PQerrorMessage(conn)); - OK = false; - } - - /* - * Check command status and return to normal libpq state. - * - * If for some reason libpq is still reporting PGRES_COPY_OUT state, we - * would like to forcibly exit that state, since our caller would be - * unable to distinguish that situation from reaching the next COPY in a - * command string that happened to contain two consecutive COPY TO STDOUT - * commands. However, libpq provides no API for doing that, and in - * principle it's a libpq bug anyway if PQgetCopyData() returns -1 or -2 - * but hasn't exited COPY_OUT state internally. So we ignore the - * possibility here. - */ - *res = PQgetResult(conn); - if (PQresultStatus(*res) != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(conn)); - OK = false; - } - - return OK; -} - -/* - * handleCopyIn - * sends data to complete a COPY ... FROM STDIN command - * - * conn should be a database connection that you just issued COPY FROM on - * and got back a PGRES_COPY_IN result. - * copystream is the file stream to read the data from. - * isbinary can be set from PQbinaryTuples(). - * The final status for the COPY is returned into *res (but note - * we already reported the error, if it's not a success result). - * - * result is true if successful, false if not. - */ - -/* read chunk size for COPY IN - size set to double that of Hadoop's default */ -#define COPYBUFSIZ 32768 - -bool -handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, - PGresult **res, uint64 copySizeLimit) -{ - bool OK; - const char *prompt; - char buf[COPYBUFSIZ]; - uint64 bytesCopied = 0; - - /* - * Establish longjmp destination for exiting from wait-for-input. (This is - * only effective while sigint_interrupt_enabled is TRUE.) - */ - if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) - { - /* got here with longjmp */ - - /* Terminate data transfer */ - PQputCopyEnd(conn, - (PQprotocolVersion(conn) < 3) ? NULL : - _("canceled by user")); - - OK = false; - goto copyin_cleanup; - } - - /* Prompt if interactive input */ - if (isatty(fileno(copystream))) - { - if (!pset.quiet) - puts(_("Enter data to be copied followed by a newline.\n" - "End with a backslash and a period on a line by itself.")); - prompt = get_prompt(PROMPT_COPY); - } - else - prompt = NULL; - - OK = true; - - if (isbinary) - { - /* interactive input probably silly, but give one prompt anyway */ - if (prompt) - { - fputs(prompt, stdout); - fflush(stdout); - } - - for (;;) - { - int buflen; - - /* enable longjmp while waiting for input */ - sigint_interrupt_enabled = true; - - buflen = fread(buf, 1, COPYBUFSIZ, copystream); - - sigint_interrupt_enabled = false; - - if (buflen <= 0) - break; - - if (PQputCopyData(conn, buf, buflen) <= 0) - { - OK = false; - break; - } - - /* if size limit is set, copy at most that many bytes*/ - bytesCopied += buflen; - if (copySizeLimit > 0 && bytesCopied >= copySizeLimit) - { - break; - } - } - } - else - { - bool copydone = false; - - while (!copydone) - { /* for each input line ... */ - bool firstload; - bool linedone; - - if (prompt) - { - fputs(prompt, stdout); - fflush(stdout); - } - - firstload = true; - linedone = false; - - while (!linedone) - { /* for each bufferload in line ... */ - int linelen = 0; - char *fgresult; - - /* enable longjmp while waiting for input */ - sigint_interrupt_enabled = true; - - fgresult = fgets(buf, sizeof(buf), copystream); - - sigint_interrupt_enabled = false; - - if (!fgresult) - { - copydone = true; - break; - } - - linelen = strlen(buf); - - /* current line is done? */ - if (linelen > 0 && buf[linelen - 1] == '\n') - linedone = true; - - /* check for EOF marker, but not on a partial line */ - if (firstload) - { - /* - * This code erroneously assumes '\.' on a line alone - * inside a quoted CSV string terminates the \copy. - * http://www.postgresql.org/message-id/E1TdNVQ-0001ju-GO@w - * rigleys.postgresql.org - */ - if (strcmp(buf, "\\.\n") == 0 || - strcmp(buf, "\\.\r\n") == 0) - { - copydone = true; - break; - } - - firstload = false; - } - - if (PQputCopyData(conn, buf, linelen) <= 0) - { - OK = false; - copydone = true; - break; - } - else - { - bytesCopied += linelen; - } - } - - if (copystream == pset.cur_cmd_source) - pset.lineno++; - - /* if size limit is set, copy at most that many bytes */ - if (copySizeLimit > 0 && bytesCopied >= copySizeLimit) - { - break; - } - } - } - - /* Check for read error */ - if (ferror(copystream)) - OK = false; - - /* - * Terminate data transfer. We can't send an error message if we're using - * protocol version 2. - */ - if (PQputCopyEnd(conn, - (OK || PQprotocolVersion(conn) < 3) ? NULL : - _("aborted because of read failure")) <= 0) - OK = false; - -copyin_cleanup: - - /* - * Check command status and return to normal libpq state. - * - * We do not want to return with the status still PGRES_COPY_IN: our - * caller would be unable to distinguish that situation from reaching the - * next COPY in a command string that happened to contain two consecutive - * COPY FROM STDIN commands. We keep trying PQputCopyEnd() in the hope - * it'll work eventually. (What's actually likely to happen is that in - * attempting to flush the data, libpq will eventually realize that the - * connection is lost. But that's fine; it will get us out of COPY_IN - * state, which is what we need.) - */ - while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN) - { - OK = false; - PQclear(*res); - /* We can't send an error message if we're using protocol version 2 */ - PQputCopyEnd(conn, - (PQprotocolVersion(conn) < 3) ? NULL : - _("trying to exit copy mode")); - } - if (PQresultStatus(*res) != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(conn)); - OK = false; - } - - return OK; -} diff --git a/src/bin/csql/copy.h b/src/bin/csql/copy.h deleted file mode 100644 index d127e61fb..000000000 --- a/src/bin/csql/copy.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/copy.h - */ -#ifndef COPY_H -#define COPY_H - -#include "libpq-fe.h" - -#include "copy_options.h" -#include "pqexpbuffer.h" - - -/* handler for \copy */ -extern bool do_copy(const char *args); - -/* lower level processors for copy in/out streams */ - -extern bool handleCopyOut(PGconn *conn, FILE *copystream, - PGresult **res); -extern bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, - PGresult **res, uint64 copySizeLimit); - -/* Function declarations shared between copy and stage commands */ -bool HandleCopyData(PGconn *connection, ExecStatusType copyStatus, - bool copyIsBinary, FILE *copyStream, uint64 copySizeLimit); -FILE * OpenCopyStream(const copy_options *options); -bool CloseCopyStream(const copy_options *options, FILE *copyStream); - -#endif diff --git a/src/bin/csql/copy_options.c b/src/bin/csql/copy_options.c deleted file mode 100644 index 5d54b3f92..000000000 --- a/src/bin/csql/copy_options.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * csql - the Citus interactive terminal - * copy_options.c - * Routines for parsing copy and stage meta commands. - * - * Copyright (c) 2012-2016, Citus Data, Inc. - * - * $Id$ - */ - -#include "postgres_fe.h" -#include "copy_options.h" - -#include "common.h" -#include "settings.h" -#include "stringutils.h" - - -/* *INDENT-OFF* */ -void -free_copy_options(copy_options * ptr) -{ - if (!ptr) - return; - free(ptr->before_tofrom); - free(ptr->after_tofrom); - free(ptr->file); - free(ptr->tableName); - free(ptr->columnList); - free(ptr); -} - - -/* concatenate "more" onto "var", freeing the original value of *var */ -static void -xstrcat(char **var, const char *more) -{ - char *newvar; - - newvar = psprintf("%s%s", *var, more); - free(*var); - *var = newvar; -} - - -/* - * parse_slash_copy parses copy options from the given meta-command line. The - * function then returns a dynamically allocated structure with the options, or - * Null on parsing error. - */ -copy_options * -parse_slash_copy(const char *args) -{ - struct copy_options *result; - char *token; - const char *whitespace = " \t\n\r"; - char nonstd_backslash = standard_strings() ? 0 : '\\'; - - if (!args) - { - psql_error("\\copy: arguments required\n"); - return NULL; - } - - result = pg_malloc0(sizeof(struct copy_options)); - - result->before_tofrom = pg_strdup(""); /* initialize for appending */ - - token = strtokx(args, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - - /* The following can be removed when we drop 7.3 syntax support */ - if (pg_strcasecmp(token, "binary") == 0) - { - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - } - - /* Handle COPY (SELECT) case */ - if (token[0] == '(') - { - int parens = 1; - - while (parens > 0) - { - xstrcat(&result->before_tofrom, " "); - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, "()", "\"'", - nonstd_backslash, true, false, pset.encoding); - if (!token) - goto error; - if (token[0] == '(') - parens++; - else if (token[0] == ')') - parens--; - } - } - - xstrcat(&result->before_tofrom, " "); - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - - /* - * strtokx() will not have returned a multi-character token starting with - * '.', so we don't need strcmp() here. Likewise for '(', etc, below. - */ - if (token[0] == '.') - { - /* handle schema . table */ - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - } - - if (token[0] == '(') - { - /* handle parenthesized column list */ - for (;;) - { - xstrcat(&result->before_tofrom, " "); - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, "()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - if (token[0] == ')') - break; - } - xstrcat(&result->before_tofrom, " "); - xstrcat(&result->before_tofrom, token); - token = strtokx(NULL, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - if (!token) - goto error; - } - - if (pg_strcasecmp(token, "from") == 0) - result->from = true; - else if (pg_strcasecmp(token, "to") == 0) - result->from = false; - else - goto error; - - /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */ - token = strtokx(NULL, whitespace, ";", "'", - 0, false, false, pset.encoding); - if (!token) - goto error; - - if (pg_strcasecmp(token, "program") == 0) - { - int toklen; - - token = strtokx(NULL, whitespace, ";", "'", - 0, false, false, pset.encoding); - if (!token) - goto error; - - /* - * The shell command must be quoted. This isn't fool-proof, but - * catches most quoting errors. - */ - toklen = strlen(token); - if (token[0] != '\'' || toklen < 2 || token[toklen - 1] != '\'') - goto error; - - strip_quotes(token, '\'', 0, pset.encoding); - - result->program = true; - result->file = pg_strdup(token); - } - else if (pg_strcasecmp(token, "stdin") == 0 || - pg_strcasecmp(token, "stdout") == 0) - { - result->file = NULL; - } - else if (pg_strcasecmp(token, "pstdin") == 0 || - pg_strcasecmp(token, "pstdout") == 0) - { - result->psql_inout = true; - result->file = NULL; - } - else - { - /* filename can be optionally quoted */ - strip_quotes(token, '\'', 0, pset.encoding); - result->file = pg_strdup(token); - expand_tilde(&result->file); - } - - /* Collect the rest of the line (COPY options) */ - token = strtokx(NULL, "", NULL, NULL, - 0, false, false, pset.encoding); - if (token) - result->after_tofrom = pg_strdup(token); - - /* set data staging options to null */ - result->tableName = NULL; - result->columnList = NULL; - - return result; - -error: - if (token) - psql_error("\\copy: parse error at \"%s\"\n", token); - else - psql_error("\\copy: parse error at end of line\n"); - free_copy_options(result); - - return NULL; -} - -/* *INDENT-ON* */ - -/* Frees copy options. */ - -/* - * ParseStageOptions takes the given copy options, parses the additional options - * needed for the \stage command, and sets them in the copy options structure. - * The additional parsed options are the table name and the column list. - */ -copy_options * -ParseStageOptions(copy_options *copyOptions) -{ - copy_options *stageOptions = NULL; - const char *whitespace = " \t\n\r"; - char *tableName = NULL; - char *columnList = NULL; - char *token = NULL; - - const char *beforeToFrom = copyOptions->before_tofrom; - Assert(beforeToFrom != NULL); - - token = strtokx(beforeToFrom, whitespace, ".,()", "\"", - 0, false, false, pset.encoding); - - /* - * We should have errored out earlier if the token were null. Similarly, we - * should have errored out on the "\stage (select) to" case. - */ - Assert(token != NULL); - Assert(token[0] != '('); - - /* we do not support PostgreSQL's 7.3 syntax */ - if (pg_strcasecmp(token, "binary") == 0) - { - psql_error("\\stage: binary keyword before to/from is not supported\n"); - Assert(false); - } - - /* init table name and append either the table name or schema name */ - tableName = pg_strdup(""); - xstrcat(&tableName, token); - - /* check for the schema.table use case */ - token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); - - if (token != NULL && token[0] == '.') - { - /* append the dot token */ - xstrcat(&tableName, token); - - token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); - Assert(token != NULL); - - /* append the table name token */ - xstrcat(&tableName, token); - - token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); - } - - /* check for the column list use case */ - if (token != NULL && token[0] == '(') - { - /* init column list, and add columns */ - columnList = pg_strdup(""); - for (;;) - { - xstrcat(&columnList, " "); - xstrcat(&columnList, token); - - token = strtokx(NULL, whitespace, "()", "\"", 0, false, false, pset.encoding); - Assert(token != NULL); - - if (token[0] == ')') - { - break; - } - } - xstrcat(&columnList, " "); - xstrcat(&columnList, token); - } - - /* finally set additional stage options */ - stageOptions = copyOptions; - stageOptions->tableName = tableName; - stageOptions->columnList = columnList; - - return stageOptions; -} diff --git a/src/bin/csql/copy_options.h b/src/bin/csql/copy_options.h deleted file mode 100644 index 4873df79f..000000000 --- a/src/bin/csql/copy_options.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * csql - the Citus interactive terminal - * copy_options.h - * Shared declarations for parsing copy and stage meta-commands. The stage - * meta-command borrows from copy's syntax, but does not yet support - * outputting table data to a file. Further, the stage command reuses copy's - * declarations to maintain compatibility with the copy command. - * - * Copyright (c) 2012-2016, Citus Data, Inc. - * - * $Id$ - */ - -#ifndef COPY_OPTIONS_H -#define COPY_OPTIONS_H - -#include "libpq-fe.h" - - -/* - * The documented syntax is: - * \copy tablename [(columnlist)] from|to filename [options] - * \copy ( select stmt ) to filename [options] - * - * where 'filename' can be one of the following: - * '' | PROGRAM '' | stdin | stdout | pstdout | pstdout - * - * An undocumented fact is that you can still write BINARY before the - * tablename; this is a hangover from the pre-7.3 syntax. The options - * syntax varies across backend versions, but we avoid all that mess - * by just transmitting the stuff after the filename literally. - * - * table name can be double-quoted and can have a schema part. - * column names can be double-quoted. - * filename can be single-quoted like SQL literals. - * command must be single-quoted like SQL literals. - * - * returns a malloc'ed structure with the options, or NULL on parsing error - */ -typedef struct copy_options -{ - char *before_tofrom; /* COPY string before TO/FROM */ - char *after_tofrom; /* COPY string after TO/FROM filename */ - char *file; /* NULL = stdin/stdout */ - bool program; /* is 'file' a program to popen? */ - bool psql_inout; /* true = use psql stdin/stdout */ - bool from; /* true = FROM, false = TO */ - - char *tableName; /* table name to stage data to */ - char *columnList; /* optional column list used in staging */ -} copy_options; - - -/* Function declarations for parsing and freeing copy options */ -copy_options * parse_slash_copy(const char *args); -void free_copy_options(copy_options * ptr); -copy_options * ParseStageOptions(copy_options *copyOptions); - - -#endif /* COPY_OPTIONS_H */ diff --git a/src/bin/csql/create_help.pl b/src/bin/csql/create_help.pl deleted file mode 100644 index bbebe522f..000000000 --- a/src/bin/csql/create_help.pl +++ /dev/null @@ -1,214 +0,0 @@ -#! /usr/bin/perl -w - -################################################################# -# create_help.pl -- converts SGML docs to internal psql help -# -# Copyright (c) 2000-2015, PostgreSQL Global Development Group -# -# src/bin/psql/create_help.pl -################################################################# - -# -# This script automatically generates the help on SQL in psql from -# the SGML docs. So far the format of the docs was consistent -# enough that this worked, but this here is by no means an SGML -# parser. -# -# Call: perl create_help.pl docdir sql_help -# The name of the header file doesn't matter to this script, but it -# sure does matter to the rest of the source. -# - -use strict; - -my $docdir = $ARGV[0] or die "$0: missing required argument: docdir\n"; -my $hfile = $ARGV[1] . '.h' - or die "$0: missing required argument: output file\n"; -my $cfile = $ARGV[1] . '.c'; - -my $hfilebasename; -if ($hfile =~ m!.*/([^/]+)$!) -{ - $hfilebasename = $1; -} -else -{ - $hfilebasename = $hfile; -} - -my $define = $hfilebasename; -$define =~ tr/a-z/A-Z/; -$define =~ s/\W/_/g; - -opendir(DIR, $docdir) - or die "$0: could not open documentation source dir '$docdir': $!\n"; -open(HFILE, ">$hfile") - or die "$0: could not open output file '$hfile': $!\n"; -open(CFILE, ">$cfile") - or die "$0: could not open output file '$cfile': $!\n"; - -print HFILE "/* - * *** Do not change this file by hand. It is automatically - * *** generated from the DocBook documentation. - * - * generated by - * $^X $0 @ARGV - * - */ - -#ifndef $define -#define $define - -#define N_(x) (x) /* gettext noop */ - -#include \"postgres_fe.h\" -#include \"pqexpbuffer.h\" - -struct _helpStruct -{ - const char *cmd; /* the command name */ - const char *help; /* the help associated with it */ - void (*syntaxfunc)(PQExpBuffer); /* function that prints the syntax associated with it */ - int nl_count; /* number of newlines in syntax (for pager) */ -}; - -"; - -print CFILE "/* - * *** Do not change this file by hand. It is automatically - * *** generated from the DocBook documentation. - * - * generated by - * $^X $0 @ARGV - * - */ - -#include \"$hfile\" - -"; - -my $maxlen = 0; - -my %entries; - -foreach my $file (sort readdir DIR) -{ - my (@cmdnames, $cmddesc, $cmdsynopsis); - $file =~ /\.sgml$/ or next; - - open(FILE, "$docdir/$file") or next; - my $filecontent = join('', ); - close FILE; - - # Ignore files that are not for SQL language statements - $filecontent =~ - m!\s*SQL - Language Statements\s*!i - or next; - - # Collect multiple refnames - LOOP: - { - $filecontent =~ m!\G.*?\s*([a-z ]+?)\s*!cgis - and push @cmdnames, $1 - and redo LOOP; - } - $filecontent =~ m!\s*(.+?)\s*!is - and $cmddesc = $1; - $filecontent =~ m!\s*(.+?)\s*!is - and $cmdsynopsis = $1; - - if (@cmdnames && $cmddesc && $cmdsynopsis) - { - s/\"/\\"/g foreach @cmdnames; - - $cmddesc =~ s/<[^>]+>//g; - $cmddesc =~ s/\s+/ /g; - $cmddesc =~ s/\"/\\"/g; - - my @params = (); - - my $nl_count = () = $cmdsynopsis =~ /\n/g; - - $cmdsynopsis =~ m!! - and die "$0:$file: null end tag not supported in synopsis\n"; - $cmdsynopsis =~ s/%/%%/g; - - while ($cmdsynopsis =~ m!<(\w+)[^>]*>(.+?)]*>!) - { - my $match = $2; - $match =~ s/<[^>]+>//g; - $match =~ s/%%/%/g; - push @params, $match; - $cmdsynopsis =~ s!<(\w+)[^>]*>.+?]*>!%s!; - } - $cmdsynopsis =~ s/\r?\n/\\n/g; - $cmdsynopsis =~ s/\"/\\"/g; - - foreach my $cmdname (@cmdnames) - { - $entries{$cmdname} = { - cmddesc => $cmddesc, - cmdsynopsis => $cmdsynopsis, - params => \@params, - nl_count => $nl_count }; - $maxlen = - ($maxlen >= length $cmdname) ? $maxlen : length $cmdname; - } - } - else - { - die "$0: parsing file '$file' failed (N='@cmdnames' D='$cmddesc')\n"; - } -} - -foreach (sort keys %entries) -{ - my $prefix = "\t" x 5 . ' '; - my $id = $_; - $id =~ s/ /_/g; - my $synopsis = "\"$entries{$_}{cmdsynopsis}\""; - $synopsis =~ s/\\n/\\n"\n$prefix"/g; - my @args = - ("buf", $synopsis, map("_(\"$_\")", @{ $entries{$_}{params} })); - print HFILE "extern void sql_help_$id(PQExpBuffer buf);\n"; - print CFILE "void -sql_help_$id(PQExpBuffer buf) -{ -\tappendPQExpBuffer(" . join(",\n$prefix", @args) . "); -} - -"; -} - -print HFILE " - -static const struct _helpStruct QL_HELP[] = { -"; -foreach (sort keys %entries) -{ - my $id = $_; - $id =~ s/ /_/g; - print HFILE " { \"$_\", - N_(\"$entries{$_}{cmddesc}\"), - sql_help_$id, - $entries{$_}{nl_count} }, - -"; -} - -print HFILE " - { NULL, NULL, NULL } /* End of list marker */ -}; - - -#define QL_HELP_COUNT " - . scalar(keys %entries) . " /* number of help items */ -#define QL_MAX_CMD_LEN $maxlen /* largest strlen(cmd) */ - - -#endif /* $define */ -"; - -close CFILE; -close HFILE; -closedir DIR; diff --git a/src/bin/csql/describe.c b/src/bin/csql/describe.c deleted file mode 100644 index bb59bc236..000000000 --- a/src/bin/csql/describe.c +++ /dev/null @@ -1,4613 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Support for the various \d ("describe") commands. Note that the current - * expectation is that all functions in this file will succeed when working - * with servers of versions 7.4 and up. It's okay to omit irrelevant - * information for an old server, but not to fail outright. - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/describe.c - */ -#include "postgres_fe.h" - -#include - -#include "catalog/pg_default_acl.h" - -#include "common.h" -#include "describe.h" -#include "dumputils.h" -#include "mbprint.h" -#include "print.h" -#include "settings.h" -#include "variables.h" - - -static bool describeOneTableDetails(const char *schemaname, - const char *relationname, - const char *oid, - bool verbose); -static void add_tablespace_footer(printTableContent *const cont, char relkind, - Oid tablespace, const bool newline); -static void add_role_attribute(PQExpBuffer buf, const char *const str); -static bool listTSParsersVerbose(const char *pattern); -static bool describeOneTSParser(const char *oid, const char *nspname, - const char *prsname); -static bool listTSConfigsVerbose(const char *pattern); -static bool describeOneTSConfig(const char *oid, const char *nspname, - const char *cfgname, - const char *pnspname, const char *prsname); -static void printACLColumn(PQExpBuffer buf, const char *colname); -static bool listOneExtensionContents(const char *extname, const char *oid); - - -/*---------------- - * Handlers for various slash commands displaying some sort of list - * of things in the database. - * - * Note: try to format the queries to look nice in -E output. - *---------------- - */ - - -/* \da - * Takes an optional regexp to select particular aggregates - */ -bool -describeAggregates(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " p.proname AS \"%s\",\n" - " pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Result data type")); - - if (pset.sversion >= 80400) - appendPQExpBuffer(&buf, - " CASE WHEN p.pronargs = 0\n" - " THEN CAST('*' AS pg_catalog.text)\n" - " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n" - " END AS \"%s\",\n", - gettext_noop("Argument data types")); - else if (pset.sversion >= 80200) - appendPQExpBuffer(&buf, - " CASE WHEN p.pronargs = 0\n" - " THEN CAST('*' AS pg_catalog.text)\n" - " ELSE\n" - " pg_catalog.array_to_string(ARRAY(\n" - " SELECT\n" - " pg_catalog.format_type(p.proargtypes[s.i], NULL)\n" - " FROM\n" - " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n" - " ), ', ')\n" - " END AS \"%s\",\n", - gettext_noop("Argument data types")); - else - appendPQExpBuffer(&buf, - " pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n", - gettext_noop("Argument data types")); - - appendPQExpBuffer(&buf, - " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n" - "FROM pg_catalog.pg_proc p\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n" - "WHERE p.proisagg\n", - gettext_noop("Description")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "p.proname", NULL, - "pg_catalog.pg_function_is_visible(p.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of aggregate functions"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* \db - * Takes an optional regexp to select particular tablespaces - */ -bool -describeTablespaces(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80000) - { - psql_error("The server (version %d.%d) does not support tablespaces.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - - if (pset.sversion >= 90200) - printfPQExpBuffer(&buf, - "SELECT spcname AS \"%s\",\n" - " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n" - " pg_catalog.pg_tablespace_location(oid) AS \"%s\"", - gettext_noop("Name"), - gettext_noop("Owner"), - gettext_noop("Location")); - else - printfPQExpBuffer(&buf, - "SELECT spcname AS \"%s\",\n" - " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n" - " spclocation AS \"%s\"", - gettext_noop("Name"), - gettext_noop("Owner"), - gettext_noop("Location")); - - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n "); - printACLColumn(&buf, "spcacl"); - } - - if (verbose && pset.sversion >= 90000) - appendPQExpBuffer(&buf, - ",\n spcoptions AS \"%s\"", - gettext_noop("Options")); - - if (verbose && pset.sversion >= 90200) - appendPQExpBuffer(&buf, - ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\"", - gettext_noop("Size")); - - if (verbose && pset.sversion >= 80200) - appendPQExpBuffer(&buf, - ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_tablespace\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "spcname", NULL, - NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of tablespaces"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* \df - * Takes an optional regexp to select particular functions. - * - * As with \d, you can specify the kinds of functions you want: - * - * a for aggregates - * n for normal - * t for trigger - * w for window - * - * and you can mix and match these in any order. - */ -bool -describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem) -{ - bool showAggregate = strchr(functypes, 'a') != NULL; - bool showNormal = strchr(functypes, 'n') != NULL; - bool showTrigger = strchr(functypes, 't') != NULL; - bool showWindow = strchr(functypes, 'w') != NULL; - bool have_where; - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, true, true, true, false, false, false, false}; - - if (strlen(functypes) != strspn(functypes, "antwS+")) - { - psql_error("\\df only takes [antwS+] as options\n"); - return true; - } - - if (showWindow && pset.sversion < 80400) - { - psql_error("\\df does not take a \"w\" option with server version %d.%d\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - if (!showAggregate && !showNormal && !showTrigger && !showWindow) - { - showAggregate = showNormal = showTrigger = true; - if (pset.sversion >= 80400) - showWindow = true; - } - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " p.proname as \"%s\",\n", - gettext_noop("Schema"), - gettext_noop("Name")); - - if (pset.sversion >= 80400) - appendPQExpBuffer(&buf, - " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n" - " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n" - " CASE\n" - " WHEN p.proisagg THEN '%s'\n" - " WHEN p.proiswindow THEN '%s'\n" - " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n" - " ELSE '%s'\n" - " END as \"%s\"", - gettext_noop("Result data type"), - gettext_noop("Argument data types"), - /* translator: "agg" is short for "aggregate" */ - gettext_noop("agg"), - gettext_noop("window"), - gettext_noop("trigger"), - gettext_noop("normal"), - gettext_noop("Type")); - else if (pset.sversion >= 80100) - appendPQExpBuffer(&buf, - " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" - " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n" - " CASE WHEN proallargtypes IS NOT NULL THEN\n" - " pg_catalog.array_to_string(ARRAY(\n" - " SELECT\n" - " CASE\n" - " WHEN p.proargmodes[s.i] = 'i' THEN ''\n" - " WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n" - " WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n" - " WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n" - " END ||\n" - " CASE\n" - " WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n" - " ELSE p.proargnames[s.i] || ' ' \n" - " END ||\n" - " pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n" - " FROM\n" - " pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n" - " ), ', ')\n" - " ELSE\n" - " pg_catalog.array_to_string(ARRAY(\n" - " SELECT\n" - " CASE\n" - " WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n" - " ELSE p.proargnames[s.i+1] || ' '\n" - " END ||\n" - " pg_catalog.format_type(p.proargtypes[s.i], NULL)\n" - " FROM\n" - " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n" - " ), ', ')\n" - " END AS \"%s\",\n" - " CASE\n" - " WHEN p.proisagg THEN '%s'\n" - " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n" - " ELSE '%s'\n" - " END AS \"%s\"", - gettext_noop("Result data type"), - gettext_noop("Argument data types"), - /* translator: "agg" is short for "aggregate" */ - gettext_noop("agg"), - gettext_noop("trigger"), - gettext_noop("normal"), - gettext_noop("Type")); - else - appendPQExpBuffer(&buf, - " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" - " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n" - " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n" - " CASE\n" - " WHEN p.proisagg THEN '%s'\n" - " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n" - " ELSE '%s'\n" - " END AS \"%s\"", - gettext_noop("Result data type"), - gettext_noop("Argument data types"), - /* translator: "agg" is short for "aggregate" */ - gettext_noop("agg"), - gettext_noop("trigger"), - gettext_noop("normal"), - gettext_noop("Type")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\"" - ",\n CASE\n" - " WHEN p.provolatile = 'i' THEN '%s'\n" - " WHEN p.provolatile = 's' THEN '%s'\n" - " WHEN p.provolatile = 'v' THEN '%s'\n" - " END as \"%s\"" - ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\",\n" - " l.lanname as \"%s\",\n" - " p.prosrc as \"%s\",\n" - " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"", - gettext_noop("definer"), - gettext_noop("invoker"), - gettext_noop("Security"), - gettext_noop("immutable"), - gettext_noop("stable"), - gettext_noop("volatile"), - gettext_noop("Volatility"), - gettext_noop("Owner"), - gettext_noop("Language"), - gettext_noop("Source code"), - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_proc p" - "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"); - - if (verbose) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n"); - - have_where = false; - - /* filter by function type, if requested */ - if (showNormal && showAggregate && showTrigger && showWindow) - /* Do nothing */ ; - else if (showNormal) - { - if (!showAggregate) - { - if (have_where) - appendPQExpBufferStr(&buf, " AND "); - else - { - appendPQExpBufferStr(&buf, "WHERE "); - have_where = true; - } - appendPQExpBufferStr(&buf, "NOT p.proisagg\n"); - } - if (!showTrigger) - { - if (have_where) - appendPQExpBufferStr(&buf, " AND "); - else - { - appendPQExpBufferStr(&buf, "WHERE "); - have_where = true; - } - appendPQExpBufferStr(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n"); - } - if (!showWindow && pset.sversion >= 80400) - { - if (have_where) - appendPQExpBufferStr(&buf, " AND "); - else - { - appendPQExpBufferStr(&buf, "WHERE "); - have_where = true; - } - appendPQExpBufferStr(&buf, "NOT p.proiswindow\n"); - } - } - else - { - bool needs_or = false; - - appendPQExpBufferStr(&buf, "WHERE (\n "); - have_where = true; - /* Note: at least one of these must be true ... */ - if (showAggregate) - { - appendPQExpBufferStr(&buf, "p.proisagg\n"); - needs_or = true; - } - if (showTrigger) - { - if (needs_or) - appendPQExpBufferStr(&buf, " OR "); - appendPQExpBufferStr(&buf, - "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n"); - needs_or = true; - } - if (showWindow) - { - if (needs_or) - appendPQExpBufferStr(&buf, " OR "); - appendPQExpBufferStr(&buf, "p.proiswindow\n"); - needs_or = true; - } - appendPQExpBufferStr(&buf, " )\n"); - } - - processSQLNamePattern(pset.db, &buf, pattern, have_where, false, - "n.nspname", "p.proname", NULL, - "pg_catalog.pg_function_is_visible(p.oid)"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of functions"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - - -/* - * \dT - * describe types - */ -bool -describeTypes(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n", - gettext_noop("Schema"), - gettext_noop("Name")); - if (verbose) - appendPQExpBuffer(&buf, - " t.typname AS \"%s\",\n" - " CASE WHEN t.typrelid != 0\n" - " THEN CAST('tuple' AS pg_catalog.text)\n" - " WHEN t.typlen < 0\n" - " THEN CAST('var' AS pg_catalog.text)\n" - " ELSE CAST(t.typlen AS pg_catalog.text)\n" - " END AS \"%s\",\n", - gettext_noop("Internal name"), - gettext_noop("Size")); - if (verbose && pset.sversion >= 80300) - { - appendPQExpBufferStr(&buf, - " pg_catalog.array_to_string(\n" - " ARRAY(\n" - " SELECT e.enumlabel\n" - " FROM pg_catalog.pg_enum e\n" - " WHERE e.enumtypid = t.oid\n"); - - if (pset.sversion >= 90100) - appendPQExpBufferStr(&buf, - " ORDER BY e.enumsortorder\n"); - else - appendPQExpBufferStr(&buf, - " ORDER BY e.oid\n"); - - appendPQExpBuffer(&buf, - " ),\n" - " E'\\n'\n" - " ) AS \"%s\",\n", - gettext_noop("Elements")); - } - if (verbose) - { - appendPQExpBuffer(&buf, - " pg_catalog.pg_get_userbyid(t.typowner) AS \"%s\",\n", - gettext_noop("Owner")); - } - if (verbose && pset.sversion >= 90200) - { - printACLColumn(&buf, "t.typacl"); - appendPQExpBufferStr(&buf, ",\n "); - } - - appendPQExpBuffer(&buf, - " pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_type t\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"); - - /* - * do not include complex types (typrelid!=0) unless they are standalone - * composite types - */ - appendPQExpBufferStr(&buf, "WHERE (t.typrelid = 0 "); - appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c " - "WHERE c.oid = t.typrelid))\n"); - - /* - * do not include array types (before 8.3 we have to use the assumption - * that their names start with underscore) - */ - if (pset.sversion >= 80300) - appendPQExpBufferStr(&buf, " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n"); - else - appendPQExpBufferStr(&buf, " AND t.typname !~ '^_'\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - /* Match name pattern against either internal or external name */ - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "t.typname", - "pg_catalog.format_type(t.oid, NULL)", - "pg_catalog.pg_type_is_visible(t.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of data types"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* \do - * Describe operators - */ -bool -describeOperators(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - /* - * Note: before Postgres 9.1, we did not assign comments to any built-in - * operators, preferring to let the comment on the underlying function - * suffice. The coalesce() on the obj_description() calls below supports - * this convention by providing a fallback lookup of a comment on the - * operator's function. As of 9.1 there is a policy that every built-in - * operator should have a comment; so the coalesce() is no longer - * necessary so far as built-in operators are concerned. We keep it - * anyway, for now, because (1) third-party modules may still be following - * the old convention, and (2) we'd need to do it anyway when talking to a - * pre-9.1 server. - */ - - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " o.oprname AS \"%s\",\n" - " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n" - " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n" - " pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Left arg type"), - gettext_noop("Right arg type"), - gettext_noop("Result type")); - - if (verbose) - appendPQExpBuffer(&buf, - " o.oprcode AS \"%s\",\n", - gettext_noop("Function")); - - appendPQExpBuffer(&buf, - " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n" - " pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n" - "FROM pg_catalog.pg_operator o\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n", - gettext_noop("Description")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, true, - "n.nspname", "o.oprname", NULL, - "pg_catalog.pg_operator_is_visible(o.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of operators"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * listAllDbs - * - * for \l, \list, and -l switch - */ -bool -listAllDbs(const char *pattern, bool verbose) -{ - PGresult *res; - PQExpBufferData buf; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT d.datname as \"%s\",\n" - " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n" - " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n", - gettext_noop("Name"), - gettext_noop("Owner"), - gettext_noop("Encoding")); - if (pset.sversion >= 80400) - appendPQExpBuffer(&buf, - " d.datcollate as \"%s\",\n" - " d.datctype as \"%s\",\n", - gettext_noop("Collate"), - gettext_noop("Ctype")); - appendPQExpBufferStr(&buf, " "); - printACLColumn(&buf, "d.datacl"); - if (verbose && pset.sversion >= 80200) - appendPQExpBuffer(&buf, - ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n" - " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n" - " ELSE 'No Access'\n" - " END as \"%s\"", - gettext_noop("Size")); - if (verbose && pset.sversion >= 80000) - appendPQExpBuffer(&buf, - ",\n t.spcname as \"%s\"", - gettext_noop("Tablespace")); - if (verbose && pset.sversion >= 80200) - appendPQExpBuffer(&buf, - ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"", - gettext_noop("Description")); - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_database d\n"); - if (verbose && pset.sversion >= 80000) - appendPQExpBufferStr(&buf, - " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n"); - - if (pattern) - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "d.datname", NULL, NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of databases"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * List Tables' Grant/Revoke Permissions - * \z (now also \dp -- perhaps more mnemonic) - */ -bool -permissionsList(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false, false, false}; - - initPQExpBuffer(&buf); - - /* - * we ignore indexes and toast tables since they have no meaningful rights - */ - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " c.relname as \"%s\",\n" - " CASE c.relkind" - " WHEN 'r' THEN '%s'" - " WHEN 'v' THEN '%s'" - " WHEN 'm' THEN '%s'" - " WHEN 'S' THEN '%s'" - " WHEN 'f' THEN '%s'" - " END as \"%s\",\n" - " ", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("table"), - gettext_noop("view"), - gettext_noop("materialized view"), - gettext_noop("sequence"), - gettext_noop("foreign table"), - gettext_noop("Type")); - - printACLColumn(&buf, "c.relacl"); - - if (pset.sversion >= 80400) - appendPQExpBuffer(&buf, - ",\n pg_catalog.array_to_string(ARRAY(\n" - " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n" - " FROM pg_catalog.pg_attribute a\n" - " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n" - " ), E'\\n') AS \"%s\"", - gettext_noop("Column privileges")); - - if (pset.sversion >= 90500) - appendPQExpBuffer(&buf, - ",\n pg_catalog.array_to_string(ARRAY(\n" - " SELECT polname\n" - " || CASE WHEN polcmd != '*' THEN\n" - " E' (' || polcmd || E'):'\n" - " ELSE E':' \n" - " END\n" - " || CASE WHEN polqual IS NOT NULL THEN\n" - " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n" - " ELSE E''\n" - " END\n" - " || CASE WHEN polwithcheck IS NOT NULL THEN\n" - " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n" - " ELSE E''\n" - " END" - " || CASE WHEN polroles <> '{0}' THEN\n" - " E'\\n to: ' || pg_catalog.array_to_string(\n" - " ARRAY(\n" - " SELECT rolname\n" - " FROM pg_catalog.pg_roles\n" - " WHERE oid = ANY (polroles)\n" - " ORDER BY 1\n" - " ), E', ')\n" - " ELSE E''\n" - " END\n" - " FROM pg_catalog.pg_policy pol\n" - " WHERE polrelid = c.oid), E'\\n')\n" - " AS \"%s\"", - gettext_noop("Policies")); - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); - - /* - * Unless a schema pattern is specified, we suppress system and temp - * tables, since they normally aren't very interesting from a permissions - * point of view. You can see 'em by explicit request though, eg with \z - * pg_catalog.* - */ - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "c.relname", NULL, - "n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - if (!res) - { - termPQExpBuffer(&buf); - return false; - } - - myopt.nullPrint = NULL; - printfPQExpBuffer(&buf, _("Access privileges")); - myopt.title = buf.data; - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - termPQExpBuffer(&buf); - PQclear(res); - return true; -} - - -/* - * \ddp - * - * List Default ACLs. The pattern can match either schema or role name. - */ -bool -listDefaultACLs(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false}; - - if (pset.sversion < 90000) - { - psql_error("The server (version %d.%d) does not support altering default privileges.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n" - " n.nspname AS \"%s\",\n" - " CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n" - " ", - gettext_noop("Owner"), - gettext_noop("Schema"), - DEFACLOBJ_RELATION, - gettext_noop("table"), - DEFACLOBJ_SEQUENCE, - gettext_noop("sequence"), - DEFACLOBJ_FUNCTION, - gettext_noop("function"), - DEFACLOBJ_TYPE, - gettext_noop("type"), - gettext_noop("Type")); - - printACLColumn(&buf, "d.defaclacl"); - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, - "n.nspname", - "pg_catalog.pg_get_userbyid(d.defaclrole)", - NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;"); - - res = PSQLexec(buf.data); - if (!res) - { - termPQExpBuffer(&buf); - return false; - } - - myopt.nullPrint = NULL; - printfPQExpBuffer(&buf, _("Default access privileges")); - myopt.title = buf.data; - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - termPQExpBuffer(&buf); - PQclear(res); - return true; -} - - -/* - * Get object comments - * - * \dd [foo] - * - * Note: This command only lists comments for object types which do not have - * their comments displayed by their own backslash commands. The following - * types of objects will be displayed: constraint, operator class, - * operator family, rule, and trigger. - * - */ -bool -objectDescription(const char *pattern, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false}; - - initPQExpBuffer(&buf); - - appendPQExpBuffer(&buf, - "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n" - "FROM (\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Object"), - gettext_noop("Description")); - - /* Table constraint descriptions */ - appendPQExpBuffer(&buf, - " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n" - " n.nspname as nspname,\n" - " CAST(pgc.conname AS pg_catalog.text) as name," - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_constraint pgc\n" - " JOIN pg_catalog.pg_class c " - "ON c.oid = pgc.conrelid\n" - " LEFT JOIN pg_catalog.pg_namespace n " - " ON n.oid = c.relnamespace\n", - gettext_noop("table constraint")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, - false, "n.nspname", "pgc.conname", NULL, - "pg_catalog.pg_table_is_visible(c.oid)"); - - /* Domain constraint descriptions */ - appendPQExpBuffer(&buf, - "UNION ALL\n" - " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n" - " n.nspname as nspname,\n" - " CAST(pgc.conname AS pg_catalog.text) as name," - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_constraint pgc\n" - " JOIN pg_catalog.pg_type t " - "ON t.oid = pgc.contypid\n" - " LEFT JOIN pg_catalog.pg_namespace n " - " ON n.oid = t.typnamespace\n", - gettext_noop("domain constraint")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, - false, "n.nspname", "pgc.conname", NULL, - "pg_catalog.pg_type_is_visible(t.oid)"); - - - /* - * pg_opclass.opcmethod only available in 8.3+ - */ - if (pset.sversion >= 80300) - { - /* Operator class descriptions */ - appendPQExpBuffer(&buf, - "UNION ALL\n" - " SELECT o.oid as oid, o.tableoid as tableoid,\n" - " n.nspname as nspname,\n" - " CAST(o.opcname AS pg_catalog.text) as name,\n" - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_opclass o\n" - " JOIN pg_catalog.pg_am am ON " - "o.opcmethod = am.oid\n" - " JOIN pg_catalog.pg_namespace n ON " - "n.oid = o.opcnamespace\n", - gettext_noop("operator class")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "o.opcname", NULL, - "pg_catalog.pg_opclass_is_visible(o.oid)"); - } - - /* - * although operator family comments have been around since 8.3, - * pg_opfamily_is_visible is only available in 9.2+ - */ - if (pset.sversion >= 90200) - { - /* Operator family descriptions */ - appendPQExpBuffer(&buf, - "UNION ALL\n" - " SELECT opf.oid as oid, opf.tableoid as tableoid,\n" - " n.nspname as nspname,\n" - " CAST(opf.opfname AS pg_catalog.text) AS name,\n" - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_opfamily opf\n" - " JOIN pg_catalog.pg_am am " - "ON opf.opfmethod = am.oid\n" - " JOIN pg_catalog.pg_namespace n " - "ON opf.opfnamespace = n.oid\n", - gettext_noop("operator family")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "opf.opfname", NULL, - "pg_catalog.pg_opfamily_is_visible(opf.oid)"); - } - - /* Rule descriptions (ignore rules for views) */ - appendPQExpBuffer(&buf, - "UNION ALL\n" - " SELECT r.oid as oid, r.tableoid as tableoid,\n" - " n.nspname as nspname,\n" - " CAST(r.rulename AS pg_catalog.text) as name," - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_rewrite r\n" - " JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - " WHERE r.rulename != '_RETURN'\n", - gettext_noop("rule")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "r.rulename", NULL, - "pg_catalog.pg_table_is_visible(c.oid)"); - - /* Trigger descriptions */ - appendPQExpBuffer(&buf, - "UNION ALL\n" - " SELECT t.oid as oid, t.tableoid as tableoid,\n" - " n.nspname as nspname,\n" - " CAST(t.tgname AS pg_catalog.text) as name," - " CAST('%s' AS pg_catalog.text) as object\n" - " FROM pg_catalog.pg_trigger t\n" - " JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n", - gettext_noop("trigger")); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false, - "n.nspname", "t.tgname", NULL, - "pg_catalog.pg_table_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, - ") AS tt\n" - " JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("Object descriptions"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * describeTableDetails (for \d) - * - * This routine finds the tables to be displayed, and calls - * describeOneTableDetails for each one. - * - * verbose: if true, this is \d+ - */ -bool -describeTableDetails(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - int i; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT c.oid,\n" - " n.nspname,\n" - " c.relname\n" - "FROM pg_catalog.pg_class c\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false, - "n.nspname", "c.relname", NULL, - "pg_catalog.pg_table_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 2, 3;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - if (PQntuples(res) == 0) - { - if (!pset.quiet) - psql_error("Did not find any relation named \"%s\".\n", - pattern); - PQclear(res); - return false; - } - - for (i = 0; i < PQntuples(res); i++) - { - const char *oid; - const char *nspname; - const char *relname; - - oid = PQgetvalue(res, i, 0); - nspname = PQgetvalue(res, i, 1); - relname = PQgetvalue(res, i, 2); - - if (!describeOneTableDetails(nspname, relname, oid, verbose)) - { - PQclear(res); - return false; - } - if (cancel_pressed) - { - PQclear(res); - return false; - } - } - - PQclear(res); - return true; -} - -/* - * describeOneTableDetails (for \d) - * - * Unfortunately, the information presented here is so complicated that it - * cannot be done in a single query. So we have to assemble the printed table - * by hand and pass it to the underlying printTable() function. - */ -static bool -describeOneTableDetails(const char *schemaname, - const char *relationname, - const char *oid, - bool verbose) -{ - PQExpBufferData buf; - PGresult *res = NULL; - printTableOpt myopt = pset.popt.topt; - printTableContent cont; - bool printTableInitialized = false; - int i; - char *view_def = NULL; - char *headers[9]; - char **seq_values = NULL; - char **modifiers = NULL; - char **ptr; - PQExpBufferData title; - PQExpBufferData tmpbuf; - int cols; - int numrows = 0; - struct - { - int16 checks; - char relkind; - bool hasindex; - bool hasrules; - bool hastriggers; - bool rowsecurity; - bool forcerowsecurity; - bool hasoids; - Oid tablespace; - char *reloptions; - char *reloftype; - char relpersistence; - char relreplident; - } tableinfo; - bool show_modifiers = false; - bool retval; - - retval = false; - - myopt.default_footer = false; - /* This output looks confusing in expanded mode. */ - myopt.expanded = false; - - initPQExpBuffer(&buf); - initPQExpBuffer(&title); - initPQExpBuffer(&tmpbuf); - - /* Get general table info */ - if (pset.sversion >= 90500) - { - printfPQExpBuffer(&buf, - "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " - "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, " - "c.relhasoids, %s, c.reltablespace, " - "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " - "c.relpersistence, c.relreplident\n" - "FROM pg_catalog.pg_class c\n " - "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" - "WHERE c.oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(c.reloptions || " - "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" - : "''"), - oid); - } - else if (pset.sversion >= 90400) - { - printfPQExpBuffer(&buf, - "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " - "c.relhastriggers, false, false, c.relhasoids, " - "%s, c.reltablespace, " - "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " - "c.relpersistence, c.relreplident\n" - "FROM pg_catalog.pg_class c\n " - "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" - "WHERE c.oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(c.reloptions || " - "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" - : "''"), - oid); - } - else if (pset.sversion >= 90100) - { - printfPQExpBuffer(&buf, - "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " - "c.relhastriggers, false, false, c.relhasoids, " - "%s, c.reltablespace, " - "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " - "c.relpersistence\n" - "FROM pg_catalog.pg_class c\n " - "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" - "WHERE c.oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(c.reloptions || " - "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" - : "''"), - oid); - } - else if (pset.sversion >= 90000) - { - printfPQExpBuffer(&buf, - "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " - "c.relhastriggers, false, false, c.relhasoids, " - "%s, c.reltablespace, " - "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n" - "FROM pg_catalog.pg_class c\n " - "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" - "WHERE c.oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(c.reloptions || " - "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" - : "''"), - oid); - } - else if (pset.sversion >= 80400) - { - printfPQExpBuffer(&buf, - "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " - "c.relhastriggers, false, false, c.relhasoids, " - "%s, c.reltablespace\n" - "FROM pg_catalog.pg_class c\n " - "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" - "WHERE c.oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(c.reloptions || " - "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" - : "''"), - oid); - } - else if (pset.sversion >= 80200) - { - printfPQExpBuffer(&buf, - "SELECT relchecks, relkind, relhasindex, relhasrules, " - "reltriggers <> 0, false, false, relhasoids, " - "%s, reltablespace\n" - "FROM pg_catalog.pg_class WHERE oid = '%s';", - (verbose ? - "pg_catalog.array_to_string(reloptions, E', ')" : "''"), - oid); - } - else if (pset.sversion >= 80000) - { - printfPQExpBuffer(&buf, - "SELECT relchecks, relkind, relhasindex, relhasrules, " - "reltriggers <> 0, false, false, relhasoids, " - "'', reltablespace\n" - "FROM pg_catalog.pg_class WHERE oid = '%s';", - oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT relchecks, relkind, relhasindex, relhasrules, " - "reltriggers <> 0, false, false, relhasoids, " - "'', ''\n" - "FROM pg_catalog.pg_class WHERE oid = '%s';", - oid); - } - - res = PSQLexec(buf.data); - if (!res) - goto error_return; - - /* Did we get anything? */ - if (PQntuples(res) == 0) - { - if (!pset.quiet) - psql_error("Did not find any relation with OID %s.\n", oid); - goto error_return; - } - - tableinfo.checks = atoi(PQgetvalue(res, 0, 0)); - tableinfo.relkind = *(PQgetvalue(res, 0, 1)); - tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0; - tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0; - tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0; - tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0; - tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0; - tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0; - tableinfo.reloptions = (pset.sversion >= 80200) ? - pg_strdup(PQgetvalue(res, 0, 8)) : NULL; - tableinfo.tablespace = (pset.sversion >= 80000) ? - atooid(PQgetvalue(res, 0, 9)) : 0; - tableinfo.reloftype = (pset.sversion >= 90000 && - strcmp(PQgetvalue(res, 0, 10), "") != 0) ? - pg_strdup(PQgetvalue(res, 0, 10)) : NULL; - tableinfo.relpersistence = (pset.sversion >= 90100) ? - *(PQgetvalue(res, 0, 11)) : 0; - tableinfo.relreplident = (pset.sversion >= 90400) ? - *(PQgetvalue(res, 0, 12)) : 'd'; - PQclear(res); - res = NULL; - - /* - * If it's a sequence, fetch its values and store into an array that will - * be used later. - */ - if (tableinfo.relkind == 'S') - { - printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname)); - /* must be separate because fmtId isn't reentrant */ - appendPQExpBuffer(&buf, ".%s;", fmtId(relationname)); - - res = PSQLexec(buf.data); - if (!res) - goto error_return; - - seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values)); - - for (i = 0; i < PQnfields(res); i++) - seq_values[i] = pg_strdup(PQgetvalue(res, 0, i)); - seq_values[i] = NULL; - - PQclear(res); - res = NULL; - } - - /* - * Get column info - * - * You need to modify value of "firstvcol" which will be defined below if - * you are adding column(s) preceding to verbose-only columns. - */ - printfPQExpBuffer(&buf, "SELECT a.attname,"); - appendPQExpBufferStr(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod)," - "\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)" - "\n FROM pg_catalog.pg_attrdef d" - "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)," - "\n a.attnotnull, a.attnum,"); - if (pset.sversion >= 90100) - appendPQExpBufferStr(&buf, "\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n" - " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation"); - else - appendPQExpBufferStr(&buf, "\n NULL AS attcollation"); - if (tableinfo.relkind == 'i') - appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef"); - else - appendPQExpBufferStr(&buf, ",\n NULL AS indexdef"); - if (tableinfo.relkind == 'f' && pset.sversion >= 90200) - appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE " - " '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value) FROM " - " pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions"); - else - appendPQExpBufferStr(&buf, ",\n NULL AS attfdwoptions"); - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n a.attstorage"); - appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget"); - - /* - * In 9.0+, we have column comments for: relations, views, composite - * types, and foreign tables (c.f. CommentObject() in comment.c). - */ - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') - appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); - } - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a"); - appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid); - appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;"); - - res = PSQLexec(buf.data); - if (!res) - goto error_return; - numrows = PQntuples(res); - - /* Make title */ - switch (tableinfo.relkind) - { - case 'r': - if (tableinfo.relpersistence == 'u') - printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), - schemaname, relationname); - else - printfPQExpBuffer(&title, _("Table \"%s.%s\""), - schemaname, relationname); - break; - case 'v': - printfPQExpBuffer(&title, _("View \"%s.%s\""), - schemaname, relationname); - break; - case 'm': - if (tableinfo.relpersistence == 'u') - printfPQExpBuffer(&title, _("Unlogged materialized view \"%s.%s\""), - schemaname, relationname); - else - printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""), - schemaname, relationname); - break; - case 'S': - printfPQExpBuffer(&title, _("Sequence \"%s.%s\""), - schemaname, relationname); - break; - case 'i': - if (tableinfo.relpersistence == 'u') - printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""), - schemaname, relationname); - else - printfPQExpBuffer(&title, _("Index \"%s.%s\""), - schemaname, relationname); - break; - case 's': - /* not used as of 8.2, but keep it for backwards compatibility */ - printfPQExpBuffer(&title, _("Special relation \"%s.%s\""), - schemaname, relationname); - break; - case 't': - printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""), - schemaname, relationname); - break; - case 'c': - printfPQExpBuffer(&title, _("Composite type \"%s.%s\""), - schemaname, relationname); - break; - case 'f': - printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), - schemaname, relationname); - break; - default: - /* untranslated unknown relkind */ - printfPQExpBuffer(&title, "?%c? \"%s.%s\"", - tableinfo.relkind, schemaname, relationname); - break; - } - - /* Set the number of columns, and their names */ - headers[0] = gettext_noop("Column"); - headers[1] = gettext_noop("Type"); - cols = 2; - - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') - { - show_modifiers = true; - headers[cols++] = gettext_noop("Modifiers"); - modifiers = pg_malloc0((numrows + 1) * sizeof(*modifiers)); - } - - if (tableinfo.relkind == 'S') - headers[cols++] = gettext_noop("Value"); - - if (tableinfo.relkind == 'i') - headers[cols++] = gettext_noop("Definition"); - - if (tableinfo.relkind == 'f' && pset.sversion >= 90200) - headers[cols++] = gettext_noop("FDW Options"); - - if (verbose) - { - headers[cols++] = gettext_noop("Storage"); - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') - headers[cols++] = gettext_noop("Stats target"); - /* Column comments, if the relkind supports this feature. */ - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') - headers[cols++] = gettext_noop("Description"); - } - - printTableInit(&cont, &myopt, title.data, cols, numrows); - printTableInitialized = true; - - for (i = 0; i < cols; i++) - printTableAddHeader(&cont, headers[i], true, 'l'); - - /* Check if table is a view or materialized view */ - if ((tableinfo.relkind == 'v' || tableinfo.relkind == 'm') && verbose) - { - PGresult *result; - - printfPQExpBuffer(&buf, - "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - - if (PQntuples(result) > 0) - view_def = pg_strdup(PQgetvalue(result, 0, 0)); - - PQclear(result); - } - - /* Generate table cells to be printed */ - for (i = 0; i < numrows; i++) - { - /* Column */ - printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false); - - /* Type */ - printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false); - - /* Modifiers: collate, not null, default */ - if (show_modifiers) - { - resetPQExpBuffer(&tmpbuf); - - if (!PQgetisnull(res, i, 5)) - { - if (tmpbuf.len > 0) - appendPQExpBufferChar(&tmpbuf, ' '); - appendPQExpBuffer(&tmpbuf, _("collate %s"), - PQgetvalue(res, i, 5)); - } - - if (strcmp(PQgetvalue(res, i, 3), "t") == 0) - { - if (tmpbuf.len > 0) - appendPQExpBufferChar(&tmpbuf, ' '); - appendPQExpBufferStr(&tmpbuf, _("not null")); - } - - /* handle "default" here */ - /* (note: above we cut off the 'default' string at 128) */ - if (strlen(PQgetvalue(res, i, 2)) != 0) - { - if (tmpbuf.len > 0) - appendPQExpBufferChar(&tmpbuf, ' '); - /* translator: default values of column definitions */ - appendPQExpBuffer(&tmpbuf, _("default %s"), - PQgetvalue(res, i, 2)); - } - - modifiers[i] = pg_strdup(tmpbuf.data); - printTableAddCell(&cont, modifiers[i], false, false); - } - - /* Value: for sequences only */ - if (tableinfo.relkind == 'S') - printTableAddCell(&cont, seq_values[i], false, false); - - /* Expression for index column */ - if (tableinfo.relkind == 'i') - printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false); - - /* FDW options for foreign table column, only for 9.2 or later */ - if (tableinfo.relkind == 'f' && pset.sversion >= 90200) - printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); - - /* Storage and Description */ - if (verbose) - { - int firstvcol = 8; - char *storage = PQgetvalue(res, i, firstvcol); - - /* these strings are literal in our syntax, so not translated. */ - printTableAddCell(&cont, (storage[0] == 'p' ? "plain" : - (storage[0] == 'm' ? "main" : - (storage[0] == 'x' ? "extended" : - (storage[0] == 'e' ? "external" : - "???")))), - false, false); - - /* Statistics target, if the relkind supports this feature */ - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') - { - printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), - false, false); - } - - /* Column comments, if the relkind supports this feature. */ - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') - printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), - false, false); - } - } - - /* Make footers */ - if (tableinfo.relkind == 'i') - { - /* Footer information about an index */ - PGresult *result; - - printfPQExpBuffer(&buf, - "SELECT i.indisunique, i.indisprimary, i.indisclustered, "); - if (pset.sversion >= 80200) - appendPQExpBufferStr(&buf, "i.indisvalid,\n"); - else - appendPQExpBufferStr(&buf, "true AS indisvalid,\n"); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - " (NOT i.indimmediate) AND " - "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " - "WHERE conrelid = i.indrelid AND " - "conindid = i.indexrelid AND " - "contype IN ('p','u','x') AND " - "condeferrable) AS condeferrable,\n" - " (NOT i.indimmediate) AND " - "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint " - "WHERE conrelid = i.indrelid AND " - "conindid = i.indexrelid AND " - "contype IN ('p','u','x') AND " - "condeferred) AS condeferred,\n"); - else - appendPQExpBufferStr(&buf, - " false AS condeferrable, false AS condeferred,\n"); - - if (pset.sversion >= 90400) - appendPQExpBuffer(&buf, "i.indisreplident,\n"); - else - appendPQExpBuffer(&buf, "false AS indisreplident,\n"); - - appendPQExpBuffer(&buf, " a.amname, c2.relname, " - "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n" - "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n" - "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n" - "AND i.indrelid = c2.oid;", - oid); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else if (PQntuples(result) != 1) - { - PQclear(result); - goto error_return; - } - else - { - char *indisunique = PQgetvalue(result, 0, 0); - char *indisprimary = PQgetvalue(result, 0, 1); - char *indisclustered = PQgetvalue(result, 0, 2); - char *indisvalid = PQgetvalue(result, 0, 3); - char *deferrable = PQgetvalue(result, 0, 4); - char *deferred = PQgetvalue(result, 0, 5); - char *indisreplident = PQgetvalue(result, 0, 6); - char *indamname = PQgetvalue(result, 0, 7); - char *indtable = PQgetvalue(result, 0, 8); - char *indpred = PQgetvalue(result, 0, 9); - - if (strcmp(indisprimary, "t") == 0) - printfPQExpBuffer(&tmpbuf, _("primary key, ")); - else if (strcmp(indisunique, "t") == 0) - printfPQExpBuffer(&tmpbuf, _("unique, ")); - else - resetPQExpBuffer(&tmpbuf); - appendPQExpBuffer(&tmpbuf, "%s, ", indamname); - - /* we assume here that index and table are in same schema */ - appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""), - schemaname, indtable); - - if (strlen(indpred)) - appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred); - - if (strcmp(indisclustered, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", clustered")); - - if (strcmp(indisvalid, "t") != 0) - appendPQExpBufferStr(&tmpbuf, _(", invalid")); - - if (strcmp(deferrable, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", deferrable")); - - if (strcmp(deferred, "t") == 0) - appendPQExpBufferStr(&tmpbuf, _(", initially deferred")); - - if (strcmp(indisreplident, "t") == 0) - appendPQExpBuffer(&tmpbuf, _(", replica identity")); - - printTableAddFooter(&cont, tmpbuf.data); - add_tablespace_footer(&cont, tableinfo.relkind, - tableinfo.tablespace, true); - } - - PQclear(result); - } - else if (tableinfo.relkind == 'S') - { - /* Footer information about a sequence */ - PGresult *result = NULL; - - /* Get the column that owns this sequence */ - printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||" - "\n pg_catalog.quote_ident(relname) || '.' ||" - "\n pg_catalog.quote_ident(attname)" - "\nFROM pg_catalog.pg_class c" - "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid" - "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace" - "\nINNER JOIN pg_catalog.pg_attribute a ON (" - "\n a.attrelid=c.oid AND" - "\n a.attnum=d.refobjsubid)" - "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass" - "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass" - "\n AND d.objid=%s" - "\n AND d.deptype='a'", - oid); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else if (PQntuples(result) == 1) - { - printfPQExpBuffer(&buf, _("Owned by: %s"), - PQgetvalue(result, 0, 0)); - printTableAddFooter(&cont, buf.data); - } - - /* - * If we get no rows back, don't show anything (obviously). We should - * never get more than one row back, but if we do, just ignore it and - * don't print anything. - */ - PQclear(result); - } - else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') - { - /* Footer information about a table */ - PGresult *result = NULL; - int tuples = 0; - - /* print indexes */ - if (tableinfo.hasindex) - { - printfPQExpBuffer(&buf, - "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, "); - if (pset.sversion >= 80200) - appendPQExpBufferStr(&buf, "i.indisvalid, "); - else - appendPQExpBufferStr(&buf, "true as indisvalid, "); - appendPQExpBufferStr(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - "pg_catalog.pg_get_constraintdef(con.oid, true), " - "contype, condeferrable, condeferred"); - else - appendPQExpBufferStr(&buf, - "null AS constraintdef, null AS contype, " - "false AS condeferrable, false AS condeferred"); - if (pset.sversion >= 90400) - appendPQExpBufferStr(&buf, ", i.indisreplident"); - else - appendPQExpBufferStr(&buf, ", false AS indisreplident"); - if (pset.sversion >= 80000) - appendPQExpBufferStr(&buf, ", c2.reltablespace"); - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"); - if (pset.sversion >= 90000) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"); - appendPQExpBuffer(&buf, - "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" - "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - printTableAddFooter(&cont, _("Indexes:")); - for (i = 0; i < tuples; i++) - { - /* untranslated index name */ - printfPQExpBuffer(&buf, " \"%s\"", - PQgetvalue(result, i, 0)); - - /* If exclusion constraint, print the constraintdef */ - if (strcmp(PQgetvalue(result, i, 7), "x") == 0) - { - appendPQExpBuffer(&buf, " %s", - PQgetvalue(result, i, 6)); - } - else - { - const char *indexdef; - const char *usingpos; - - /* Label as primary key or unique (but not both) */ - if (strcmp(PQgetvalue(result, i, 1), "t") == 0) - appendPQExpBufferStr(&buf, " PRIMARY KEY,"); - else if (strcmp(PQgetvalue(result, i, 2), "t") == 0) - { - if (strcmp(PQgetvalue(result, i, 7), "u") == 0) - appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,"); - else - appendPQExpBufferStr(&buf, " UNIQUE,"); - } - - /* Everything after "USING" is echoed verbatim */ - indexdef = PQgetvalue(result, i, 5); - usingpos = strstr(indexdef, " USING "); - if (usingpos) - indexdef = usingpos + 7; - appendPQExpBuffer(&buf, " %s", indexdef); - - /* Need these for deferrable PK/UNIQUE indexes */ - if (strcmp(PQgetvalue(result, i, 8), "t") == 0) - appendPQExpBufferStr(&buf, " DEFERRABLE"); - - if (strcmp(PQgetvalue(result, i, 9), "t") == 0) - appendPQExpBufferStr(&buf, " INITIALLY DEFERRED"); - } - - /* Add these for all cases */ - if (strcmp(PQgetvalue(result, i, 3), "t") == 0) - appendPQExpBufferStr(&buf, " CLUSTER"); - - if (strcmp(PQgetvalue(result, i, 4), "t") != 0) - appendPQExpBufferStr(&buf, " INVALID"); - - if (strcmp(PQgetvalue(result, i, 10), "t") == 0) - appendPQExpBuffer(&buf, " REPLICA IDENTITY"); - - printTableAddFooter(&cont, buf.data); - - /* Print tablespace of the index on the same line */ - if (pset.sversion >= 80000) - add_tablespace_footer(&cont, 'i', - atooid(PQgetvalue(result, i, 11)), - false); - } - } - PQclear(result); - } - - /* print table (and column) check constraints */ - if (tableinfo.checks) - { - printfPQExpBuffer(&buf, - "SELECT r.conname, " - "pg_catalog.pg_get_constraintdef(r.oid, true)\n" - "FROM pg_catalog.pg_constraint r\n" - "WHERE r.conrelid = '%s' AND r.contype = 'c'\n" - "ORDER BY 1;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - printTableAddFooter(&cont, _("Check constraints:")); - for (i = 0; i < tuples; i++) - { - /* untranslated contraint name and def */ - printfPQExpBuffer(&buf, " \"%s\" %s", - PQgetvalue(result, i, 0), - PQgetvalue(result, i, 1)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - - /* print foreign-key constraints (there are none if no triggers) */ - if (tableinfo.hastriggers) - { - printfPQExpBuffer(&buf, - "SELECT conname,\n" - " pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n" - "FROM pg_catalog.pg_constraint r\n" - "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - printTableAddFooter(&cont, _("Foreign-key constraints:")); - for (i = 0; i < tuples; i++) - { - /* untranslated constraint name and def */ - printfPQExpBuffer(&buf, " \"%s\" %s", - PQgetvalue(result, i, 0), - PQgetvalue(result, i, 1)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - - /* print incoming foreign-key references (none if no triggers) */ - if (tableinfo.hastriggers) - { - printfPQExpBuffer(&buf, - "SELECT conname, conrelid::pg_catalog.regclass,\n" - " pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n" - "FROM pg_catalog.pg_constraint c\n" - "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - printTableAddFooter(&cont, _("Referenced by:")); - for (i = 0; i < tuples; i++) - { - printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s", - PQgetvalue(result, i, 1), - PQgetvalue(result, i, 0), - PQgetvalue(result, i, 2)); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - - /* print any row-level policies */ - if (pset.sversion >= 90500) - { - printfPQExpBuffer(&buf, - "SELECT pol.polname,\n" - "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE array_to_string(array(select rolname from pg_roles where oid = any (pol.polroles) order by 1),',') END,\n" - "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n" - "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n" - "CASE pol.polcmd \n" - "WHEN 'r' THEN 'SELECT'\n" - "WHEN 'a' THEN 'INSERT'\n" - "WHEN 'w' THEN 'UPDATE'\n" - "WHEN 'd' THEN 'DELETE'\n" - "WHEN '*' THEN 'ALL'\n" - "END AS cmd\n" - "FROM pg_catalog.pg_policy pol\n" - "WHERE pol.polrelid = '%s' ORDER BY 1;", - oid); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - /* - * Handle cases where RLS is enabled and there are policies, or - * there aren't policies, or RLS isn't enabled but there are - * policies - */ - if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies:")); - - if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies (forced row security enabled):")); - - if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0) - printTableAddFooter(&cont, _("Policies (row security enabled): (none)")); - - if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0) - printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)")); - - if (!tableinfo.rowsecurity && tuples > 0) - printTableAddFooter(&cont, _("Policies (row security disabled):")); - - /* Might be an empty set - that's ok */ - for (i = 0; i < tuples; i++) - { - printfPQExpBuffer(&buf, " POLICY \"%s\"", - PQgetvalue(result, i, 0)); - - if (!PQgetisnull(result, i, 4)) - appendPQExpBuffer(&buf, " FOR %s", - PQgetvalue(result, i, 4)); - - if (!PQgetisnull(result, i, 1)) - { - appendPQExpBuffer(&buf, "\n TO %s", - PQgetvalue(result, i, 1)); - } - - if (!PQgetisnull(result, i, 2)) - appendPQExpBuffer(&buf, "\n USING (%s)", - PQgetvalue(result, i, 2)); - - if (!PQgetisnull(result, i, 3)) - appendPQExpBuffer(&buf, "\n WITH CHECK (%s)", - PQgetvalue(result, i, 3)); - - printTableAddFooter(&cont, buf.data); - - } - PQclear(result); - } - - /* print rules */ - if (tableinfo.hasrules && tableinfo.relkind != 'm') - { - if (pset.sversion >= 80300) - { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " - "ev_enabled\n" - "FROM pg_catalog.pg_rewrite r\n" - "WHERE r.ev_class = '%s' ORDER BY 1;", - oid); - } - else - { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " - "'O'::char AS ev_enabled\n" - "FROM pg_catalog.pg_rewrite r\n" - "WHERE r.ev_class = '%s' ORDER BY 1;", - oid); - } - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - bool have_heading; - int category; - - for (category = 0; category < 4; category++) - { - have_heading = false; - - for (i = 0; i < tuples; i++) - { - const char *ruledef; - bool list_rule = false; - - switch (category) - { - case 0: - if (*PQgetvalue(result, i, 2) == 'O') - list_rule = true; - break; - case 1: - if (*PQgetvalue(result, i, 2) == 'D') - list_rule = true; - break; - case 2: - if (*PQgetvalue(result, i, 2) == 'A') - list_rule = true; - break; - case 3: - if (*PQgetvalue(result, i, 2) == 'R') - list_rule = true; - break; - } - if (!list_rule) - continue; - - if (!have_heading) - { - switch (category) - { - case 0: - printfPQExpBuffer(&buf, _("Rules:")); - break; - case 1: - printfPQExpBuffer(&buf, _("Disabled rules:")); - break; - case 2: - printfPQExpBuffer(&buf, _("Rules firing always:")); - break; - case 3: - printfPQExpBuffer(&buf, _("Rules firing on replica only:")); - break; - } - printTableAddFooter(&cont, buf.data); - have_heading = true; - } - - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result, i, 1); - ruledef += 12; - printfPQExpBuffer(&buf, " %s", ruledef); - printTableAddFooter(&cont, buf.data); - } - } - } - PQclear(result); - } - } - - if (view_def) - { - PGresult *result = NULL; - - /* Footer information about a view */ - printTableAddFooter(&cont, _("View definition:")); - printTableAddFooter(&cont, view_def); - - /* print rules */ - if (tableinfo.hasrules) - { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n" - "FROM pg_catalog.pg_rewrite r\n" - "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - - if (PQntuples(result) > 0) - { - printTableAddFooter(&cont, _("Rules:")); - for (i = 0; i < PQntuples(result); i++) - { - const char *ruledef; - - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result, i, 1); - ruledef += 12; - - printfPQExpBuffer(&buf, " %s", ruledef); - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - } - } - - /* - * Print triggers next, if any (but only user-defined triggers). This - * could apply to either a table or a view. - */ - if (tableinfo.hastriggers) - { - PGresult *result; - int tuples; - - printfPQExpBuffer(&buf, - "SELECT t.tgname, " - "pg_catalog.pg_get_triggerdef(t.oid%s), " - "t.tgenabled, %s\n" - "FROM pg_catalog.pg_trigger t\n" - "WHERE t.tgrelid = '%s' AND ", - (pset.sversion >= 90000 ? ", true" : ""), - (pset.sversion >= 90000 ? "t.tgisinternal" : - pset.sversion >= 80300 ? - "t.tgconstraint <> 0 AS tgisinternal" : - "false AS tgisinternal"), oid); - if (pset.sversion >= 90000) - /* display/warn about disabled internal triggers */ - appendPQExpBuffer(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))"); - else if (pset.sversion >= 80300) - appendPQExpBufferStr(&buf, "(t.tgconstraint = 0 OR (t.tgconstraint <> 0 AND t.tgenabled = 'D'))"); - else - appendPQExpBufferStr(&buf, - "(NOT tgisconstraint " - " OR NOT EXISTS" - " (SELECT 1 FROM pg_catalog.pg_depend d " - " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) " - " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))"); - appendPQExpBufferStr(&buf, "\nORDER BY 1;"); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (tuples > 0) - { - bool have_heading; - int category; - - /* - * split the output into 4 different categories. Enabled triggers, - * disabled triggers and the two special ALWAYS and REPLICA - * configurations. - */ - for (category = 0; category <= 4; category++) - { - have_heading = false; - for (i = 0; i < tuples; i++) - { - bool list_trigger; - const char *tgdef; - const char *usingpos; - const char *tgenabled; - const char *tgisinternal; - - /* - * Check if this trigger falls into the current category - */ - tgenabled = PQgetvalue(result, i, 2); - tgisinternal = PQgetvalue(result, i, 3); - list_trigger = false; - switch (category) - { - case 0: - if (*tgenabled == 'O' || *tgenabled == 't') - list_trigger = true; - break; - case 1: - if ((*tgenabled == 'D' || *tgenabled == 'f') && - *tgisinternal == 'f') - list_trigger = true; - break; - case 2: - if ((*tgenabled == 'D' || *tgenabled == 'f') && - *tgisinternal == 't') - list_trigger = true; - break; - case 3: - if (*tgenabled == 'A') - list_trigger = true; - break; - case 4: - if (*tgenabled == 'R') - list_trigger = true; - break; - } - if (list_trigger == false) - continue; - - /* Print the category heading once */ - if (have_heading == false) - { - switch (category) - { - case 0: - printfPQExpBuffer(&buf, _("Triggers:")); - break; - case 1: - if (pset.sversion >= 80300) - printfPQExpBuffer(&buf, _("Disabled user triggers:")); - else - printfPQExpBuffer(&buf, _("Disabled triggers:")); - break; - case 2: - printfPQExpBuffer(&buf, _("Disabled internal triggers:")); - break; - case 3: - printfPQExpBuffer(&buf, _("Triggers firing always:")); - break; - case 4: - printfPQExpBuffer(&buf, _("Triggers firing on replica only:")); - break; - - } - printTableAddFooter(&cont, buf.data); - have_heading = true; - } - - /* Everything after "TRIGGER" is echoed verbatim */ - tgdef = PQgetvalue(result, i, 1); - usingpos = strstr(tgdef, " TRIGGER "); - if (usingpos) - tgdef = usingpos + 9; - - printfPQExpBuffer(&buf, " %s", tgdef); - printTableAddFooter(&cont, buf.data); - } - } - } - PQclear(result); - } - - /* - * Finish printing the footer information about a table. - */ - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') - { - PGresult *result; - int tuples; - - /* print foreign server name */ - if (tableinfo.relkind == 'f') - { - char *ftoptions; - - /* Footer information about foreign table */ - printfPQExpBuffer(&buf, - "SELECT s.srvname,\n" - " array_to_string(ARRAY(SELECT " - " quote_ident(option_name) || ' ' || " - " quote_literal(option_value) FROM " - " pg_options_to_table(ftoptions)), ', ') " - "FROM pg_catalog.pg_foreign_table f,\n" - " pg_catalog.pg_foreign_server s\n" - "WHERE f.ftrelid = %s AND s.oid = f.ftserver;", - oid); - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else if (PQntuples(result) != 1) - { - PQclear(result); - goto error_return; - } - - /* Print server name */ - printfPQExpBuffer(&buf, "Server: %s", - PQgetvalue(result, 0, 0)); - printTableAddFooter(&cont, buf.data); - - /* Print per-table FDW options, if any */ - ftoptions = PQgetvalue(result, 0, 1); - if (ftoptions && ftoptions[0] != '\0') - { - printfPQExpBuffer(&buf, "FDW Options: (%s)", ftoptions); - printTableAddFooter(&cont, buf.data); - } - PQclear(result); - } - - /* print inherited tables */ - printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno;", oid); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - { - const char *s = _("Inherits"); - int sw = pg_wcswidth(s, strlen(s), pset.encoding); - - tuples = PQntuples(result); - - for (i = 0; i < tuples; i++) - { - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", - s, PQgetvalue(result, i, 0)); - else - printfPQExpBuffer(&buf, "%*s %s", - sw, "", PQgetvalue(result, i, 0)); - if (i < tuples - 1) - appendPQExpBufferChar(&buf, ','); - - printTableAddFooter(&cont, buf.data); - } - - PQclear(result); - } - - /* print child tables */ - if (pset.sversion >= 80300) - printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid); - else - printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid); - - result = PSQLexec(buf.data); - if (!result) - goto error_return; - else - tuples = PQntuples(result); - - if (!verbose) - { - /* print the number of child tables, if any */ - if (tuples > 0) - { - printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples); - printTableAddFooter(&cont, buf.data); - } - } - else - { - /* display the list of child tables */ - const char *ct = _("Child tables"); - int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding); - - for (i = 0; i < tuples; i++) - { - if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", - ct, PQgetvalue(result, i, 0)); - else - printfPQExpBuffer(&buf, "%*s %s", - ctw, "", PQgetvalue(result, i, 0)); - if (i < tuples - 1) - appendPQExpBufferChar(&buf, ','); - - printTableAddFooter(&cont, buf.data); - } - } - PQclear(result); - - /* Table type */ - if (tableinfo.reloftype) - { - printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype); - printTableAddFooter(&cont, buf.data); - } - - if (verbose && (tableinfo.relkind == 'r' || tableinfo.relkind == 'm') && - - /* - * No need to display default values; we already display a REPLICA - * IDENTITY marker on indexes. - */ - tableinfo.relreplident != 'i' && - ((strcmp(schemaname, "pg_catalog") != 0 && tableinfo.relreplident != 'd') || - (strcmp(schemaname, "pg_catalog") == 0 && tableinfo.relreplident != 'n'))) - { - const char *s = _("Replica Identity"); - - printfPQExpBuffer(&buf, "%s: %s", - s, - tableinfo.relreplident == 'f' ? "FULL" : - tableinfo.relreplident == 'n' ? "NOTHING" : - "???"); - - printTableAddFooter(&cont, buf.data); - } - - /* OIDs, if verbose and not a materialized view */ - if (verbose && tableinfo.relkind != 'm' && tableinfo.hasoids) - printTableAddFooter(&cont, _("Has OIDs: yes")); - - /* Tablespace info */ - add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, - true); - } - - /* reloptions, if verbose */ - if (verbose && - tableinfo.reloptions && tableinfo.reloptions[0] != '\0') - { - const char *t = _("Options"); - - printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions); - printTableAddFooter(&cont, buf.data); - } - - printTable(&cont, pset.queryFout, false, pset.logfile); - - retval = true; - -error_return: - - /* clean up */ - if (printTableInitialized) - printTableCleanup(&cont); - termPQExpBuffer(&buf); - termPQExpBuffer(&title); - termPQExpBuffer(&tmpbuf); - - if (seq_values) - { - for (ptr = seq_values; *ptr; ptr++) - free(*ptr); - free(seq_values); - } - - if (modifiers) - { - for (ptr = modifiers; *ptr; ptr++) - free(*ptr); - free(modifiers); - } - - if (view_def) - free(view_def); - - if (res) - PQclear(res); - - return retval; -} - -/* - * Add a tablespace description to a footer. If 'newline' is true, it is added - * in a new line; otherwise it's appended to the current value of the last - * footer. - */ -static void -add_tablespace_footer(printTableContent *const cont, char relkind, - Oid tablespace, const bool newline) -{ - /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') - { - /* - * We ignore the database default tablespace so that users not using - * tablespaces don't need to know about them. This case also covers - * pre-8.0 servers, for which tablespace will always be 0. - */ - if (tablespace != 0) - { - PGresult *result = NULL; - PQExpBufferData buf; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT spcname FROM pg_catalog.pg_tablespace\n" - "WHERE oid = '%u';", tablespace); - result = PSQLexec(buf.data); - if (!result) - return; - /* Should always be the case, but.... */ - if (PQntuples(result) > 0) - { - if (newline) - { - /* Add the tablespace as a new footer */ - printfPQExpBuffer(&buf, _("Tablespace: \"%s\""), - PQgetvalue(result, 0, 0)); - printTableAddFooter(cont, buf.data); - } - else - { - /* Append the tablespace to the latest footer */ - printfPQExpBuffer(&buf, "%s", cont->footer->data); - - /*------- - translator: before this string there's an index description like - '"foo_pkey" PRIMARY KEY, btree (a)' */ - appendPQExpBuffer(&buf, _(", tablespace \"%s\""), - PQgetvalue(result, 0, 0)); - printTableSetFooter(cont, buf.data); - } - } - PQclear(result); - termPQExpBuffer(&buf); - } - } -} - -/* - * \du or \dg - * - * Describes roles. Any schema portion of the pattern is ignored. - */ -bool -describeRoles(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printTableContent cont; - printTableOpt myopt = pset.popt.topt; - int ncols = 3; - int nrows = 0; - int i; - int conns; - const char align = 'l'; - char **attr; - - myopt.default_footer = false; - - initPQExpBuffer(&buf); - - if (pset.sversion >= 80100) - { - printfPQExpBuffer(&buf, - "SELECT r.rolname, r.rolsuper, r.rolinherit,\n" - " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n" - " r.rolconnlimit, r.rolvaliduntil,\n" - " ARRAY(SELECT b.rolname\n" - " FROM pg_catalog.pg_auth_members m\n" - " JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n" - " WHERE m.member = r.oid) as memberof"); - - if (verbose && pset.sversion >= 80200) - { - appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description"); - ncols++; - } - if (pset.sversion >= 90100) - { - appendPQExpBufferStr(&buf, "\n, r.rolreplication"); - } - - if (pset.sversion >= 90500) - { - appendPQExpBufferStr(&buf, "\n, r.rolbypassrls"); - } - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "r.rolname", NULL, NULL); - } - else - { - printfPQExpBuffer(&buf, - "SELECT u.usename AS rolname,\n" - " u.usesuper AS rolsuper,\n" - " true AS rolinherit, false AS rolcreaterole,\n" - " u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n" - " -1 AS rolconnlimit," - " u.valuntil as rolvaliduntil,\n" - " ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof" - "\nFROM pg_catalog.pg_user u\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "u.usename", NULL, NULL); - } - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - if (!res) - return false; - - nrows = PQntuples(res); - attr = pg_malloc0((nrows + 1) * sizeof(*attr)); - - printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows); - - printTableAddHeader(&cont, gettext_noop("Role name"), true, align); - printTableAddHeader(&cont, gettext_noop("Attributes"), true, align); - printTableAddHeader(&cont, gettext_noop("Member of"), true, align); - - if (verbose && pset.sversion >= 80200) - printTableAddHeader(&cont, gettext_noop("Description"), true, align); - - for (i = 0; i < nrows; i++) - { - printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false); - - resetPQExpBuffer(&buf); - if (strcmp(PQgetvalue(res, i, 1), "t") == 0) - add_role_attribute(&buf, _("Superuser")); - - if (strcmp(PQgetvalue(res, i, 2), "t") != 0) - add_role_attribute(&buf, _("No inheritance")); - - if (strcmp(PQgetvalue(res, i, 3), "t") == 0) - add_role_attribute(&buf, _("Create role")); - - if (strcmp(PQgetvalue(res, i, 4), "t") == 0) - add_role_attribute(&buf, _("Create DB")); - - if (strcmp(PQgetvalue(res, i, 5), "t") != 0) - add_role_attribute(&buf, _("Cannot login")); - - if (pset.sversion >= 90100) - if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0) - add_role_attribute(&buf, _("Replication")); - - if (pset.sversion >= 90500) - if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0) - add_role_attribute(&buf, _("Bypass RLS")); - - conns = atoi(PQgetvalue(res, i, 6)); - if (conns >= 0) - { - if (buf.len > 0) - appendPQExpBufferChar(&buf, '\n'); - - if (conns == 0) - appendPQExpBufferStr(&buf, _("No connections")); - else - appendPQExpBuffer(&buf, ngettext("%d connection", - "%d connections", - conns), - conns); - } - - if (strcmp(PQgetvalue(res, i, 7), "") != 0) - { - if (buf.len > 0) - appendPQExpBufferStr(&buf, "\n"); - appendPQExpBufferStr(&buf, _("Password valid until ")); - appendPQExpBufferStr(&buf, PQgetvalue(res, i, 7)); - } - - attr[i] = pg_strdup(buf.data); - - printTableAddCell(&cont, attr[i], false, false); - - printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false); - - if (verbose && pset.sversion >= 80200) - printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); - } - termPQExpBuffer(&buf); - - printTable(&cont, pset.queryFout, false, pset.logfile); - printTableCleanup(&cont); - - for (i = 0; i < nrows; i++) - free(attr[i]); - free(attr); - - PQclear(res); - return true; -} - -static void -add_role_attribute(PQExpBuffer buf, const char *const str) -{ - if (buf->len > 0) - appendPQExpBufferStr(buf, ", "); - - appendPQExpBufferStr(buf, str); -} - -/* - * \drds - */ -bool -listDbRoleSettings(const char *pattern, const char *pattern2) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - if (pset.sversion >= 90000) - { - bool havewhere; - - printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n" - "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n" - "FROM pg_db_role_setting AS s\n" - "LEFT JOIN pg_database ON pg_database.oid = setdatabase\n" - "LEFT JOIN pg_roles ON pg_roles.oid = setrole\n", - gettext_noop("Role"), - gettext_noop("Database"), - gettext_noop("Settings")); - havewhere = processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "pg_roles.rolname", NULL, NULL); - processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false, - NULL, "pg_database.datname", NULL, NULL); - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - } - else - { - fprintf(pset.queryFout, - _("No per-database role settings support in this server version.\n")); - return false; - } - - res = PSQLexec(buf.data); - if (!res) - return false; - - if (PQntuples(res) == 0 && !pset.quiet) - { - if (pattern) - fprintf(pset.queryFout, _("No matching settings found.\n")); - else - fprintf(pset.queryFout, _("No settings found.\n")); - } - else - { - myopt.nullPrint = NULL; - myopt.title = _("List of settings"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - } - - PQclear(res); - resetPQExpBuffer(&buf); - return true; -} - - -/* - * listTables() - * - * handler for \dt, \di, etc. - * - * tabtypes is an array of characters, specifying what info is desired: - * t - tables - * i - indexes - * v - views - * m - materialized views - * s - sequences - * E - foreign table (Note: different from 'f', the relkind value) - * (any order of the above is fine) - * If tabtypes is empty, we default to \dtvsE. - */ -bool -listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem) -{ - bool showTables = strchr(tabtypes, 't') != NULL; - bool showIndexes = strchr(tabtypes, 'i') != NULL; - bool showViews = strchr(tabtypes, 'v') != NULL; - bool showMatViews = strchr(tabtypes, 'm') != NULL; - bool showSeq = strchr(tabtypes, 's') != NULL; - bool showForeign = strchr(tabtypes, 'E') != NULL; - - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false, false, false, false}; - - if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign)) - showTables = showViews = showMatViews = showSeq = showForeign = true; - - initPQExpBuffer(&buf); - - /* - * Note: as of Pg 8.2, we no longer use relkind 's', but we keep it here - * for backwards compatibility. - */ - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " c.relname as \"%s\",\n" - " CASE c.relkind" - " WHEN 'r' THEN '%s'" - " WHEN 'v' THEN '%s'" - " WHEN 'm' THEN '%s'" - " WHEN 'i' THEN '%s'" - " WHEN 'S' THEN '%s'" - " WHEN 's' THEN '%s'" - " WHEN 'f' THEN '%s'" - " END as \"%s\",\n" - " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("table"), - gettext_noop("view"), - gettext_noop("materialized view"), - gettext_noop("index"), - gettext_noop("sequence"), - gettext_noop("special"), - gettext_noop("foreign table"), - gettext_noop("Type"), - gettext_noop("Owner")); - - if (showIndexes) - appendPQExpBuffer(&buf, - ",\n c2.relname as \"%s\"", - gettext_noop("Table")); - - if (verbose) - { - /* - * As of PostgreSQL 9.0, use pg_table_size() to show a more acurate - * size of a table, including FSM, VM and TOAST tables. - */ - if (pset.sversion >= 90000) - appendPQExpBuffer(&buf, - ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\"", - gettext_noop("Size")); - else if (pset.sversion >= 80100) - appendPQExpBuffer(&buf, - ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"", - gettext_noop("Size")); - - appendPQExpBuffer(&buf, - ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"", - gettext_noop("Description")); - } - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_class c" - "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"); - if (showIndexes) - appendPQExpBufferStr(&buf, - "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid" - "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid"); - - appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); - if (showTables) - appendPQExpBufferStr(&buf, "'r',"); - if (showViews) - appendPQExpBufferStr(&buf, "'v',"); - if (showMatViews) - appendPQExpBufferStr(&buf, "'m',"); - if (showIndexes) - appendPQExpBufferStr(&buf, "'i',"); - if (showSeq) - appendPQExpBufferStr(&buf, "'S',"); - if (showSystem || pattern) - appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL in <= - * 8.1 */ - if (showForeign) - appendPQExpBufferStr(&buf, "'f',"); - - appendPQExpBufferStr(&buf, "''"); /* dummy */ - appendPQExpBufferStr(&buf, ")\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - /* - * TOAST objects are suppressed unconditionally. Since we don't provide - * any way to select relkind 't' above, we would never show toast tables - * in any case; it seems a bit confusing to allow their indexes to be - * shown. Use plain \d if you really need to look at a TOAST table/index. - */ - appendPQExpBufferStr(&buf, " AND n.nspname !~ '^pg_toast'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "c.relname", NULL, - "pg_catalog.pg_table_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1,2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - if (PQntuples(res) == 0 && !pset.quiet) - { - if (pattern) - fprintf(pset.queryFout, _("No matching relations found.\n")); - else - fprintf(pset.queryFout, _("No relations found.\n")); - } - else - { - myopt.nullPrint = NULL; - myopt.title = _("List of relations"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - } - - PQclear(res); - return true; -} - - -/* - * \dL - * - * Describes languages. - */ -bool -listLanguages(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT l.lanname AS \"%s\",\n", - gettext_noop("Name")); - if (pset.sversion >= 80300) - appendPQExpBuffer(&buf, - " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n", - gettext_noop("Owner")); - - appendPQExpBuffer(&buf, - " l.lanpltrusted AS \"%s\"", - gettext_noop("Trusted")); - - if (verbose) - { - appendPQExpBuffer(&buf, - ",\n NOT l.lanispl AS \"%s\",\n" - " l.lanplcallfoid::regprocedure AS \"%s\",\n" - " l.lanvalidator::regprocedure AS \"%s\",\n ", - gettext_noop("Internal Language"), - gettext_noop("Call Handler"), - gettext_noop("Validator")); - if (pset.sversion >= 90000) - appendPQExpBuffer(&buf, "l.laninline::regprocedure AS \"%s\",\n ", - gettext_noop("Inline Handler")); - printACLColumn(&buf, "l.lanacl"); - } - - appendPQExpBuffer(&buf, - ",\n d.description AS \"%s\"" - "\nFROM pg_catalog.pg_language l\n" - "LEFT JOIN pg_catalog.pg_description d\n" - " ON d.classoid = l.tableoid AND d.objoid = l.oid\n" - " AND d.objsubid = 0\n", - gettext_noop("Description")); - - if (pattern) - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "l.lanname", NULL, NULL); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n"); - - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of languages"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * \dD - * - * Describes domains. - */ -bool -listDomains(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname as \"%s\",\n" - " t.typname as \"%s\",\n" - " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n" - " TRIM(LEADING\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Type")); - - if (pset.sversion >= 90100) - appendPQExpBufferStr(&buf, - " COALESCE((SELECT ' collate ' || c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n" - " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation), '') ||\n"); - appendPQExpBuffer(&buf, - " CASE WHEN t.typnotnull THEN ' not null' ELSE '' END ||\n" - " CASE WHEN t.typdefault IS NOT NULL THEN ' default ' || t.typdefault ELSE '' END\n" - " ) as \"%s\",\n" - " pg_catalog.array_to_string(ARRAY(\n" - " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n" - " ), ' ') as \"%s\"", - gettext_noop("Modifier"), - gettext_noop("Check")); - - if (verbose) - { - if (pset.sversion >= 90200) - { - appendPQExpBufferStr(&buf, ",\n "); - printACLColumn(&buf, "t.typacl"); - } - appendPQExpBuffer(&buf, - ",\n d.description as \"%s\"", - gettext_noop("Description")); - } - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_type t\n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"); - - if (verbose) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_description d " - "ON d.classoid = t.tableoid AND d.objoid = t.oid " - "AND d.objsubid = 0\n"); - - appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "t.typname", NULL, - "pg_catalog.pg_type_is_visible(t.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of domains"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dc - * - * Describes conversions. - */ -bool -listConversions(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = - {false, false, false, false, true, false}; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname AS \"%s\",\n" - " c.conname AS \"%s\",\n" - " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n" - " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n" - " CASE WHEN c.condefault THEN '%s'\n" - " ELSE '%s' END AS \"%s\"", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Source"), - gettext_noop("Destination"), - gettext_noop("yes"), gettext_noop("no"), - gettext_noop("Default?")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n d.description AS \"%s\"", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_conversion c\n" - " JOIN pg_catalog.pg_namespace n " - "ON n.oid = c.connamespace\n"); - - if (verbose) - appendPQExpBufferStr(&buf, - "LEFT JOIN pg_catalog.pg_description d " - "ON d.classoid = c.tableoid\n" - " AND d.objoid = c.oid " - "AND d.objsubid = 0\n"); - - appendPQExpBufferStr(&buf, "WHERE true\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "c.conname", NULL, - "pg_catalog.pg_conversion_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of conversions"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dy - * - * Describes Event Triggers. - */ -bool -listEventTriggers(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = - {false, false, false, true, false, false, false}; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT evtname as \"%s\", " - "evtevent as \"%s\", " - "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n" - " case evtenabled when 'O' then '%s'" - " when 'R' then '%s'" - " when 'A' then '%s'" - " when 'D' then '%s' end as \"%s\",\n" - " e.evtfoid::pg_catalog.regproc as \"%s\", " - "pg_catalog.array_to_string(array(select x" - " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"", - gettext_noop("Name"), - gettext_noop("Event"), - gettext_noop("Owner"), - gettext_noop("enabled"), - gettext_noop("replica"), - gettext_noop("always"), - gettext_noop("disabled"), - gettext_noop("Enabled"), - gettext_noop("Procedure"), - gettext_noop("Tags")); - if (verbose) - appendPQExpBuffer(&buf, - ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"", - gettext_noop("Description")); - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_event_trigger e "); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "evtname", NULL, NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of event triggers"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dC - * - * Describes casts. - */ -bool -listCasts(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, true, false}; - - initPQExpBuffer(&buf); - - /* - * We need a left join to pg_proc for binary casts; the others are just - * paranoia. Also note that we don't attempt to localize '(binary - * coercible)', because there's too much risk of gettext translating a - * function name that happens to match some string in the PO database. - */ - printfPQExpBuffer(&buf, - "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n" - " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n" - " CASE WHEN castfunc = 0 THEN '(binary coercible)'\n" - " ELSE p.proname\n" - " END as \"%s\",\n" - " CASE WHEN c.castcontext = 'e' THEN '%s'\n" - " WHEN c.castcontext = 'a' THEN '%s'\n" - " ELSE '%s'\n" - " END as \"%s\"", - gettext_noop("Source type"), - gettext_noop("Target type"), - gettext_noop("Function"), - gettext_noop("no"), - gettext_noop("in assignment"), - gettext_noop("yes"), - gettext_noop("Implicit?")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n d.description AS \"%s\"\n", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n" - " ON c.castfunc = p.oid\n" - " LEFT JOIN pg_catalog.pg_type ts\n" - " ON c.castsource = ts.oid\n" - " LEFT JOIN pg_catalog.pg_namespace ns\n" - " ON ns.oid = ts.typnamespace\n" - " LEFT JOIN pg_catalog.pg_type tt\n" - " ON c.casttarget = tt.oid\n" - " LEFT JOIN pg_catalog.pg_namespace nt\n" - " ON nt.oid = tt.typnamespace\n"); - - if (verbose) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_description d\n" - " ON d.classoid = c.tableoid AND d.objoid = " - "c.oid AND d.objsubid = 0\n"); - - appendPQExpBufferStr(&buf, "WHERE ( (true"); - - /* - * Match name pattern against either internal or external name of either - * castsource or casttarget - */ - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "ns.nspname", "ts.typname", - "pg_catalog.format_type(ts.oid, NULL)", - "pg_catalog.pg_type_is_visible(ts.oid)"); - - appendPQExpBufferStr(&buf, ") OR (true"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "nt.nspname", "tt.typname", - "pg_catalog.format_type(tt.oid, NULL)", - "pg_catalog.pg_type_is_visible(tt.oid)"); - - appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of casts"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dO - * - * Describes collations. - */ -bool -listCollations(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, false}; - - if (pset.sversion < 90100) - { - psql_error("The server (version %d.%d) does not support collations.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT n.nspname AS \"%s\",\n" - " c.collname AS \"%s\",\n" - " c.collcollate AS \"%s\",\n" - " c.collctype AS \"%s\"", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Collate"), - gettext_noop("Ctype")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n" - "WHERE n.oid = c.collnamespace\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n" - " AND n.nspname <> 'information_schema'\n"); - - /* - * Hide collations that aren't usable in the current database's encoding. - * If you think to change this, note that pg_collation_is_visible rejects - * unusable collations, so you will need to hack name pattern processing - * somehow to avoid inconsistent behavior. - */ - appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n"); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "c.collname", NULL, - "pg_catalog.pg_collation_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of collations"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dn - * - * Describes schemas (namespaces) - */ -bool -listSchemas(const char *pattern, bool verbose, bool showSystem) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT n.nspname AS \"%s\",\n" - " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"", - gettext_noop("Name"), - gettext_noop("Owner")); - - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n "); - printACLColumn(&buf, "n.nspacl"); - appendPQExpBuffer(&buf, - ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"", - gettext_noop("Description")); - } - - appendPQExpBuffer(&buf, - "\nFROM pg_catalog.pg_namespace n\n"); - - if (!showSystem && !pattern) - appendPQExpBufferStr(&buf, - "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n"); - - processSQLNamePattern(pset.db, &buf, pattern, - !showSystem && !pattern, false, - NULL, "n.nspname", NULL, - NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of schemas"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * \dFp - * list text search parsers - */ -bool -listTSParsers(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80300) - { - psql_error("The server (version %d.%d) does not support full text search.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - if (verbose) - return listTSParsersVerbose(pattern); - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT \n" - " n.nspname as \"%s\",\n" - " p.prsname as \"%s\",\n" - " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n" - "FROM pg_catalog.pg_ts_parser p \n" - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Description") - ); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - "n.nspname", "p.prsname", NULL, - "pg_catalog.pg_ts_parser_is_visible(p.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of text search parsers"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * full description of parsers - */ -static bool -listTSParsersVerbose(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - int i; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT p.oid, \n" - " n.nspname, \n" - " p.prsname \n" - "FROM pg_catalog.pg_ts_parser p\n" - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n" - ); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - "n.nspname", "p.prsname", NULL, - "pg_catalog.pg_ts_parser_is_visible(p.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - if (PQntuples(res) == 0) - { - if (!pset.quiet) - psql_error("Did not find any text search parser named \"%s\".\n", - pattern); - PQclear(res); - return false; - } - - for (i = 0; i < PQntuples(res); i++) - { - const char *oid; - const char *nspname = NULL; - const char *prsname; - - oid = PQgetvalue(res, i, 0); - if (!PQgetisnull(res, i, 1)) - nspname = PQgetvalue(res, i, 1); - prsname = PQgetvalue(res, i, 2); - - if (!describeOneTSParser(oid, nspname, prsname)) - { - PQclear(res); - return false; - } - - if (cancel_pressed) - { - PQclear(res); - return false; - } - } - - PQclear(res); - return true; -} - -static bool -describeOneTSParser(const char *oid, const char *nspname, const char *prsname) -{ - PQExpBufferData buf; - PGresult *res; - char title[1024]; - printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {true, false, false}; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT '%s' AS \"%s\", \n" - " p.prsstart::pg_catalog.regproc AS \"%s\", \n" - " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\" \n" - " FROM pg_catalog.pg_ts_parser p \n" - " WHERE p.oid = '%s' \n" - "UNION ALL \n" - "SELECT '%s', \n" - " p.prstoken::pg_catalog.regproc, \n" - " pg_catalog.obj_description(p.prstoken, 'pg_proc') \n" - " FROM pg_catalog.pg_ts_parser p \n" - " WHERE p.oid = '%s' \n" - "UNION ALL \n" - "SELECT '%s', \n" - " p.prsend::pg_catalog.regproc, \n" - " pg_catalog.obj_description(p.prsend, 'pg_proc') \n" - " FROM pg_catalog.pg_ts_parser p \n" - " WHERE p.oid = '%s' \n" - "UNION ALL \n" - "SELECT '%s', \n" - " p.prsheadline::pg_catalog.regproc, \n" - " pg_catalog.obj_description(p.prsheadline, 'pg_proc') \n" - " FROM pg_catalog.pg_ts_parser p \n" - " WHERE p.oid = '%s' \n" - "UNION ALL \n" - "SELECT '%s', \n" - " p.prslextype::pg_catalog.regproc, \n" - " pg_catalog.obj_description(p.prslextype, 'pg_proc') \n" - " FROM pg_catalog.pg_ts_parser p \n" - " WHERE p.oid = '%s';", - gettext_noop("Start parse"), - gettext_noop("Method"), - gettext_noop("Function"), - gettext_noop("Description"), - oid, - gettext_noop("Get next token"), - oid, - gettext_noop("End parse"), - oid, - gettext_noop("Get headline"), - oid, - gettext_noop("Get token types"), - oid); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - if (nspname) - sprintf(title, _("Text search parser \"%s.%s\""), nspname, prsname); - else - sprintf(title, _("Text search parser \"%s\""), prsname); - myopt.title = title; - myopt.footers = NULL; - myopt.topt.default_footer = false; - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT t.alias as \"%s\", \n" - " t.description as \"%s\" \n" - "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t \n" - "ORDER BY 1;", - gettext_noop("Token name"), - gettext_noop("Description"), - oid); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - if (nspname) - sprintf(title, _("Token types for parser \"%s.%s\""), nspname, prsname); - else - sprintf(title, _("Token types for parser \"%s\""), prsname); - myopt.title = title; - myopt.footers = NULL; - myopt.topt.default_footer = true; - myopt.translate_header = true; - myopt.translate_columns = NULL; - myopt.n_translate_columns = 0; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * \dFd - * list text search dictionaries - */ -bool -listTSDictionaries(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80300) - { - psql_error("The server (version %d.%d) does not support full text search.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT \n" - " n.nspname as \"%s\",\n" - " d.dictname as \"%s\",\n", - gettext_noop("Schema"), - gettext_noop("Name")); - - if (verbose) - { - appendPQExpBuffer(&buf, - " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM \n" - " pg_catalog.pg_ts_template t \n" - " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace \n" - " WHERE d.dicttemplate = t.oid ) AS \"%s\", \n" - " d.dictinitoption as \"%s\", \n", - gettext_noop("Template"), - gettext_noop("Init options")); - } - - appendPQExpBuffer(&buf, - " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n", - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n" - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - "n.nspname", "d.dictname", NULL, - "pg_catalog.pg_ts_dict_is_visible(d.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of text search dictionaries"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * \dFt - * list text search templates - */ -bool -listTSTemplates(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80300) - { - psql_error("The server (version %d.%d) does not support full text search.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - - if (verbose) - printfPQExpBuffer(&buf, - "SELECT \n" - " n.nspname AS \"%s\",\n" - " t.tmplname AS \"%s\",\n" - " t.tmplinit::pg_catalog.regproc AS \"%s\",\n" - " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n" - " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Init"), - gettext_noop("Lexize"), - gettext_noop("Description")); - else - printfPQExpBuffer(&buf, - "SELECT \n" - " n.nspname AS \"%s\",\n" - " t.tmplname AS \"%s\",\n" - " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n" - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - "n.nspname", "t.tmplname", NULL, - "pg_catalog.pg_ts_template_is_visible(t.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of text search templates"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - - -/* - * \dF - * list text search configurations - */ -bool -listTSConfigs(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80300) - { - psql_error("The server (version %d.%d) does not support full text search.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - if (verbose) - return listTSConfigsVerbose(pattern); - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT \n" - " n.nspname as \"%s\",\n" - " c.cfgname as \"%s\",\n" - " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n" - "FROM pg_catalog.pg_ts_config c\n" - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace \n", - gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Description") - ); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - "n.nspname", "c.cfgname", NULL, - "pg_catalog.pg_ts_config_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of text search configurations"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -static bool -listTSConfigsVerbose(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - int i; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT c.oid, c.cfgname,\n" - " n.nspname, \n" - " p.prsname, \n" - " np.nspname as pnspname \n" - "FROM pg_catalog.pg_ts_config c \n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace, \n" - " pg_catalog.pg_ts_parser p \n" - " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace \n" - "WHERE p.oid = c.cfgparser\n" - ); - - processSQLNamePattern(pset.db, &buf, pattern, true, false, - "n.nspname", "c.cfgname", NULL, - "pg_catalog.pg_ts_config_is_visible(c.oid)"); - - appendPQExpBufferStr(&buf, "ORDER BY 3, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - if (PQntuples(res) == 0) - { - if (!pset.quiet) - psql_error("Did not find any text search configuration named \"%s\".\n", - pattern); - PQclear(res); - return false; - } - - for (i = 0; i < PQntuples(res); i++) - { - const char *oid; - const char *cfgname; - const char *nspname = NULL; - const char *prsname; - const char *pnspname = NULL; - - oid = PQgetvalue(res, i, 0); - cfgname = PQgetvalue(res, i, 1); - if (!PQgetisnull(res, i, 2)) - nspname = PQgetvalue(res, i, 2); - prsname = PQgetvalue(res, i, 3); - if (!PQgetisnull(res, i, 4)) - pnspname = PQgetvalue(res, i, 4); - - if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname)) - { - PQclear(res); - return false; - } - - if (cancel_pressed) - { - PQclear(res); - return false; - } - } - - PQclear(res); - return true; -} - -static bool -describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, - const char *pnspname, const char *prsname) -{ - PQExpBufferData buf, - title; - PGresult *res; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - - printfPQExpBuffer(&buf, - "SELECT \n" - " ( SELECT t.alias FROM \n" - " pg_catalog.ts_token_type(c.cfgparser) AS t \n" - " WHERE t.tokid = m.maptokentype ) AS \"%s\", \n" - " pg_catalog.btrim( \n" - " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary \n" - " FROM pg_catalog.pg_ts_config_map AS mm \n" - " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype \n" - " ORDER BY mapcfg, maptokentype, mapseqno \n" - " ) :: pg_catalog.text , \n" - " '{}') AS \"%s\" \n" - "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n" - "WHERE c.oid = '%s' AND m.mapcfg = c.oid \n" - "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n" - "ORDER BY 1;", - gettext_noop("Token"), - gettext_noop("Dictionaries"), - oid); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - initPQExpBuffer(&title); - - if (nspname) - appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""), - nspname, cfgname); - else - appendPQExpBuffer(&title, _("Text search configuration \"%s\""), - cfgname); - - if (pnspname) - appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""), - pnspname, prsname); - else - appendPQExpBuffer(&title, _("\nParser: \"%s\""), - prsname); - - myopt.nullPrint = NULL; - myopt.title = title.data; - myopt.footers = NULL; - myopt.topt.default_footer = false; - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - termPQExpBuffer(&title); - - PQclear(res); - return true; -} - - -/* - * \dew - * - * Describes foreign-data wrappers - */ -bool -listForeignDataWrappers(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80400) - { - psql_error("The server (version %d.%d) does not support foreign-data wrappers.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT fdw.fdwname AS \"%s\",\n" - " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n", - gettext_noop("Name"), - gettext_noop("Owner")); - if (pset.sversion >= 90100) - appendPQExpBuffer(&buf, - " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n", - gettext_noop("Handler")); - appendPQExpBuffer(&buf, - " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"", - gettext_noop("Validator")); - - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n "); - printACLColumn(&buf, "fdwacl"); - appendPQExpBuffer(&buf, - ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE " - " '(' || array_to_string(ARRAY(SELECT " - " quote_ident(option_name) || ' ' || " - " quote_literal(option_value) FROM " - " pg_options_to_table(fdwoptions)), ', ') || ')' " - " END AS \"%s\"", - gettext_noop("FDW Options")); - - if (pset.sversion >= 90100) - appendPQExpBuffer(&buf, - ",\n d.description AS \"%s\" ", - gettext_noop("Description")); - } - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n"); - - if (verbose && pset.sversion >= 90100) - appendPQExpBufferStr(&buf, - "LEFT JOIN pg_catalog.pg_description d\n" - " ON d.classoid = fdw.tableoid " - "AND d.objoid = fdw.oid AND d.objsubid = 0\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "fdwname", NULL, NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of foreign-data wrappers"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \des - * - * Describes foreign servers. - */ -bool -listForeignServers(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80400) - { - psql_error("The server (version %d.%d) does not support foreign servers.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT s.srvname AS \"%s\",\n" - " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n" - " f.fdwname AS \"%s\"", - gettext_noop("Name"), - gettext_noop("Owner"), - gettext_noop("Foreign-data wrapper")); - - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n "); - printACLColumn(&buf, "s.srvacl"); - appendPQExpBuffer(&buf, - ",\n" - " s.srvtype AS \"%s\",\n" - " s.srvversion AS \"%s\",\n" - " CASE WHEN srvoptions IS NULL THEN '' ELSE " - " '(' || array_to_string(ARRAY(SELECT " - " quote_ident(option_name) || ' ' || " - " quote_literal(option_value) FROM " - " pg_options_to_table(srvoptions)), ', ') || ')' " - " END AS \"%s\",\n" - " d.description AS \"%s\"", - gettext_noop("Type"), - gettext_noop("Version"), - gettext_noop("FDW Options"), - gettext_noop("Description")); - } - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_foreign_server s\n" - " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"); - - if (verbose) - appendPQExpBufferStr(&buf, - "LEFT JOIN pg_description d\n " - "ON d.classoid = s.tableoid AND d.objoid = s.oid " - "AND d.objsubid = 0\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "s.srvname", NULL, NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of foreign servers"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \deu - * - * Describes user mappings. - */ -bool -listUserMappings(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 80400) - { - psql_error("The server (version %d.%d) does not support user mappings.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT um.srvname AS \"%s\",\n" - " um.usename AS \"%s\"", - gettext_noop("Server"), - gettext_noop("User name")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n CASE WHEN umoptions IS NULL THEN '' ELSE " - " '(' || array_to_string(ARRAY(SELECT " - " quote_ident(option_name) || ' ' || " - " quote_literal(option_value) FROM " - " pg_options_to_table(umoptions)), ', ') || ')' " - " END AS \"%s\"", - gettext_noop("FDW Options")); - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "um.srvname", "um.usename", NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of user mappings"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \det - * - * Describes foreign tables. - */ -bool -listForeignTables(const char *pattern, bool verbose) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 90100) - { - psql_error("The server (version %d.%d) does not support foreign tables.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT n.nspname AS \"%s\",\n" - " c.relname AS \"%s\",\n" - " s.srvname AS \"%s\"", - gettext_noop("Schema"), - gettext_noop("Table"), - gettext_noop("Server")); - - if (verbose) - appendPQExpBuffer(&buf, - ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE " - " '(' || array_to_string(ARRAY(SELECT " - " quote_ident(option_name) || ' ' || " - " quote_literal(option_value) FROM " - " pg_options_to_table(ftoptions)), ', ') || ')' " - " END AS \"%s\",\n" - " d.description AS \"%s\"", - gettext_noop("FDW Options"), - gettext_noop("Description")); - - appendPQExpBufferStr(&buf, - "\nFROM pg_catalog.pg_foreign_table ft\n" - " INNER JOIN pg_catalog.pg_class c" - " ON c.oid = ft.ftrelid\n" - " INNER JOIN pg_catalog.pg_namespace n" - " ON n.oid = c.relnamespace\n" - " INNER JOIN pg_catalog.pg_foreign_server s" - " ON s.oid = ft.ftserver\n"); - if (verbose) - appendPQExpBufferStr(&buf, - " LEFT JOIN pg_catalog.pg_description d\n" - " ON d.classoid = c.tableoid AND " - "d.objoid = c.oid AND d.objsubid = 0\n"); - - processSQLNamePattern(pset.db, &buf, pattern, false, false, - NULL, "n.nspname", "c.relname", NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1, 2;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of foreign tables"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dx - * - * Briefly describes installed extensions. - */ -bool -listExtensions(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - printQueryOpt myopt = pset.popt; - - if (pset.sversion < 90100) - { - psql_error("The server (version %d.%d) does not support extensions.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT e.extname AS \"%s\", " - "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n" - "FROM pg_catalog.pg_extension e " - "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace " - "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid " - "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n", - gettext_noop("Name"), - gettext_noop("Version"), - gettext_noop("Schema"), - gettext_noop("Description")); - - processSQLNamePattern(pset.db, &buf, pattern, - false, false, - NULL, "e.extname", NULL, - NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - myopt.title = _("List of installed extensions"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * \dx+ - * - * List contents of installed extensions. - */ -bool -listExtensionContents(const char *pattern) -{ - PQExpBufferData buf; - PGresult *res; - int i; - - if (pset.sversion < 90100) - { - psql_error("The server (version %d.%d) does not support extensions.\n", - pset.sversion / 10000, (pset.sversion / 100) % 100); - return true; - } - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT e.extname, e.oid\n" - "FROM pg_catalog.pg_extension e\n"); - - processSQLNamePattern(pset.db, &buf, pattern, - false, false, - NULL, "e.extname", NULL, - NULL); - - appendPQExpBufferStr(&buf, "ORDER BY 1;"); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - if (PQntuples(res) == 0) - { - if (!pset.quiet) - { - if (pattern) - psql_error("Did not find any extension named \"%s\".\n", - pattern); - else - psql_error("Did not find any extensions.\n"); - } - PQclear(res); - return false; - } - - for (i = 0; i < PQntuples(res); i++) - { - const char *extname; - const char *oid; - - extname = PQgetvalue(res, i, 0); - oid = PQgetvalue(res, i, 1); - - if (!listOneExtensionContents(extname, oid)) - { - PQclear(res); - return false; - } - if (cancel_pressed) - { - PQclear(res); - return false; - } - } - - PQclear(res); - return true; -} - -static bool -listOneExtensionContents(const char *extname, const char *oid) -{ - PQExpBufferData buf; - PGresult *res; - char title[1024]; - printQueryOpt myopt = pset.popt; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, - "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n" - "FROM pg_catalog.pg_depend\n" - "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n" - "ORDER BY 1;", - gettext_noop("Object Description"), - oid); - - res = PSQLexec(buf.data); - termPQExpBuffer(&buf); - if (!res) - return false; - - myopt.nullPrint = NULL; - snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname); - myopt.title = title; - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} - -/* - * printACLColumn - * - * Helper function for consistently formatting ACL (privilege) columns. - * The proper targetlist entry is appended to buf. Note lack of any - * whitespace or comma decoration. - */ -static void -printACLColumn(PQExpBuffer buf, const char *colname) -{ - if (pset.sversion >= 80100) - appendPQExpBuffer(buf, - "pg_catalog.array_to_string(%s, E'\\n') AS \"%s\"", - colname, gettext_noop("Access privileges")); - else - appendPQExpBuffer(buf, - "pg_catalog.array_to_string(%s, '\\n') AS \"%s\"", - colname, gettext_noop("Access privileges")); -} diff --git a/src/bin/csql/describe.h b/src/bin/csql/describe.h deleted file mode 100644 index 822e71ae4..000000000 --- a/src/bin/csql/describe.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/describe.h - */ -#ifndef DESCRIBE_H -#define DESCRIBE_H - - -/* \da */ -extern bool describeAggregates(const char *pattern, bool verbose, bool showSystem); - -/* \db */ -extern bool describeTablespaces(const char *pattern, bool verbose); - -/* \df, \dfa, \dfn, \dft, \dfw, etc. */ -extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem); - -/* \dT */ -extern bool describeTypes(const char *pattern, bool verbose, bool showSystem); - -/* \do */ -extern bool describeOperators(const char *pattern, bool verbose, bool showSystem); - -/* \du, \dg */ -extern bool describeRoles(const char *pattern, bool verbose); - -/* \drds */ -extern bool listDbRoleSettings(const char *pattern1, const char *pattern2); - -/* \z (or \dp) */ -extern bool permissionsList(const char *pattern); - -/* \ddp */ -extern bool listDefaultACLs(const char *pattern); - -/* \dd */ -extern bool objectDescription(const char *pattern, bool showSystem); - -/* \d foo */ -extern bool describeTableDetails(const char *pattern, bool verbose, bool showSystem); - -/* \dF */ -extern bool listTSConfigs(const char *pattern, bool verbose); - -/* \dFp */ -extern bool listTSParsers(const char *pattern, bool verbose); - -/* \dFd */ -extern bool listTSDictionaries(const char *pattern, bool verbose); - -/* \dFt */ -extern bool listTSTemplates(const char *pattern, bool verbose); - -/* \l */ -extern bool listAllDbs(const char *pattern, bool verbose); - -/* \dt, \di, \ds, \dS, etc. */ -extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem); - -/* \dD */ -extern bool listDomains(const char *pattern, bool verbose, bool showSystem); - -/* \dc */ -extern bool listConversions(const char *pattern, bool verbose, bool showSystem); - -/* \dC */ -extern bool listCasts(const char *pattern, bool verbose); - -/* \dO */ -extern bool listCollations(const char *pattern, bool verbose, bool showSystem); - -/* \dn */ -extern bool listSchemas(const char *pattern, bool verbose, bool showSystem); - -/* \dew */ -extern bool listForeignDataWrappers(const char *pattern, bool verbose); - -/* \des */ -extern bool listForeignServers(const char *pattern, bool verbose); - -/* \deu */ -extern bool listUserMappings(const char *pattern, bool verbose); - -/* \det */ -extern bool listForeignTables(const char *pattern, bool verbose); - -/* \dL */ -extern bool listLanguages(const char *pattern, bool verbose, bool showSystem); - -/* \dx */ -extern bool listExtensions(const char *pattern); - -/* \dx+ */ -extern bool listExtensionContents(const char *pattern); - -/* \dy */ -extern bool listEventTriggers(const char *pattern, bool verbose); - -#endif /* DESCRIBE_H */ diff --git a/src/bin/csql/dumputils.c b/src/bin/csql/dumputils.c deleted file mode 100644 index d7506e119..000000000 --- a/src/bin/csql/dumputils.c +++ /dev/null @@ -1,1243 +0,0 @@ -/*------------------------------------------------------------------------- - * - * Utility routines for SQL dumping - * Basically this is stuff that is useful in both pg_dump and pg_dumpall. - * Lately it's also being used by psql and bin/scripts/ ... - * - * - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/bin/pg_dump/dumputils.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include - -#include "dumputils.h" - -#include "parser/keywords.h" - - -/* Globals from keywords.c */ -extern const ScanKeyword FEScanKeywords[]; -extern const int NumFEScanKeywords; - -#define supports_grant_options(version) ((version) >= 70400) - -static bool parseAclItem(const char *item, const char *type, - const char *name, const char *subname, int remoteVersion, - PQExpBuffer grantee, PQExpBuffer grantor, - PQExpBuffer privs, PQExpBuffer privswgo); -static char *copyAclUserName(PQExpBuffer output, char *input); -static void AddAcl(PQExpBuffer aclbuf, const char *keyword, - const char *subname); -static PQExpBuffer defaultGetLocalPQExpBuffer(void); - -/* Globals exported by this file */ -int quote_all_identifiers = 0; -PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer; - -/* - * Returns a temporary PQExpBuffer, valid until the next call to the function. - * This is used by fmtId and fmtQualifiedId. - * - * Non-reentrant and non-thread-safe but reduces memory leakage. You can - * replace this with a custom version by setting the getLocalPQExpBuffer - * function pointer. - */ -static PQExpBuffer -defaultGetLocalPQExpBuffer(void) -{ - static PQExpBuffer id_return = NULL; - - if (id_return) /* first time through? */ - { - /* same buffer, just wipe contents */ - resetPQExpBuffer(id_return); - } - else - { - /* new buffer */ - id_return = createPQExpBuffer(); - } - - return id_return; -} - -/* - * Quotes input string if it's not a legitimate SQL identifier as-is. - * - * Note that the returned string must be used before calling fmtId again, - * since we re-use the same return buffer each time. - */ -const char * -fmtId(const char *rawid) -{ - PQExpBuffer id_return = getLocalPQExpBuffer(); - - const char *cp; - bool need_quotes = false; - - /* - * These checks need to match the identifier production in scan.l. Don't - * use islower() etc. - */ - if (quote_all_identifiers) - need_quotes = true; - /* slightly different rules for first character */ - else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) - need_quotes = true; - else - { - /* otherwise check the entire string */ - for (cp = rawid; *cp; cp++) - { - if (!((*cp >= 'a' && *cp <= 'z') - || (*cp >= '0' && *cp <= '9') - || (*cp == '_'))) - { - need_quotes = true; - break; - } - } - } - - if (!need_quotes) - { - /* - * Check for keyword. We quote keywords except for unreserved ones. - * (In some cases we could avoid quoting a col_name or type_func_name - * keyword, but it seems much harder than it's worth to tell that.) - * - * Note: ScanKeywordLookup() does case-insensitive comparison, but - * that's fine, since we already know we have all-lower-case. - */ - const ScanKeyword *keyword = ScanKeywordLookup(rawid, - FEScanKeywords, - NumFEScanKeywords); - - if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) - need_quotes = true; - } - - if (!need_quotes) - { - /* no quoting needed */ - appendPQExpBufferStr(id_return, rawid); - } - else - { - appendPQExpBufferChar(id_return, '\"'); - for (cp = rawid; *cp; cp++) - { - /* - * Did we find a double-quote in the string? Then make this a - * double double-quote per SQL99. Before, we put in a - * backslash/double-quote pair. - thomas 2000-08-05 - */ - if (*cp == '\"') - appendPQExpBufferChar(id_return, '\"'); - appendPQExpBufferChar(id_return, *cp); - } - appendPQExpBufferChar(id_return, '\"'); - } - - return id_return->data; -} - -/* - * fmtQualifiedId - convert a qualified name to the proper format for - * the source database. - * - * Like fmtId, use the result before calling again. - * - * Since we call fmtId and it also uses getThreadLocalPQExpBuffer() we cannot - * use it until we're finished with calling fmtId(). - */ -const char * -fmtQualifiedId(int remoteVersion, const char *schema, const char *id) -{ - PQExpBuffer id_return; - PQExpBuffer lcl_pqexp = createPQExpBuffer(); - - /* Suppress schema name if fetching from pre-7.3 DB */ - if (remoteVersion >= 70300 && schema && *schema) - { - appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema)); - } - appendPQExpBufferStr(lcl_pqexp, fmtId(id)); - - id_return = getLocalPQExpBuffer(); - - appendPQExpBufferStr(id_return, lcl_pqexp->data); - destroyPQExpBuffer(lcl_pqexp); - - return id_return->data; -} - -/* - * Convert a string value to an SQL string literal and append it to - * the given buffer. We assume the specified client_encoding and - * standard_conforming_strings settings. - * - * This is essentially equivalent to libpq's PQescapeStringInternal, - * except for the output buffer structure. We need it in situations - * where we do not have a PGconn available. Where we do, - * appendStringLiteralConn is a better choice. - */ -void -appendStringLiteral(PQExpBuffer buf, const char *str, - int encoding, bool std_strings) -{ - size_t length = strlen(str); - const char *source = str; - char *target; - - if (!enlargePQExpBuffer(buf, 2 * length + 2)) - return; - - target = buf->data + buf->len; - *target++ = '\''; - - while (*source != '\0') - { - char c = *source; - int len; - int i; - - /* Fast path for plain ASCII */ - if (!IS_HIGHBIT_SET(c)) - { - /* Apply quoting if needed */ - if (SQL_STR_DOUBLE(c, !std_strings)) - *target++ = c; - /* Copy the character */ - *target++ = c; - source++; - continue; - } - - /* Slow path for possible multibyte characters */ - len = PQmblen(source, encoding); - - /* Copy the character */ - for (i = 0; i < len; i++) - { - if (*source == '\0') - break; - *target++ = *source++; - } - - /* - * If we hit premature end of string (ie, incomplete multibyte - * character), try to pad out to the correct length with spaces. We - * may not be able to pad completely, but we will always be able to - * insert at least one pad space (since we'd not have quoted a - * multibyte character). This should be enough to make a string that - * the server will error out on. - */ - if (i < len) - { - char *stop = buf->data + buf->maxlen - 2; - - for (; i < len; i++) - { - if (target >= stop) - break; - *target++ = ' '; - } - break; - } - } - - /* Write the terminating quote and NUL character. */ - *target++ = '\''; - *target = '\0'; - - buf->len = target - buf->data; -} - - -/* - * Convert a string value to an SQL string literal and append it to - * the given buffer. Encoding and string syntax rules are as indicated - * by current settings of the PGconn. - */ -void -appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn) -{ - size_t length = strlen(str); - - /* - * XXX This is a kluge to silence escape_string_warning in our utility - * programs. It should go away someday. - */ - if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100) - { - /* ensure we are not adjacent to an identifier */ - if (buf->len > 0 && buf->data[buf->len - 1] != ' ') - appendPQExpBufferChar(buf, ' '); - appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX); - appendStringLiteral(buf, str, PQclientEncoding(conn), false); - return; - } - /* XXX end kluge */ - - if (!enlargePQExpBuffer(buf, 2 * length + 2)) - return; - appendPQExpBufferChar(buf, '\''); - buf->len += PQescapeStringConn(conn, buf->data + buf->len, - str, length, NULL); - appendPQExpBufferChar(buf, '\''); -} - - -/* - * Convert a string value to a dollar quoted literal and append it to - * the given buffer. If the dqprefix parameter is not NULL then the - * dollar quote delimiter will begin with that (after the opening $). - * - * No escaping is done at all on str, in compliance with the rules - * for parsing dollar quoted strings. Also, we need not worry about - * encoding issues. - */ -void -appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix) -{ - static const char suffixes[] = "_XXXXXXX"; - int nextchar = 0; - PQExpBuffer delimBuf = createPQExpBuffer(); - - /* start with $ + dqprefix if not NULL */ - appendPQExpBufferChar(delimBuf, '$'); - if (dqprefix) - appendPQExpBufferStr(delimBuf, dqprefix); - - /* - * Make sure we choose a delimiter which (without the trailing $) is not - * present in the string being quoted. We don't check with the trailing $ - * because a string ending in $foo must not be quoted with $foo$. - */ - while (strstr(str, delimBuf->data) != NULL) - { - appendPQExpBufferChar(delimBuf, suffixes[nextchar++]); - nextchar %= sizeof(suffixes) - 1; - } - - /* add trailing $ */ - appendPQExpBufferChar(delimBuf, '$'); - - /* quote it and we are all done */ - appendPQExpBufferStr(buf, delimBuf->data); - appendPQExpBufferStr(buf, str); - appendPQExpBufferStr(buf, delimBuf->data); - - destroyPQExpBuffer(delimBuf); -} - - -/* - * Convert a bytea value (presented as raw bytes) to an SQL string literal - * and append it to the given buffer. We assume the specified - * standard_conforming_strings setting. - * - * This is needed in situations where we do not have a PGconn available. - * Where we do, PQescapeByteaConn is a better choice. - */ -void -appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length, - bool std_strings) -{ - const unsigned char *source = str; - char *target; - - static const char hextbl[] = "0123456789abcdef"; - - /* - * This implementation is hard-wired to produce hex-format output. We do - * not know the server version the output will be loaded into, so making - * an intelligent format choice is impossible. It might be better to - * always use the old escaped format. - */ - if (!enlargePQExpBuffer(buf, 2 * length + 5)) - return; - - target = buf->data + buf->len; - *target++ = '\''; - if (!std_strings) - *target++ = '\\'; - *target++ = '\\'; - *target++ = 'x'; - - while (length-- > 0) - { - unsigned char c = *source++; - - *target++ = hextbl[(c >> 4) & 0xF]; - *target++ = hextbl[c & 0xF]; - } - - /* Write the terminating quote and NUL character. */ - *target++ = '\''; - *target = '\0'; - - buf->len = target - buf->data; -} - - -/* - * Deconstruct the text representation of a 1-dimensional Postgres array - * into individual items. - * - * On success, returns true and sets *itemarray and *nitems to describe - * an array of individual strings. On parse failure, returns false; - * *itemarray may exist or be NULL. - * - * NOTE: free'ing itemarray is sufficient to deallocate the working storage. - */ -bool -parsePGArray(const char *atext, char ***itemarray, int *nitems) -{ - int inputlen; - char **items; - char *strings; - int curitem; - - /* - * We expect input in the form of "{item,item,item}" where any item is - * either raw data, or surrounded by double quotes (in which case embedded - * characters including backslashes and quotes are backslashed). - * - * We build the result as an array of pointers followed by the actual - * string data, all in one malloc block for convenience of deallocation. - * The worst-case storage need is not more than one pointer and one - * character for each input character (consider "{,,,,,,,,,,}"). - */ - *itemarray = NULL; - *nitems = 0; - inputlen = strlen(atext); - if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}') - return false; /* bad input */ - items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char))); - if (items == NULL) - return false; /* out of memory */ - *itemarray = items; - strings = (char *) (items + inputlen); - - atext++; /* advance over initial '{' */ - curitem = 0; - while (*atext != '}') - { - if (*atext == '\0') - return false; /* premature end of string */ - items[curitem] = strings; - while (*atext != '}' && *atext != ',') - { - if (*atext == '\0') - return false; /* premature end of string */ - if (*atext != '"') - *strings++ = *atext++; /* copy unquoted data */ - else - { - /* process quoted substring */ - atext++; - while (*atext != '"') - { - if (*atext == '\0') - return false; /* premature end of string */ - if (*atext == '\\') - { - atext++; - if (*atext == '\0') - return false; /* premature end of string */ - } - *strings++ = *atext++; /* copy quoted data */ - } - atext++; - } - } - *strings++ = '\0'; - if (*atext == ',') - atext++; - curitem++; - } - if (atext[1] != '\0') - return false; /* bogus syntax (embedded '}') */ - *nitems = curitem; - return true; -} - - -/* - * Build GRANT/REVOKE command(s) for an object. - * - * name: the object name, in the form to use in the commands (already quoted) - * subname: the sub-object name, if any (already quoted); NULL if none - * type: the object type (as seen in GRANT command: must be one of - * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE, - * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT) - * acls: the ACL string fetched from the database - * owner: username of object owner (will be passed through fmtId); can be - * NULL or empty string to indicate "no owner known" - * prefix: string to prefix to each generated command; typically empty - * remoteVersion: version of database - * - * Returns TRUE if okay, FALSE if could not parse the acl string. - * The resulting commands (if any) are appended to the contents of 'sql'. - * - * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES " - * or something similar, and name is an empty string. - * - * Note: beware of passing a fmtId() result directly as 'name' or 'subname', - * since this routine uses fmtId() internally. - */ -bool -buildACLCommands(const char *name, const char *subname, - const char *type, const char *acls, const char *owner, - const char *prefix, int remoteVersion, - PQExpBuffer sql) -{ - bool ok = true; - char **aclitems; - int naclitems; - int i; - PQExpBuffer grantee, - grantor, - privs, - privswgo; - PQExpBuffer firstsql, - secondsql; - bool found_owner_privs = false; - - if (strlen(acls) == 0) - return true; /* object has default permissions */ - - /* treat empty-string owner same as NULL */ - if (owner && *owner == '\0') - owner = NULL; - - if (!parsePGArray(acls, &aclitems, &naclitems)) - { - if (aclitems) - free(aclitems); - return false; - } - - grantee = createPQExpBuffer(); - grantor = createPQExpBuffer(); - privs = createPQExpBuffer(); - privswgo = createPQExpBuffer(); - - /* - * At the end, these two will be pasted together to form the result. But - * the owner privileges need to go before the other ones to keep the - * dependencies valid. In recent versions this is normally the case, but - * in old versions they come after the PUBLIC privileges and that results - * in problems if we need to run REVOKE on the owner privileges. - */ - firstsql = createPQExpBuffer(); - secondsql = createPQExpBuffer(); - - /* - * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to - * wire-in knowledge about the default public privileges for different - * kinds of objects. - */ - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name); - - /* - * We still need some hacking though to cover the case where new default - * public privileges are added in new versions: the REVOKE ALL will revoke - * them, leading to behavior different from what the old version had, - * which is generally not what's wanted. So add back default privs if the - * source database is too old to have had that particular priv. - */ - if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0) - { - /* database CONNECT priv didn't exist before 8.2 */ - appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n", - prefix, type, name); - } - - /* Scan individual ACL items */ - for (i = 0; i < naclitems; i++) - { - if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion, - grantee, grantor, privs, privswgo)) - { - ok = false; - break; - } - - if (grantor->len == 0 && owner) - printfPQExpBuffer(grantor, "%s", owner); - - if (privs->len > 0 || privswgo->len > 0) - { - if (owner - && strcmp(grantee->data, owner) == 0 - && strcmp(grantor->data, owner) == 0) - { - found_owner_privs = true; - - /* - * For the owner, the default privilege level is ALL WITH - * GRANT OPTION (only ALL prior to 7.4). - */ - if (supports_grant_options(remoteVersion) - ? strcmp(privswgo->data, "ALL") != 0 - : strcmp(privs->data, "ALL") != 0) - { - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", - type, name, fmtId(grantee->data)); - if (privs->len > 0) - appendPQExpBuffer(firstsql, - "%sGRANT %s ON %s %s TO %s;\n", - prefix, privs->data, type, name, - fmtId(grantee->data)); - if (privswgo->len > 0) - appendPQExpBuffer(firstsql, - "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", - prefix, privswgo->data, type, name, - fmtId(grantee->data)); - } - } - else - { - /* - * Otherwise can assume we are starting from no privs. - */ - if (grantor->len > 0 - && (!owner || strcmp(owner, grantor->data) != 0)) - appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n", - fmtId(grantor->data)); - - if (privs->len > 0) - { - appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ", - prefix, privs->data, type, name); - if (grantee->len == 0) - appendPQExpBufferStr(secondsql, "PUBLIC;\n"); - else if (strncmp(grantee->data, "group ", - strlen("group ")) == 0) - appendPQExpBuffer(secondsql, "GROUP %s;\n", - fmtId(grantee->data + strlen("group "))); - else - appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data)); - } - if (privswgo->len > 0) - { - appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ", - prefix, privswgo->data, type, name); - if (grantee->len == 0) - appendPQExpBufferStr(secondsql, "PUBLIC"); - else if (strncmp(grantee->data, "group ", - strlen("group ")) == 0) - appendPQExpBuffer(secondsql, "GROUP %s", - fmtId(grantee->data + strlen("group "))); - else - appendPQExpBufferStr(secondsql, fmtId(grantee->data)); - appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n"); - } - - if (grantor->len > 0 - && (!owner || strcmp(owner, grantor->data) != 0)) - appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n"); - } - } - } - - /* - * If we didn't find any owner privs, the owner must have revoked 'em all - */ - if (!found_owner_privs && owner) - { - appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix); - if (subname) - appendPQExpBuffer(firstsql, "(%s)", subname); - appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", - type, name, fmtId(owner)); - } - - destroyPQExpBuffer(grantee); - destroyPQExpBuffer(grantor); - destroyPQExpBuffer(privs); - destroyPQExpBuffer(privswgo); - - appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data); - destroyPQExpBuffer(firstsql); - destroyPQExpBuffer(secondsql); - - free(aclitems); - - return ok; -} - -/* - * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry. - * - * type: the object type (TABLES, FUNCTIONS, etc) - * nspname: schema name, or NULL for global default privileges - * acls: the ACL string fetched from the database - * owner: username of privileges owner (will be passed through fmtId) - * remoteVersion: version of database - * - * Returns TRUE if okay, FALSE if could not parse the acl string. - * The resulting commands (if any) are appended to the contents of 'sql'. - */ -bool -buildDefaultACLCommands(const char *type, const char *nspname, - const char *acls, const char *owner, - int remoteVersion, - PQExpBuffer sql) -{ - bool result; - PQExpBuffer prefix; - - prefix = createPQExpBuffer(); - - /* - * We incorporate the target role directly into the command, rather than - * playing around with SET ROLE or anything like that. This is so that a - * permissions error leads to nothing happening, rather than changing - * default privileges for the wrong user. - */ - appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ", - fmtId(owner)); - if (nspname) - appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname)); - - result = buildACLCommands("", NULL, - type, acls, owner, - prefix->data, remoteVersion, - sql); - - destroyPQExpBuffer(prefix); - - return result; -} - -/* - * This will parse an aclitem string, having the general form - * username=privilegecodes/grantor - * or - * group groupname=privilegecodes/grantor - * (the /grantor part will not be present if pre-7.4 database). - * - * The returned grantee string will be the dequoted username or groupname - * (preceded with "group " in the latter case). The returned grantor is - * the dequoted grantor name or empty. Privilege characters are decoded - * and split between privileges with grant option (privswgo) and without - * (privs). - * - * Note: for cross-version compatibility, it's important to use ALL when - * appropriate. - */ -static bool -parseAclItem(const char *item, const char *type, - const char *name, const char *subname, int remoteVersion, - PQExpBuffer grantee, PQExpBuffer grantor, - PQExpBuffer privs, PQExpBuffer privswgo) -{ - char *buf; - bool all_with_go = true; - bool all_without_go = true; - char *eqpos; - char *slpos; - char *pos; - - buf = strdup(item); - if (!buf) - return false; - - /* user or group name is string up to = */ - eqpos = copyAclUserName(grantee, buf); - if (*eqpos != '=') - { - free(buf); - return false; - } - - /* grantor may be listed after / */ - slpos = strchr(eqpos + 1, '/'); - if (slpos) - { - *slpos++ = '\0'; - slpos = copyAclUserName(grantor, slpos); - if (*slpos != '\0') - { - free(buf); - return false; - } - } - else - resetPQExpBuffer(grantor); - - /* privilege codes */ -#define CONVERT_PRIV(code, keywd) \ -do { \ - if ((pos = strchr(eqpos + 1, code))) \ - { \ - if (*(pos + 1) == '*') \ - { \ - AddAcl(privswgo, keywd, subname); \ - all_without_go = false; \ - } \ - else \ - { \ - AddAcl(privs, keywd, subname); \ - all_with_go = false; \ - } \ - } \ - else \ - all_with_go = all_without_go = false; \ -} while (0) - - resetPQExpBuffer(privs); - resetPQExpBuffer(privswgo); - - if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 || - strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0) - { - CONVERT_PRIV('r', "SELECT"); - - if (strcmp(type, "SEQUENCE") == 0 || - strcmp(type, "SEQUENCES") == 0) - /* sequence only */ - CONVERT_PRIV('U', "USAGE"); - else - { - /* table only */ - CONVERT_PRIV('a', "INSERT"); - if (remoteVersion >= 70200) - CONVERT_PRIV('x', "REFERENCES"); - /* rest are not applicable to columns */ - if (subname == NULL) - { - if (remoteVersion >= 70200) - { - CONVERT_PRIV('d', "DELETE"); - CONVERT_PRIV('t', "TRIGGER"); - } - if (remoteVersion >= 80400) - CONVERT_PRIV('D', "TRUNCATE"); - } - } - - /* UPDATE */ - if (remoteVersion >= 70200 || - strcmp(type, "SEQUENCE") == 0 || - strcmp(type, "SEQUENCES") == 0) - CONVERT_PRIV('w', "UPDATE"); - else - /* 7.0 and 7.1 have a simpler worldview */ - CONVERT_PRIV('w', "UPDATE,DELETE"); - } - else if (strcmp(type, "FUNCTION") == 0 || - strcmp(type, "FUNCTIONS") == 0) - CONVERT_PRIV('X', "EXECUTE"); - else if (strcmp(type, "LANGUAGE") == 0) - CONVERT_PRIV('U', "USAGE"); - else if (strcmp(type, "SCHEMA") == 0) - { - CONVERT_PRIV('C', "CREATE"); - CONVERT_PRIV('U', "USAGE"); - } - else if (strcmp(type, "DATABASE") == 0) - { - CONVERT_PRIV('C', "CREATE"); - CONVERT_PRIV('c', "CONNECT"); - CONVERT_PRIV('T', "TEMPORARY"); - } - else if (strcmp(type, "TABLESPACE") == 0) - CONVERT_PRIV('C', "CREATE"); - else if (strcmp(type, "TYPE") == 0 || - strcmp(type, "TYPES") == 0) - CONVERT_PRIV('U', "USAGE"); - else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0) - CONVERT_PRIV('U', "USAGE"); - else if (strcmp(type, "FOREIGN SERVER") == 0) - CONVERT_PRIV('U', "USAGE"); - else if (strcmp(type, "FOREIGN TABLE") == 0) - CONVERT_PRIV('r', "SELECT"); - else if (strcmp(type, "LARGE OBJECT") == 0) - { - CONVERT_PRIV('r', "SELECT"); - CONVERT_PRIV('w', "UPDATE"); - } - else - abort(); - -#undef CONVERT_PRIV - - if (all_with_go) - { - resetPQExpBuffer(privs); - printfPQExpBuffer(privswgo, "ALL"); - if (subname) - appendPQExpBuffer(privswgo, "(%s)", subname); - } - else if (all_without_go) - { - resetPQExpBuffer(privswgo); - printfPQExpBuffer(privs, "ALL"); - if (subname) - appendPQExpBuffer(privs, "(%s)", subname); - } - - free(buf); - - return true; -} - -/* - * Transfer a user or group name starting at *input into the output buffer, - * dequoting if needed. Returns a pointer to just past the input name. - * The name is taken to end at an unquoted '=' or end of string. - */ -static char * -copyAclUserName(PQExpBuffer output, char *input) -{ - resetPQExpBuffer(output); - - while (*input && *input != '=') - { - /* - * If user name isn't quoted, then just add it to the output buffer - */ - if (*input != '"') - appendPQExpBufferChar(output, *input++); - else - { - /* Otherwise, it's a quoted username */ - input++; - /* Loop until we come across an unescaped quote */ - while (!(*input == '"' && *(input + 1) != '"')) - { - if (*input == '\0') - return input; /* really a syntax error... */ - - /* - * Quoting convention is to escape " as "". Keep this code in - * sync with putid() in backend's acl.c. - */ - if (*input == '"' && *(input + 1) == '"') - input++; - appendPQExpBufferChar(output, *input++); - } - input++; - } - } - return input; -} - -/* - * Append a privilege keyword to a keyword list, inserting comma if needed. - */ -static void -AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname) -{ - if (aclbuf->len > 0) - appendPQExpBufferChar(aclbuf, ','); - appendPQExpBufferStr(aclbuf, keyword); - if (subname) - appendPQExpBuffer(aclbuf, "(%s)", subname); -} - - -/* - * processSQLNamePattern - * - * Scan a wildcard-pattern string and generate appropriate WHERE clauses - * to limit the set of objects returned. The WHERE clauses are appended - * to the already-partially-constructed query in buf. Returns whether - * any clause was added. - * - * conn: connection query will be sent to (consulted for escaping rules). - * buf: output parameter. - * pattern: user-specified pattern option, or NULL if none ("*" is implied). - * have_where: true if caller already emitted "WHERE" (clauses will be ANDed - * onto the existing WHERE clause). - * force_escape: always quote regexp special characters, even outside - * double quotes (else they are quoted only between double quotes). - * schemavar: name of query variable to match against a schema-name pattern. - * Can be NULL if no schema. - * namevar: name of query variable to match against an object-name pattern. - * altnamevar: NULL, or name of an alternative variable to match against name. - * visibilityrule: clause to use if we want to restrict to visible objects - * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL. - * - * Formatting note: the text already present in buf should end with a newline. - * The appended text, if any, will end with one too. - */ -bool -processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, - bool have_where, bool force_escape, - const char *schemavar, const char *namevar, - const char *altnamevar, const char *visibilityrule) -{ - PQExpBufferData schemabuf; - PQExpBufferData namebuf; - int encoding = PQclientEncoding(conn); - bool inquotes; - const char *cp; - int i; - bool added_clause = false; - -#define WHEREAND() \ - (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \ - have_where = true, added_clause = true) - - if (pattern == NULL) - { - /* Default: select all visible objects */ - if (visibilityrule) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s\n", visibilityrule); - } - return added_clause; - } - - initPQExpBuffer(&schemabuf); - initPQExpBuffer(&namebuf); - - /* - * Parse the pattern, converting quotes and lower-casing unquoted letters. - * Also, adjust shell-style wildcard characters into regexp notation. - * - * We surround the pattern with "^(...)$" to force it to match the whole - * string, as per SQL practice. We have to have parens in case the string - * contains "|", else the "^" and "$" will be bound into the first and - * last alternatives which is not what we want. - * - * Note: the result of this pass is the actual regexp pattern(s) we want - * to execute. Quoting/escaping into SQL literal format will be done - * below using appendStringLiteralConn(). - */ - appendPQExpBufferStr(&namebuf, "^("); - - inquotes = false; - cp = pattern; - - while (*cp) - { - char ch = *cp; - - if (ch == '"') - { - if (inquotes && cp[1] == '"') - { - /* emit one quote, stay in inquotes mode */ - appendPQExpBufferChar(&namebuf, '"'); - cp++; - } - else - inquotes = !inquotes; - cp++; - } - else if (!inquotes && isupper((unsigned char) ch)) - { - appendPQExpBufferChar(&namebuf, - pg_tolower((unsigned char) ch)); - cp++; - } - else if (!inquotes && ch == '*') - { - appendPQExpBufferStr(&namebuf, ".*"); - cp++; - } - else if (!inquotes && ch == '?') - { - appendPQExpBufferChar(&namebuf, '.'); - cp++; - } - else if (!inquotes && ch == '.') - { - /* Found schema/name separator, move current pattern to schema */ - resetPQExpBuffer(&schemabuf); - appendPQExpBufferStr(&schemabuf, namebuf.data); - resetPQExpBuffer(&namebuf); - appendPQExpBufferStr(&namebuf, "^("); - cp++; - } - else if (ch == '$') - { - /* - * Dollar is always quoted, whether inside quotes or not. The - * reason is that it's allowed in SQL identifiers, so there's a - * significant use-case for treating it literally, while because - * we anchor the pattern automatically there is no use-case for - * having it possess its regexp meaning. - */ - appendPQExpBufferStr(&namebuf, "\\$"); - cp++; - } - else - { - /* - * Ordinary data character, transfer to pattern - * - * Inside double quotes, or at all times if force_escape is true, - * quote regexp special characters with a backslash to avoid - * regexp errors. Outside quotes, however, let them pass through - * as-is; this lets knowledgeable users build regexp expressions - * that are more powerful than shell-style patterns. - */ - if ((inquotes || force_escape) && - strchr("|*+?()[]{}.^$\\", ch)) - appendPQExpBufferChar(&namebuf, '\\'); - i = PQmblen(cp, encoding); - while (i-- && *cp) - { - appendPQExpBufferChar(&namebuf, *cp); - cp++; - } - } - } - - /* - * Now decide what we need to emit. Note there will be a leading "^(" in - * the patterns in any case. - */ - if (namebuf.len > 2) - { - /* We have a name pattern, so constrain the namevar(s) */ - - appendPQExpBufferStr(&namebuf, ")$"); - /* Optimize away a "*" pattern */ - if (strcmp(namebuf.data, "^(.*)$") != 0) - { - WHEREAND(); - if (altnamevar) - { - appendPQExpBuffer(buf, "(%s ~ ", namevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBufferStr(buf, ")\n"); - } - else - { - appendPQExpBuffer(buf, "%s ~ ", namevar); - appendStringLiteralConn(buf, namebuf.data, conn); - appendPQExpBufferChar(buf, '\n'); - } - } - } - - if (schemabuf.len > 2) - { - /* We have a schema pattern, so constrain the schemavar */ - - appendPQExpBufferStr(&schemabuf, ")$"); - /* Optimize away a "*" pattern */ - if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s ~ ", schemavar); - appendStringLiteralConn(buf, schemabuf.data, conn); - appendPQExpBufferChar(buf, '\n'); - } - } - else - { - /* No schema pattern given, so select only visible objects */ - if (visibilityrule) - { - WHEREAND(); - appendPQExpBuffer(buf, "%s\n", visibilityrule); - } - } - - termPQExpBuffer(&schemabuf); - termPQExpBuffer(&namebuf); - - return added_clause; -#undef WHEREAND -} - -/* - * buildShSecLabelQuery - * - * Build a query to retrieve security labels for a shared object. - */ -void -buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId, - PQExpBuffer sql) -{ - appendPQExpBuffer(sql, - "SELECT provider, label FROM pg_catalog.pg_shseclabel " - "WHERE classoid = '%s'::pg_catalog.regclass AND " - "objoid = %u", catalog_name, objectId); -} - -/* - * emitShSecLabels - * - * Format security label data retrieved by the query generated in - * buildShSecLabelQuery. - */ -void -emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, - const char *target, const char *objname) -{ - int i; - - for (i = 0; i < PQntuples(res); i++) - { - char *provider = PQgetvalue(res, i, 0); - char *label = PQgetvalue(res, i, 1); - - /* must use fmtId result before calling it again */ - appendPQExpBuffer(buffer, - "SECURITY LABEL FOR %s ON %s", - fmtId(provider), target); - appendPQExpBuffer(buffer, - " %s IS ", - fmtId(objname)); - appendStringLiteralConn(buffer, label, conn); - appendPQExpBufferStr(buffer, ";\n"); - } -} - - -void -simple_string_list_append(SimpleStringList *list, const char *val) -{ - SimpleStringListCell *cell; - - cell = (SimpleStringListCell *) - pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1); - - cell->next = NULL; - strcpy(cell->val, val); - - if (list->tail) - list->tail->next = cell; - else - list->head = cell; - list->tail = cell; -} - -bool -simple_string_list_member(SimpleStringList *list, const char *val) -{ - SimpleStringListCell *cell; - - for (cell = list->head; cell; cell = cell->next) - { - if (strcmp(cell->val, val) == 0) - return true; - } - return false; -} diff --git a/src/bin/csql/help.c b/src/bin/csql/help.c deleted file mode 100644 index b5c2e405e..000000000 --- a/src/bin/csql/help.c +++ /dev/null @@ -1,572 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/help.c - */ -#include "postgres_fe.h" - -#ifndef WIN32 -#include /* (ditto) */ -#include /* for geteuid() */ -#else -#include -#endif - -#ifndef WIN32 -#include /* for ioctl() */ -#endif - -#ifdef HAVE_TERMIOS_H -#include -#endif - -#include "common.h" -#include "common/username.h" -#include "help.h" -#include "input.h" -#include "settings.h" -#include "sql_help.h" - - -/* - * PLEASE: - * If you change something in this file, also make the same changes - * in the DocBook documentation, file ref/psql-ref.sgml. If you don't - * know how to do it, please find someone who can help you. - */ - - -/* - * usage - * - * print out command line arguments - */ -#define ON(var) (var ? _("on") : _("off")) - -void -usage(unsigned short int pager) -{ - const char *env; - const char *user; - char *errstr; - FILE *output; - - /* Find default user, in case we need it. */ - user = getenv("PGUSER"); - if (!user) - { - user = get_user_name(&errstr); - if (!user) - { - psql_error("%s\n", errstr); - exit(EXIT_FAILURE); - } - } - - output = PageOutput(59, pager ? &(pset.popt.topt) : NULL); - - printf(_("csql is the Citus interactive terminal.\n\n")); - fprintf(output, _("Usage:\n")); - printf(_(" csql [OPTION]... [DBNAME [USERNAME]]\n\n")); - - fprintf(output, _("General options:\n")); - /* Display default database */ - env = getenv("PGDATABASE"); - if (!env) - env = user; - fprintf(output, _(" -c, --command=COMMAND run only single command (SQL or internal) and exit\n")); - fprintf(output, _(" -d, --dbname=DBNAME database name to connect to (default: \"%s\")\n"), env); - fprintf(output, _(" -f, --file=FILENAME execute commands from file, then exit\n")); - fprintf(output, _(" -l, --list list available databases, then exit\n")); - fprintf(output, _(" -v, --set=, --variable=NAME=VALUE\n" - " set psql variable NAME to VALUE\n" - " (e.g., -v ON_ERROR_STOP=1)\n")); - fprintf(output, _(" -V, --version output version information, then exit\n")); - fprintf(output, _(" -X, --no-psqlrc do not read startup file (~/.psqlrc)\n")); - fprintf(output, _(" -1 (\"one\"), --single-transaction\n" - " execute as a single transaction (if non-interactive)\n")); - fprintf(output, _(" -?, --help[=options] show this help, then exit\n")); - fprintf(output, _(" --help=commands list backslash commands, then exit\n")); - fprintf(output, _(" --help=variables list special variables, then exit\n")); - - fprintf(output, _("\nInput and output options:\n")); - fprintf(output, _(" -a, --echo-all echo all input from script\n")); - fprintf(output, _(" -b, --echo-errors echo failed commands\n")); - fprintf(output, _(" -e, --echo-queries echo commands sent to server\n")); - fprintf(output, _(" -E, --echo-hidden display queries that internal commands generate\n")); - fprintf(output, _(" -L, --log-file=FILENAME send session log to file\n")); - fprintf(output, _(" -n, --no-readline disable enhanced command line editing (readline)\n")); - fprintf(output, _(" -o, --output=FILENAME send query results to file (or |pipe)\n")); - fprintf(output, _(" -q, --quiet run quietly (no messages, only query output)\n")); - fprintf(output, _(" -s, --single-step single-step mode (confirm each query)\n")); - fprintf(output, _(" -S, --single-line single-line mode (end of line terminates SQL command)\n")); - - fprintf(output, _("\nOutput format options:\n")); - fprintf(output, _(" -A, --no-align unaligned table output mode\n")); - fprintf(output, _(" -F, --field-separator=STRING\n" - " field separator for unaligned output (default: \"%s\")\n"), - DEFAULT_FIELD_SEP); - fprintf(output, _(" -H, --html HTML table output mode\n")); - fprintf(output, _(" -P, --pset=VAR[=ARG] set printing option VAR to ARG (see \\pset command)\n")); - fprintf(output, _(" -R, --record-separator=STRING\n" - " record separator for unaligned output (default: newline)\n")); - fprintf(output, _(" -t, --tuples-only print rows only\n")); - fprintf(output, _(" -T, --table-attr=TEXT set HTML table tag attributes (e.g., width, border)\n")); - fprintf(output, _(" -x, --expanded turn on expanded table output\n")); - fprintf(output, _(" -z, --field-separator-zero\n" - " set field separator for unaligned output to zero byte\n")); - fprintf(output, _(" -0, --record-separator-zero\n" - " set record separator for unaligned output to zero byte\n")); - - fprintf(output, _("\nConnection options:\n")); - /* Display default host */ - env = getenv("PGHOST"); - fprintf(output, _(" -h, --host=HOSTNAME database server host or socket directory (default: \"%s\")\n"), - env ? env : _("local socket")); - /* Display default port */ - env = getenv("PGPORT"); - fprintf(output, _(" -p, --port=PORT database server port (default: \"%s\")\n"), - env ? env : DEF_PGPORT_STR); - /* Display default user */ - env = getenv("PGUSER"); - if (!env) - env = user; - fprintf(output, _(" -U, --username=USERNAME database user name (default: \"%s\")\n"), env); - fprintf(output, _(" -w, --no-password never prompt for password\n")); - fprintf(output, _(" -W, --password force password prompt (should happen automatically)\n")); - - fprintf(output, _("\nFor more information, type \"\\?\" (for internal commands) or \"\\help\" (for SQL\n" - "commands) from within psql, or consult the psql section in the PostgreSQL\n" - "documentation.\n\n")); - fprintf(output, _("Report bugs to .\n")); - - ClosePager(output); -} - - -/* - * slashUsage - * - * print out help for the backslash commands - */ -void -slashUsage(unsigned short int pager) -{ - FILE *output; - char *currdb; - - currdb = PQdb(pset.db); - - output = PageOutput(103, pager ? &(pset.popt.topt) : NULL); - - /* if you add/remove a line here, change the row count above */ - - fprintf(output, _("General\n")); - fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n")); - fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n")); - fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n")); - fprintf(output, _(" \\q quit psql\n")); - fprintf(output, _(" \\watch [SEC] execute query every SEC seconds\n")); - fprintf(output, "\n"); - - fprintf(output, _("Help\n")); - - fprintf(output, _(" \\? [commands] show help on backslash commands\n")); - fprintf(output, _(" \\? options show help on psql command-line options\n")); - fprintf(output, _(" \\? variables show help on special variables\n")); - fprintf(output, _(" \\h [NAME] help on syntax of SQL commands, * for all commands\n")); - fprintf(output, "\n"); - - fprintf(output, _("Query Buffer\n")); - fprintf(output, _(" \\e [FILE] [LINE] edit the query buffer (or file) with external editor\n")); - fprintf(output, _(" \\ef [FUNCNAME [LINE]] edit function definition with external editor\n")); - fprintf(output, _(" \\p show the contents of the query buffer\n")); - fprintf(output, _(" \\r reset (clear) the query buffer\n")); -#ifdef USE_READLINE - fprintf(output, _(" \\s [FILE] display history or save it to file\n")); -#endif - fprintf(output, _(" \\w FILE write query buffer to file\n")); - fprintf(output, "\n"); - - fprintf(output, _("Input/Output\n")); - fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n")); - fprintf(output, _(" \\echo [STRING] write string to standard output\n")); - fprintf(output, _(" \\i FILE execute commands from file\n")); - fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n")); - fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n")); - fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n")); - fprintf(output, "\n"); - - fprintf(output, _("Informational\n")); - fprintf(output, _(" (options: S = show system objects, + = additional detail)\n")); - fprintf(output, _(" \\d[S+] list tables, views, and sequences\n")); - fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n")); - fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n")); - fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n")); - fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n")); - fprintf(output, _(" \\dC[+] [PATTERN] list casts\n")); - fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n")); - fprintf(output, _(" \\ddp [PATTERN] list default privileges\n")); - fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n")); - fprintf(output, _(" \\det[+] [PATTERN] list foreign tables\n")); - fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n")); - fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n")); - fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n")); - fprintf(output, _(" \\df[antw][S+] [PATRN] list [only agg/normal/trigger/window] functions\n")); - fprintf(output, _(" \\dF[+] [PATTERN] list text search configurations\n")); - fprintf(output, _(" \\dFd[+] [PATTERN] list text search dictionaries\n")); - fprintf(output, _(" \\dFp[+] [PATTERN] list text search parsers\n")); - fprintf(output, _(" \\dFt[+] [PATTERN] list text search templates\n")); - fprintf(output, _(" \\dg[+] [PATTERN] list roles\n")); - fprintf(output, _(" \\di[S+] [PATTERN] list indexes\n")); - fprintf(output, _(" \\dl list large objects, same as \\lo_list\n")); - fprintf(output, _(" \\dL[S+] [PATTERN] list procedural languages\n")); - fprintf(output, _(" \\dm[S+] [PATTERN] list materialized views\n")); - fprintf(output, _(" \\dn[S+] [PATTERN] list schemas\n")); - fprintf(output, _(" \\do[S] [PATTERN] list operators\n")); - fprintf(output, _(" \\dO[S+] [PATTERN] list collations\n")); - fprintf(output, _(" \\dp [PATTERN] list table, view, and sequence access privileges\n")); - fprintf(output, _(" \\drds [PATRN1 [PATRN2]] list per-database role settings\n")); - fprintf(output, _(" \\ds[S+] [PATTERN] list sequences\n")); - fprintf(output, _(" \\dt[S+] [PATTERN] list tables\n")); - fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n")); - fprintf(output, _(" \\du[+] [PATTERN] list roles\n")); - fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); - fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); - fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); - fprintf(output, _(" \\dy [PATTERN] list event triggers\n")); - fprintf(output, _(" \\l[+] [PATTERN] list databases\n")); - fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); - fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); - fprintf(output, "\n"); - - fprintf(output, _("Formatting\n")); - fprintf(output, _(" \\a toggle between unaligned and aligned output mode\n")); - fprintf(output, _(" \\C [STRING] set table title, or unset if none\n")); - fprintf(output, _(" \\f [STRING] show or set field separator for unaligned query output\n")); - fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"), - ON(pset.popt.topt.format == PRINT_HTML)); - fprintf(output, _(" \\pset [NAME [VALUE]] set table output option\n" - " (NAME := {format|border|expanded|fieldsep|fieldsep_zero|footer|null|\n" - " numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager|\n" - " unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle})\n")); - fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"), - ON(pset.popt.topt.tuples_only)); - fprintf(output, _(" \\T [STRING] set HTML
tag attributes, or unset if none\n")); - fprintf(output, _(" \\x [on|off|auto] toggle expanded output (currently %s)\n"), - pset.popt.topt.expanded == 2 ? "auto" : ON(pset.popt.topt.expanded)); - fprintf(output, "\n"); - - fprintf(output, _("Connection\n")); - if (currdb) - fprintf(output, _(" \\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo}\n" - " connect to new database (currently \"%s\")\n"), - currdb); - else - fprintf(output, _(" \\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo}\n" - " connect to new database (currently no connection)\n")); - fprintf(output, _(" \\encoding [ENCODING] show or set client encoding\n")); - fprintf(output, _(" \\password [USERNAME] securely change the password for a user\n")); - fprintf(output, _(" \\conninfo display information about current connection\n")); - fprintf(output, "\n"); - - fprintf(output, _("Operating System\n")); - fprintf(output, _(" \\cd [DIR] change the current working directory\n")); - fprintf(output, _(" \\setenv NAME [VALUE] set or unset environment variable\n")); - fprintf(output, _(" \\timing [on|off] toggle timing of commands (currently %s)\n"), - ON(pset.timing)); - fprintf(output, _(" \\! [COMMAND] execute command in shell or start interactive shell\n")); - fprintf(output, "\n"); - - fprintf(output, _("Variables\n")); - fprintf(output, _(" \\prompt [TEXT] NAME prompt user to set internal variable\n")); - fprintf(output, _(" \\set [NAME [VALUE]] set internal variable, or list all if no parameters\n")); - fprintf(output, _(" \\unset NAME unset (delete) internal variable\n")); - fprintf(output, "\n"); - - fprintf(output, _("Large Objects\n")); - fprintf(output, _(" \\lo_export LOBOID FILE\n" - " \\lo_import FILE [COMMENT]\n" - " \\lo_list\n" - " \\lo_unlink LOBOID large object operations\n")); - - ClosePager(output); -} - - -/* - * helpVariables - * - * show list of available variables (options) from command line - */ -void -helpVariables(unsigned short int pager) -{ - FILE *output; - - output = PageOutput(85, pager ? &(pset.popt.topt) : NULL); - - fprintf(output, _("List of specially treated variables\n\n")); - - fprintf(output, _("psql variables:\n")); - fprintf(output, _("Usage:\n")); - fprintf(output, _(" psql --set=NAME=VALUE\n or \\set NAME VALUE inside psql\n\n")); - - fprintf(output, _(" AUTOCOMMIT if set, successful SQL commands are automatically committed\n")); - fprintf(output, _(" COMP_KEYWORD_CASE determines the case used to complete SQL key words\n" - " [lower, upper, preserve-lower, preserve-upper]\n")); - fprintf(output, _(" DBNAME the currently connected database name\n")); - fprintf(output, _(" ECHO controls what input is written to standard output\n" - " [all, errors, none, queries]\n")); - fprintf(output, _(" ECHO_HIDDEN if set, display internal queries executed by backslash commands;\n" - " if set to \"noexec\", just show without execution\n")); - fprintf(output, _(" ENCODING current client character set encoding\n")); - fprintf(output, _(" FETCH_COUNT the number of result rows to fetch and display at a time\n" - " (default: 0=unlimited)\n")); - fprintf(output, _(" HISTCONTROL controls command history [ignorespace, ignoredups, ignoreboth]\n")); - fprintf(output, _(" HISTFILE file name used to store the command history\n")); - fprintf(output, _(" HISTSIZE the number of commands to store in the command history\n")); - fprintf(output, _(" HOST the currently connected database server host\n")); - fprintf(output, _(" IGNOREEOF if unset, sending an EOF to interactive session terminates application\n")); - fprintf(output, _(" LASTOID value of the last affected OID\n")); - fprintf(output, _(" ON_ERROR_ROLLBACK if set, an error doesn't stop a transaction (uses implicit savepoints)\n")); - fprintf(output, _(" ON_ERROR_STOP stop batch execution after error\n")); - fprintf(output, _(" PORT server port of the current connection\n")); - fprintf(output, _(" PROMPT1 specifies the standard psql prompt\n")); - fprintf(output, _(" PROMPT2 specifies the prompt used when a statement continues from a previous line\n")); - fprintf(output, _(" PROMPT3 specifies the prompt used during COPY ... FROM STDIN\n")); - fprintf(output, _(" QUIET run quietly (same as -q option)\n")); - fprintf(output, _(" SINGLELINE end of line terminates SQL command mode (same as -S option)\n")); - fprintf(output, _(" SINGLESTEP single-step mode (same as -s option)\n")); - fprintf(output, _(" USER the currently connected database user\n")); - fprintf(output, _(" VERBOSITY controls verbosity of error reports [default, verbose, terse]\n")); - - fprintf(output, _("\nDisplay settings:\n")); - fprintf(output, _("Usage:\n")); - fprintf(output, _(" psql --pset=NAME[=VALUE]\n or \\pset NAME [VALUE] inside psql\n\n")); - - fprintf(output, _(" border border style (number)\n")); - fprintf(output, _(" columns target width for the wrapped format\n")); - fprintf(output, _(" expanded (or x) expanded output [on, off, auto]\n")); - fprintf(output, _(" fieldsep field separator for unaligned output (default \"%s\")\n"), DEFAULT_FIELD_SEP); - fprintf(output, _(" fieldsep_zero set field separator for unaligned output to zero byte\n")); - fprintf(output, _(" format set output format [unaligned, aligned, wrapped, html, asciidoc, ...]\n")); - fprintf(output, _(" footer enable or disable display of the table footer [on, off]\n")); - fprintf(output, _(" linestyle set the border line drawing style [ascii, old-ascii, unicode]\n")); - fprintf(output, _(" null set the string to be printed in place of a null value\n")); - fprintf(output, _(" numericlocale enable or disable display of a locale-specific character to separate\n" - " groups of digits [on, off]\n")); - fprintf(output, _(" pager control when an external pager is used [yes, no, always]\n")); - fprintf(output, _(" recordsep record (line) separator for unaligned output\n")); - fprintf(output, _(" recordsep_zero set record separator for unaligned output to zero byte\n")); - fprintf(output, _(" tableattr (or T) specify attributes for table tag in html format or proportional\n" - " column widths for left-aligned data types in latex-longtable format\n")); - fprintf(output, _(" title set the table title for any subsequently printed tables\n")); - fprintf(output, _(" tuples_only if set, only actual table data is shown\n")); - fprintf(output, _(" unicode_border_linestyle\n" - " unicode_column_linestyle\n" - " unicode_header_linestyle\n" - " set the style of Unicode line drawing [single, double]\n")); - - fprintf(output, _("\nEnvironment variables:\n")); - fprintf(output, _("Usage:\n")); - -#ifndef WIN32 - fprintf(output, _(" NAME=VALUE [NAME=VALUE] psql ...\n or \\setenv NAME [VALUE] inside psql\n\n")); -#else - fprintf(output, _(" set NAME=VALUE\n psql ...\n or \\setenv NAME [VALUE] inside psql\n\n")); -#endif - - fprintf(output, _(" COLUMNS number of columns for wrapped format\n")); - fprintf(output, _(" PAGER name of external pager program\n")); - fprintf(output, _(" PGAPPNAME same as the application_name connection parameter\n")); - fprintf(output, _(" PGDATABASE same as the dbname connection parameter\n")); - fprintf(output, _(" PGHOST same as the host connection parameter\n")); - fprintf(output, _(" PGPORT same as the port connection parameter\n")); - fprintf(output, _(" PGUSER same as the user connection parameter\n")); - fprintf(output, _(" PGPASSWORD connection password (not recommended)\n")); - fprintf(output, _(" PGPASSFILE password file name\n")); - fprintf(output, _(" PSQL_EDITOR, EDITOR, VISUAL\n" - " editor used by the \\e and \\ef commands\n")); - fprintf(output, _(" PSQL_EDITOR_LINENUMBER_ARG\n" - " how to specify a line number when invoking the editor\n")); - fprintf(output, _(" PSQL_HISTORY alternative location for the command history file\n")); - fprintf(output, _(" PSQLRC alternative location for the user's .psqlrc file\n")); - fprintf(output, _(" SHELL shell used by the \\! command\n")); - fprintf(output, _(" TMPDIR directory for temporary files\n")); - - ClosePager(output); -} - - -/* - * helpSQL -- help with SQL commands - * - * Note: we assume caller removed any trailing spaces in "topic". - */ -void -helpSQL(const char *topic, unsigned short int pager) -{ -#define VALUE_OR_NULL(a) ((a) ? (a) : "") - - if (!topic || strlen(topic) == 0) - { - /* Print all the available command names */ - int screen_width; - int ncolumns; - int nrows; - FILE *output; - int i; - int j; - -#ifdef TIOCGWINSZ - struct winsize screen_size; - - if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1) - screen_width = 80; /* ioctl failed, assume 80 */ - else - screen_width = screen_size.ws_col; -#else - screen_width = 80; /* default assumption */ -#endif - - ncolumns = (screen_width - 3) / (QL_MAX_CMD_LEN + 1); - ncolumns = Max(ncolumns, 1); - nrows = (QL_HELP_COUNT + (ncolumns - 1)) / ncolumns; - - output = PageOutput(nrows + 1, pager ? &(pset.popt.topt) : NULL); - - fputs(_("Available help:\n"), output); - - for (i = 0; i < nrows; i++) - { - fprintf(output, " "); - for (j = 0; j < ncolumns - 1; j++) - fprintf(output, "%-*s", - QL_MAX_CMD_LEN + 1, - VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd)); - if (i + j * nrows < QL_HELP_COUNT) - fprintf(output, "%s", - VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd)); - fputc('\n', output); - } - - ClosePager(output); - } - else - { - int i, - j, - x = 0; - bool help_found = false; - FILE *output = NULL; - size_t len, - wordlen; - int nl_count = 0; - - /* - * We first try exact match, then first + second words, then first - * word only. - */ - len = strlen(topic); - - for (x = 1; x <= 3; x++) - { - if (x > 1) /* Nothing on first pass - try the opening - * word(s) */ - { - wordlen = j = 1; - while (topic[j] != ' ' && j++ < len) - wordlen++; - if (x == 2) - { - j++; - while (topic[j] != ' ' && j++ <= len) - wordlen++; - } - if (wordlen >= len) /* Don't try again if the same word */ - { - if (!output) - output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL); - break; - } - len = wordlen; - } - - /* Count newlines for pager */ - for (i = 0; QL_HELP[i].cmd; i++) - { - if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 || - strcmp(topic, "*") == 0) - { - nl_count += 5 + QL_HELP[i].nl_count; - - /* If we have an exact match, exit. Fixes \h SELECT */ - if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0) - break; - } - } - - if (!output) - output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL); - - for (i = 0; QL_HELP[i].cmd; i++) - { - if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 || - strcmp(topic, "*") == 0) - { - PQExpBufferData buffer; - - initPQExpBuffer(&buffer); - QL_HELP[i].syntaxfunc(&buffer); - help_found = true; - fprintf(output, _("Command: %s\n" - "Description: %s\n" - "Syntax:\n%s\n\n"), - QL_HELP[i].cmd, - _(QL_HELP[i].help), - buffer.data); - /* If we have an exact match, exit. Fixes \h SELECT */ - if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0) - break; - } - } - if (help_found) /* Don't keep trying if we got a match */ - break; - } - - if (!help_found) - fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic); - - ClosePager(output); - } -} - - - -void -print_copyright(void) -{ - puts( - "PostgreSQL Database Management System\n" - "(formerly known as Postgres, then as Postgres95)\n\n" - "Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group\n\n" - "Portions Copyright (c) 1994, The Regents of the University of California\n\n" - "Permission to use, copy, modify, and distribute this software and its\n" - "documentation for any purpose, without fee, and without a written agreement\n" - "is hereby granted, provided that the above copyright notice and this\n" - "paragraph and the following two paragraphs appear in all copies.\n\n" - "IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR\n" - "DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING\n" - "LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\n" - "DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE\n" - "POSSIBILITY OF SUCH DAMAGE.\n\n" - "THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,\n" - "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\n" - "AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS\n" - "ON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO\n" - "PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n" - ); -} diff --git a/src/bin/csql/help.h b/src/bin/csql/help.h deleted file mode 100644 index 0fc4f6708..000000000 --- a/src/bin/csql/help.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/help.h - */ -#ifndef HELP_H -#define HELP_H - -void usage(unsigned short int pager); - -void slashUsage(unsigned short int pager); - -void helpVariables(unsigned short int pager); - -void helpSQL(const char *topic, unsigned short int pager); - -void print_copyright(void); - -#endif diff --git a/src/bin/csql/input.c b/src/bin/csql/input.c deleted file mode 100644 index 2bc065adc..000000000 --- a/src/bin/csql/input.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/input.c - */ -#include "postgres_fe.h" - -#ifndef WIN32 -#include -#endif -#include -#include - -#include "input.h" -#include "settings.h" -#include "tab-complete.h" -#include "common.h" - -#ifndef WIN32 -#define PSQLHISTORY ".psql_history" -#else -#define PSQLHISTORY "psql_history" -#endif - -/* Runtime options for turning off readline and history */ -/* (of course there is no runtime command for doing that :) */ -#ifdef USE_READLINE -static bool useReadline; -static bool useHistory; - -static char *psql_history; - -static int history_lines_added; - - -/* - * Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY - * - * It is assumed NL_IN_HISTORY will never be entered by the user - * nor appear inside a multi-byte string. 0x00 is not properly - * handled by the readline routines so it can not be used - * for this purpose. - */ -#define NL_IN_HISTORY 0x01 -#endif - -static void finishInput(void); - - -/* - * gets_interactive() - * - * Gets a line of interactive input, using readline if desired. - * The result is a malloc'd string. - * - * Caller *must* have set up sigint_interrupt_jmp before calling. - */ -char * -gets_interactive(const char *prompt) -{ -#ifdef USE_READLINE - if (useReadline) - { - char *result; - - /* - * Some versions of readline don't notice SIGWINCH signals that arrive - * when not actively reading input. The simplest fix is to always - * re-read the terminal size. This leaves a window for SIGWINCH to be - * missed between here and where readline() enables libreadline's - * signal handler, but that's probably short enough to be ignored. - */ -#ifdef HAVE_RL_RESET_SCREEN_SIZE - rl_reset_screen_size(); -#endif - - /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ - sigint_interrupt_enabled = true; - - /* On some platforms, readline is declared as readline(char *) */ - result = readline((char *) prompt); - - /* Disable SIGINT again */ - sigint_interrupt_enabled = false; - - return result; - } -#endif - - fputs(prompt, stdout); - fflush(stdout); - return gets_fromFile(stdin); -} - - -/* - * Append the line to the history buffer, making sure there is a trailing '\n' - */ -void -pg_append_history(const char *s, PQExpBuffer history_buf) -{ -#ifdef USE_READLINE - if (useHistory && s) - { - appendPQExpBufferStr(history_buf, s); - if (!s[0] || s[strlen(s) - 1] != '\n') - appendPQExpBufferChar(history_buf, '\n'); - } -#endif -} - - -/* - * Emit accumulated history entry to readline's history mechanism, - * then reset the buffer to empty. - * - * Note: we write nothing if history_buf is empty, so extra calls to this - * function don't hurt. There must have been at least one line added by - * pg_append_history before we'll do anything. - */ -void -pg_send_history(PQExpBuffer history_buf) -{ -#ifdef USE_READLINE - static char *prev_hist = NULL; - - char *s = history_buf->data; - int i; - - /* Trim any trailing \n's (OK to scribble on history_buf) */ - for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--) - ; - s[i + 1] = '\0'; - - if (useHistory && s[0]) - { - if (((pset.histcontrol & hctl_ignorespace) && - s[0] == ' ') || - ((pset.histcontrol & hctl_ignoredups) && - prev_hist && strcmp(s, prev_hist) == 0)) - { - /* Ignore this line as far as history is concerned */ - } - else - { - /* Save each previous line for ignoredups processing */ - if (prev_hist) - free(prev_hist); - prev_hist = pg_strdup(s); - /* And send it to readline */ - add_history(s); - /* Count lines added to history for use later */ - history_lines_added++; - } - } - - resetPQExpBuffer(history_buf); -#endif -} - - -/* - * gets_fromFile - * - * Gets a line of noninteractive input from a file (which could be stdin). - * The result is a malloc'd string, or NULL on EOF or input error. - * - * Caller *must* have set up sigint_interrupt_jmp before calling. - * - * Note: we re-use a static PQExpBuffer for each call. This is to avoid - * leaking memory if interrupted by SIGINT. - */ -char * -gets_fromFile(FILE *source) -{ - static PQExpBuffer buffer = NULL; - - char line[1024]; - - if (buffer == NULL) /* first time through? */ - buffer = createPQExpBuffer(); - else - resetPQExpBuffer(buffer); - - for (;;) - { - char *result; - - /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ - sigint_interrupt_enabled = true; - - /* Get some data */ - result = fgets(line, sizeof(line), source); - - /* Disable SIGINT again */ - sigint_interrupt_enabled = false; - - /* EOF or error? */ - if (result == NULL) - { - if (ferror(source)) - { - psql_error("could not read from input file: %s\n", - strerror(errno)); - return NULL; - } - break; - } - - appendPQExpBufferStr(buffer, line); - - if (PQExpBufferBroken(buffer)) - { - psql_error("out of memory\n"); - return NULL; - } - - /* EOL? */ - if (buffer->data[buffer->len - 1] == '\n') - { - buffer->data[buffer->len - 1] = '\0'; - return pg_strdup(buffer->data); - } - } - - if (buffer->len > 0) /* EOF after reading some bufferload(s) */ - return pg_strdup(buffer->data); - - /* EOF, so return null */ - return NULL; -} - - -#ifdef USE_READLINE - -/* - * Macros to iterate over each element of the history list in order - * - * You would think this would be simple enough, but in its inimitable fashion - * libedit has managed to break it: in libreadline we must use next_history() - * to go from oldest to newest, but in libedit we must use previous_history(). - * To detect what to do, we make a trial call of previous_history(): if it - * fails, then either next_history() is what to use, or there's zero or one - * history entry so that it doesn't matter which direction we go. - * - * In case that wasn't disgusting enough: the code below is not as obvious as - * it might appear. In some libedit releases history_set_pos(0) fails until - * at least one add_history() call has been done. This is not an issue for - * printHistory() or encode_history(), which cannot be invoked before that has - * happened. In decode_history(), that's not so, and what actually happens is - * that we are sitting on the newest entry to start with, previous_history() - * fails, and we iterate over all the entries using next_history(). So the - * decode_history() loop iterates over the entries in the wrong order when - * using such a libedit release, and if there were another attempt to use - * BEGIN_ITERATE_HISTORY() before some add_history() call had happened, it - * wouldn't work. Fortunately we don't care about either of those things. - * - * Usage pattern is: - * - * BEGIN_ITERATE_HISTORY(varname); - * { - * loop body referencing varname->line; - * } - * END_ITERATE_HISTORY(); - */ -#define BEGIN_ITERATE_HISTORY(VARNAME) \ - do { \ - HIST_ENTRY *VARNAME; \ - bool use_prev_; \ - \ - history_set_pos(0); \ - use_prev_ = (previous_history() != NULL); \ - history_set_pos(0); \ - for (VARNAME = current_history(); VARNAME != NULL; \ - VARNAME = use_prev_ ? previous_history() : next_history()) \ - { \ - (void) 0 - -#define END_ITERATE_HISTORY() \ - } \ - } while(0) - - -/* - * Convert newlines to NL_IN_HISTORY for safe saving in readline history file - */ -static void -encode_history(void) -{ - BEGIN_ITERATE_HISTORY(cur_hist); - { - char *cur_ptr; - - /* some platforms declare HIST_ENTRY.line as const char * */ - for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++) - { - if (*cur_ptr == '\n') - *cur_ptr = NL_IN_HISTORY; - } - } - END_ITERATE_HISTORY(); -} - -/* - * Reverse the above encoding - */ -static void -decode_history(void) -{ - BEGIN_ITERATE_HISTORY(cur_hist); - { - char *cur_ptr; - - /* some platforms declare HIST_ENTRY.line as const char * */ - for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++) - { - if (*cur_ptr == NL_IN_HISTORY) - *cur_ptr = '\n'; - } - } - END_ITERATE_HISTORY(); -} -#endif /* USE_READLINE */ - - -/* - * Put any startup stuff related to input in here. It's good to maintain - * abstraction this way. - * - * The only "flag" right now is 1 for use readline & history. - */ -void -initializeInput(int flags) -{ -#ifdef USE_READLINE - if (flags & 1) - { - const char *histfile; - char home[MAXPGPATH]; - - useReadline = true; - - /* these two things must be done in this order: */ - initialize_readline(); - rl_initialize(); - - useHistory = true; - using_history(); - history_lines_added = 0; - - histfile = GetVariable(pset.vars, "HISTFILE"); - - if (histfile == NULL) - { - char *envhist; - - envhist = getenv("PSQL_HISTORY"); - if (envhist != NULL && strlen(envhist) > 0) - histfile = envhist; - } - - if (histfile == NULL) - { - if (get_home_path(home)) - psql_history = psprintf("%s/%s", home, PSQLHISTORY); - } - else - { - psql_history = pg_strdup(histfile); - expand_tilde(&psql_history); - } - - if (psql_history) - { - read_history(psql_history); - decode_history(); - } - } -#endif - - atexit(finishInput); -} - - -/* - * This function saves the readline history when psql exits. - * - * fname: pathname of history file. (Should really be "const char *", - * but some ancient versions of readline omit the const-decoration.) - * - * max_lines: if >= 0, limit history file to that many entries. - */ -#ifdef USE_READLINE -static bool -saveHistory(char *fname, int max_lines) -{ - int errnum; - - /* - * Suppressing the write attempt when HISTFILE is set to /dev/null may - * look like a negligible optimization, but it's necessary on e.g. Darwin, - * where write_history will fail because it tries to chmod the target - * file. - */ - if (strcmp(fname, DEVNULL) != 0) - { - /* - * Encode \n, since otherwise readline will reload multiline history - * entries as separate lines. (libedit doesn't really need this, but - * we do it anyway since it's too hard to tell which implementation we - * are using.) - */ - encode_history(); - - /* - * On newer versions of libreadline, truncate the history file as - * needed and then append what we've added. This avoids overwriting - * history from other concurrent sessions (although there are still - * race conditions when two sessions exit at about the same time). If - * we don't have those functions, fall back to write_history(). - */ -#if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY) - { - int nlines; - int fd; - - /* truncate previous entries if needed */ - if (max_lines >= 0) - { - nlines = Max(max_lines - history_lines_added, 0); - (void) history_truncate_file(fname, nlines); - } - /* append_history fails if file doesn't already exist :-( */ - fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600); - if (fd >= 0) - close(fd); - /* append the appropriate number of lines */ - if (max_lines >= 0) - nlines = Min(max_lines, history_lines_added); - else - nlines = history_lines_added; - errnum = append_history(nlines, fname); - if (errnum == 0) - return true; - } -#else /* don't have append support */ - { - /* truncate what we have ... */ - if (max_lines >= 0) - stifle_history(max_lines); - /* ... and overwrite file. Tough luck for concurrent sessions. */ - errnum = write_history(fname); - if (errnum == 0) - return true; - } -#endif - - psql_error("could not save history to file \"%s\": %s\n", - fname, strerror(errnum)); - } - return false; -} -#endif - - - -/* - * Print history to the specified file, or to the console if fname is NULL - * (psql \s command) - * - * We used to use saveHistory() for this purpose, but that doesn't permit - * use of a pager; moreover libedit's implementation behaves incompatibly - * (preferring to encode its output) and may fail outright when the target - * file is specified as /dev/tty. - */ -bool -printHistory(const char *fname, unsigned short int pager) -{ -#ifdef USE_READLINE - FILE *output; - bool is_pager; - - if (!useHistory) - return false; - - if (fname == NULL) - { - /* use pager, if enabled, when printing to console */ - output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL); - is_pager = true; - } - else - { - output = fopen(fname, "w"); - if (output == NULL) - { - psql_error("could not save history to file \"%s\": %s\n", - fname, strerror(errno)); - return false; - } - is_pager = false; - } - - BEGIN_ITERATE_HISTORY(cur_hist); - { - fprintf(output, "%s\n", cur_hist->line); - } - END_ITERATE_HISTORY(); - - if (is_pager) - ClosePager(output); - else - fclose(output); - - return true; -#else - psql_error("history is not supported by this installation\n"); - return false; -#endif -} - - -static void -finishInput(void) -{ -#ifdef USE_READLINE - if (useHistory && psql_history) - { - int hist_size; - - hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true); - (void) saveHistory(psql_history, hist_size); - free(psql_history); - psql_history = NULL; - } -#endif -} diff --git a/src/bin/csql/input.h b/src/bin/csql/input.h deleted file mode 100644 index abd7012d1..000000000 --- a/src/bin/csql/input.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/input.h - */ -#ifndef INPUT_H -#define INPUT_H - -/* - * If some other file needs to have access to readline/history, include this - * file and save yourself all this work. - * - * USE_READLINE is the definite pointers regarding existence or not. - */ -#ifdef HAVE_LIBREADLINE -#define USE_READLINE 1 - -#if defined(HAVE_READLINE_READLINE_H) -#include -#if defined(HAVE_READLINE_HISTORY_H) -#include -#endif -#elif defined(HAVE_EDITLINE_READLINE_H) -#include -#if defined(HAVE_EDITLINE_HISTORY_H) -#include -#endif -#elif defined(HAVE_READLINE_H) -#include -#if defined(HAVE_HISTORY_H) -#include -#endif -#endif /* HAVE_READLINE_READLINE_H, etc */ -#endif /* HAVE_LIBREADLINE */ - -#include "pqexpbuffer.h" - - -char *gets_interactive(const char *prompt); -char *gets_fromFile(FILE *source); - -void initializeInput(int flags); - -bool printHistory(const char *fname, unsigned short int pager); - -void pg_append_history(const char *s, PQExpBuffer history_buf); -void pg_send_history(PQExpBuffer history_buf); - -#endif /* INPUT_H */ diff --git a/src/bin/csql/keywords.c b/src/bin/csql/keywords.c deleted file mode 100644 index 5775cad0f..000000000 --- a/src/bin/csql/keywords.c +++ /dev/null @@ -1,30 +0,0 @@ -/*------------------------------------------------------------------------- - * - * keywords.c - * lexical token lookup for key words in PostgreSQL - * - * - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/bin/pg_dump/keywords.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include "parser/keywords.h" - -/* - * We don't need the token number, so leave it out to avoid requiring other - * backend headers. - */ -#define PG_KEYWORD(a,b,c) {a,0,c}, - -const ScanKeyword FEScanKeywords[] = { -#include "parser/kwlist.h" -}; - -const int NumFEScanKeywords = lengthof(FEScanKeywords); diff --git a/src/bin/csql/kwlookup.c b/src/bin/csql/kwlookup.c deleted file mode 100644 index 85ba4bd74..000000000 --- a/src/bin/csql/kwlookup.c +++ /dev/null @@ -1,89 +0,0 @@ -/*------------------------------------------------------------------------- - * - * kwlookup.c - * lexical token lookup for key words in PostgreSQL - * - * NB - this file is also used by ECPG and several frontend programs in - * src/bin/ including pg_dump and psql - * - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/parser/kwlookup.c - * - *------------------------------------------------------------------------- - */ - -/* use c.h so this can be built as either frontend or backend */ -#include "c.h" - -#include - -#include "parser/keywords.h" - -/* - * ScanKeywordLookup - see if a given word is a keyword - * - * Returns a pointer to the ScanKeyword table entry, or NULL if no match. - * - * The match is done case-insensitively. Note that we deliberately use a - * dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z', - * even if we are in a locale where tolower() would produce more or different - * translations. This is to conform to the SQL99 spec, which says that - * keywords are to be matched in this way even though non-keyword identifiers - * receive a different case-normalization mapping. - */ -const ScanKeyword * -ScanKeywordLookup(const char *text, - const ScanKeyword *keywords, - int num_keywords) -{ - int len, - i; - char word[NAMEDATALEN]; - const ScanKeyword *low; - const ScanKeyword *high; - - len = strlen(text); - /* We assume all keywords are shorter than NAMEDATALEN. */ - if (len >= NAMEDATALEN) - return NULL; - - /* - * Apply an ASCII-only downcasing. We must not use tolower() since it may - * produce the wrong translation in some locales (eg, Turkish). - */ - for (i = 0; i < len; i++) - { - char ch = text[i]; - - if (ch >= 'A' && ch <= 'Z') - ch += 'a' - 'A'; - word[i] = ch; - } - word[len] = '\0'; - - /* - * Now do a binary search using plain strcmp() comparison. - */ - low = keywords; - high = keywords + (num_keywords - 1); - while (low <= high) - { - const ScanKeyword *middle; - int difference; - - middle = low + (high - low) / 2; - difference = strcmp(middle->name, word); - if (difference == 0) - return middle; - else if (difference < 0) - low = middle + 1; - else - high = middle - 1; - } - - return NULL; -} diff --git a/src/bin/csql/large_obj.c b/src/bin/csql/large_obj.c deleted file mode 100644 index 35cfdb90e..000000000 --- a/src/bin/csql/large_obj.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/large_obj.c - */ -#include "postgres_fe.h" -#include "large_obj.h" - - -#include "settings.h" -#include "common.h" - -static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2); - -static void -print_lo_result(const char *fmt,...) -{ - va_list ap; - - if (!pset.quiet) - { - if (pset.popt.topt.format == PRINT_HTML) - fputs("

", pset.queryFout); - - va_start(ap, fmt); - vfprintf(pset.queryFout, fmt, ap); - va_end(ap); - - if (pset.popt.topt.format == PRINT_HTML) - fputs("

\n", pset.queryFout); - else - fputs("\n", pset.queryFout); - } - - if (pset.logfile) - { - va_start(ap, fmt); - vfprintf(pset.logfile, fmt, ap); - va_end(ap); - fputs("\n", pset.logfile); - } -} - - -/* - * Prepare to do a large-object operation. We *must* be inside a transaction - * block for all these operations, so start one if needed. - * - * Returns TRUE if okay, FALSE if failed. *own_transaction is set to indicate - * if we started our own transaction or not. - */ -static bool -start_lo_xact(const char *operation, bool *own_transaction) -{ - PGTransactionStatusType tstatus; - PGresult *res; - - *own_transaction = false; - - if (!pset.db) - { - psql_error("%s: not connected to a database\n", operation); - return false; - } - - tstatus = PQtransactionStatus(pset.db); - - switch (tstatus) - { - case PQTRANS_IDLE: - /* need to start our own xact */ - if (!(res = PSQLexec("BEGIN"))) - return false; - PQclear(res); - *own_transaction = true; - break; - case PQTRANS_INTRANS: - /* use the existing xact */ - break; - case PQTRANS_INERROR: - psql_error("%s: current transaction is aborted\n", operation); - return false; - default: - psql_error("%s: unknown transaction status\n", operation); - return false; - } - - return true; -} - -/* - * Clean up after a successful LO operation - */ -static bool -finish_lo_xact(const char *operation, bool own_transaction) -{ - PGresult *res; - - if (own_transaction && pset.autocommit) - { - /* close out our own xact */ - if (!(res = PSQLexec("COMMIT"))) - { - res = PSQLexec("ROLLBACK"); - PQclear(res); - return false; - } - PQclear(res); - } - - return true; -} - -/* - * Clean up after a failed LO operation - */ -static bool -fail_lo_xact(const char *operation, bool own_transaction) -{ - PGresult *res; - - if (own_transaction && pset.autocommit) - { - /* close out our own xact */ - res = PSQLexec("ROLLBACK"); - PQclear(res); - } - - return false; /* always */ -} - - -/* - * do_lo_export() - * - * Write a large object to a file - */ -bool -do_lo_export(const char *loid_arg, const char *filename_arg) -{ - int status; - bool own_transaction; - - if (!start_lo_xact("\\lo_export", &own_transaction)) - return false; - - SetCancelConn(); - status = lo_export(pset.db, atooid(loid_arg), filename_arg); - ResetCancelConn(); - - /* of course this status is documented nowhere :( */ - if (status != 1) - { - psql_error("%s", PQerrorMessage(pset.db)); - return fail_lo_xact("\\lo_export", own_transaction); - } - - if (!finish_lo_xact("\\lo_export", own_transaction)) - return false; - - print_lo_result("lo_export"); - - return true; -} - - -/* - * do_lo_import() - * - * Copy large object from file to database - */ -bool -do_lo_import(const char *filename_arg, const char *comment_arg) -{ - PGresult *res; - Oid loid; - char oidbuf[32]; - bool own_transaction; - - if (!start_lo_xact("\\lo_import", &own_transaction)) - return false; - - SetCancelConn(); - loid = lo_import(pset.db, filename_arg); - ResetCancelConn(); - - if (loid == InvalidOid) - { - psql_error("%s", PQerrorMessage(pset.db)); - return fail_lo_xact("\\lo_import", own_transaction); - } - - /* insert description if given */ - if (comment_arg) - { - char *cmdbuf; - char *bufptr; - size_t slen = strlen(comment_arg); - - cmdbuf = malloc(slen * 2 + 256); - if (!cmdbuf) - return fail_lo_xact("\\lo_import", own_transaction); - sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid); - bufptr = cmdbuf + strlen(cmdbuf); - bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL); - strcpy(bufptr, "'"); - - if (!(res = PSQLexec(cmdbuf))) - { - free(cmdbuf); - return fail_lo_xact("\\lo_import", own_transaction); - } - - PQclear(res); - free(cmdbuf); - } - - if (!finish_lo_xact("\\lo_import", own_transaction)) - return false; - - print_lo_result("lo_import %u", loid); - - sprintf(oidbuf, "%u", loid); - SetVariable(pset.vars, "LASTOID", oidbuf); - - return true; -} - - -/* - * do_lo_unlink() - * - * removes a large object out of the database - */ -bool -do_lo_unlink(const char *loid_arg) -{ - int status; - Oid loid = atooid(loid_arg); - bool own_transaction; - - if (!start_lo_xact("\\lo_unlink", &own_transaction)) - return false; - - SetCancelConn(); - status = lo_unlink(pset.db, loid); - ResetCancelConn(); - - if (status == -1) - { - psql_error("%s", PQerrorMessage(pset.db)); - return fail_lo_xact("\\lo_unlink", own_transaction); - } - - if (!finish_lo_xact("\\lo_unlink", own_transaction)) - return false; - - print_lo_result("lo_unlink %u", loid); - - return true; -} - - - -/* - * do_lo_list() - * - * Show all large objects in database with comments - */ -bool -do_lo_list(void) -{ - PGresult *res; - char buf[1024]; - printQueryOpt myopt = pset.popt; - - if (pset.sversion >= 90000) - { - snprintf(buf, sizeof(buf), - "SELECT oid as \"%s\",\n" - " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n" - " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" - " FROM pg_catalog.pg_largeobject_metadata " - " ORDER BY oid", - gettext_noop("ID"), - gettext_noop("Owner"), - gettext_noop("Description")); - } - else - { - snprintf(buf, sizeof(buf), - "SELECT loid as \"%s\",\n" - " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" - "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" - "ORDER BY 1", - gettext_noop("ID"), - gettext_noop("Description")); - } - - res = PSQLexec(buf); - if (!res) - return false; - - myopt.topt.tuples_only = false; - myopt.nullPrint = NULL; - myopt.title = _("Large objects"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} diff --git a/src/bin/csql/large_obj.h b/src/bin/csql/large_obj.h deleted file mode 100644 index 3d3780197..000000000 --- a/src/bin/csql/large_obj.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/large_obj.h - */ -#ifndef LARGE_OBJ_H -#define LARGE_OBJ_H - -bool do_lo_export(const char *loid_arg, const char *filename_arg); -bool do_lo_import(const char *filename_arg, const char *comment_arg); -bool do_lo_unlink(const char *loid_arg); -bool do_lo_list(void); - -#endif /* LARGE_OBJ_H */ diff --git a/src/bin/csql/mainloop.c b/src/bin/csql/mainloop.c deleted file mode 100644 index 65070e643..000000000 --- a/src/bin/csql/mainloop.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/mainloop.c - */ -#include "postgres_fe.h" -#include "mainloop.h" - - -#include "command.h" -#include "common.h" -#include "input.h" -#include "settings.h" - -#include "mb/pg_wchar.h" - - -/* - * Main processing loop for reading lines of input - * and sending them to the backend. - * - * This loop is re-entrant. May be called by \i command - * which reads input from a file. - */ -int -MainLoop(FILE *source) -{ - PsqlScanState scan_state; /* lexer working state */ - volatile PQExpBuffer query_buf; /* buffer for query being accumulated */ - volatile PQExpBuffer previous_buf; /* if there isn't anything in the new - * buffer yet, use this one for \e, - * etc. */ - PQExpBuffer history_buf; /* earlier lines of a multi-line command, not - * yet saved to readline history */ - char *line; /* current line of input */ - int added_nl_pos; - bool success; - bool line_saved_in_history; - volatile int successResult = EXIT_SUCCESS; - volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN; - volatile promptStatus_t prompt_status = PROMPT_READY; - volatile int count_eof = 0; - volatile bool die_on_error = false; - - /* Save the prior command source */ - FILE *prev_cmd_source; - bool prev_cmd_interactive; - uint64 prev_lineno; - - /* Save old settings */ - prev_cmd_source = pset.cur_cmd_source; - prev_cmd_interactive = pset.cur_cmd_interactive; - prev_lineno = pset.lineno; - - /* Establish new source */ - pset.cur_cmd_source = source; - pset.cur_cmd_interactive = ((source == stdin) && !pset.notty); - pset.lineno = 0; - pset.stmt_lineno = 1; - - /* Create working state */ - scan_state = psql_scan_create(); - - query_buf = createPQExpBuffer(); - previous_buf = createPQExpBuffer(); - history_buf = createPQExpBuffer(); - if (PQExpBufferBroken(query_buf) || - PQExpBufferBroken(previous_buf) || - PQExpBufferBroken(history_buf)) - { - psql_error("out of memory\n"); - exit(EXIT_FAILURE); - } - - /* main loop to get queries and execute them */ - while (successResult == EXIT_SUCCESS) - { - /* - * Clean up after a previous Control-C - */ - if (cancel_pressed) - { - if (!pset.cur_cmd_interactive) - { - /* - * You get here if you stopped a script with Ctrl-C. - */ - successResult = EXIT_USER; - break; - } - - cancel_pressed = false; - } - - /* - * Establish longjmp destination for exiting from wait-for-input. We - * must re-do this each time through the loop for safety, since the - * jmpbuf might get changed during command execution. - */ - if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) - { - /* got here with longjmp */ - - /* reset parsing state */ - psql_scan_finish(scan_state); - psql_scan_reset(scan_state); - resetPQExpBuffer(query_buf); - resetPQExpBuffer(history_buf); - count_eof = 0; - slashCmdStatus = PSQL_CMD_UNKNOWN; - prompt_status = PROMPT_READY; - pset.stmt_lineno = 1; - cancel_pressed = false; - - if (pset.cur_cmd_interactive) - putc('\n', stdout); - else - { - successResult = EXIT_USER; - break; - } - } - - fflush(stdout); - - /* - * get another line - */ - if (pset.cur_cmd_interactive) - { - /* May need to reset prompt, eg after \r command */ - if (query_buf->len == 0) - prompt_status = PROMPT_READY; - line = gets_interactive(get_prompt(prompt_status)); - } - else - { - line = gets_fromFile(source); - if (!line && ferror(source)) - successResult = EXIT_FAILURE; - } - - /* - * query_buf holds query already accumulated. line is the malloc'd - * new line of input (note it must be freed before looping around!) - */ - - /* No more input. Time to quit, or \i done */ - if (line == NULL) - { - if (pset.cur_cmd_interactive) - { - /* This tries to mimic bash's IGNOREEOF feature. */ - count_eof++; - - if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false)) - { - if (!pset.quiet) - printf(_("Use \"\\q\" to leave %s.\n"), pset.progname); - continue; - } - - puts(pset.quiet ? "" : "\\q"); - } - break; - } - - count_eof = 0; - - pset.lineno++; - - /* ignore UTF-8 Unicode byte-order mark */ - if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0) - memmove(line, line + 3, strlen(line + 3) + 1); - - /* Detect attempts to run custom-format dumps as SQL scripts */ - if (pset.lineno == 1 && !pset.cur_cmd_interactive && - strncmp(line, "PGDMP", 5) == 0) - { - free(line); - puts(_("The input is a PostgreSQL custom-format dump.\n" - "Use the pg_restore command-line client to restore this dump to a database.\n")); - fflush(stdout); - successResult = EXIT_FAILURE; - break; - } - - /* no further processing of empty lines, unless within a literal */ - if (line[0] == '\0' && !psql_scan_in_quote(scan_state)) - { - free(line); - continue; - } - - /* A request for help? Be friendly and give them some guidance */ - if (pset.cur_cmd_interactive && query_buf->len == 0 && - pg_strncasecmp(line, "help", 4) == 0 && - (line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4]))) - { - free(line); - puts(_("You are using csql, the command-line interface to Citus.")); - printf(_("Type: \\copyright for distribution terms\n" - " \\h for help with SQL commands\n" - " \\? for help with csql commands\n" - " \\g or terminate with semicolon to execute query\n" - " \\q to quit\n")); - - fflush(stdout); - continue; - } - - /* echo back if flag is set, unless interactive */ - if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive) - { - puts(line); - fflush(stdout); - } - - /* insert newlines into query buffer between source lines */ - if (query_buf->len > 0) - { - appendPQExpBufferChar(query_buf, '\n'); - added_nl_pos = query_buf->len; - } - else - added_nl_pos = -1; /* flag we didn't add one */ - - /* Setting this will not have effect until next line. */ - die_on_error = pset.on_error_stop; - - /* - * Parse line, looking for command separators. - */ - psql_scan_setup(scan_state, line, strlen(line)); - success = true; - line_saved_in_history = false; - - while (success || !die_on_error) - { - PsqlScanResult scan_result; - promptStatus_t prompt_tmp = prompt_status; - size_t pos_in_query; - char *tmp_line; - - pos_in_query = query_buf->len; - scan_result = psql_scan(scan_state, query_buf, &prompt_tmp); - prompt_status = prompt_tmp; - - if (PQExpBufferBroken(query_buf)) - { - psql_error("out of memory\n"); - exit(EXIT_FAILURE); - } - - /* - * Increase statement line number counter for each linebreak added - * to the query buffer by the last psql_scan() call. There only - * will be ones to add when navigating to a statement in - * readline's history containing newlines. - */ - tmp_line = query_buf->data + pos_in_query; - while (*tmp_line != '\0') - { - if (*(tmp_line++) == '\n') - pset.stmt_lineno++; - } - - if (scan_result == PSCAN_EOL) - pset.stmt_lineno++; - - /* - * Send command if semicolon found, or if end of line and we're in - * single-line mode. - */ - if (scan_result == PSCAN_SEMICOLON || - (scan_result == PSCAN_EOL && pset.singleline)) - { - /* - * Save query in history. We use history_buf to accumulate - * multi-line queries into a single history entry. - */ - if (pset.cur_cmd_interactive && !line_saved_in_history) - { - pg_append_history(line, history_buf); - pg_send_history(history_buf); - line_saved_in_history = true; - } - - /* execute query */ - success = SendQuery(query_buf->data); - slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; - pset.stmt_lineno = 1; - - /* transfer query to previous_buf by pointer-swapping */ - { - PQExpBuffer swap_buf = previous_buf; - - previous_buf = query_buf; - query_buf = swap_buf; - } - resetPQExpBuffer(query_buf); - - added_nl_pos = -1; - /* we need not do psql_scan_reset() here */ - } - else if (scan_result == PSCAN_BACKSLASH) - { - /* handle backslash command */ - - /* - * If we added a newline to query_buf, and nothing else has - * been inserted in query_buf by the lexer, then strip off the - * newline again. This avoids any change to query_buf when a - * line contains only a backslash command. Also, in this - * situation we force out any previous lines as a separate - * history entry; we don't want SQL and backslash commands - * intermixed in history if at all possible. - */ - if (query_buf->len == added_nl_pos) - { - query_buf->data[--query_buf->len] = '\0'; - pg_send_history(history_buf); - } - added_nl_pos = -1; - - /* save backslash command in history */ - if (pset.cur_cmd_interactive && !line_saved_in_history) - { - pg_append_history(line, history_buf); - pg_send_history(history_buf); - line_saved_in_history = true; - } - - /* execute backslash command */ - slashCmdStatus = HandleSlashCmds(scan_state, - query_buf->len > 0 ? - query_buf : previous_buf); - - success = slashCmdStatus != PSQL_CMD_ERROR; - pset.stmt_lineno = 1; - - if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && - query_buf->len == 0) - { - /* copy previous buffer to current for handling */ - appendPQExpBufferStr(query_buf, previous_buf->data); - } - - if (slashCmdStatus == PSQL_CMD_SEND) - { - success = SendQuery(query_buf->data); - - /* transfer query to previous_buf by pointer-swapping */ - { - PQExpBuffer swap_buf = previous_buf; - - previous_buf = query_buf; - query_buf = swap_buf; - } - resetPQExpBuffer(query_buf); - - /* flush any paren nesting info after forced send */ - psql_scan_reset(scan_state); - } - else if (slashCmdStatus == PSQL_CMD_NEWEDIT) - { - /* rescan query_buf as new input */ - psql_scan_finish(scan_state); - free(line); - line = pg_strdup(query_buf->data); - resetPQExpBuffer(query_buf); - /* reset parsing state since we are rescanning whole line */ - psql_scan_reset(scan_state); - psql_scan_setup(scan_state, line, strlen(line)); - line_saved_in_history = false; - prompt_status = PROMPT_READY; - } - else if (slashCmdStatus == PSQL_CMD_TERMINATE) - break; - } - - /* fall out of loop if lexer reached EOL */ - if (scan_result == PSCAN_INCOMPLETE || - scan_result == PSCAN_EOL) - break; - } - - /* Add line to pending history if we didn't execute anything yet */ - if (pset.cur_cmd_interactive && !line_saved_in_history) - pg_append_history(line, history_buf); - - psql_scan_finish(scan_state); - free(line); - - if (slashCmdStatus == PSQL_CMD_TERMINATE) - { - successResult = EXIT_SUCCESS; - break; - } - - if (!pset.cur_cmd_interactive) - { - if (!success && die_on_error) - successResult = EXIT_USER; - /* Have we lost the db connection? */ - else if (!pset.db) - successResult = EXIT_BADCONN; - } - } /* while !endoffile/session */ - - /* - * Process query at the end of file without a semicolon - */ - if (query_buf->len > 0 && !pset.cur_cmd_interactive && - successResult == EXIT_SUCCESS) - { - /* save query in history */ - if (pset.cur_cmd_interactive) - pg_send_history(history_buf); - - /* execute query */ - success = SendQuery(query_buf->data); - - if (!success && die_on_error) - successResult = EXIT_USER; - else if (pset.db == NULL) - successResult = EXIT_BADCONN; - } - - /* - * Let's just make real sure the SIGINT handler won't try to use - * sigint_interrupt_jmp after we exit this routine. If there is an outer - * MainLoop instance, it will reset sigint_interrupt_jmp to point to - * itself at the top of its loop, before any further interactive input - * happens. - */ - sigint_interrupt_enabled = false; - - destroyPQExpBuffer(query_buf); - destroyPQExpBuffer(previous_buf); - destroyPQExpBuffer(history_buf); - - psql_scan_destroy(scan_state); - - pset.cur_cmd_source = prev_cmd_source; - pset.cur_cmd_interactive = prev_cmd_interactive; - pset.lineno = prev_lineno; - - return successResult; -} /* MainLoop() */ - - -/* - * psqlscan.c is #include'd here instead of being compiled on its own. - * This is because we need postgres_fe.h to be read before any system - * include files, else things tend to break on platforms that have - * multiple infrastructures for stdio.h and so on. flex is absolutely - * uncooperative about that, so we can't compile psqlscan.c on its own. - */ -#include "psqlscan.c" diff --git a/src/bin/csql/mainloop.h b/src/bin/csql/mainloop.h deleted file mode 100644 index 8f1325c4f..000000000 --- a/src/bin/csql/mainloop.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/mainloop.h - */ -#ifndef MAINLOOP_H -#define MAINLOOP_H - -#include "postgres_fe.h" - -int MainLoop(FILE *source); - -#endif /* MAINLOOP_H */ diff --git a/src/bin/csql/mbprint.c b/src/bin/csql/mbprint.c deleted file mode 100644 index e29c61902..000000000 --- a/src/bin/csql/mbprint.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/mbprint.c - * - * XXX this file does not really belong in psql/. Perhaps move to libpq? - * It also seems that the mbvalidate function is redundant with existing - * functionality. - */ - -#include "postgres_fe.h" -#include "mbprint.h" -#ifndef PGSCRIPTS -#include "settings.h" -#endif - -/* - * To avoid version-skew problems, this file must not use declarations - * from pg_wchar.h: the encoding IDs we are dealing with are determined - * by the libpq.so we are linked with, and that might not match the - * numbers we see at compile time. (If this file were inside libpq, - * the problem would go away...) - * - * Hence, we have our own definition of pg_wchar, and we get the values - * of any needed encoding IDs on-the-fly. - */ - -typedef unsigned int pg_wchar; - -static int -pg_get_utf8_id(void) -{ - static int utf8_id = -1; - - if (utf8_id < 0) - utf8_id = pg_char_to_encoding("utf8"); - return utf8_id; -} - -#define PG_UTF8 pg_get_utf8_id() - - -/* - * Convert a UTF-8 character to a Unicode code point. - * This is a one-character version of pg_utf2wchar_with_len. - * - * No error checks here, c must point to a long-enough string. - */ -static pg_wchar -utf8_to_unicode(const unsigned char *c) -{ - if ((*c & 0x80) == 0) - return (pg_wchar) c[0]; - else if ((*c & 0xe0) == 0xc0) - return (pg_wchar) (((c[0] & 0x1f) << 6) | - (c[1] & 0x3f)); - else if ((*c & 0xf0) == 0xe0) - return (pg_wchar) (((c[0] & 0x0f) << 12) | - ((c[1] & 0x3f) << 6) | - (c[2] & 0x3f)); - else if ((*c & 0xf8) == 0xf0) - return (pg_wchar) (((c[0] & 0x07) << 18) | - ((c[1] & 0x3f) << 12) | - ((c[2] & 0x3f) << 6) | - (c[3] & 0x3f)); - else - /* that is an invalid code on purpose */ - return 0xffffffff; -} - - -/* - * Unicode 3.1 compliant validation : for each category, it checks the - * combination of each byte to make sure it maps to a valid range. It also - * returns -1 for the following UCS values: ucs > 0x10ffff ucs & 0xfffe = - * 0xfffe 0xfdd0 < ucs < 0xfdef ucs & 0xdb00 = 0xd800 (surrogates) - */ -static int -utf_charcheck(const unsigned char *c) -{ - if ((*c & 0x80) == 0) - return 1; - else if ((*c & 0xe0) == 0xc0) - { - /* two-byte char */ - if (((c[1] & 0xc0) == 0x80) && ((c[0] & 0x1f) > 0x01)) - return 2; - return -1; - } - else if ((*c & 0xf0) == 0xe0) - { - /* three-byte char */ - if (((c[1] & 0xc0) == 0x80) && - (((c[0] & 0x0f) != 0x00) || ((c[1] & 0x20) == 0x20)) && - ((c[2] & 0xc0) == 0x80)) - { - int z = c[0] & 0x0f; - int yx = ((c[1] & 0x3f) << 6) | (c[0] & 0x3f); - int lx = yx & 0x7f; - - /* check 0xfffe/0xffff, 0xfdd0..0xfedf range, surrogates */ - if (((z == 0x0f) && - (((yx & 0xffe) == 0xffe) || - (((yx & 0xf80) == 0xd80) && (lx >= 0x30) && (lx <= 0x4f)))) || - ((z == 0x0d) && ((yx & 0xb00) == 0x800))) - return -1; - return 3; - } - return -1; - } - else if ((*c & 0xf8) == 0xf0) - { - int u = ((c[0] & 0x07) << 2) | ((c[1] & 0x30) >> 4); - - /* four-byte char */ - if (((c[1] & 0xc0) == 0x80) && - (u > 0x00) && (u <= 0x10) && - ((c[2] & 0xc0) == 0x80) && ((c[3] & 0xc0) == 0x80)) - { - /* test for 0xzzzzfffe/0xzzzzfffff */ - if (((c[1] & 0x0f) == 0x0f) && ((c[2] & 0x3f) == 0x3f) && - ((c[3] & 0x3e) == 0x3e)) - return -1; - return 4; - } - return -1; - } - return -1; -} - - -static void -mb_utf_validate(unsigned char *pwcs) -{ - unsigned char *p = pwcs; - - while (*pwcs) - { - int len; - - if ((len = utf_charcheck(pwcs)) > 0) - { - if (p != pwcs) - { - int i; - - for (i = 0; i < len; i++) - *p++ = *pwcs++; - } - else - { - pwcs += len; - p += len; - } - } - else - /* we skip the char */ - pwcs++; - } - if (p != pwcs) - *p = '\0'; -} - -/* - * public functions : wcswidth and mbvalidate - */ - -/* - * pg_wcswidth is the dumb display-width function. - * It assumes that everything will appear on one line. - * OTOH it is easier to use than pg_wcssize if this applies to you. - */ -int -pg_wcswidth(const char *pwcs, size_t len, int encoding) -{ - int width = 0; - - while (len > 0) - { - int chlen, - chwidth; - - chlen = PQmblen(pwcs, encoding); - if (len < (size_t) chlen) - break; /* Invalid string */ - - chwidth = PQdsplen(pwcs, encoding); - if (chwidth > 0) - width += chwidth; - - pwcs += chlen; - len -= chlen; - } - return width; -} - -/* - * pg_wcssize takes the given string in the given encoding and returns three - * values: - * result_width: Width in display characters of the longest line in string - * result_height: Number of lines in display output - * result_format_size: Number of bytes required to store formatted - * representation of string - * - * This MUST be kept in sync with pg_wcsformat! - */ -void -pg_wcssize(const unsigned char *pwcs, size_t len, int encoding, - int *result_width, int *result_height, int *result_format_size) -{ - int w, - chlen = 0, - linewidth = 0; - int width = 0; - int height = 1; - int format_size = 0; - - for (; *pwcs && len > 0; pwcs += chlen) - { - chlen = PQmblen((const char *) pwcs, encoding); - if (len < (size_t) chlen) - break; - w = PQdsplen((const char *) pwcs, encoding); - - if (chlen == 1) /* single-byte char */ - { - if (*pwcs == '\n') /* Newline */ - { - if (linewidth > width) - width = linewidth; - linewidth = 0; - height += 1; - format_size += 1; /* For NUL char */ - } - else if (*pwcs == '\r') /* Linefeed */ - { - linewidth += 2; - format_size += 2; - } - else if (*pwcs == '\t') /* Tab */ - { - do - { - linewidth++; - format_size++; - } while (linewidth % 8 != 0); - } - else if (w < 0) /* Other control char */ - { - linewidth += 4; - format_size += 4; - } - else /* Output it as-is */ - { - linewidth += w; - format_size += 1; - } - } - else if (w < 0) /* Non-ascii control char */ - { - linewidth += 6; /* \u0000 */ - format_size += 6; - } - else /* All other chars */ - { - linewidth += w; - format_size += chlen; - } - len -= chlen; - } - if (linewidth > width) - width = linewidth; - format_size += 1; /* For NUL char */ - - /* Set results */ - if (result_width) - *result_width = width; - if (result_height) - *result_height = height; - if (result_format_size) - *result_format_size = format_size; -} - -/* - * Format a string into one or more "struct lineptr" lines. - * lines[i].ptr == NULL indicates the end of the array. - * - * This MUST be kept in sync with pg_wcssize! - */ -void -pg_wcsformat(const unsigned char *pwcs, size_t len, int encoding, - struct lineptr * lines, int count) -{ - int w, - chlen = 0; - int linewidth = 0; - unsigned char *ptr = lines->ptr; /* Pointer to data area */ - - for (; *pwcs && len > 0; pwcs += chlen) - { - chlen = PQmblen((const char *) pwcs, encoding); - if (len < (size_t) chlen) - break; - w = PQdsplen((const char *) pwcs, encoding); - - if (chlen == 1) /* single-byte char */ - { - if (*pwcs == '\n') /* Newline */ - { - *ptr++ = '\0'; - lines->width = linewidth; - linewidth = 0; - lines++; - count--; - if (count <= 0) - exit(1); /* Screwup */ - - /* make next line point to remaining memory */ - lines->ptr = ptr; - } - else if (*pwcs == '\r') /* Linefeed */ - { - strcpy((char *) ptr, "\\r"); - linewidth += 2; - ptr += 2; - } - else if (*pwcs == '\t') /* Tab */ - { - do - { - *ptr++ = ' '; - linewidth++; - } while (linewidth % 8 != 0); - } - else if (w < 0) /* Other control char */ - { - sprintf((char *) ptr, "\\x%02X", *pwcs); - linewidth += 4; - ptr += 4; - } - else /* Output it as-is */ - { - linewidth += w; - *ptr++ = *pwcs; - } - } - else if (w < 0) /* Non-ascii control char */ - { - if (encoding == PG_UTF8) - sprintf((char *) ptr, "\\u%04X", utf8_to_unicode(pwcs)); - else - { - /* - * This case cannot happen in the current code because only - * UTF-8 signals multibyte control characters. But we may need - * to support it at some stage - */ - sprintf((char *) ptr, "\\u????"); - } - ptr += 6; - linewidth += 6; - } - else /* All other chars */ - { - int i; - - for (i = 0; i < chlen; i++) - *ptr++ = pwcs[i]; - linewidth += w; - } - len -= chlen; - } - lines->width = linewidth; - *ptr++ = '\0'; /* Terminate formatted string */ - - if (count <= 0) - exit(1); /* Screwup */ - - (lines + 1)->ptr = NULL; /* terminate line array */ -} - -unsigned char * -mbvalidate(unsigned char *pwcs, int encoding) -{ - if (encoding == PG_UTF8) - mb_utf_validate(pwcs); - else - { - /* - * other encodings needing validation should add their own routines - * here - */ - } - - return pwcs; -} diff --git a/src/bin/csql/mbprint.h b/src/bin/csql/mbprint.h deleted file mode 100644 index 01064d310..000000000 --- a/src/bin/csql/mbprint.h +++ /dev/null @@ -1,18 +0,0 @@ -/* src/bin/psql/mbprint.h */ -#ifndef MBPRINT_H -#define MBPRINT_H - - -struct lineptr -{ - unsigned char *ptr; - int width; -}; - -extern unsigned char *mbvalidate(unsigned char *pwcs, int encoding); -extern int pg_wcswidth(const char *pwcs, size_t len, int encoding); -extern void pg_wcsformat(const unsigned char *pwcs, size_t len, int encoding, struct lineptr * lines, int count); -extern void pg_wcssize(const unsigned char *pwcs, size_t len, int encoding, - int *width, int *height, int *format_size); - -#endif /* MBPRINT_H */ diff --git a/src/bin/csql/print.c b/src/bin/csql/print.c deleted file mode 100644 index 05d4b3162..000000000 --- a/src/bin/csql/print.c +++ /dev/null @@ -1,3495 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/print.c - */ -#include "postgres_fe.h" - -#include -#include -#include -#include - -#ifndef WIN32 -#include /* for ioctl() */ -#endif - -#ifdef HAVE_TERMIOS_H -#include -#endif - -#include - -#include "catalog/pg_type.h" - -#include "common.h" -#include "mbprint.h" -#include "print.h" - -/* - * We define the cancel_pressed flag in this file, rather than common.c where - * it naturally belongs, because this file is also used by non-psql programs - * (see the bin/scripts/ directory). In those programs cancel_pressed will - * never become set and will have no effect. - * - * Note: print.c's general strategy for when to check cancel_pressed is to do - * so at completion of each row of output. - */ -volatile bool cancel_pressed = false; - -/* - * Likewise, the sigpipe_trap and pager open/close functions are here rather - * than in common.c so that this file can be used by non-psql programs. - */ -static bool always_ignore_sigpipe = false; - - -/* info for locale-aware numeric formatting; set up by setDecimalLocale() */ -static char *decimal_point; -static int groupdigits; -static char *thousands_sep; - -static char default_footer[100]; -static printTableFooter default_footer_cell = {default_footer, NULL}; - -/* Line style control structures */ -const printTextFormat pg_asciiformat = -{ - "ascii", - { - {"-", "+", "+", "+"}, - {"-", "+", "+", "+"}, - {"-", "+", "+", "+"}, - {"", "|", "|", "|"} - }, - "|", - "|", - "|", - " ", - "+", - " ", - "+", - ".", - ".", - true -}; - -const printTextFormat pg_asciiformat_old = -{ - "old-ascii", - { - {"-", "+", "+", "+"}, - {"-", "+", "+", "+"}, - {"-", "+", "+", "+"}, - {"", "|", "|", "|"} - }, - ":", - ";", - " ", - "+", - " ", - " ", - " ", - " ", - " ", - false -}; - -/* Default unicode linestyle format */ -const printTextFormat pg_utf8format; - -typedef struct unicodeStyleRowFormat -{ - const char *horizontal; - const char *vertical_and_right[2]; - const char *vertical_and_left[2]; -} unicodeStyleRowFormat; - -typedef struct unicodeStyleColumnFormat -{ - const char *vertical; - const char *vertical_and_horizontal[2]; - const char *up_and_horizontal[2]; - const char *down_and_horizontal[2]; -} unicodeStyleColumnFormat; - -typedef struct unicodeStyleBorderFormat -{ - const char *up_and_right; - const char *vertical; - const char *down_and_right; - const char *horizontal; - const char *down_and_left; - const char *left_and_right; -} unicodeStyleBorderFormat; - -typedef struct unicodeStyleFormat -{ - unicodeStyleRowFormat row_style[2]; - unicodeStyleColumnFormat column_style[2]; - unicodeStyleBorderFormat border_style[2]; - const char *header_nl_left; - const char *header_nl_right; - const char *nl_left; - const char *nl_right; - const char *wrap_left; - const char *wrap_right; - bool wrap_right_border; -} unicodeStyleFormat; - -const unicodeStyleFormat unicode_style = { - { - { - /* ─ */ - "\342\224\200", - /* ├╟ */ - {"\342\224\234", "\342\225\237"}, - /* ┤╢ */ - {"\342\224\244", "\342\225\242"}, - }, - { - /* ═ */ - "\342\225\220", - /* ╞╠ */ - {"\342\225\236", "\342\225\240"}, - /* ╡╣ */ - {"\342\225\241", "\342\225\243"}, - }, - }, - { - { - /* │ */ - "\342\224\202", - /* ┼╪ */ - {"\342\224\274", "\342\225\252"}, - /* ┴╧ */ - {"\342\224\264", "\342\225\247"}, - /* ┬╤ */ - {"\342\224\254", "\342\225\244"}, - }, - { - /* ║ */ - "\342\225\221", - /* ╫╬ */ - {"\342\225\253", "\342\225\254"}, - /* ╨╩ */ - {"\342\225\250", "\342\225\251"}, - /* ╥╦ */ - {"\342\225\245", "\342\225\246"}, - }, - }, - { - /* └│┌─┐┘ */ - {"\342\224\224", "\342\224\202", "\342\224\214", "\342\224\200", "\342\224\220", "\342\224\230"}, - /* ╚║╔═╗╝ */ - {"\342\225\232", "\342\225\221", "\342\225\224", "\342\225\220", "\342\225\227", "\342\225\235"}, - }, - " ", - "\342\206\265", /* ↵ */ - " ", - "\342\206\265", /* ↵ */ - "\342\200\246", /* … */ - "\342\200\246", /* … */ - true -}; - - -/* Local functions */ -static int strlen_max_width(unsigned char *str, int *target_width, int encoding); -static void IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded, - FILE **fout, bool *is_pager); - -static void print_aligned_vertical(const printTableContent *cont, - FILE *fout, bool is_pager); - - -/* Count number of digits in integral part of number */ -static int -integer_digits(const char *my_str) -{ - /* ignoring any sign ... */ - if (my_str[0] == '-' || my_str[0] == '+') - my_str++; - /* ... count initial integral digits */ - return strspn(my_str, "0123456789"); -} - -/* Compute additional length required for locale-aware numeric output */ -static int -additional_numeric_locale_len(const char *my_str) -{ - int int_len = integer_digits(my_str), - len = 0; - - /* Account for added thousands_sep instances */ - if (int_len > groupdigits) - len += ((int_len - 1) / groupdigits) * strlen(thousands_sep); - - /* Account for possible additional length of decimal_point */ - if (strchr(my_str, '.') != NULL) - len += strlen(decimal_point) - 1; - - return len; -} - -/* - * Format a numeric value per current LC_NUMERIC locale setting - * - * Returns the appropriately formatted string in a new allocated block, - * caller must free. - * - * setDecimalLocale() must have been called earlier. - */ -static char * -format_numeric_locale(const char *my_str) -{ - char *new_str; - int new_len, - int_len, - leading_digits, - i, - new_str_pos; - - /* - * If the string doesn't look like a number, return it unchanged. This - * check is essential to avoid mangling already-localized "money" values. - */ - if (strspn(my_str, "0123456789+-.eE") != strlen(my_str)) - return pg_strdup(my_str); - - new_len = strlen(my_str) + additional_numeric_locale_len(my_str); - new_str = pg_malloc(new_len + 1); - new_str_pos = 0; - int_len = integer_digits(my_str); - - /* number of digits in first thousands group */ - leading_digits = int_len % groupdigits; - if (leading_digits == 0) - leading_digits = groupdigits; - - /* process sign */ - if (my_str[0] == '-' || my_str[0] == '+') - { - new_str[new_str_pos++] = my_str[0]; - my_str++; - } - - /* process integer part of number */ - for (i = 0; i < int_len; i++) - { - /* Time to insert separator? */ - if (i > 0 && --leading_digits == 0) - { - strcpy(&new_str[new_str_pos], thousands_sep); - new_str_pos += strlen(thousands_sep); - leading_digits = groupdigits; - } - new_str[new_str_pos++] = my_str[i]; - } - - /* handle decimal point if any */ - if (my_str[i] == '.') - { - strcpy(&new_str[new_str_pos], decimal_point); - new_str_pos += strlen(decimal_point); - i++; - } - - /* copy the rest (fractional digits and/or exponent, and \0 terminator) */ - strcpy(&new_str[new_str_pos], &my_str[i]); - - /* assert we didn't underestimate new_len (an overestimate is OK) */ - Assert(strlen(new_str) <= new_len); - - return new_str; -} - - -/* - * fputnbytes: print exactly N bytes to a file - * - * We avoid using %.*s here because it can misbehave if the data - * is not valid in what libc thinks is the prevailing encoding. - */ -static void -fputnbytes(FILE *f, const char *str, size_t n) -{ - while (n-- > 0) - fputc(*str++, f); -} - - -static void -print_separator(struct separator sep, FILE *fout) -{ - if (sep.separator_zero) - fputc('\000', fout); - else if (sep.separator) - fputs(sep.separator, fout); -} - - -/* - * Return the list of explicitly-requested footers or, when applicable, the - * default "(xx rows)" footer. Always omit the default footer when given - * non-default footers, "\pset footer off", or a specific instruction to that - * effect from a calling backslash command. Vertical formats number each row, - * making the default footer redundant; they do not call this function. - * - * The return value may point to static storage; do not keep it across calls. - */ -static printTableFooter * -footers_with_default(const printTableContent *cont) -{ - if (cont->footers == NULL && cont->opt->default_footer) - { - unsigned long total_records; - - total_records = cont->opt->prior_records + cont->nrows; - snprintf(default_footer, sizeof(default_footer), - ngettext("(%lu row)", "(%lu rows)", total_records), - total_records); - - return &default_footer_cell; - } - else - return cont->footers; -} - - -/*************************/ -/* Unaligned text */ -/*************************/ - - -static void -print_unaligned_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned int i; - const char *const * ptr; - bool need_recordsep = false; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(cont->title, fout); - print_separator(cont->opt->recordSep, fout); - } - - /* print headers */ - if (!opt_tuples_only) - { - for (ptr = cont->headers; *ptr; ptr++) - { - if (ptr != cont->headers) - print_separator(cont->opt->fieldSep, fout); - fputs(*ptr, fout); - } - need_recordsep = true; - } - } - else - /* assume continuing printout */ - need_recordsep = true; - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (need_recordsep) - { - print_separator(cont->opt->recordSep, fout); - need_recordsep = false; - if (cancel_pressed) - break; - } - fputs(*ptr, fout); - - if ((i + 1) % cont->ncolumns) - print_separator(cont->opt->fieldSep, fout); - else - need_recordsep = true; - } - - /* print footers */ - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - if (!opt_tuples_only && footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - for (f = footers; f; f = f->next) - { - if (need_recordsep) - { - print_separator(cont->opt->recordSep, fout); - need_recordsep = false; - } - fputs(f->data, fout); - need_recordsep = true; - } - } - - /* - * The last record is terminated by a newline, independent of the set - * record separator. But when the record separator is a zero byte, we - * use that (compatible with find -print0 and xargs). - */ - if (need_recordsep) - { - if (cont->opt->recordSep.separator_zero) - print_separator(cont->opt->recordSep, fout); - else - fputc('\n', fout); - } - } -} - - -static void -print_unaligned_vertical(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned int i; - const char *const * ptr; - bool need_recordsep = false; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(cont->title, fout); - need_recordsep = true; - } - } - else - /* assume continuing printout */ - need_recordsep = true; - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (need_recordsep) - { - /* record separator is 2 occurrences of recordsep in this mode */ - print_separator(cont->opt->recordSep, fout); - print_separator(cont->opt->recordSep, fout); - need_recordsep = false; - if (cancel_pressed) - break; - } - - fputs(cont->headers[i % cont->ncolumns], fout); - print_separator(cont->opt->fieldSep, fout); - fputs(*ptr, fout); - - if ((i + 1) % cont->ncolumns) - print_separator(cont->opt->recordSep, fout); - else - need_recordsep = true; - } - - if (cont->opt->stop_table) - { - /* print footers */ - if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - print_separator(cont->opt->recordSep, fout); - for (f = cont->footers; f; f = f->next) - { - print_separator(cont->opt->recordSep, fout); - fputs(f->data, fout); - } - } - - /* see above in print_unaligned_text() */ - if (need_recordsep) - { - if (cont->opt->recordSep.separator_zero) - print_separator(cont->opt->recordSep, fout); - else - fputc('\n', fout); - } - } -} - - -/********************/ -/* Aligned text */ -/********************/ - - -/* draw "line" */ -static void -_print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths, - unsigned short border, printTextRule pos, - const printTextFormat *format, - FILE *fout) -{ - const printTextLineFormat *lformat = &format->lrule[pos]; - unsigned int i, - j; - - if (border == 1) - fputs(lformat->hrule, fout); - else if (border == 2) - fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); - - for (i = 0; i < ncolumns; i++) - { - for (j = 0; j < widths[i]; j++) - fputs(lformat->hrule, fout); - - if (i < ncolumns - 1) - { - if (border == 0) - fputc(' ', fout); - else - fprintf(fout, "%s%s%s", lformat->hrule, - lformat->midvrule, lformat->hrule); - } - } - - if (border == 2) - fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); - else if (border == 1) - fputs(lformat->hrule, fout); - - fputc('\n', fout); -} - - -/* - * Print pretty boxes around cells. - */ -static void -print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager) -{ - bool opt_tuples_only = cont->opt->tuples_only; - int encoding = cont->opt->encoding; - unsigned short opt_border = cont->opt->border; - const printTextFormat *format = get_line_style(cont->opt); - const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; - - unsigned int col_count = 0, - cell_count = 0; - - unsigned int i, - j; - - unsigned int *width_header, - *max_width, - *width_wrap, - *width_average; - unsigned int *max_nl_lines, /* value split by newlines */ - *curr_nl_line, - *max_bytes; - unsigned char **format_buf; - unsigned int width_total; - unsigned int total_header_width; - unsigned int extra_row_output_lines = 0; - unsigned int extra_output_lines = 0; - - const char *const * ptr; - - struct lineptr **col_lineptrs; /* pointers to line pointer per column */ - - bool *header_done; /* Have all header lines been output? */ - int *bytes_output; /* Bytes output for column value */ - printTextLineWrap *wrap; /* Wrap status for each column */ - int output_columns = 0; /* Width of interactive console */ - bool is_local_pager = false; - - if (cancel_pressed) - return; - - if (opt_border > 2) - opt_border = 2; - - if (cont->ncolumns > 0) - { - col_count = cont->ncolumns; - width_header = pg_malloc0(col_count * sizeof(*width_header)); - width_average = pg_malloc0(col_count * sizeof(*width_average)); - max_width = pg_malloc0(col_count * sizeof(*max_width)); - width_wrap = pg_malloc0(col_count * sizeof(*width_wrap)); - max_nl_lines = pg_malloc0(col_count * sizeof(*max_nl_lines)); - curr_nl_line = pg_malloc0(col_count * sizeof(*curr_nl_line)); - col_lineptrs = pg_malloc0(col_count * sizeof(*col_lineptrs)); - max_bytes = pg_malloc0(col_count * sizeof(*max_bytes)); - format_buf = pg_malloc0(col_count * sizeof(*format_buf)); - header_done = pg_malloc0(col_count * sizeof(*header_done)); - bytes_output = pg_malloc0(col_count * sizeof(*bytes_output)); - wrap = pg_malloc0(col_count * sizeof(*wrap)); - } - else - { - width_header = NULL; - width_average = NULL; - max_width = NULL; - width_wrap = NULL; - max_nl_lines = NULL; - curr_nl_line = NULL; - col_lineptrs = NULL; - max_bytes = NULL; - format_buf = NULL; - header_done = NULL; - bytes_output = NULL; - wrap = NULL; - } - - /* scan all column headers, find maximum width and max max_nl_lines */ - for (i = 0; i < col_count; i++) - { - int width, - nl_lines, - bytes_required; - - pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]), - encoding, &width, &nl_lines, &bytes_required); - if (width > max_width[i]) - max_width[i] = width; - if (nl_lines > max_nl_lines[i]) - max_nl_lines[i] = nl_lines; - if (bytes_required > max_bytes[i]) - max_bytes[i] = bytes_required; - if (nl_lines > extra_row_output_lines) - extra_row_output_lines = nl_lines; - - width_header[i] = width; - } - /* Add height of tallest header column */ - extra_output_lines += extra_row_output_lines; - extra_row_output_lines = 0; - - /* scan all cells, find maximum width, compute cell_count */ - for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++) - { - int width, - nl_lines, - bytes_required; - - pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding, - &width, &nl_lines, &bytes_required); - - if (width > max_width[i % col_count]) - max_width[i % col_count] = width; - if (nl_lines > max_nl_lines[i % col_count]) - max_nl_lines[i % col_count] = nl_lines; - if (bytes_required > max_bytes[i % col_count]) - max_bytes[i % col_count] = bytes_required; - - width_average[i % col_count] += width; - } - - /* If we have rows, compute average */ - if (col_count != 0 && cell_count != 0) - { - int rows = cell_count / col_count; - - for (i = 0; i < col_count; i++) - width_average[i] /= rows; - } - - /* adjust the total display width based on border style */ - if (opt_border == 0) - width_total = col_count; - else if (opt_border == 1) - width_total = col_count * 3 - ((col_count > 0) ? 1 : 0); - else - width_total = col_count * 3 + 1; - total_header_width = width_total; - - for (i = 0; i < col_count; i++) - { - width_total += max_width[i]; - total_header_width += width_header[i]; - } - - /* - * At this point: max_width[] contains the max width of each column, - * max_nl_lines[] contains the max number of lines in each column, - * max_bytes[] contains the maximum storage space for formatting strings, - * width_total contains the giant width sum. Now we allocate some memory - * for line pointers. - */ - for (i = 0; i < col_count; i++) - { - /* Add entry for ptr == NULL array termination */ - col_lineptrs[i] = pg_malloc0((max_nl_lines[i] + 1) * - sizeof(**col_lineptrs)); - - format_buf[i] = pg_malloc(max_bytes[i] + 1); - - col_lineptrs[i]->ptr = format_buf[i]; - } - - /* Default word wrap to the full width, i.e. no word wrap */ - for (i = 0; i < col_count; i++) - width_wrap[i] = max_width[i]; - - /* - * Choose target output width: \pset columns, or $COLUMNS, or ioctl - */ - if (cont->opt->columns > 0) - output_columns = cont->opt->columns; - else if ((fout == stdout && isatty(fileno(stdout))) || is_pager) - { - if (cont->opt->env_columns > 0) - output_columns = cont->opt->env_columns; -#ifdef TIOCGWINSZ - else - { - struct winsize screen_size; - - if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1) - output_columns = screen_size.ws_col; - } -#endif - } - - if (cont->opt->format == PRINT_WRAPPED) - { - /* - * Optional optimized word wrap. Shrink columns with a high max/avg - * ratio. Slightly bias against wider columns. (Increases chance a - * narrow column will fit in its cell.) If available columns is - * positive... and greater than the width of the unshrinkable column - * headers - */ - if (output_columns > 0 && output_columns >= total_header_width) - { - /* While there is still excess width... */ - while (width_total > output_columns) - { - double max_ratio = 0; - int worst_col = -1; - - /* - * Find column that has the highest ratio of its maximum width - * compared to its average width. This tells us which column - * will produce the fewest wrapped values if shortened. - * width_wrap starts as equal to max_width. - */ - for (i = 0; i < col_count; i++) - { - if (width_average[i] && width_wrap[i] > width_header[i]) - { - /* Penalize wide columns by 1% of their width */ - double ratio; - - ratio = (double) width_wrap[i] / width_average[i] + - max_width[i] * 0.01; - if (ratio > max_ratio) - { - max_ratio = ratio; - worst_col = i; - } - } - } - - /* Exit loop if we can't squeeze any more. */ - if (worst_col == -1) - break; - - /* Decrease width of target column by one. */ - width_wrap[worst_col]--; - width_total--; - } - } - } - - /* - * If in expanded auto mode, we have now calculated the expected width, so - * we can now escape to vertical mode if necessary. - */ - if (cont->opt->expanded == 2 && output_columns > 0 && - (output_columns < total_header_width || output_columns < width_total)) - { - print_aligned_vertical(cont, fout, is_pager); - goto cleanup; - } - - /* If we wrapped beyond the display width, use the pager */ - if (!is_pager && fout == stdout && output_columns > 0 && - (output_columns < total_header_width || output_columns < width_total)) - { - fout = PageOutput(INT_MAX, cont->opt); /* force pager */ - is_pager = is_local_pager = true; - } - - /* Check if newlines or our wrapping now need the pager */ - if (!is_pager && fout == stdout) - { - /* scan all cells, find maximum width, compute cell_count */ - for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++) - { - int width, - nl_lines, - bytes_required; - - pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding, - &width, &nl_lines, &bytes_required); - - /* - * A row can have both wrapping and newlines that cause it to - * display across multiple lines. We check for both cases below. - */ - if (width > 0 && width_wrap[i]) - { - unsigned int extra_lines; - - /* don't count the first line of nl_lines - it's not "extra" */ - extra_lines = ((width - 1) / width_wrap[i]) + nl_lines - 1; - if (extra_lines > extra_row_output_lines) - extra_row_output_lines = extra_lines; - } - - /* i is the current column number: increment with wrap */ - if (++i >= col_count) - { - i = 0; - /* At last column of each row, add tallest column height */ - extra_output_lines += extra_row_output_lines; - extra_row_output_lines = 0; - } - } - IsPagerNeeded(cont, extra_output_lines, false, &fout, &is_pager); - is_local_pager = is_pager; - } - - /* time to output */ - if (cont->opt->start_table) - { - /* print title */ - if (cont->title && !opt_tuples_only) - { - int width, - height; - - pg_wcssize((const unsigned char *) cont->title, strlen(cont->title), - encoding, &width, &height, NULL); - if (width >= width_total) - /* Aligned */ - fprintf(fout, "%s\n", cont->title); - else - /* Centered */ - fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", - cont->title); - } - - /* print headers */ - if (!opt_tuples_only) - { - int more_col_wrapping; - int curr_nl_line; - - if (opt_border == 2) - _print_horizontal_line(col_count, width_wrap, opt_border, - PRINT_RULE_TOP, format, fout); - - for (i = 0; i < col_count; i++) - pg_wcsformat((const unsigned char *) cont->headers[i], - strlen(cont->headers[i]), encoding, - col_lineptrs[i], max_nl_lines[i]); - - more_col_wrapping = col_count; - curr_nl_line = 0; - memset(header_done, false, col_count * sizeof(bool)); - while (more_col_wrapping) - { - if (opt_border == 2) - fputs(dformat->leftvrule, fout); - - for (i = 0; i < cont->ncolumns; i++) - { - struct lineptr *this_line = col_lineptrs[i] + curr_nl_line; - unsigned int nbspace; - - if (opt_border != 0 || - (!format->wrap_right_border && i > 0)) - fputs(curr_nl_line ? format->header_nl_left : " ", - fout); - - if (!header_done[i]) - { - nbspace = width_wrap[i] - this_line->width; - - /* centered */ - fprintf(fout, "%-*s%s%-*s", - nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, ""); - - if (!(this_line + 1)->ptr) - { - more_col_wrapping--; - header_done[i] = 1; - } - } - else - fprintf(fout, "%*s", width_wrap[i], ""); - - if (opt_border != 0 || format->wrap_right_border) - fputs(!header_done[i] ? format->header_nl_right : " ", - fout); - - if (opt_border != 0 && col_count > 0 && i < col_count - 1) - fputs(dformat->midvrule, fout); - } - curr_nl_line++; - - if (opt_border == 2) - fputs(dformat->rightvrule, fout); - fputc('\n', fout); - } - - _print_horizontal_line(col_count, width_wrap, opt_border, - PRINT_RULE_MIDDLE, format, fout); - } - } - - /* print cells, one loop per row */ - for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count) - { - bool more_lines; - - if (cancel_pressed) - break; - - /* - * Format each cell. - */ - for (j = 0; j < col_count; j++) - { - pg_wcsformat((const unsigned char *) ptr[j], strlen(ptr[j]), encoding, - col_lineptrs[j], max_nl_lines[j]); - curr_nl_line[j] = 0; - } - - memset(bytes_output, 0, col_count * sizeof(int)); - - /* - * Each time through this loop, one display line is output. It can - * either be a full value or a partial value if embedded newlines - * exist or if 'format=wrapping' mode is enabled. - */ - do - { - more_lines = false; - - /* left border */ - if (opt_border == 2) - fputs(dformat->leftvrule, fout); - - /* for each column */ - for (j = 0; j < col_count; j++) - { - /* We have a valid array element, so index it */ - struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]]; - int bytes_to_output; - int chars_to_output = width_wrap[j]; - bool finalspaces = (opt_border == 2 || - (col_count > 0 && j < col_count - 1)); - - /* Print left-hand wrap or newline mark */ - if (opt_border != 0) - { - if (wrap[j] == PRINT_LINE_WRAP_WRAP) - fputs(format->wrap_left, fout); - else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE) - fputs(format->nl_left, fout); - else - fputc(' ', fout); - } - - if (!this_line->ptr) - { - /* Past newline lines so just pad for other columns */ - if (finalspaces) - fprintf(fout, "%*s", chars_to_output, ""); - } - else - { - /* Get strlen() of the characters up to width_wrap */ - bytes_to_output = - strlen_max_width(this_line->ptr + bytes_output[j], - &chars_to_output, encoding); - - /* - * If we exceeded width_wrap, it means the display width - * of a single character was wider than our target width. - * In that case, we have to pretend we are only printing - * the target display width and make the best of it. - */ - if (chars_to_output > width_wrap[j]) - chars_to_output = width_wrap[j]; - - if (cont->aligns[j] == 'r') /* Right aligned cell */ - { - /* spaces first */ - fprintf(fout, "%*s", width_wrap[j] - chars_to_output, ""); - fputnbytes(fout, - (char *) (this_line->ptr + bytes_output[j]), - bytes_to_output); - } - else /* Left aligned cell */ - { - /* spaces second */ - fputnbytes(fout, - (char *) (this_line->ptr + bytes_output[j]), - bytes_to_output); - } - - bytes_output[j] += bytes_to_output; - - /* Do we have more text to wrap? */ - if (*(this_line->ptr + bytes_output[j]) != '\0') - more_lines = true; - else - { - /* Advance to next newline line */ - curr_nl_line[j]++; - if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL) - more_lines = true; - bytes_output[j] = 0; - } - } - - /* Determine next line's wrap status for this column */ - wrap[j] = PRINT_LINE_WRAP_NONE; - if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL) - { - if (bytes_output[j] != 0) - wrap[j] = PRINT_LINE_WRAP_WRAP; - else if (curr_nl_line[j] != 0) - wrap[j] = PRINT_LINE_WRAP_NEWLINE; - } - - /* - * If left-aligned, pad out remaining space if needed (not - * last column, and/or wrap marks required). - */ - if (cont->aligns[j] != 'r') /* Left aligned cell */ - { - if (finalspaces || - wrap[j] == PRINT_LINE_WRAP_WRAP || - wrap[j] == PRINT_LINE_WRAP_NEWLINE) - fprintf(fout, "%*s", - width_wrap[j] - chars_to_output, ""); - } - - /* Print right-hand wrap or newline mark */ - if (wrap[j] == PRINT_LINE_WRAP_WRAP) - fputs(format->wrap_right, fout); - else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE) - fputs(format->nl_right, fout); - else if (opt_border == 2 || (col_count > 0 && j < col_count - 1)) - fputc(' ', fout); - - /* Print column divider, if not the last column */ - if (opt_border != 0 && (col_count > 0 && j < col_count - 1)) - { - if (wrap[j + 1] == PRINT_LINE_WRAP_WRAP) - fputs(format->midvrule_wrap, fout); - else if (wrap[j + 1] == PRINT_LINE_WRAP_NEWLINE) - fputs(format->midvrule_nl, fout); - else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL) - fputs(format->midvrule_blank, fout); - else - fputs(dformat->midvrule, fout); - } - } - - /* end-of-row border */ - if (opt_border == 2) - fputs(dformat->rightvrule, fout); - fputc('\n', fout); - - } while (more_lines); - } - - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - if (opt_border == 2 && !cancel_pressed) - _print_horizontal_line(col_count, width_wrap, opt_border, - PRINT_RULE_BOTTOM, format, fout); - - /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - { - printTableFooter *f; - - for (f = footers; f; f = f->next) - fprintf(fout, "%s\n", f->data); - } - - fputc('\n', fout); - } - -cleanup: - /* clean up */ - for (i = 0; i < col_count; i++) - { - free(col_lineptrs[i]); - free(format_buf[i]); - } - free(width_header); - free(width_average); - free(max_width); - free(width_wrap); - free(max_nl_lines); - free(curr_nl_line); - free(col_lineptrs); - free(max_bytes); - free(format_buf); - free(header_done); - free(bytes_output); - free(wrap); - - if (is_local_pager) - ClosePager(fout); -} - - -static void -print_aligned_vertical_line(const printTextFormat *format, - const unsigned short opt_border, - unsigned long record, - unsigned int hwidth, - unsigned int dwidth, - printTextRule pos, - FILE *fout) -{ - const printTextLineFormat *lformat = &format->lrule[pos]; - unsigned int i; - int reclen = 0; - - if (opt_border == 2) - fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); - else if (opt_border == 1) - fputs(lformat->hrule, fout); - - if (record) - { - if (opt_border == 0) - reclen = fprintf(fout, "* Record %lu", record); - else - reclen = fprintf(fout, "[ RECORD %lu ]", record); - } - if (opt_border != 2) - reclen++; - if (reclen < 0) - reclen = 0; - for (i = reclen; i < hwidth; i++) - fputs(opt_border > 0 ? lformat->hrule : " ", fout); - reclen -= hwidth; - - if (opt_border > 0) - { - if (reclen-- <= 0) - fputs(lformat->hrule, fout); - if (reclen-- <= 0) - fputs(lformat->midvrule, fout); - if (reclen-- <= 0) - fputs(lformat->hrule, fout); - } - else - { - if (reclen-- <= 0) - fputc(' ', fout); - } - if (reclen < 0) - reclen = 0; - for (i = reclen; i < dwidth; i++) - fputs(opt_border > 0 ? lformat->hrule : " ", fout); - if (opt_border == 2) - fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); - fputc('\n', fout); -} - -static void -print_aligned_vertical(const printTableContent *cont, - FILE *fout, bool is_pager) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - const printTextFormat *format = get_line_style(cont->opt); - const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; - int encoding = cont->opt->encoding; - unsigned long record = cont->opt->prior_records + 1; - const char *const * ptr; - unsigned int i, - hwidth = 0, - dwidth = 0, - hheight = 1, - dheight = 1, - hformatsize = 0, - dformatsize = 0; - struct lineptr *hlineptr, - *dlineptr; - bool is_local_pager = false, - hmultiline = false, - dmultiline = false; - int output_columns = 0; /* Width of interactive console */ - - if (cancel_pressed) - return; - - if (opt_border > 2) - opt_border = 2; - - if (cont->cells[0] == NULL && cont->opt->start_table && - cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - if (!opt_tuples_only && !cancel_pressed && footers) - { - printTableFooter *f; - - for (f = footers; f; f = f->next) - fprintf(fout, "%s\n", f->data); - } - - fputc('\n', fout); - - return; - } - - /* - * Deal with the pager here instead of in printTable(), because we could - * get here via print_aligned_text() in expanded auto mode, and so we have - * to recalculate the pager requirement based on vertical output. - */ - if (!is_pager) - { - IsPagerNeeded(cont, 0, true, &fout, &is_pager); - is_local_pager = is_pager; - } - - /* Find the maximum dimensions for the headers */ - for (i = 0; i < cont->ncolumns; i++) - { - int width, - height, - fs; - - pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]), - encoding, &width, &height, &fs); - if (width > hwidth) - hwidth = width; - if (height > hheight) - { - hheight = height; - hmultiline = true; - } - if (fs > hformatsize) - hformatsize = fs; - } - - /* find longest data cell */ - for (i = 0, ptr = cont->cells; *ptr; ptr++, i++) - { - int width, - height, - fs; - - pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding, - &width, &height, &fs); - if (width > dwidth) - dwidth = width; - if (height > dheight) - { - dheight = height; - dmultiline = true; - } - if (fs > dformatsize) - dformatsize = fs; - } - - /* - * We now have all the information we need to setup the formatting - * structures - */ - dlineptr = pg_malloc((sizeof(*dlineptr)) * (dheight + 1)); - hlineptr = pg_malloc((sizeof(*hlineptr)) * (hheight + 1)); - - dlineptr->ptr = pg_malloc(dformatsize); - hlineptr->ptr = pg_malloc(hformatsize); - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - fprintf(fout, "%s\n", cont->title); - } - - /* - * Choose target output width: \pset columns, or $COLUMNS, or ioctl - */ - if (cont->opt->columns > 0) - output_columns = cont->opt->columns; - else if ((fout == stdout && isatty(fileno(stdout))) || is_pager) - { - if (cont->opt->env_columns > 0) - output_columns = cont->opt->env_columns; -#ifdef TIOCGWINSZ - else - { - struct winsize screen_size; - - if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1) - output_columns = screen_size.ws_col; - } -#endif - } - - /* - * Calculate available width for data in wrapped mode - */ - if (cont->opt->format == PRINT_WRAPPED) - { - unsigned int swidth, - rwidth = 0, - newdwidth; - - if (opt_border == 0) - { - /* - * For border = 0, one space in the middle. (If we discover we - * need to wrap, the spacer column will be replaced by a wrap - * marker, and we'll make room below for another wrap marker at - * the end of the line. But for now, assume no wrap is needed.) - */ - swidth = 1; - - /* We might need a column for header newline markers, too */ - if (hmultiline) - swidth++; - } - else if (opt_border == 1) - { - /* - * For border = 1, two spaces and a vrule in the middle. (As - * above, we might need one more column for a wrap marker.) - */ - swidth = 3; - - /* We might need a column for left header newline markers, too */ - if (hmultiline && (format == &pg_asciiformat_old)) - swidth++; - } - else - { - /* - * For border = 2, two more for the vrules at the beginning and - * end of the lines, plus spacer columns adjacent to these. (We - * won't need extra columns for wrap/newline markers, we'll just - * repurpose the spacers.) - */ - swidth = 7; - } - - /* Reserve a column for data newline indicators, too, if needed */ - if (dmultiline && - opt_border < 2 && format != &pg_asciiformat_old) - swidth++; - - /* Determine width required for record header lines */ - if (!opt_tuples_only) - { - if (cont->nrows > 0) - rwidth = 1 + (int) log10(cont->nrows); - if (opt_border == 0) - rwidth += 9; /* "* RECORD " */ - else if (opt_border == 1) - rwidth += 12; /* "-[ RECORD ]" */ - else - rwidth += 15; /* "+-[ RECORD ]-+" */ - } - - /* We might need to do the rest of the calculation twice */ - for (;;) - { - unsigned int width; - - /* Total width required to not wrap data */ - width = hwidth + swidth + dwidth; - /* ... and not the header lines, either */ - if (width < rwidth) - width = rwidth; - - if (output_columns > 0) - { - unsigned int min_width; - - /* Minimum acceptable width: room for just 3 columns of data */ - min_width = hwidth + swidth + 3; - /* ... but not less than what the record header lines need */ - if (min_width < rwidth) - min_width = rwidth; - - if (output_columns >= width) - { - /* Plenty of room, use native data width */ - /* (but at least enough for the record header lines) */ - newdwidth = width - hwidth - swidth; - } - else if (output_columns < min_width) - { - /* Set data width to match min_width */ - newdwidth = min_width - hwidth - swidth; - } - else - { - /* Set data width to match output_columns */ - newdwidth = output_columns - hwidth - swidth; - } - } - else - { - /* Don't know the wrap limit, so use native data width */ - /* (but at least enough for the record header lines) */ - newdwidth = width - hwidth - swidth; - } - - /* - * If we will need to wrap data and didn't already allocate a data - * newline/wrap marker column, do so and recompute. - */ - if (newdwidth < dwidth && !dmultiline && - opt_border < 2 && format != &pg_asciiformat_old) - { - dmultiline = true; - swidth++; - } - else - break; - } - - dwidth = newdwidth; - } - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - printTextRule pos; - int dline, - hline, - dcomplete, - hcomplete, - offset, - chars_to_output; - - if (cancel_pressed) - break; - - if (i == 0) - pos = PRINT_RULE_TOP; - else - pos = PRINT_RULE_MIDDLE; - - /* Print record header (e.g. "[ RECORD N ]") above each record */ - if (i % cont->ncolumns == 0) - { - unsigned int lhwidth = hwidth; - - if ((opt_border < 2) && - (hmultiline) && - (format == &pg_asciiformat_old)) - lhwidth++; /* for newline indicators */ - - if (!opt_tuples_only) - print_aligned_vertical_line(format, opt_border, record++, - lhwidth, dwidth, pos, fout); - else if (i != 0 || !cont->opt->start_table || opt_border == 2) - print_aligned_vertical_line(format, opt_border, 0, lhwidth, - dwidth, pos, fout); - } - - /* Format the header */ - pg_wcsformat((const unsigned char *) cont->headers[i % cont->ncolumns], - strlen(cont->headers[i % cont->ncolumns]), - encoding, hlineptr, hheight); - /* Format the data */ - pg_wcsformat((const unsigned char *) *ptr, strlen(*ptr), encoding, - dlineptr, dheight); - - /* - * Loop through header and data in parallel dealing with newlines and - * wrapped lines until they're both exhausted - */ - dline = hline = 0; - dcomplete = hcomplete = 0; - offset = 0; - chars_to_output = dlineptr[dline].width; - while (!dcomplete || !hcomplete) - { - /* Left border */ - if (opt_border == 2) - fprintf(fout, "%s", dformat->leftvrule); - - /* Header (never wrapped so just need to deal with newlines) */ - if (!hcomplete) - { - int swidth = hwidth, - target_width = hwidth; - - /* - * Left spacer or new line indicator - */ - if ((opt_border == 2) || - (hmultiline && (format == &pg_asciiformat_old))) - fputs(hline ? format->header_nl_left : " ", fout); - - /* - * Header text - */ - strlen_max_width(hlineptr[hline].ptr, &target_width, - encoding); - fprintf(fout, "%-s", hlineptr[hline].ptr); - - /* - * Spacer - */ - swidth -= target_width; - if (swidth > 0) - fprintf(fout, "%*s", swidth, " "); - - /* - * New line indicator or separator's space - */ - if (hlineptr[hline + 1].ptr) - { - /* More lines after this one due to a newline */ - if ((opt_border > 0) || - (hmultiline && (format != &pg_asciiformat_old))) - fputs(format->header_nl_right, fout); - hline++; - } - else - { - /* This was the last line of the header */ - if ((opt_border > 0) || - (hmultiline && (format != &pg_asciiformat_old))) - fputs(" ", fout); - hcomplete = 1; - } - } - else - { - unsigned int swidth = hwidth + opt_border; - - if ((opt_border < 2) && - (hmultiline) && - (format == &pg_asciiformat_old)) - swidth++; - - if ((opt_border == 0) && - (format != &pg_asciiformat_old) && - (hmultiline)) - swidth++; - - fprintf(fout, "%*s", swidth, " "); - } - - /* Separator */ - if (opt_border > 0) - { - if (offset) - fputs(format->midvrule_wrap, fout); - else if (dline == 0) - fputs(dformat->midvrule, fout); - else - fputs(format->midvrule_nl, fout); - } - - /* Data */ - if (!dcomplete) - { - int target_width = dwidth, - bytes_to_output, - swidth = dwidth; - - /* - * Left spacer or wrap indicator - */ - fputs(offset == 0 ? " " : format->wrap_left, fout); - - /* - * Data text - */ - bytes_to_output = strlen_max_width(dlineptr[dline].ptr + offset, - &target_width, encoding); - fputnbytes(fout, (char *) (dlineptr[dline].ptr + offset), - bytes_to_output); - - chars_to_output -= target_width; - offset += bytes_to_output; - - /* Spacer */ - swidth -= target_width; - - if (chars_to_output) - { - /* continuing a wrapped column */ - if ((opt_border > 1) || - (dmultiline && (format != &pg_asciiformat_old))) - { - if (swidth > 0) - fprintf(fout, "%*s", swidth, " "); - fputs(format->wrap_right, fout); - } - } - else if (dlineptr[dline + 1].ptr) - { - /* reached a newline in the column */ - if ((opt_border > 1) || - (dmultiline && (format != &pg_asciiformat_old))) - { - if (swidth > 0) - fprintf(fout, "%*s", swidth, " "); - fputs(format->nl_right, fout); - } - dline++; - offset = 0; - chars_to_output = dlineptr[dline].width; - } - else - { - /* reached the end of the cell */ - if (opt_border > 1) - { - if (swidth > 0) - fprintf(fout, "%*s", swidth, " "); - fputs(" ", fout); - } - dcomplete = 1; - } - - /* Right border */ - if (opt_border == 2) - fputs(dformat->rightvrule, fout); - - fputs("\n", fout); - } - else - { - /* - * data exhausted (this can occur if header is longer than the - * data due to newlines in the header) - */ - if (opt_border < 2) - fputs("\n", fout); - else - fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule); - } - } - } - - if (cont->opt->stop_table) - { - if (opt_border == 2 && !cancel_pressed) - print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, - PRINT_RULE_BOTTOM, fout); - - /* print footers */ - if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - if (opt_border < 2) - fputc('\n', fout); - for (f = cont->footers; f; f = f->next) - fprintf(fout, "%s\n", f->data); - } - - fputc('\n', fout); - } - - free(hlineptr->ptr); - free(dlineptr->ptr); - free(hlineptr); - free(dlineptr); - - if (is_local_pager) - ClosePager(fout); -} - - -/**********************/ -/* HTML printing ******/ -/**********************/ - - -void -html_escaped_print(const char *in, FILE *fout) -{ - const char *p; - bool leading_space = true; - - for (p = in; *p; p++) - { - switch (*p) - { - case '&': - fputs("&", fout); - break; - case '<': - fputs("<", fout); - break; - case '>': - fputs(">", fout); - break; - case '\n': - fputs("
\n", fout); - break; - case '"': - fputs(""", fout); - break; - case ' ': - /* protect leading space, for EXPLAIN output */ - if (leading_space) - fputs(" ", fout); - else - fputs(" ", fout); - break; - default: - fputc(*p, fout); - } - if (*p != ' ') - leading_space = false; - } -} - - -static void -print_html_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - const char *opt_table_attr = cont->opt->tableAttr; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - fprintf(fout, "
\n", fout); - - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(" \n", fout); - } - - /* print headers */ - if (!opt_tuples_only) - { - fputs(" \n", fout); - for (ptr = cont->headers; *ptr; ptr++) - { - fputs(" \n", fout); - } - fputs(" \n", fout); - } - } - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - fputs(" \n", fout); - } - - fprintf(fout, " \n", fout); - - if ((i + 1) % cont->ncolumns == 0) - fputs(" \n", fout); - } - - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - fputs("
", fout); - html_escaped_print(cont->title, fout); - fputs("
", fout); - html_escaped_print(*ptr, fout); - fputs("
", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left"); - /* is string only whitespace? */ - if ((*ptr)[strspn(*ptr, " \t")] == '\0') - fputs("  ", fout); - else - html_escaped_print(*ptr, fout); - - fputs("
\n", fout); - - /* print footers */ - if (!opt_tuples_only && footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - fputs("

", fout); - for (f = footers; f; f = f->next) - { - html_escaped_print(f->data, fout); - fputs("
\n", fout); - } - fputs("

", fout); - } - - fputc('\n', fout); - } -} - - -static void -print_html_vertical(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - const char *opt_table_attr = cont->opt->tableAttr; - unsigned long record = cont->opt->prior_records + 1; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - fprintf(fout, "\n", fout); - - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(" \n", fout); - } - } - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - if (!opt_tuples_only) - fprintf(fout, - "\n \n", - record++); - else - fputs("\n \n", fout); - } - fputs(" \n" - " \n", fout); - - fprintf(fout, " \n \n", fout); - } - - if (cont->opt->stop_table) - { - fputs("
", fout); - html_escaped_print(cont->title, fout); - fputs("
Record %lu
 
", fout); - html_escaped_print(cont->headers[i % cont->ncolumns], fout); - fputs("", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left"); - /* is string only whitespace? */ - if ((*ptr)[strspn(*ptr, " \t")] == '\0') - fputs("  ", fout); - else - html_escaped_print(*ptr, fout); - - fputs("
\n", fout); - - /* print footers */ - if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - fputs("

", fout); - for (f = cont->footers; f; f = f->next) - { - html_escaped_print(f->data, fout); - fputs("
\n", fout); - } - fputs("

", fout); - } - - fputc('\n', fout); - } -} - - -/*************************/ -/* ASCIIDOC */ -/*************************/ - -static void -asciidoc_escaped_print(const char *in, FILE *fout) -{ - const char *p; - - for (p = in; *p; p++) - { - switch (*p) - { - case '|': - fputs("\\|", fout); - break; - default: - fputc(*p, fout); - } - } -} - -static void -print_asciidoc_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - /* print table in new paragraph - enforce preliminary new line */ - fputs("\n", fout); - - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(".", fout); - fputs(cont->title, fout); - fputs("\n", fout); - } - - /* print table [] header definition */ - fprintf(fout, "[%scols=\"", !opt_tuples_only ? "options=\"header\"," : ""); - for (i = 0; i < cont->ncolumns; i++) - { - if (i != 0) - fputs(",", fout); - fprintf(fout, "%s", cont->aligns[(i) % cont->ncolumns] == 'r' ? ">l" : "headers; *ptr; ptr++) - { - if (ptr != cont->headers) - fputs(" ", fout); - fputs("^l|", fout); - asciidoc_escaped_print(*ptr, fout); - } - fputs("\n", fout); - } - } - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - } - - if (i % cont->ncolumns != 0) - fputs(" ", fout); - fputs("|", fout); - - /* protect against needless spaces */ - if ((*ptr)[strspn(*ptr, " \t")] == '\0') - { - if ((i + 1) % cont->ncolumns != 0) - fputs(" ", fout); - } - else - asciidoc_escaped_print(*ptr, fout); - - if ((i + 1) % cont->ncolumns == 0) - fputs("\n", fout); - } - - fputs("|====\n", fout); - - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - /* print footers */ - if (!opt_tuples_only && footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - fputs("\n....\n", fout); - for (f = footers; f; f = f->next) - { - fputs(f->data, fout); - fputs("\n", fout); - } - fputs("....\n", fout); - } - } -} - -static void -print_asciidoc_vertical(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned long record = cont->opt->prior_records + 1; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (cont->opt->start_table) - { - /* print table in new paragraph - enforce preliminary new line */ - fputs("\n", fout); - - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(".", fout); - fputs(cont->title, fout); - fputs("\n", fout); - } - - /* print table [] header definition */ - fputs("[cols=\"h,l\"", fout); - switch (opt_border) - { - case 0: - fputs(",frame=\"none\",grid=\"none\"", fout); - break; - case 1: - fputs(",frame=\"none\"", fout); - break; - case 2: - fputs(",frame=\"all\",grid=\"all\"", fout); - break; - } - fputs("]\n", fout); - fputs("|====\n", fout); - } - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - if (!opt_tuples_only) - fprintf(fout, - "2+^|Record %lu\n", - record++); - else - fputs("2+|\n", fout); - } - - fputs("headers[i % cont->ncolumns], fout); - - fprintf(fout, " %s|", cont->aligns[i % cont->ncolumns] == 'r' ? ">l" : "opt->stop_table) - { - /* print footers */ - if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) - { - printTableFooter *f; - - fputs("\n....\n", fout); - for (f = cont->footers; f; f = f->next) - { - fputs(f->data, fout); - fputs("\n", fout); - } - fputs("....\n", fout); - } - } -} - -/*************************/ -/* LaTeX */ -/*************************/ - - -static void -latex_escaped_print(const char *in, FILE *fout) -{ - const char *p; - - for (p = in; *p; p++) - switch (*p) - { - case '&': - fputs("\\&", fout); - break; - case '%': - fputs("\\%", fout); - break; - case '$': - fputs("\\$", fout); - break; - case '_': - fputs("\\_", fout); - break; - case '{': - fputs("\\{", fout); - break; - case '}': - fputs("\\}", fout); - break; - case '\\': - fputs("\\backslash", fout); - break; - case '\n': - fputs("\\\\", fout); - break; - default: - fputc(*p, fout); - } -} - - -static void -print_latex_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (opt_border > 3) - opt_border = 3; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs("\\begin{center}\n", fout); - latex_escaped_print(cont->title, fout); - fputs("\n\\end{center}\n\n", fout); - } - - /* begin environment and set alignments and borders */ - fputs("\\begin{tabular}{", fout); - - if (opt_border >= 2) - fputs("| ", fout); - for (i = 0; i < cont->ncolumns; i++) - { - fputc(*(cont->aligns + i), fout); - if (opt_border != 0 && i < cont->ncolumns - 1) - fputs(" | ", fout); - } - if (opt_border >= 2) - fputs(" |", fout); - - fputs("}\n", fout); - - if (!opt_tuples_only && opt_border >= 2) - fputs("\\hline\n", fout); - - /* print headers */ - if (!opt_tuples_only) - { - for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) - { - if (i != 0) - fputs(" & ", fout); - fputs("\\textit{", fout); - latex_escaped_print(*ptr, fout); - fputc('}', fout); - } - fputs(" \\\\\n", fout); - fputs("\\hline\n", fout); - } - } - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - latex_escaped_print(*ptr, fout); - - if ((i + 1) % cont->ncolumns == 0) - { - fputs(" \\\\\n", fout); - if (opt_border == 3) - fputs("\\hline\n", fout); - if (cancel_pressed) - break; - } - else - fputs(" & ", fout); - } - - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - if (opt_border == 2) - fputs("\\hline\n", fout); - - fputs("\\end{tabular}\n\n\\noindent ", fout); - - /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - { - printTableFooter *f; - - for (f = footers; f; f = f->next) - { - latex_escaped_print(f->data, fout); - fputs(" \\\\\n", fout); - } - } - - fputc('\n', fout); - } -} - - -static void -print_latex_longtable_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned int i; - const char *opt_table_attr = cont->opt->tableAttr; - const char *next_opt_table_attr_char = opt_table_attr; - const char *last_opt_table_attr_char = NULL; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (opt_border > 3) - opt_border = 3; - - if (cont->opt->start_table) - { - /* begin environment and set alignments and borders */ - fputs("\\begin{longtable}{", fout); - - if (opt_border >= 2) - fputs("| ", fout); - - for (i = 0; i < cont->ncolumns; i++) - { - /* longtable supports either a width (p) or an alignment (l/r) */ - /* Are we left-justified and was a proportional width specified? */ - if (*(cont->aligns + i) == 'l' && opt_table_attr) - { -#define LONGTABLE_WHITESPACE " \t\n" - - /* advance over whitespace */ - next_opt_table_attr_char += strspn(next_opt_table_attr_char, - LONGTABLE_WHITESPACE); - /* We have a value? */ - if (next_opt_table_attr_char[0] != '\0') - { - fputs("p{", fout); - fwrite(next_opt_table_attr_char, strcspn(next_opt_table_attr_char, - LONGTABLE_WHITESPACE), 1, fout); - last_opt_table_attr_char = next_opt_table_attr_char; - next_opt_table_attr_char += strcspn(next_opt_table_attr_char, - LONGTABLE_WHITESPACE); - fputs("\\textwidth}", fout); - } - /* use previous value */ - else if (last_opt_table_attr_char != NULL) - { - fputs("p{", fout); - fwrite(last_opt_table_attr_char, strcspn(last_opt_table_attr_char, - LONGTABLE_WHITESPACE), 1, fout); - fputs("\\textwidth}", fout); - } - else - fputc('l', fout); - } - else - fputc(*(cont->aligns + i), fout); - - if (opt_border != 0 && i < cont->ncolumns - 1) - fputs(" | ", fout); - } - - if (opt_border >= 2) - fputs(" |", fout); - - fputs("}\n", fout); - - /* print headers */ - if (!opt_tuples_only) - { - /* firsthead */ - if (opt_border >= 2) - fputs("\\toprule\n", fout); - for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) - { - if (i != 0) - fputs(" & ", fout); - fputs("\\small\\textbf{\\textit{", fout); - latex_escaped_print(*ptr, fout); - fputs("}}", fout); - } - fputs(" \\\\\n", fout); - fputs("\\midrule\n\\endfirsthead\n", fout); - - /* secondary heads */ - if (opt_border >= 2) - fputs("\\toprule\n", fout); - for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) - { - if (i != 0) - fputs(" & ", fout); - fputs("\\small\\textbf{\\textit{", fout); - latex_escaped_print(*ptr, fout); - fputs("}}", fout); - } - fputs(" \\\\\n", fout); - /* If the line under the row already appeared, don't do another */ - if (opt_border != 3) - fputs("\\midrule\n", fout); - fputs("\\endhead\n", fout); - - /* table name, caption? */ - if (!opt_tuples_only && cont->title) - { - /* Don't output if we are printing a line under each row */ - if (opt_border == 2) - fputs("\\bottomrule\n", fout); - fputs("\\caption[", fout); - latex_escaped_print(cont->title, fout); - fputs(" (Continued)]{", fout); - latex_escaped_print(cont->title, fout); - fputs("}\n\\endfoot\n", fout); - if (opt_border == 2) - fputs("\\bottomrule\n", fout); - fputs("\\caption[", fout); - latex_escaped_print(cont->title, fout); - fputs("]{", fout); - latex_escaped_print(cont->title, fout); - fputs("}\n\\endlastfoot\n", fout); - } - /* output bottom table line? */ - else if (opt_border >= 2) - { - fputs("\\bottomrule\n\\endfoot\n", fout); - fputs("\\bottomrule\n\\endlastfoot\n", fout); - } - } - } - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - /* Add a line under each row? */ - if (i != 0 && i % cont->ncolumns != 0) - fputs("\n&\n", fout); - fputs("\\raggedright{", fout); - latex_escaped_print(*ptr, fout); - fputc('}', fout); - if ((i + 1) % cont->ncolumns == 0) - { - fputs(" \\tabularnewline\n", fout); - if (opt_border == 3) - fputs(" \\hline\n", fout); - } - if (cancel_pressed) - break; - } - - if (cont->opt->stop_table) - fputs("\\end{longtable}\n", fout); -} - - -static void -print_latex_vertical(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned long record = cont->opt->prior_records + 1; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (opt_border > 2) - opt_border = 2; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs("\\begin{center}\n", fout); - latex_escaped_print(cont->title, fout); - fputs("\n\\end{center}\n\n", fout); - } - - /* begin environment and set alignments and borders */ - fputs("\\begin{tabular}{", fout); - if (opt_border == 0) - fputs("cl", fout); - else if (opt_border == 1) - fputs("c|l", fout); - else if (opt_border == 2) - fputs("|c|l|", fout); - fputs("}\n", fout); - } - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - /* new record */ - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - if (!opt_tuples_only) - { - if (opt_border == 2) - { - fputs("\\hline\n", fout); - fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++); - } - else - fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++); - } - if (opt_border >= 1) - fputs("\\hline\n", fout); - } - - latex_escaped_print(cont->headers[i % cont->ncolumns], fout); - fputs(" & ", fout); - latex_escaped_print(*ptr, fout); - fputs(" \\\\\n", fout); - } - - if (cont->opt->stop_table) - { - if (opt_border == 2) - fputs("\\hline\n", fout); - - fputs("\\end{tabular}\n\n\\noindent ", fout); - - /* print footers */ - if (cont->footers && !opt_tuples_only && !cancel_pressed) - { - printTableFooter *f; - - for (f = cont->footers; f; f = f->next) - { - latex_escaped_print(f->data, fout); - fputs(" \\\\\n", fout); - } - } - - fputc('\n', fout); - } -} - - -/*************************/ -/* Troff -ms */ -/*************************/ - - -static void -troff_ms_escaped_print(const char *in, FILE *fout) -{ - const char *p; - - for (p = in; *p; p++) - switch (*p) - { - case '\\': - fputs("\\(rs", fout); - break; - default: - fputc(*p, fout); - } -} - - -static void -print_troff_ms_text(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned int i; - const char *const * ptr; - - if (cancel_pressed) - return; - - if (opt_border > 2) - opt_border = 2; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(".LP\n.DS C\n", fout); - troff_ms_escaped_print(cont->title, fout); - fputs("\n.DE\n", fout); - } - - /* begin environment and set alignments and borders */ - fputs(".LP\n.TS\n", fout); - if (opt_border == 2) - fputs("center box;\n", fout); - else - fputs("center;\n", fout); - - for (i = 0; i < cont->ncolumns; i++) - { - fputc(*(cont->aligns + i), fout); - if (opt_border > 0 && i < cont->ncolumns - 1) - fputs(" | ", fout); - } - fputs(".\n", fout); - - /* print headers */ - if (!opt_tuples_only) - { - for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) - { - if (i != 0) - fputc('\t', fout); - fputs("\\fI", fout); - troff_ms_escaped_print(*ptr, fout); - fputs("\\fP", fout); - } - fputs("\n_\n", fout); - } - } - - /* print cells */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - troff_ms_escaped_print(*ptr, fout); - - if ((i + 1) % cont->ncolumns == 0) - { - fputc('\n', fout); - if (cancel_pressed) - break; - } - else - fputc('\t', fout); - } - - if (cont->opt->stop_table) - { - printTableFooter *footers = footers_with_default(cont); - - fputs(".TE\n.DS L\n", fout); - - /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - { - printTableFooter *f; - - for (f = footers; f; f = f->next) - { - troff_ms_escaped_print(f->data, fout); - fputc('\n', fout); - } - } - - fputs(".DE\n", fout); - } -} - - -static void -print_troff_ms_vertical(const printTableContent *cont, FILE *fout) -{ - bool opt_tuples_only = cont->opt->tuples_only; - unsigned short opt_border = cont->opt->border; - unsigned long record = cont->opt->prior_records + 1; - unsigned int i; - const char *const * ptr; - unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ - - if (cancel_pressed) - return; - - if (opt_border > 2) - opt_border = 2; - - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - { - fputs(".LP\n.DS C\n", fout); - troff_ms_escaped_print(cont->title, fout); - fputs("\n.DE\n", fout); - } - - /* begin environment and set alignments and borders */ - fputs(".LP\n.TS\n", fout); - if (opt_border == 2) - fputs("center box;\n", fout); - else - fputs("center;\n", fout); - - /* basic format */ - if (opt_tuples_only) - fputs("c l;\n", fout); - } - else - current_format = 2; /* assume tuples printed already */ - - /* print records */ - for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) - { - /* new record */ - if (i % cont->ncolumns == 0) - { - if (cancel_pressed) - break; - if (!opt_tuples_only) - { - if (current_format != 1) - { - if (opt_border == 2 && record > 1) - fputs("_\n", fout); - if (current_format != 0) - fputs(".T&\n", fout); - fputs("c s.\n", fout); - current_format = 1; - } - fprintf(fout, "\\fIRecord %lu\\fP\n", record++); - } - if (opt_border >= 1) - fputs("_\n", fout); - } - - if (!opt_tuples_only) - { - if (current_format != 2) - { - if (current_format != 0) - fputs(".T&\n", fout); - if (opt_border != 1) - fputs("c l.\n", fout); - else - fputs("c | l.\n", fout); - current_format = 2; - } - } - - troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout); - fputc('\t', fout); - troff_ms_escaped_print(*ptr, fout); - - fputc('\n', fout); - } - - if (cont->opt->stop_table) - { - fputs(".TE\n.DS L\n", fout); - - /* print footers */ - if (cont->footers && !opt_tuples_only && !cancel_pressed) - { - printTableFooter *f; - - for (f = cont->footers; f; f = f->next) - { - troff_ms_escaped_print(f->data, fout); - fputc('\n', fout); - } - } - - fputs(".DE\n", fout); - } -} - - -/********************************/ -/* Public functions */ -/********************************/ - - -/* - * disable_sigpipe_trap - * - * Turn off SIGPIPE interrupt --- call this before writing to a temporary - * query output file that is a pipe. - * - * No-op on Windows, where there's no SIGPIPE interrupts. - */ -void -disable_sigpipe_trap(void) -{ -#ifndef WIN32 - pqsignal(SIGPIPE, SIG_IGN); -#endif -} - -/* - * restore_sigpipe_trap - * - * Restore normal SIGPIPE interrupt --- call this when done writing to a - * temporary query output file that was (or might have been) a pipe. - * - * Note: within psql, we enable SIGPIPE interrupts unless the permanent query - * output file is a pipe, in which case they should be kept off. This - * approach works only because psql is not currently complicated enough to - * have nested usages of short-lived output files. Otherwise we'd probably - * need a genuine save-and-restore-state approach; but for now, that would be - * useless complication. In non-psql programs, this always enables SIGPIPE. - * - * No-op on Windows, where there's no SIGPIPE interrupts. - */ -void -restore_sigpipe_trap(void) -{ -#ifndef WIN32 - pqsignal(SIGPIPE, always_ignore_sigpipe ? SIG_IGN : SIG_DFL); -#endif -} - -/* - * set_sigpipe_trap_state - * - * Set the trap state that restore_sigpipe_trap should restore to. - */ -void -set_sigpipe_trap_state(bool ignore) -{ - always_ignore_sigpipe = ignore; -} - - -/* - * PageOutput - * - * Tests if pager is needed and returns appropriate FILE pointer. - * - * If the topt argument is NULL no pager is used. - */ -FILE * -PageOutput(int lines, const printTableOpt *topt) -{ - /* check whether we need / can / are supposed to use pager */ - if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout))) - { -#ifdef TIOCGWINSZ - unsigned short int pager = topt->pager; - int min_lines = topt->pager_min_lines; - int result; - struct winsize screen_size; - - result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size); - - /* >= accounts for a one-line prompt */ - if (result == -1 - || (lines >= screen_size.ws_row && lines >= min_lines) - || pager > 1) -#endif - { - const char *pagerprog; - FILE *pagerpipe; - - pagerprog = getenv("PAGER"); - if (!pagerprog) - pagerprog = DEFAULT_PAGER; - disable_sigpipe_trap(); - pagerpipe = popen(pagerprog, "w"); - if (pagerpipe) - return pagerpipe; - } - } - - return stdout; -} - -/* - * ClosePager - * - * Close previously opened pager pipe, if any - */ -void -ClosePager(FILE *pagerpipe) -{ - if (pagerpipe && pagerpipe != stdout) - { - /* - * If printing was canceled midstream, warn about it. - * - * Some pagers like less use Ctrl-C as part of their command set. Even - * so, we abort our processing and warn the user what we did. If the - * pager quit as a result of the SIGINT, this message won't go - * anywhere ... - */ - if (cancel_pressed) - fprintf(pagerpipe, _("Interrupted\n")); - - pclose(pagerpipe); - restore_sigpipe_trap(); - } -} - -/* - * Initialise a table contents struct. - * Must be called before any other printTable method is used. - * - * The title is not duplicated; the caller must ensure that the buffer - * is available for the lifetime of the printTableContent struct. - * - * If you call this, you must call printTableCleanup once you're done with the - * table. - */ -void -printTableInit(printTableContent *const content, const printTableOpt *opt, - const char *title, const int ncolumns, const int nrows) -{ - content->opt = opt; - content->title = title; - content->ncolumns = ncolumns; - content->nrows = nrows; - - content->headers = pg_malloc0((ncolumns + 1) * sizeof(*content->headers)); - - content->cells = pg_malloc0((ncolumns * nrows + 1) * sizeof(*content->cells)); - - content->cellmustfree = NULL; - content->footers = NULL; - - content->aligns = pg_malloc0((ncolumns + 1) * sizeof(*content->align)); - - content->header = content->headers; - content->cell = content->cells; - content->footer = content->footers; - content->align = content->aligns; - content->cellsadded = 0; -} - -/* - * Add a header to the table. - * - * Headers are not duplicated; you must ensure that the header string is - * available for the lifetime of the printTableContent struct. - * - * If translate is true, the function will pass the header through gettext. - * Otherwise, the header will not be translated. - * - * align is either 'l' or 'r', and specifies the alignment for cells in this - * column. - */ -void -printTableAddHeader(printTableContent *const content, char *header, - const bool translate, const char align) -{ -#ifndef ENABLE_NLS - (void) translate; /* unused parameter */ -#endif - - if (content->header >= content->headers + content->ncolumns) - { - fprintf(stderr, _("Cannot add header to table content: " - "column count of %d exceeded.\n"), - content->ncolumns); - exit(EXIT_FAILURE); - } - - *content->header = (char *) mbvalidate((unsigned char *) header, - content->opt->encoding); -#ifdef ENABLE_NLS - if (translate) - *content->header = _(*content->header); -#endif - content->header++; - - *content->align = align; - content->align++; -} - -/* - * Add a cell to the table. - * - * Cells are not duplicated; you must ensure that the cell string is available - * for the lifetime of the printTableContent struct. - * - * If translate is true, the function will pass the cell through gettext. - * Otherwise, the cell will not be translated. - * - * If mustfree is true, the cell string is freed by printTableCleanup(). - * Note: Automatic freeing of translatable strings is not supported. - */ -void -printTableAddCell(printTableContent *const content, char *cell, - const bool translate, const bool mustfree) -{ -#ifndef ENABLE_NLS - (void) translate; /* unused parameter */ -#endif - - if (content->cellsadded >= content->ncolumns * content->nrows) - { - fprintf(stderr, _("Cannot add cell to table content: " - "total cell count of %d exceeded.\n"), - content->ncolumns * content->nrows); - exit(EXIT_FAILURE); - } - - *content->cell = (char *) mbvalidate((unsigned char *) cell, - content->opt->encoding); - -#ifdef ENABLE_NLS - if (translate) - *content->cell = _(*content->cell); -#endif - - if (mustfree) - { - if (content->cellmustfree == NULL) - content->cellmustfree = - pg_malloc0((content->ncolumns * content->nrows + 1) * sizeof(bool)); - - content->cellmustfree[content->cellsadded] = true; - } - content->cell++; - content->cellsadded++; -} - -/* - * Add a footer to the table. - * - * Footers are added as elements of a singly-linked list, and the content is - * strdup'd, so there is no need to keep the original footer string around. - * - * Footers are never translated by the function. If you want the footer - * translated you must do so yourself, before calling printTableAddFooter. The - * reason this works differently to headers and cells is that footers tend to - * be made of up individually translated components, rather than being - * translated as a whole. - */ -void -printTableAddFooter(printTableContent *const content, const char *footer) -{ - printTableFooter *f; - - f = pg_malloc0(sizeof(*f)); - f->data = pg_strdup(footer); - - if (content->footers == NULL) - content->footers = f; - else - content->footer->next = f; - - content->footer = f; -} - -/* - * Change the content of the last-added footer. - * - * The current contents of the last-added footer are freed, and replaced by the - * content given in *footer. If there was no previous footer, add a new one. - * - * The content is strdup'd, so there is no need to keep the original string - * around. - */ -void -printTableSetFooter(printTableContent *const content, const char *footer) -{ - if (content->footers != NULL) - { - free(content->footer->data); - content->footer->data = pg_strdup(footer); - } - else - printTableAddFooter(content, footer); -} - -/* - * Free all memory allocated to this struct. - * - * Once this has been called, the struct is unusable unless you pass it to - * printTableInit() again. - */ -void -printTableCleanup(printTableContent *const content) -{ - if (content->cellmustfree) - { - int i; - - for (i = 0; i < content->nrows * content->ncolumns; i++) - { - if (content->cellmustfree[i]) - free((char *) content->cells[i]); - } - free(content->cellmustfree); - content->cellmustfree = NULL; - } - free(content->headers); - free(content->cells); - free(content->aligns); - - content->opt = NULL; - content->title = NULL; - content->headers = NULL; - content->cells = NULL; - content->aligns = NULL; - content->header = NULL; - content->cell = NULL; - content->align = NULL; - - if (content->footers) - { - for (content->footer = content->footers; content->footer;) - { - printTableFooter *f; - - f = content->footer; - content->footer = f->next; - free(f->data); - free(f); - } - } - content->footers = NULL; - content->footer = NULL; -} - -/* - * IsPagerNeeded - * - * Setup pager if required - */ -static void -IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded, - FILE **fout, bool *is_pager) -{ - if (*fout == stdout) - { - int lines; - - if (expanded) - lines = (cont->ncolumns + 1) * cont->nrows; - else - lines = cont->nrows + 1; - - if (!cont->opt->tuples_only) - { - printTableFooter *f; - - /* - * FIXME -- this is slightly bogus: it counts the number of - * footers, not the number of lines in them. - */ - for (f = cont->footers; f; f = f->next) - lines++; - } - - *fout = PageOutput(lines + extra_lines, cont->opt); - *is_pager = (*fout != stdout); - } - else - *is_pager = false; -} - -/* - * Use this to print any table in the supported formats. - * - * cont: table data and formatting options - * fout: where to print to - * is_pager: true if caller has already redirected fout to be a pager pipe - * flog: if not null, also print the table there (for --log-file option) - */ -void -printTable(const printTableContent *cont, - FILE *fout, bool is_pager, FILE *flog) -{ - bool is_local_pager = false; - - if (cancel_pressed) - return; - - if (cont->opt->format == PRINT_NOTHING) - return; - - /* print_aligned_*() handle the pager themselves */ - if (!is_pager && - cont->opt->format != PRINT_ALIGNED && - cont->opt->format != PRINT_WRAPPED) - { - IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager); - is_local_pager = is_pager; - } - - /* print the stuff */ - - if (flog) - print_aligned_text(cont, flog, false); - - switch (cont->opt->format) - { - case PRINT_UNALIGNED: - if (cont->opt->expanded == 1) - print_unaligned_vertical(cont, fout); - else - print_unaligned_text(cont, fout); - break; - case PRINT_ALIGNED: - case PRINT_WRAPPED: - - /* - * In expanded-auto mode, force vertical if a pager is passed in; - * else we may make different decisions for different hunks of the - * query result. - */ - if (cont->opt->expanded == 1 || - (cont->opt->expanded == 2 && is_pager)) - print_aligned_vertical(cont, fout, is_pager); - else - print_aligned_text(cont, fout, is_pager); - break; - case PRINT_HTML: - if (cont->opt->expanded == 1) - print_html_vertical(cont, fout); - else - print_html_text(cont, fout); - break; - case PRINT_ASCIIDOC: - if (cont->opt->expanded == 1) - print_asciidoc_vertical(cont, fout); - else - print_asciidoc_text(cont, fout); - break; - case PRINT_LATEX: - if (cont->opt->expanded == 1) - print_latex_vertical(cont, fout); - else - print_latex_text(cont, fout); - break; - case PRINT_LATEX_LONGTABLE: - if (cont->opt->expanded == 1) - print_latex_vertical(cont, fout); - else - print_latex_longtable_text(cont, fout); - break; - case PRINT_TROFF_MS: - if (cont->opt->expanded == 1) - print_troff_ms_vertical(cont, fout); - else - print_troff_ms_text(cont, fout); - break; - default: - fprintf(stderr, _("invalid output format (internal error): %d"), - cont->opt->format); - exit(EXIT_FAILURE); - } - - if (is_local_pager) - ClosePager(fout); -} - -/* - * Use this to print query results - * - * result: result of a successful query - * opt: formatting options - * fout: where to print to - * is_pager: true if caller has already redirected fout to be a pager pipe - * flog: if not null, also print the data there (for --log-file option) - */ -void -printQuery(const PGresult *result, const printQueryOpt *opt, - FILE *fout, bool is_pager, FILE *flog) -{ - printTableContent cont; - int i, - r, - c; - - if (cancel_pressed) - return; - - printTableInit(&cont, &opt->topt, opt->title, - PQnfields(result), PQntuples(result)); - - /* Assert caller supplied enough translate_columns[] entries */ - Assert(opt->translate_columns == NULL || - opt->n_translate_columns >= cont.ncolumns); - - for (i = 0; i < cont.ncolumns; i++) - { - char align; - Oid ftype = PQftype(result, i); - - switch (ftype) - { - case INT2OID: - case INT4OID: - case INT8OID: - case FLOAT4OID: - case FLOAT8OID: - case NUMERICOID: - case OIDOID: - case XIDOID: - case CIDOID: - case CASHOID: - align = 'r'; - break; - default: - align = 'l'; - break; - } - - printTableAddHeader(&cont, PQfname(result, i), - opt->translate_header, align); - } - - /* set cells */ - for (r = 0; r < cont.nrows; r++) - { - for (c = 0; c < cont.ncolumns; c++) - { - char *cell; - bool mustfree = false; - bool translate; - - if (PQgetisnull(result, r, c)) - cell = opt->nullPrint ? opt->nullPrint : ""; - else - { - cell = PQgetvalue(result, r, c); - if (cont.aligns[c] == 'r' && opt->topt.numericLocale) - { - cell = format_numeric_locale(cell); - mustfree = true; - } - } - - translate = (opt->translate_columns && opt->translate_columns[c]); - printTableAddCell(&cont, cell, translate, mustfree); - } - } - - /* set footers */ - if (opt->footers) - { - char **footer; - - for (footer = opt->footers; *footer; footer++) - printTableAddFooter(&cont, *footer); - } - - printTable(&cont, fout, is_pager, flog); - printTableCleanup(&cont); -} - - -void -setDecimalLocale(void) -{ - struct lconv *extlconv; - - extlconv = localeconv(); - - /* Don't accept an empty decimal_point string */ - if (*extlconv->decimal_point) - decimal_point = pg_strdup(extlconv->decimal_point); - else - decimal_point = "."; /* SQL output standard */ - - /* - * Although the Open Group standard allows locales to supply more than one - * group width, we consider only the first one, and we ignore any attempt - * to suppress grouping by specifying CHAR_MAX. As in the backend's - * cash.c, we must apply a range check to avoid being fooled by variant - * CHAR_MAX values. - */ - groupdigits = *extlconv->grouping; - if (groupdigits <= 0 || groupdigits > 6) - groupdigits = 3; /* most common */ - - /* Don't accept an empty thousands_sep string, either */ - /* similar code exists in formatting.c */ - if (*extlconv->thousands_sep) - thousands_sep = pg_strdup(extlconv->thousands_sep); - /* Make sure thousands separator doesn't match decimal point symbol. */ - else if (strcmp(decimal_point, ",") != 0) - thousands_sep = ","; - else - thousands_sep = "."; -} - -/* get selected or default line style */ -const printTextFormat * -get_line_style(const printTableOpt *opt) -{ - /* - * Note: this function mainly exists to preserve the convention that a - * printTableOpt struct can be initialized to zeroes to get default - * behavior. - */ - if (opt->line_style != NULL) - return opt->line_style; - else - return &pg_asciiformat; -} - -void -refresh_utf8format(const printTableOpt *opt) -{ - printTextFormat *popt = (printTextFormat *) &pg_utf8format; - - const unicodeStyleBorderFormat *border; - const unicodeStyleRowFormat *header; - const unicodeStyleColumnFormat *column; - - popt->name = "unicode"; - - border = &unicode_style.border_style[opt->unicode_border_linestyle]; - header = &unicode_style.row_style[opt->unicode_header_linestyle]; - column = &unicode_style.column_style[opt->unicode_column_linestyle]; - - popt->lrule[PRINT_RULE_TOP].hrule = border->horizontal; - popt->lrule[PRINT_RULE_TOP].leftvrule = border->down_and_right; - popt->lrule[PRINT_RULE_TOP].midvrule = column->down_and_horizontal[opt->unicode_border_linestyle]; - popt->lrule[PRINT_RULE_TOP].rightvrule = border->down_and_left; - - popt->lrule[PRINT_RULE_MIDDLE].hrule = header->horizontal; - popt->lrule[PRINT_RULE_MIDDLE].leftvrule = header->vertical_and_right[opt->unicode_border_linestyle]; - popt->lrule[PRINT_RULE_MIDDLE].midvrule = column->vertical_and_horizontal[opt->unicode_header_linestyle]; - popt->lrule[PRINT_RULE_MIDDLE].rightvrule = header->vertical_and_left[opt->unicode_border_linestyle]; - - popt->lrule[PRINT_RULE_BOTTOM].hrule = border->horizontal; - popt->lrule[PRINT_RULE_BOTTOM].leftvrule = border->up_and_right; - popt->lrule[PRINT_RULE_BOTTOM].midvrule = column->up_and_horizontal[opt->unicode_border_linestyle]; - popt->lrule[PRINT_RULE_BOTTOM].rightvrule = border->left_and_right; - - /* N/A */ - popt->lrule[PRINT_RULE_DATA].hrule = ""; - popt->lrule[PRINT_RULE_DATA].leftvrule = border->vertical; - popt->lrule[PRINT_RULE_DATA].midvrule = column->vertical; - popt->lrule[PRINT_RULE_DATA].rightvrule = border->vertical; - - popt->midvrule_nl = column->vertical; - popt->midvrule_wrap = column->vertical; - popt->midvrule_blank = column->vertical; - - /* Same for all unicode today */ - popt->header_nl_left = unicode_style.header_nl_left; - popt->header_nl_right = unicode_style.header_nl_right; - popt->nl_left = unicode_style.nl_left; - popt->nl_right = unicode_style.nl_right; - popt->wrap_left = unicode_style.wrap_left; - popt->wrap_right = unicode_style.wrap_right; - popt->wrap_right_border = unicode_style.wrap_right_border; - - return; -} - -/* - * Compute the byte distance to the end of the string or *target_width - * display character positions, whichever comes first. Update *target_width - * to be the number of display character positions actually filled. - */ -static int -strlen_max_width(unsigned char *str, int *target_width, int encoding) -{ - unsigned char *start = str; - unsigned char *end = str + strlen((char *) str); - int curr_width = 0; - - while (str < end) - { - int char_width = PQdsplen((char *) str, encoding); - - /* - * If the display width of the new character causes the string to - * exceed its target width, skip it and return. However, if this is - * the first character of the string (curr_width == 0), we have to - * accept it. - */ - if (*target_width < curr_width + char_width && curr_width != 0) - break; - - curr_width += char_width; - - str += PQmblen((char *) str, encoding); - } - - *target_width = curr_width; - - return str - start; -} diff --git a/src/bin/csql/print.h b/src/bin/csql/print.h deleted file mode 100644 index fd5659842..000000000 --- a/src/bin/csql/print.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/print.h - */ -#ifndef PRINT_H -#define PRINT_H - -#include "libpq-fe.h" - - -enum printFormat -{ - PRINT_NOTHING = 0, /* to make sure someone initializes this */ - PRINT_UNALIGNED, - PRINT_ALIGNED, - PRINT_WRAPPED, - PRINT_HTML, - PRINT_ASCIIDOC, - PRINT_LATEX, - PRINT_LATEX_LONGTABLE, - PRINT_TROFF_MS - /* add your favourite output format here ... */ -}; - -typedef struct printTextLineFormat -{ - /* Line drawing characters to be used in various contexts */ - const char *hrule; /* horizontal line character */ - const char *leftvrule; /* left vertical line (+horizontal) */ - const char *midvrule; /* intra-column vertical line (+horizontal) */ - const char *rightvrule; /* right vertical line (+horizontal) */ -} printTextLineFormat; - -typedef enum printTextRule -{ - /* Additional context for selecting line drawing characters */ - PRINT_RULE_TOP, /* top horizontal line */ - PRINT_RULE_MIDDLE, /* intra-data horizontal line */ - PRINT_RULE_BOTTOM, /* bottom horizontal line */ - PRINT_RULE_DATA /* data line (hrule is unused here) */ -} printTextRule; - -typedef enum printTextLineWrap -{ - /* Line wrapping conditions */ - PRINT_LINE_WRAP_NONE, /* No wrapping */ - PRINT_LINE_WRAP_WRAP, /* Wraparound due to overlength line */ - PRINT_LINE_WRAP_NEWLINE /* Newline in data */ -} printTextLineWrap; - -typedef struct printTextFormat -{ - /* A complete line style */ - const char *name; /* for display purposes */ - printTextLineFormat lrule[4]; /* indexed by enum printTextRule */ - const char *midvrule_nl; /* vertical line for continue after newline */ - const char *midvrule_wrap; /* vertical line for wrapped data */ - const char *midvrule_blank; /* vertical line for blank data */ - const char *header_nl_left; /* left mark after newline */ - const char *header_nl_right; /* right mark for newline */ - const char *nl_left; /* left mark after newline */ - const char *nl_right; /* right mark for newline */ - const char *wrap_left; /* left mark after wrapped data */ - const char *wrap_right; /* right mark for wrapped data */ - bool wrap_right_border; /* use right-hand border for wrap - * marks when border=0? */ -} printTextFormat; - -typedef enum unicode_linestyle -{ - UNICODE_LINESTYLE_SINGLE = 0, - UNICODE_LINESTYLE_DOUBLE -} unicode_linestyle; - -struct separator -{ - char *separator; - bool separator_zero; -}; - -typedef struct printTableOpt -{ - enum printFormat format; /* see enum above */ - unsigned short int expanded;/* expanded/vertical output (if supported by - * output format); 0=no, 1=yes, 2=auto */ - unsigned short int border; /* Print a border around the table. 0=none, - * 1=dividing lines, 2=full */ - unsigned short int pager; /* use pager for output (if to stdout and - * stdout is a tty) 0=off 1=on 2=always */ - int pager_min_lines;/* don't use pager unless there are at least - * this many lines */ - bool tuples_only; /* don't output headers, row counts, etc. */ - bool start_table; /* print start decoration, eg */ - bool stop_table; /* print stop decoration, eg
*/ - bool default_footer; /* allow "(xx rows)" default footer */ - unsigned long prior_records; /* start offset for record counters */ - const printTextFormat *line_style; /* line style (NULL for default) */ - struct separator fieldSep; /* field separator for unaligned text mode */ - struct separator recordSep; /* record separator for unaligned text mode */ - bool numericLocale; /* locale-aware numeric units separator and - * decimal marker */ - char *tableAttr; /* attributes for HTML */ - int encoding; /* character encoding */ - int env_columns; /* $COLUMNS on psql start, 0 is unset */ - int columns; /* target width for wrapped format */ - unicode_linestyle unicode_border_linestyle; - unicode_linestyle unicode_column_linestyle; - unicode_linestyle unicode_header_linestyle; -} printTableOpt; - -/* - * Table footers are implemented as a singly-linked list. - * - * This is so that you don't need to know the number of footers in order to - * initialise the printTableContent struct, which is very convenient when - * preparing complex footers (as in describeOneTableDetails). - */ -typedef struct printTableFooter -{ - char *data; - struct printTableFooter *next; -} printTableFooter; - -/* - * The table content struct holds all the information which will be displayed - * by printTable(). - */ -typedef struct printTableContent -{ - const printTableOpt *opt; - const char *title; /* May be NULL */ - int ncolumns; /* Specified in Init() */ - int nrows; /* Specified in Init() */ - const char **headers; /* NULL-terminated array of header strings */ - const char **header; /* Pointer to the last added header */ - const char **cells; /* NULL-terminated array of cell content - * strings */ - const char **cell; /* Pointer to the last added cell */ - long cellsadded; /* Number of cells added this far */ - bool *cellmustfree; /* true for cells that need to be free()d */ - printTableFooter *footers; /* Pointer to the first footer */ - printTableFooter *footer; /* Pointer to the last added footer */ - char *aligns; /* Array of alignment specifiers; 'l' or 'r', - * one per column */ - char *align; /* Pointer to the last added alignment */ -} printTableContent; - -typedef struct printQueryOpt -{ - printTableOpt topt; /* the options above */ - char *nullPrint; /* how to print null entities */ - bool quote; /* quote all values as much as possible */ - char *title; /* override title */ - char **footers; /* override footer (default is "(xx rows)") */ - bool translate_header; /* do gettext on column headers */ - const bool *translate_columns; /* translate_columns[i-1] => do - * gettext on col i */ - int n_translate_columns; /* length of translate_columns[] */ -} printQueryOpt; - - -extern const printTextFormat pg_asciiformat; -extern const printTextFormat pg_asciiformat_old; -extern const printTextFormat pg_utf8format; - - -extern void disable_sigpipe_trap(void); -extern void restore_sigpipe_trap(void); -extern void set_sigpipe_trap_state(bool ignore); - -extern FILE *PageOutput(int lines, const printTableOpt *topt); -extern void ClosePager(FILE *pagerpipe); - -extern void html_escaped_print(const char *in, FILE *fout); - -extern void printTableInit(printTableContent *const content, - const printTableOpt *opt, const char *title, - const int ncolumns, const int nrows); -extern void printTableAddHeader(printTableContent *const content, - char *header, const bool translate, const char align); -extern void printTableAddCell(printTableContent *const content, - char *cell, const bool translate, const bool mustfree); -extern void printTableAddFooter(printTableContent *const content, - const char *footer); -extern void printTableSetFooter(printTableContent *const content, - const char *footer); -extern void printTableCleanup(printTableContent *const content); -extern void printTable(const printTableContent *cont, - FILE *fout, bool is_pager, FILE *flog); -extern void printQuery(const PGresult *result, const printQueryOpt *opt, - FILE *fout, bool is_pager, FILE *flog); - -extern void setDecimalLocale(void); -extern const printTextFormat *get_line_style(const printTableOpt *opt); -extern void refresh_utf8format(const printTableOpt *opt); - -#ifndef __CYGWIN__ -#define DEFAULT_PAGER "more" -#else -#define DEFAULT_PAGER "less" -#endif - -#endif /* PRINT_H */ diff --git a/src/bin/csql/prompt.c b/src/bin/csql/prompt.c deleted file mode 100644 index 89842673f..000000000 --- a/src/bin/csql/prompt.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/prompt.c - */ -#include "postgres_fe.h" - -#ifdef WIN32 -#include -#include -#endif - -#ifdef HAVE_UNIX_SOCKETS -#include -#include -#endif - -#include "common.h" -#include "input.h" -#include "prompt.h" -#include "settings.h" - - -/*-------------------------- - * get_prompt - * - * Returns a statically allocated prompt made by interpolating certain - * tcsh style escape sequences into pset.vars "PROMPT1|2|3". - * (might not be completely multibyte safe) - * - * Defined interpolations are: - * %M - database server "hostname.domainname", "[local]" for AF_UNIX - * sockets, "[local:/dir/name]" if not default - * %m - like %M, but hostname only (before first dot), or always "[local]" - * %> - database server port number - * %n - database user name - * %/ - current database - * %~ - like %/ but "~" when database name equals user name - * %# - "#" if superuser, ">" otherwise - * %R - in prompt1 normally =, or ^ if single line mode, - * or a ! if session is not connected to a database; - * in prompt2 -, *, ', or "; - * in prompt3 nothing - * %x - transaction status: empty, *, !, ? (unknown or no connection) - * %l - The line number inside the current statement, starting from 1. - * %? - the error code of the last query (not yet implemented) - * %% - a percent sign - * - * %[0-9] - the character with the given decimal code - * %0[0-7] - the character with the given octal code - * %0x[0-9A-Fa-f] - the character with the given hexadecimal code - * - * %`command` - The result of executing command in /bin/sh with trailing - * newline stripped. - * %:name: - The value of the psql variable 'name' - * (those will not be rescanned for more escape sequences!) - * - * %[ ... %] - tell readline that the contained text is invisible - * - * If the application-wide prompts become NULL somehow, the returned string - * will be empty (not NULL!). - *-------------------------- - */ - -char * -get_prompt(promptStatus_t status) -{ -#define MAX_PROMPT_SIZE 256 - static char destination[MAX_PROMPT_SIZE + 1]; - char buf[MAX_PROMPT_SIZE + 1]; - bool esc = false; - const char *p; - const char *prompt_string = "? "; - - switch (status) - { - case PROMPT_READY: - prompt_string = pset.prompt1; - break; - - case PROMPT_CONTINUE: - case PROMPT_SINGLEQUOTE: - case PROMPT_DOUBLEQUOTE: - case PROMPT_DOLLARQUOTE: - case PROMPT_COMMENT: - case PROMPT_PAREN: - prompt_string = pset.prompt2; - break; - - case PROMPT_COPY: - prompt_string = pset.prompt3; - break; - } - - destination[0] = '\0'; - - for (p = prompt_string; - *p && strlen(destination) < sizeof(destination) - 1; - p++) - { - memset(buf, 0, sizeof(buf)); - if (esc) - { - switch (*p) - { - /* Current database */ - case '/': - if (pset.db) - strlcpy(buf, PQdb(pset.db), sizeof(buf)); - break; - case '~': - if (pset.db) - { - const char *var; - - if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 || - ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0)) - strlcpy(buf, "~", sizeof(buf)); - else - strlcpy(buf, PQdb(pset.db), sizeof(buf)); - } - break; - - /* DB server hostname (long/short) */ - case 'M': - case 'm': - if (pset.db) - { - const char *host = PQhost(pset.db); - - /* INET socket */ - if (host && host[0] && !is_absolute_path(host)) - { - strlcpy(buf, host, sizeof(buf)); - if (*p == 'm') - buf[strcspn(buf, ".")] = '\0'; - } -#ifdef HAVE_UNIX_SOCKETS - /* UNIX socket */ - else - { - if (!host - || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0 - || *p == 'm') - strlcpy(buf, "[local]", sizeof(buf)); - else - snprintf(buf, sizeof(buf), "[local:%s]", host); - } -#endif - } - break; - /* DB server port number */ - case '>': - if (pset.db && PQport(pset.db)) - strlcpy(buf, PQport(pset.db), sizeof(buf)); - break; - /* DB server user name */ - case 'n': - if (pset.db) - strlcpy(buf, session_username(), sizeof(buf)); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - *buf = (char) strtol(p, (char **) &p, 8); - --p; - break; - case 'R': - switch (status) - { - case PROMPT_READY: - if (!pset.db) - buf[0] = '!'; - else if (!pset.singleline) - buf[0] = '='; - else - buf[0] = '^'; - break; - case PROMPT_CONTINUE: - buf[0] = '-'; - break; - case PROMPT_SINGLEQUOTE: - buf[0] = '\''; - break; - case PROMPT_DOUBLEQUOTE: - buf[0] = '"'; - break; - case PROMPT_DOLLARQUOTE: - buf[0] = '$'; - break; - case PROMPT_COMMENT: - buf[0] = '*'; - break; - case PROMPT_PAREN: - buf[0] = '('; - break; - default: - buf[0] = '\0'; - break; - } - break; - - case 'x': - if (!pset.db) - buf[0] = '?'; - else - switch (PQtransactionStatus(pset.db)) - { - case PQTRANS_IDLE: - buf[0] = '\0'; - break; - case PQTRANS_ACTIVE: - case PQTRANS_INTRANS: - buf[0] = '*'; - break; - case PQTRANS_INERROR: - buf[0] = '!'; - break; - default: - buf[0] = '?'; - break; - } - break; - - case 'l': - snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno); - break; - - case '?': - /* not here yet */ - break; - - case '#': - if (is_superuser()) - buf[0] = '#'; - else - buf[0] = '>'; - break; - - /* execute command */ - case '`': - { - FILE *fd; - char *file = pg_strdup(p + 1); - int cmdend; - - cmdend = strcspn(file, "`"); - file[cmdend] = '\0'; - fd = popen(file, "r"); - if (fd) - { - if (fgets(buf, sizeof(buf), fd) == NULL) - buf[0] = '\0'; - pclose(fd); - } - if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n') - buf[strlen(buf) - 1] = '\0'; - free(file); - p += cmdend + 1; - break; - } - - /* interpolate variable */ - case ':': - { - char *name; - const char *val; - int nameend; - - name = pg_strdup(p + 1); - nameend = strcspn(name, ":"); - name[nameend] = '\0'; - val = GetVariable(pset.vars, name); - if (val) - strlcpy(buf, val, sizeof(buf)); - free(name); - p += nameend + 1; - break; - } - - case '[': - case ']': -#if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE) - - /* - * readline >=4.0 undocumented feature: non-printing - * characters in prompt strings must be marked as such, in - * order to properly display the line during editing. - */ - buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE; - buf[1] = '\0'; -#endif /* USE_READLINE */ - break; - - default: - buf[0] = *p; - buf[1] = '\0'; - break; - - } - esc = false; - } - else if (*p == '%') - esc = true; - else - { - buf[0] = *p; - buf[1] = '\0'; - esc = false; - } - - if (!esc) - strlcat(destination, buf, sizeof(destination)); - } - - return destination; -} diff --git a/src/bin/csql/prompt.h b/src/bin/csql/prompt.h deleted file mode 100644 index e3f6ce55b..000000000 --- a/src/bin/csql/prompt.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/prompt.h - */ -#ifndef PROMPT_H -#define PROMPT_H - -typedef enum _promptStatus -{ - PROMPT_READY, - PROMPT_CONTINUE, - PROMPT_COMMENT, - PROMPT_SINGLEQUOTE, - PROMPT_DOUBLEQUOTE, - PROMPT_DOLLARQUOTE, - PROMPT_PAREN, - PROMPT_COPY -} promptStatus_t; - -char *get_prompt(promptStatus_t status); - -#endif /* PROMPT_H */ diff --git a/src/bin/csql/psqlrc.sample b/src/bin/csql/psqlrc.sample deleted file mode 100644 index 8152cace2..000000000 --- a/src/bin/csql/psqlrc.sample +++ /dev/null @@ -1,8 +0,0 @@ --- --- system-wide psql configuration file --- --- This file is read before the .psqlrc file in the user's home directory. --- --- Copy this to your installation's sysconf directory and rename it psqlrc. --- The sysconf directory can be identified via "pg_config --sysconfdir". --- diff --git a/src/bin/csql/psqlscan.h b/src/bin/csql/psqlscan.h deleted file mode 100644 index 55070ca46..000000000 --- a/src/bin/csql/psqlscan.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/psqlscan.h - */ -#ifndef PSQLSCAN_H -#define PSQLSCAN_H - -#include "pqexpbuffer.h" - -#include "prompt.h" - - -/* Abstract type for lexer's internal state */ -typedef struct PsqlScanStateData *PsqlScanState; - -/* Termination states for psql_scan() */ -typedef enum -{ - PSCAN_SEMICOLON, /* found command-ending semicolon */ - PSCAN_BACKSLASH, /* found backslash command */ - PSCAN_INCOMPLETE, /* end of line, SQL statement incomplete */ - PSCAN_EOL /* end of line, SQL possibly complete */ -} PsqlScanResult; - -/* Different ways for scan_slash_option to handle parameter words */ -enum slash_option_type -{ - OT_NORMAL, /* normal case */ - OT_SQLID, /* treat as SQL identifier */ - OT_SQLIDHACK, /* SQL identifier, but don't downcase */ - OT_FILEPIPE, /* it's a filename or pipe */ - OT_WHOLE_LINE, /* just snarf the rest of the line */ - OT_NO_EVAL /* no expansion of backticks or variables */ -}; - - -extern PsqlScanState psql_scan_create(void); -extern void psql_scan_destroy(PsqlScanState state); - -extern void psql_scan_setup(PsqlScanState state, - const char *line, int line_len); -extern void psql_scan_finish(PsqlScanState state); - -extern PsqlScanResult psql_scan(PsqlScanState state, - PQExpBuffer query_buf, - promptStatus_t *prompt); - -extern void psql_scan_reset(PsqlScanState state); - -extern bool psql_scan_in_quote(PsqlScanState state); - -extern char *psql_scan_slash_command(PsqlScanState state); - -extern char *psql_scan_slash_option(PsqlScanState state, - enum slash_option_type type, - char *quote, - bool semicolon); - -extern void psql_scan_slash_command_end(PsqlScanState state); - -#endif /* PSQLSCAN_H */ diff --git a/src/bin/csql/psqlscan.l b/src/bin/csql/psqlscan.l deleted file mode 100644 index be059abd8..000000000 --- a/src/bin/csql/psqlscan.l +++ /dev/null @@ -1,1988 +0,0 @@ -%{ -/*------------------------------------------------------------------------- - * - * psqlscan.l - * lexical scanner for psql - * - * This code is mainly needed to determine where the end of a SQL statement - * is: we are looking for semicolons that are not within quotes, comments, - * or parentheses. The most reliable way to handle this is to borrow the - * backend's flex lexer rules, lock, stock, and barrel. The rules below - * are (except for a few) the same as the backend's, but their actions are - * just ECHO whereas the backend's actions generally do other things. - * - * XXX The rules in this file must be kept in sync with the backend lexer!!! - * - * XXX Avoid creating backtracking cases --- see the backend lexer for info. - * - * The most difficult aspect of this code is that we need to work in multibyte - * encodings that are not ASCII-safe. A "safe" encoding is one in which each - * byte of a multibyte character has the high bit set (it's >= 0x80). Since - * all our lexing rules treat all high-bit-set characters alike, we don't - * really need to care whether such a byte is part of a sequence or not. - * In an "unsafe" encoding, we still expect the first byte of a multibyte - * sequence to be >= 0x80, but later bytes might not be. If we scan such - * a sequence as-is, the lexing rules could easily be fooled into matching - * such bytes to ordinary ASCII characters. Our solution for this is to - * substitute 0xFF for each non-first byte within the data presented to flex. - * The flex rules will then pass the FF's through unmolested. The emit() - * subroutine is responsible for looking back to the original string and - * replacing FF's with the corresponding original bytes. - * - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/bin/psql/psqlscan.l - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include "psqlscan.h" - -#include - -#include "common.h" -#include "settings.h" -#include "variables.h" - - -/* - * We use a stack of flex buffers to handle substitution of psql variables. - * Each stacked buffer contains the as-yet-unread text from one psql variable. - * When we pop the stack all the way, we resume reading from the outer buffer - * identified by scanbufhandle. - */ -typedef struct StackElem -{ - YY_BUFFER_STATE buf; /* flex input control structure */ - char *bufstring; /* data actually being scanned by flex */ - char *origstring; /* copy of original data, if needed */ - char *varname; /* name of variable providing data, or NULL */ - struct StackElem *next; -} StackElem; - -/* - * All working state of the lexer must be stored in PsqlScanStateData - * between calls. This allows us to have multiple open lexer operations, - * which is needed for nested include files. The lexer itself is not - * recursive, but it must be re-entrant. - */ -typedef struct PsqlScanStateData -{ - StackElem *buffer_stack; /* stack of variable expansion buffers */ - /* - * These variables always refer to the outer buffer, never to any - * stacked variable-expansion buffer. - */ - YY_BUFFER_STATE scanbufhandle; - char *scanbuf; /* start of outer-level input buffer */ - const char *scanline; /* current input line at outer level */ - - /* safe_encoding, curline, refline are used by emit() to replace FFs */ - int encoding; /* encoding being used now */ - bool safe_encoding; /* is current encoding "safe"? */ - const char *curline; /* actual flex input string for cur buf */ - const char *refline; /* original data for cur buffer */ - - /* - * All this state lives across successive input lines, until explicitly - * reset by psql_scan_reset. - */ - int start_state; /* saved YY_START */ - int paren_depth; /* depth of nesting in parentheses */ - int xcdepth; /* depth of nesting in slash-star comments */ - char *dolqstart; /* current $foo$ quote start string */ -} PsqlScanStateData; - -static PsqlScanState cur_state; /* current state while active */ - -static PQExpBuffer output_buf; /* current output buffer */ - -/* these variables do not need to be saved across calls */ -static enum slash_option_type option_type; -static char *option_quote; -static int unquoted_option_chars; -static int backtick_start_offset; - - -/* Return values from yylex() */ -#define LEXRES_EOL 0 /* end of input */ -#define LEXRES_SEMI 1 /* command-terminating semicolon found */ -#define LEXRES_BACKSLASH 2 /* backslash command start */ -#define LEXRES_OK 3 /* OK completion of backslash argument */ - - -static void evaluate_backtick(void); -static void push_new_buffer(const char *newstr, const char *varname); -static void pop_buffer_stack(PsqlScanState state); -static bool var_is_current_source(PsqlScanState state, const char *varname); -static YY_BUFFER_STATE prepare_buffer(const char *txt, int len, - char **txtcopy); -static void emit(const char *txt, int len); -static char *extract_substring(const char *txt, int len); -static void escape_variable(bool as_ident); - -#define ECHO emit(yytext, yyleng) - -%} - -%option 8bit -%option never-interactive -%option nodefault -%option noinput -%option nounput -%option noyywrap -%option warn - -/* - * All of the following definitions and rules should exactly match - * src/backend/parser/scan.l so far as the flex patterns are concerned. - * The rule bodies are just ECHO as opposed to what the backend does, - * however. (But be sure to duplicate code that affects the lexing process, - * such as BEGIN().) Also, psqlscan uses a single <> rule whereas - * scan.l has a separate one for each exclusive state. - */ - -/* - * OK, here is a short description of lex/flex rules behavior. - * The longest pattern which matches an input string is always chosen. - * For equal-length patterns, the first occurring in the rules list is chosen. - * INITIAL is the starting state, to which all non-conditional rules apply. - * Exclusive states change parsing rules while the state is active. When in - * an exclusive state, only those rules defined for that state apply. - * - * We use exclusive states for quoted strings, extended comments, - * and to eliminate parsing troubles for numeric strings. - * Exclusive states: - * bit string literal - * extended C-style comments - * delimited identifiers (double-quoted identifiers) - * hexadecimal numeric string - * standard quoted strings - * extended quoted strings (support backslash escape sequences) - * $foo$ quoted strings - * quoted identifier with Unicode escapes - * end of a quoted identifier with Unicode escapes, UESCAPE can follow - * quoted string with Unicode escapes - * end of a quoted string with Unicode escapes, UESCAPE can follow - * - * Note: we intentionally don't mimic the backend's state; we have - * no need to distinguish it from state, and no good way to get out - * of it in error cases. The backend just throws yyerror() in those - * cases, but that's not an option here. - */ - -%x xb -%x xc -%x xd -%x xh -%x xe -%x xq -%x xdolq -%x xui -%x xuiend -%x xus -%x xusend -/* Additional exclusive states for psql only: lex backslash commands */ -%x xslashcmd -%x xslashargstart -%x xslasharg -%x xslashquote -%x xslashbackquote -%x xslashdquote -%x xslashwholeline -%x xslashend - -/* - * In order to make the world safe for Windows and Mac clients as well as - * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n - * sequence will be seen as two successive newlines, but that doesn't cause - * any problems. Comments that start with -- and extend to the next - * newline are treated as equivalent to a single whitespace character. - * - * NOTE a fine point: if there is no newline following --, we will absorb - * everything to the end of the input as a comment. This is correct. Older - * versions of Postgres failed to recognize -- as a comment if the input - * did not end with a newline. - * - * XXX perhaps \f (formfeed) should be treated as a newline as well? - * - * XXX if you change the set of whitespace characters, fix scanner_isspace() - * to agree, and see also the plpgsql lexer. - */ - -space [ \t\n\r\f] -horiz_space [ \t\f] -newline [\n\r] -non_newline [^\n\r] - -comment ("--"{non_newline}*) - -whitespace ({space}+|{comment}) - -/* - * SQL requires at least one newline in the whitespace separating - * string literals that are to be concatenated. Silly, but who are we - * to argue? Note that {whitespace_with_newline} should not have * after - * it, whereas {whitespace} should generally have a * after it... - */ - -special_whitespace ({space}+|{comment}{newline}) -horiz_whitespace ({horiz_space}|{comment}) -whitespace_with_newline ({horiz_whitespace}*{newline}{special_whitespace}*) - -/* - * To ensure that {quotecontinue} can be scanned without having to back up - * if the full pattern isn't matched, we include trailing whitespace in - * {quotestop}. This matches all cases where {quotecontinue} fails to match, - * except for {quote} followed by whitespace and just one "-" (not two, - * which would start a {comment}). To cover that we have {quotefail}. - * The actions for {quotestop} and {quotefail} must throw back characters - * beyond the quote proper. - */ -quote ' -quotestop {quote}{whitespace}* -quotecontinue {quote}{whitespace_with_newline}{quote} -quotefail {quote}{whitespace}*"-" - -/* Bit string - * It is tempting to scan the string for only those characters - * which are allowed. However, this leads to silently swallowed - * characters if illegal characters are included in the string. - * For example, if xbinside is [01] then B'ABCD' is interpreted - * as a zero-length string, and the ABCD' is lost! - * Better to pass the string forward and let the input routines - * validate the contents. - */ -xbstart [bB]{quote} -xbinside [^']* - -/* Hexadecimal number */ -xhstart [xX]{quote} -xhinside [^']* - -/* National character */ -xnstart [nN]{quote} - -/* Quoted string that allows backslash escapes */ -xestart [eE]{quote} -xeinside [^\\']+ -xeescape [\\][^0-7] -xeoctesc [\\][0-7]{1,3} -xehexesc [\\]x[0-9A-Fa-f]{1,2} -xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}) -xeunicodefail [\\](u[0-9A-Fa-f]{0,3}|U[0-9A-Fa-f]{0,7}) - -/* Extended quote - * xqdouble implements embedded quote, '''' - */ -xqstart {quote} -xqdouble {quote}{quote} -xqinside [^']+ - -/* $foo$ style quotes ("dollar quoting") - * The quoted string starts with $foo$ where "foo" is an optional string - * in the form of an identifier, except that it may not contain "$", - * and extends to the first occurrence of an identical string. - * There is *no* processing of the quoted text. - * - * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim} - * fails to match its trailing "$". - */ -dolq_start [A-Za-z\200-\377_] -dolq_cont [A-Za-z\200-\377_0-9] -dolqdelim \$({dolq_start}{dolq_cont}*)?\$ -dolqfailed \${dolq_start}{dolq_cont}* -dolqinside [^$]+ - -/* Double quote - * Allows embedded spaces and other special characters into identifiers. - */ -dquote \" -xdstart {dquote} -xdstop {dquote} -xddouble {dquote}{dquote} -xdinside [^"]+ - -/* Unicode escapes */ -uescape [uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']{quote} -/* error rule to avoid backup */ -uescapefail [uU][eE][sS][cC][aA][pP][eE]{whitespace}*"-"|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*|[uU][eE][sS][cC][aA][pP]|[uU][eE][sS][cC][aA]|[uU][eE][sS][cC]|[uU][eE][sS]|[uU][eE]|[uU] - -/* Quoted identifier with Unicode escapes */ -xuistart [uU]&{dquote} - -/* Quoted string with Unicode escapes */ -xusstart [uU]&{quote} - -/* Optional UESCAPE after a quoted string or identifier with Unicode escapes. */ -xustop1 {uescapefail}? -xustop2 {uescape} - -/* error rule to avoid backup */ -xufailed [uU]& - - -/* C-style comments - * - * The "extended comment" syntax closely resembles allowable operator syntax. - * The tricky part here is to get lex to recognize a string starting with - * slash-star as a comment, when interpreting it as an operator would produce - * a longer match --- remember lex will prefer a longer match! Also, if we - * have something like plus-slash-star, lex will think this is a 3-character - * operator whereas we want to see it as a + operator and a comment start. - * The solution is two-fold: - * 1. append {op_chars}* to xcstart so that it matches as much text as - * {operator} would. Then the tie-breaker (first matching rule of same - * length) ensures xcstart wins. We put back the extra stuff with yyless() - * in case it contains a star-slash that should terminate the comment. - * 2. In the operator rule, check for slash-star within the operator, and - * if found throw it back with yyless(). This handles the plus-slash-star - * problem. - * Dash-dash comments have similar interactions with the operator rule. - */ -xcstart \/\*{op_chars}* -xcstop \*+\/ -xcinside [^*/]+ - -digit [0-9] -ident_start [A-Za-z\200-\377_] -ident_cont [A-Za-z\200-\377_0-9\$] - -identifier {ident_start}{ident_cont}* - -/* Assorted special-case operators and operator-like tokens */ -typecast "::" -dot_dot \.\. -colon_equals ":=" -equals_greater "=>" -less_equals "<=" -greater_equals ">=" -less_greater "<>" -not_equals "!=" - -/* - * "self" is the set of chars that should be returned as single-character - * tokens. "op_chars" is the set of chars that can make up "Op" tokens, - * which can be one or more characters long (but if a single-char token - * appears in the "self" set, it is not to be returned as an Op). Note - * that the sets overlap, but each has some chars that are not in the other. - * - * If you change either set, adjust the character lists appearing in the - * rule for "operator"! - */ -self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] -op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=] -operator {op_chars}+ - -/* we no longer allow unary minus in numbers. - * instead we pass it separately to parser. there it gets - * coerced via doNegate() -- Leon aug 20 1999 - * - * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10. - * - * {realfail1} and {realfail2} are added to prevent the need for scanner - * backup when the {real} rule fails to match completely. - */ - -integer {digit}+ -decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*)) -decimalfail {digit}+\.\. -real ({integer}|{decimal})[Ee][-+]?{digit}+ -realfail1 ({integer}|{decimal})[Ee] -realfail2 ({integer}|{decimal})[Ee][-+] - -param \${integer} - -/* psql-specific: characters allowed in variable names */ -variable_char [A-Za-z\200-\377_0-9] - -other . - -/* - * Dollar quoted strings are totally opaque, and no escaping is done on them. - * Other quoted strings must allow some special characters such as single-quote - * and newline. - * Embedded single-quotes are implemented both in the SQL standard - * style of two adjacent single quotes "''" and in the Postgres/Java style - * of escaped-quote "\'". - * Other embedded escaped characters are matched explicitly and the leading - * backslash is dropped from the string. - * Note that xcstart must appear before operator, as explained above! - * Also whitespace (comment) must appear before operator. - */ - -%% - -{whitespace} { - /* - * Note that the whitespace rule includes both true - * whitespace and single-line ("--" style) comments. - * We suppress whitespace at the start of the query - * buffer. We also suppress all single-line comments, - * which is pretty dubious but is the historical - * behavior. - */ - if (!(output_buf->len == 0 || yytext[0] == '-')) - ECHO; - } - -{xcstart} { - cur_state->xcdepth = 0; - BEGIN(xc); - /* Put back any characters past slash-star; see above */ - yyless(2); - ECHO; - } - -{xcstart} { - cur_state->xcdepth++; - /* Put back any characters past slash-star; see above */ - yyless(2); - ECHO; - } - -{xcstop} { - if (cur_state->xcdepth <= 0) - { - BEGIN(INITIAL); - } - else - cur_state->xcdepth--; - ECHO; - } - -{xcinside} { - ECHO; - } - -{op_chars} { - ECHO; - } - -\*+ { - ECHO; - } - -{xbstart} { - BEGIN(xb); - ECHO; - } -{quotestop} | -{quotefail} { - yyless(1); - BEGIN(INITIAL); - ECHO; - } -{xhinside} | -{xbinside} { - ECHO; - } -{quotecontinue} | -{quotecontinue} { - ECHO; - } - -{xhstart} { - /* Hexadecimal bit type. - * At some point we should simply pass the string - * forward to the parser and label it there. - * In the meantime, place a leading "x" on the string - * to mark it for the input routine as a hex string. - */ - BEGIN(xh); - ECHO; - } -{quotestop} | -{quotefail} { - yyless(1); - BEGIN(INITIAL); - ECHO; - } - -{xnstart} { - yyless(1); /* eat only 'n' this time */ - ECHO; - } - -{xqstart} { - if (standard_strings()) - BEGIN(xq); - else - BEGIN(xe); - ECHO; - } -{xestart} { - BEGIN(xe); - ECHO; - } -{xusstart} { - BEGIN(xus); - ECHO; - } -{quotestop} | -{quotefail} { - yyless(1); - BEGIN(INITIAL); - ECHO; - } -{quotestop} | -{quotefail} { - yyless(1); - BEGIN(xusend); - ECHO; - } -{whitespace} { - ECHO; - } -{other} | -{xustop1} { - yyless(0); - BEGIN(INITIAL); - ECHO; - } -{xustop2} { - BEGIN(INITIAL); - ECHO; - } -{xqdouble} { - ECHO; - } -{xqinside} { - ECHO; - } -{xeinside} { - ECHO; - } -{xeunicode} { - ECHO; - } -{xeunicodefail} { - ECHO; - } -{xeescape} { - ECHO; - } -{xeoctesc} { - ECHO; - } -{xehexesc} { - ECHO; - } -{quotecontinue} { - ECHO; - } -. { - /* This is only needed for \ just before EOF */ - ECHO; - } - -{dolqdelim} { - cur_state->dolqstart = pg_strdup(yytext); - BEGIN(xdolq); - ECHO; - } -{dolqfailed} { - /* throw back all but the initial "$" */ - yyless(1); - ECHO; - } -{dolqdelim} { - if (strcmp(yytext, cur_state->dolqstart) == 0) - { - free(cur_state->dolqstart); - cur_state->dolqstart = NULL; - BEGIN(INITIAL); - } - else - { - /* - * When we fail to match $...$ to dolqstart, transfer - * the $... part to the output, but put back the final - * $ for rescanning. Consider $delim$...$junk$delim$ - */ - yyless(yyleng-1); - } - ECHO; - } -{dolqinside} { - ECHO; - } -{dolqfailed} { - ECHO; - } -. { - /* This is only needed for $ inside the quoted text */ - ECHO; - } - -{xdstart} { - BEGIN(xd); - ECHO; - } -{xuistart} { - BEGIN(xui); - ECHO; - } -{xdstop} { - BEGIN(INITIAL); - ECHO; - } -{dquote} { - yyless(1); - BEGIN(xuiend); - ECHO; - } -{whitespace} { - ECHO; - } -{other} | -{xustop1} { - yyless(0); - BEGIN(INITIAL); - ECHO; - } -{xustop2} { - BEGIN(INITIAL); - ECHO; - } -{xddouble} { - ECHO; - } -{xdinside} { - ECHO; - } - -{xufailed} { - /* throw back all but the initial u/U */ - yyless(1); - ECHO; - } - -{typecast} { - ECHO; - } - -{dot_dot} { - ECHO; - } - -{colon_equals} { - ECHO; - } - -{equals_greater} { - ECHO; - } - -{less_equals} { - ECHO; - } - -{greater_equals} { - ECHO; - } - -{less_greater} { - ECHO; - } - -{not_equals} { - ECHO; - } - - /* - * These rules are specific to psql --- they implement parenthesis - * counting and detection of command-ending semicolon. These must - * appear before the {self} rule so that they take precedence over it. - */ - -"(" { - cur_state->paren_depth++; - ECHO; - } - -")" { - if (cur_state->paren_depth > 0) - cur_state->paren_depth--; - ECHO; - } - -";" { - ECHO; - if (cur_state->paren_depth == 0) - { - /* Terminate lexing temporarily */ - return LEXRES_SEMI; - } - } - - /* - * psql-specific rules to handle backslash commands and variable - * substitution. We want these before {self}, also. - */ - -"\\"[;:] { - /* Force a semicolon or colon into the query buffer */ - emit(yytext + 1, 1); - } - -"\\" { - /* Terminate lexing temporarily */ - return LEXRES_BACKSLASH; - } - -:{variable_char}+ { - /* Possible psql variable substitution */ - char *varname; - const char *value; - - varname = extract_substring(yytext + 1, yyleng - 1); - value = GetVariable(pset.vars, varname); - - if (value) - { - /* It is a variable, check for recursion */ - if (var_is_current_source(cur_state, varname)) - { - /* Recursive expansion --- don't go there */ - psql_error("skipping recursive expansion of variable \"%s\"\n", - varname); - /* Instead copy the string as is */ - ECHO; - } - else - { - /* OK, perform substitution */ - push_new_buffer(value, varname); - /* yy_scan_string already made buffer active */ - } - } - else - { - /* - * if the variable doesn't exist we'll copy the - * string as is - */ - ECHO; - } - - free(varname); - } - -:'{variable_char}+' { - escape_variable(false); - } - -:\"{variable_char}+\" { - escape_variable(true); - } - - /* - * These rules just avoid the need for scanner backup if one of the - * two rules above fails to match completely. - */ - -:'{variable_char}* { - /* Throw back everything but the colon */ - yyless(1); - ECHO; - } - -:\"{variable_char}* { - /* Throw back everything but the colon */ - yyless(1); - ECHO; - } - - /* - * Back to backend-compatible rules. - */ - -{self} { - ECHO; - } - -{operator} { - /* - * Check for embedded slash-star or dash-dash; those - * are comment starts, so operator must stop there. - * Note that slash-star or dash-dash at the first - * character will match a prior rule, not this one. - */ - int nchars = yyleng; - char *slashstar = strstr(yytext, "/*"); - char *dashdash = strstr(yytext, "--"); - - if (slashstar && dashdash) - { - /* if both appear, take the first one */ - if (slashstar > dashdash) - slashstar = dashdash; - } - else if (!slashstar) - slashstar = dashdash; - if (slashstar) - nchars = slashstar - yytext; - - /* - * For SQL compatibility, '+' and '-' cannot be the - * last char of a multi-char operator unless the operator - * contains chars that are not in SQL operators. - * The idea is to lex '=-' as two operators, but not - * to forbid operator names like '?-' that could not be - * sequences of SQL operators. - */ - while (nchars > 1 && - (yytext[nchars-1] == '+' || - yytext[nchars-1] == '-')) - { - int ic; - - for (ic = nchars-2; ic >= 0; ic--) - { - if (strchr("~!@#^&|`?%", yytext[ic])) - break; - } - if (ic >= 0) - break; /* found a char that makes it OK */ - nchars--; /* else remove the +/-, and check again */ - } - - if (nchars < yyleng) - { - /* Strip the unwanted chars from the token */ - yyless(nchars); - } - ECHO; - } - -{param} { - ECHO; - } - -{integer} { - ECHO; - } -{decimal} { - ECHO; - } -{decimalfail} { - /* throw back the .., and treat as integer */ - yyless(yyleng-2); - ECHO; - } -{real} { - ECHO; - } -{realfail1} { - /* - * throw back the [Ee], and treat as {decimal}. Note - * that it is possible the input is actually {integer}, - * but since this case will almost certainly lead to a - * syntax error anyway, we don't bother to distinguish. - */ - yyless(yyleng-1); - ECHO; - } -{realfail2} { - /* throw back the [Ee][+-], and proceed as above */ - yyless(yyleng-2); - ECHO; - } - - -{identifier} { - ECHO; - } - -{other} { - ECHO; - } - - - /* - * Everything from here down is psql-specific. - */ - -<> { - StackElem *stackelem = cur_state->buffer_stack; - - if (stackelem == NULL) - return LEXRES_EOL; /* end of input reached */ - - /* - * We were expanding a variable, so pop the inclusion - * stack and keep lexing - */ - pop_buffer_stack(cur_state); - - stackelem = cur_state->buffer_stack; - if (stackelem != NULL) - { - yy_switch_to_buffer(stackelem->buf); - cur_state->curline = stackelem->bufstring; - cur_state->refline = stackelem->origstring ? stackelem->origstring : stackelem->bufstring; - } - else - { - yy_switch_to_buffer(cur_state->scanbufhandle); - cur_state->curline = cur_state->scanbuf; - cur_state->refline = cur_state->scanline; - } - } - - /* - * Exclusive lexer states to handle backslash command lexing - */ - -{ - /* command name ends at whitespace or backslash; eat all else */ - -{space}|"\\" { - yyless(0); - return LEXRES_OK; - } - -{other} { ECHO; } - -} - -{ - /* - * Discard any whitespace before argument, then go to xslasharg state. - * An exception is that "|" is only special at start of argument, so we - * check for it here. - */ - -{space}+ { } - -"|" { - if (option_type == OT_FILEPIPE) - { - /* treat like whole-string case */ - ECHO; - BEGIN(xslashwholeline); - } - else - { - /* vertical bar is not special otherwise */ - yyless(0); - BEGIN(xslasharg); - } - } - -{other} { - yyless(0); - BEGIN(xslasharg); - } - -} - -{ - /* - * Default processing of text in a slash command's argument. - * - * Note: unquoted_option_chars counts the number of characters at the - * end of the argument that were not subject to any form of quoting. - * psql_scan_slash_option needs this to strip trailing semicolons safely. - */ - -{space}|"\\" { - /* - * Unquoted space is end of arg; do not eat. Likewise - * backslash is end of command or next command, do not eat - * - * XXX this means we can't conveniently accept options - * that include unquoted backslashes; therefore, option - * processing that encourages use of backslashes is rather - * broken. - */ - yyless(0); - return LEXRES_OK; - } - -{quote} { - *option_quote = '\''; - unquoted_option_chars = 0; - BEGIN(xslashquote); - } - -"`" { - backtick_start_offset = output_buf->len; - *option_quote = '`'; - unquoted_option_chars = 0; - BEGIN(xslashbackquote); - } - -{dquote} { - ECHO; - *option_quote = '"'; - unquoted_option_chars = 0; - BEGIN(xslashdquote); - } - -:{variable_char}+ { - /* Possible psql variable substitution */ - if (option_type == OT_NO_EVAL) - ECHO; - else - { - char *varname; - const char *value; - - varname = extract_substring(yytext + 1, yyleng - 1); - value = GetVariable(pset.vars, varname); - free(varname); - - /* - * The variable value is just emitted without any - * further examination. This is consistent with the - * pre-8.0 code behavior, if not with the way that - * variables are handled outside backslash commands. - * Note that we needn't guard against recursion here. - */ - if (value) - appendPQExpBufferStr(output_buf, value); - else - ECHO; - - *option_quote = ':'; - } - unquoted_option_chars = 0; - } - -:'{variable_char}+' { - if (option_type == OT_NO_EVAL) - ECHO; - else - { - escape_variable(false); - *option_quote = ':'; - } - unquoted_option_chars = 0; - } - - -:\"{variable_char}+\" { - if (option_type == OT_NO_EVAL) - ECHO; - else - { - escape_variable(true); - *option_quote = ':'; - } - unquoted_option_chars = 0; - } - -:'{variable_char}* { - /* Throw back everything but the colon */ - yyless(1); - unquoted_option_chars++; - ECHO; - } - -:\"{variable_char}* { - /* Throw back everything but the colon */ - yyless(1); - unquoted_option_chars++; - ECHO; - } - -{other} { - unquoted_option_chars++; - ECHO; - } - -} - -{ - /* - * single-quoted text: copy literally except for '' and backslash - * sequences - */ - -{quote} { BEGIN(xslasharg); } - -{xqdouble} { appendPQExpBufferChar(output_buf, '\''); } - -"\\n" { appendPQExpBufferChar(output_buf, '\n'); } -"\\t" { appendPQExpBufferChar(output_buf, '\t'); } -"\\b" { appendPQExpBufferChar(output_buf, '\b'); } -"\\r" { appendPQExpBufferChar(output_buf, '\r'); } -"\\f" { appendPQExpBufferChar(output_buf, '\f'); } - -{xeoctesc} { - /* octal case */ - appendPQExpBufferChar(output_buf, - (char) strtol(yytext + 1, NULL, 8)); - } - -{xehexesc} { - /* hex case */ - appendPQExpBufferChar(output_buf, - (char) strtol(yytext + 2, NULL, 16)); - } - -"\\". { emit(yytext + 1, 1); } - -{other}|\n { ECHO; } - -} - -{ - /* - * backticked text: copy everything until next backquote, then evaluate. - * - * XXX Possible future behavioral change: substitute for :VARIABLE? - */ - -"`" { - /* In NO_EVAL mode, don't evaluate the command */ - if (option_type != OT_NO_EVAL) - evaluate_backtick(); - BEGIN(xslasharg); - } - -{other}|\n { ECHO; } - -} - -{ - /* double-quoted text: copy verbatim, including the double quotes */ - -{dquote} { - ECHO; - BEGIN(xslasharg); - } - -{other}|\n { ECHO; } - -} - -{ - /* copy everything until end of input line */ - /* but suppress leading whitespace */ - -{space}+ { - if (output_buf->len > 0) - ECHO; - } - -{other} { ECHO; } - -} - -{ - /* at end of command, eat a double backslash, but not anything else */ - -"\\\\" { return LEXRES_OK; } - -{other}|\n { - yyless(0); - return LEXRES_OK; - } - -} - -%% - -/* - * Create a lexer working state struct. - */ -PsqlScanState -psql_scan_create(void) -{ - PsqlScanState state; - - state = (PsqlScanStateData *) pg_malloc0(sizeof(PsqlScanStateData)); - - psql_scan_reset(state); - - return state; -} - -/* - * Destroy a lexer working state struct, releasing all resources. - */ -void -psql_scan_destroy(PsqlScanState state) -{ - psql_scan_finish(state); - - psql_scan_reset(state); - - free(state); -} - -/* - * Set up to perform lexing of the given input line. - * - * The text at *line, extending for line_len bytes, will be scanned by - * subsequent calls to the psql_scan routines. psql_scan_finish should - * be called when scanning is complete. Note that the lexer retains - * a pointer to the storage at *line --- this string must not be altered - * or freed until after psql_scan_finish is called. - */ -void -psql_scan_setup(PsqlScanState state, - const char *line, int line_len) -{ - /* Mustn't be scanning already */ - Assert(state->scanbufhandle == NULL); - Assert(state->buffer_stack == NULL); - - /* Do we need to hack the character set encoding? */ - state->encoding = pset.encoding; - state->safe_encoding = pg_valid_server_encoding_id(state->encoding); - - /* needed for prepare_buffer */ - cur_state = state; - - /* Set up flex input buffer with appropriate translation and padding */ - state->scanbufhandle = prepare_buffer(line, line_len, - &state->scanbuf); - state->scanline = line; - - /* Set lookaside data in case we have to map unsafe encoding */ - state->curline = state->scanbuf; - state->refline = state->scanline; -} - -/* - * Do lexical analysis of SQL command text. - * - * The text previously passed to psql_scan_setup is scanned, and appended - * (possibly with transformation) to query_buf. - * - * The return value indicates the condition that stopped scanning: - * - * PSCAN_SEMICOLON: found a command-ending semicolon. (The semicolon is - * transferred to query_buf.) The command accumulated in query_buf should - * be executed, then clear query_buf and call again to scan the remainder - * of the line. - * - * PSCAN_BACKSLASH: found a backslash that starts a psql special command. - * Any previous data on the line has been transferred to query_buf. - * The caller will typically next call psql_scan_slash_command(), - * perhaps psql_scan_slash_option(), and psql_scan_slash_command_end(). - * - * PSCAN_INCOMPLETE: the end of the line was reached, but we have an - * incomplete SQL command. *prompt is set to the appropriate prompt type. - * - * PSCAN_EOL: the end of the line was reached, and there is no lexical - * reason to consider the command incomplete. The caller may or may not - * choose to send it. *prompt is set to the appropriate prompt type if - * the caller chooses to collect more input. - * - * In the PSCAN_INCOMPLETE and PSCAN_EOL cases, psql_scan_finish() should - * be called next, then the cycle may be repeated with a fresh input line. - * - * In all cases, *prompt is set to an appropriate prompt type code for the - * next line-input operation. - */ -PsqlScanResult -psql_scan(PsqlScanState state, - PQExpBuffer query_buf, - promptStatus_t *prompt) -{ - PsqlScanResult result; - int lexresult; - - /* Must be scanning already */ - Assert(state->scanbufhandle != NULL); - - /* Set up static variables that will be used by yylex */ - cur_state = state; - output_buf = query_buf; - - if (state->buffer_stack != NULL) - yy_switch_to_buffer(state->buffer_stack->buf); - else - yy_switch_to_buffer(state->scanbufhandle); - - BEGIN(state->start_state); - - /* And lex. */ - lexresult = yylex(); - - /* Update static vars back to the state struct */ - state->start_state = YY_START; - - /* - * Check termination state and return appropriate result info. - */ - switch (lexresult) - { - case LEXRES_EOL: /* end of input */ - switch (state->start_state) - { - /* This switch must cover all non-slash-command states. */ - case INITIAL: - case xuiend: /* we treat these like INITIAL */ - case xusend: - if (state->paren_depth > 0) - { - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_PAREN; - } - else if (query_buf->len > 0) - { - result = PSCAN_EOL; - *prompt = PROMPT_CONTINUE; - } - else - { - /* never bother to send an empty buffer */ - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_READY; - } - break; - case xb: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_SINGLEQUOTE; - break; - case xc: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_COMMENT; - break; - case xd: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_DOUBLEQUOTE; - break; - case xh: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_SINGLEQUOTE; - break; - case xe: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_SINGLEQUOTE; - break; - case xq: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_SINGLEQUOTE; - break; - case xdolq: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_DOLLARQUOTE; - break; - case xui: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_DOUBLEQUOTE; - break; - case xus: - result = PSCAN_INCOMPLETE; - *prompt = PROMPT_SINGLEQUOTE; - break; - default: - /* can't get here */ - fprintf(stderr, "invalid YY_START\n"); - exit(1); - } - break; - case LEXRES_SEMI: /* semicolon */ - result = PSCAN_SEMICOLON; - *prompt = PROMPT_READY; - break; - case LEXRES_BACKSLASH: /* backslash */ - result = PSCAN_BACKSLASH; - *prompt = PROMPT_READY; - break; - default: - /* can't get here */ - fprintf(stderr, "invalid yylex result\n"); - exit(1); - } - - return result; -} - -/* - * Clean up after scanning a string. This flushes any unread input and - * releases resources (but not the PsqlScanState itself). Note however - * that this does not reset the lexer scan state; that can be done by - * psql_scan_reset(), which is an orthogonal operation. - * - * It is legal to call this when not scanning anything (makes it easier - * to deal with error recovery). - */ -void -psql_scan_finish(PsqlScanState state) -{ - /* Drop any incomplete variable expansions. */ - while (state->buffer_stack != NULL) - pop_buffer_stack(state); - - /* Done with the outer scan buffer, too */ - if (state->scanbufhandle) - yy_delete_buffer(state->scanbufhandle); - state->scanbufhandle = NULL; - if (state->scanbuf) - free(state->scanbuf); - state->scanbuf = NULL; -} - -/* - * Reset lexer scanning state to start conditions. This is appropriate - * for executing \r psql commands (or any other time that we discard the - * prior contents of query_buf). It is not, however, necessary to do this - * when we execute and clear the buffer after getting a PSCAN_SEMICOLON or - * PSCAN_EOL scan result, because the scan state must be INITIAL when those - * conditions are returned. - * - * Note that this is unrelated to flushing unread input; that task is - * done by psql_scan_finish(). - */ -void -psql_scan_reset(PsqlScanState state) -{ - state->start_state = INITIAL; - state->paren_depth = 0; - state->xcdepth = 0; /* not really necessary */ - if (state->dolqstart) - free(state->dolqstart); - state->dolqstart = NULL; -} - -/* - * Return true if lexer is currently in an "inside quotes" state. - * - * This is pretty grotty but is needed to preserve the old behavior - * that mainloop.c drops blank lines not inside quotes without even - * echoing them. - */ -bool -psql_scan_in_quote(PsqlScanState state) -{ - return state->start_state != INITIAL; -} - -/* - * Scan the command name of a psql backslash command. This should be called - * after psql_scan() returns PSCAN_BACKSLASH. It is assumed that the input - * has been consumed through the leading backslash. - * - * The return value is a malloc'd copy of the command name, as parsed off - * from the input. - */ -char * -psql_scan_slash_command(PsqlScanState state) -{ - PQExpBufferData mybuf; - - /* Must be scanning already */ - Assert(state->scanbufhandle != NULL); - - /* Build a local buffer that we'll return the data of */ - initPQExpBuffer(&mybuf); - - /* Set up static variables that will be used by yylex */ - cur_state = state; - output_buf = &mybuf; - - if (state->buffer_stack != NULL) - yy_switch_to_buffer(state->buffer_stack->buf); - else - yy_switch_to_buffer(state->scanbufhandle); - - BEGIN(xslashcmd); - - /* And lex. */ - yylex(); - - /* There are no possible errors in this lex state... */ - - return mybuf.data; -} - -/* - * Parse off the next argument for a backslash command, and return it as a - * malloc'd string. If there are no more arguments, returns NULL. - * - * type tells what processing, if any, to perform on the option string; - * for example, if it's a SQL identifier, we want to downcase any unquoted - * letters. - * - * if quote is not NULL, *quote is set to 0 if no quoting was found, else - * the last quote symbol used in the argument. - * - * if semicolon is true, unquoted trailing semicolon(s) that would otherwise - * be taken as part of the option string will be stripped. - * - * NOTE: the only possible syntax errors for backslash options are unmatched - * quotes, which are detected when we run out of input. Therefore, on a - * syntax error we just throw away the string and return NULL; there is no - * need to worry about flushing remaining input. - */ -char * -psql_scan_slash_option(PsqlScanState state, - enum slash_option_type type, - char *quote, - bool semicolon) -{ - PQExpBufferData mybuf; - int lexresult PG_USED_FOR_ASSERTS_ONLY; - char local_quote; - - /* Must be scanning already */ - Assert(state->scanbufhandle != NULL); - - if (quote == NULL) - quote = &local_quote; - *quote = 0; - - /* Build a local buffer that we'll return the data of */ - initPQExpBuffer(&mybuf); - - /* Set up static variables that will be used by yylex */ - cur_state = state; - output_buf = &mybuf; - option_type = type; - option_quote = quote; - unquoted_option_chars = 0; - - if (state->buffer_stack != NULL) - yy_switch_to_buffer(state->buffer_stack->buf); - else - yy_switch_to_buffer(state->scanbufhandle); - - if (type == OT_WHOLE_LINE) - BEGIN(xslashwholeline); - else - BEGIN(xslashargstart); - - /* And lex. */ - lexresult = yylex(); - - /* - * Check the lex result: we should have gotten back either LEXRES_OK - * or LEXRES_EOL (the latter indicating end of string). If we were inside - * a quoted string, as indicated by YY_START, EOL is an error. - */ - Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK); - - switch (YY_START) - { - case xslashargstart: - /* empty arg */ - break; - case xslasharg: - /* Strip any unquoted trailing semi-colons if requested */ - if (semicolon) - { - while (unquoted_option_chars-- > 0 && - mybuf.len > 0 && - mybuf.data[mybuf.len - 1] == ';') - { - mybuf.data[--mybuf.len] = '\0'; - } - } - - /* - * If SQL identifier processing was requested, then we strip out - * excess double quotes and downcase unquoted letters. - * Doubled double-quotes become output double-quotes, per spec. - * - * Note that a string like FOO"BAR"BAZ will be converted to - * fooBARbaz; this is somewhat inconsistent with the SQL spec, - * which would have us parse it as several identifiers. But - * for psql's purposes, we want a string like "foo"."bar" to - * be treated as one option, so there's little choice. - */ - if (type == OT_SQLID || type == OT_SQLIDHACK) - { - bool inquotes = false; - char *cp = mybuf.data; - - while (*cp) - { - if (*cp == '"') - { - if (inquotes && cp[1] == '"') - { - /* Keep the first quote, remove the second */ - cp++; - } - inquotes = !inquotes; - /* Collapse out quote at *cp */ - memmove(cp, cp + 1, strlen(cp)); - mybuf.len--; - /* do not advance cp */ - } - else - { - if (!inquotes && type == OT_SQLID) - *cp = pg_tolower((unsigned char) *cp); - cp += PQmblen(cp, pset.encoding); - } - } - } - break; - case xslashquote: - case xslashbackquote: - case xslashdquote: - /* must have hit EOL inside quotes */ - psql_error("unterminated quoted string\n"); - termPQExpBuffer(&mybuf); - return NULL; - case xslashwholeline: - /* always okay */ - break; - default: - /* can't get here */ - fprintf(stderr, "invalid YY_START\n"); - exit(1); - } - - /* - * An unquoted empty argument isn't possible unless we are at end of - * command. Return NULL instead. - */ - if (mybuf.len == 0 && *quote == 0) - { - termPQExpBuffer(&mybuf); - return NULL; - } - - /* Else return the completed string. */ - return mybuf.data; -} - -/* - * Eat up any unused \\ to complete a backslash command. - */ -void -psql_scan_slash_command_end(PsqlScanState state) -{ - /* Must be scanning already */ - Assert(state->scanbufhandle != NULL); - - /* Set up static variables that will be used by yylex */ - cur_state = state; - output_buf = NULL; - - if (state->buffer_stack != NULL) - yy_switch_to_buffer(state->buffer_stack->buf); - else - yy_switch_to_buffer(state->scanbufhandle); - - BEGIN(xslashend); - - /* And lex. */ - yylex(); - - /* There are no possible errors in this lex state... */ -} - -/* - * Evaluate a backticked substring of a slash command's argument. - * - * The portion of output_buf starting at backtick_start_offset is evaluated - * as a shell command and then replaced by the command's output. - */ -static void -evaluate_backtick(void) -{ - char *cmd = output_buf->data + backtick_start_offset; - PQExpBufferData cmd_output; - FILE *fd; - bool error = false; - char buf[512]; - size_t result; - - initPQExpBuffer(&cmd_output); - - fd = popen(cmd, PG_BINARY_R); - if (!fd) - { - psql_error("%s: %s\n", cmd, strerror(errno)); - error = true; - } - - if (!error) - { - do - { - result = fread(buf, 1, sizeof(buf), fd); - if (ferror(fd)) - { - psql_error("%s: %s\n", cmd, strerror(errno)); - error = true; - break; - } - appendBinaryPQExpBuffer(&cmd_output, buf, result); - } while (!feof(fd)); - } - - if (fd && pclose(fd) == -1) - { - psql_error("%s: %s\n", cmd, strerror(errno)); - error = true; - } - - if (PQExpBufferDataBroken(cmd_output)) - { - psql_error("%s: out of memory\n", cmd); - error = true; - } - - /* Now done with cmd, delete it from output_buf */ - output_buf->len = backtick_start_offset; - output_buf->data[output_buf->len] = '\0'; - - /* If no error, transfer result to output_buf */ - if (!error) - { - /* strip any trailing newline */ - if (cmd_output.len > 0 && - cmd_output.data[cmd_output.len - 1] == '\n') - cmd_output.len--; - appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len); - } - - termPQExpBuffer(&cmd_output); -} - -/* - * Push the given string onto the stack of stuff to scan. - * - * cur_state must point to the active PsqlScanState. - * - * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. - */ -static void -push_new_buffer(const char *newstr, const char *varname) -{ - StackElem *stackelem; - - stackelem = (StackElem *) pg_malloc(sizeof(StackElem)); - - /* - * In current usage, the passed varname points at the current flex - * input buffer; we must copy it before calling prepare_buffer() - * because that will change the buffer state. - */ - stackelem->varname = varname ? pg_strdup(varname) : NULL; - - stackelem->buf = prepare_buffer(newstr, strlen(newstr), - &stackelem->bufstring); - cur_state->curline = stackelem->bufstring; - if (cur_state->safe_encoding) - { - stackelem->origstring = NULL; - cur_state->refline = stackelem->bufstring; - } - else - { - stackelem->origstring = pg_strdup(newstr); - cur_state->refline = stackelem->origstring; - } - stackelem->next = cur_state->buffer_stack; - cur_state->buffer_stack = stackelem; -} - -/* - * Pop the topmost buffer stack item (there must be one!) - * - * NB: after this, the flex input state is unspecified; caller must - * switch to an appropriate buffer to continue lexing. - */ -static void -pop_buffer_stack(PsqlScanState state) -{ - StackElem *stackelem = state->buffer_stack; - - state->buffer_stack = stackelem->next; - yy_delete_buffer(stackelem->buf); - free(stackelem->bufstring); - if (stackelem->origstring) - free(stackelem->origstring); - if (stackelem->varname) - free(stackelem->varname); - free(stackelem); -} - -/* - * Check if specified variable name is the source for any string - * currently being scanned - */ -static bool -var_is_current_source(PsqlScanState state, const char *varname) -{ - StackElem *stackelem; - - for (stackelem = state->buffer_stack; - stackelem != NULL; - stackelem = stackelem->next) - { - if (stackelem->varname && strcmp(stackelem->varname, varname) == 0) - return true; - } - return false; -} - -/* - * Set up a flex input buffer to scan the given data. We always make a - * copy of the data. If working in an unsafe encoding, the copy has - * multibyte sequences replaced by FFs to avoid fooling the lexer rules. - * - * cur_state must point to the active PsqlScanState. - * - * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer. - */ -static YY_BUFFER_STATE -prepare_buffer(const char *txt, int len, char **txtcopy) -{ - char *newtxt; - - /* Flex wants two \0 characters after the actual data */ - newtxt = pg_malloc(len + 2); - *txtcopy = newtxt; - newtxt[len] = newtxt[len + 1] = YY_END_OF_BUFFER_CHAR; - - if (cur_state->safe_encoding) - memcpy(newtxt, txt, len); - else - { - /* Gotta do it the hard way */ - int i = 0; - - while (i < len) - { - int thislen = PQmblen(txt + i, cur_state->encoding); - - /* first byte should always be okay... */ - newtxt[i] = txt[i]; - i++; - while (--thislen > 0 && i < len) - newtxt[i++] = (char) 0xFF; - } - } - - return yy_scan_buffer(newtxt, len + 2); -} - -/* - * emit() --- body for ECHO macro - * - * NB: this must be used for ALL and ONLY the text copied from the flex - * input data. If you pass it something that is not part of the yytext - * string, you are making a mistake. Internally generated text can be - * appended directly to output_buf. - */ -static void -emit(const char *txt, int len) -{ - if (cur_state->safe_encoding) - appendBinaryPQExpBuffer(output_buf, txt, len); - else - { - /* Gotta do it the hard way */ - const char *reference = cur_state->refline; - int i; - - reference += (txt - cur_state->curline); - - for (i = 0; i < len; i++) - { - char ch = txt[i]; - - if (ch == (char) 0xFF) - ch = reference[i]; - appendPQExpBufferChar(output_buf, ch); - } - } -} - -/* - * extract_substring --- fetch the true value of (part of) the current token - * - * This is like emit(), except that the data is returned as a malloc'd string - * rather than being pushed directly to output_buf. - */ -static char * -extract_substring(const char *txt, int len) -{ - char *result = (char *) pg_malloc(len + 1); - - if (cur_state->safe_encoding) - memcpy(result, txt, len); - else - { - /* Gotta do it the hard way */ - const char *reference = cur_state->refline; - int i; - - reference += (txt - cur_state->curline); - - for (i = 0; i < len; i++) - { - char ch = txt[i]; - - if (ch == (char) 0xFF) - ch = reference[i]; - result[i] = ch; - } - } - result[len] = '\0'; - return result; -} - -/* - * escape_variable --- process :'VARIABLE' or :"VARIABLE" - * - * If the variable name is found, escape its value using the appropriate - * quoting method and emit the value to output_buf. (Since the result is - * surely quoted, there is never any reason to rescan it.) If we don't - * find the variable or the escaping function fails, emit the token as-is. - */ -static void -escape_variable(bool as_ident) -{ - char *varname; - const char *value; - - /* Variable lookup. */ - varname = extract_substring(yytext + 2, yyleng - 3); - value = GetVariable(pset.vars, varname); - free(varname); - - /* Escaping. */ - if (value) - { - if (!pset.db) - psql_error("can't escape without active connection\n"); - else - { - char *escaped_value; - - if (as_ident) - escaped_value = - PQescapeIdentifier(pset.db, value, strlen(value)); - else - escaped_value = - PQescapeLiteral(pset.db, value, strlen(value)); - - if (escaped_value == NULL) - { - const char *error = PQerrorMessage(pset.db); - - psql_error("%s", error); - } - else - { - appendPQExpBufferStr(output_buf, escaped_value); - PQfreemem(escaped_value); - return; - } - } - } - - /* - * If we reach this point, some kind of error has occurred. Emit the - * original text into the output buffer. - */ - emit(yytext, yyleng); -} diff --git a/src/bin/csql/settings.h b/src/bin/csql/settings.h deleted file mode 100644 index d34dc2806..000000000 --- a/src/bin/csql/settings.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/settings.h - */ -#ifndef SETTINGS_H -#define SETTINGS_H - - -#include "variables.h" -#include "print.h" - -#define DEFAULT_FIELD_SEP "|" -#define DEFAULT_RECORD_SEP "\n" - -#if defined(WIN32) || defined(__CYGWIN__) -#define DEFAULT_EDITOR "notepad.exe" -/* no DEFAULT_EDITOR_LINENUMBER_ARG for Notepad */ -#else -#define DEFAULT_EDITOR "vi" -#define DEFAULT_EDITOR_LINENUMBER_ARG "+" -#endif - -#define DEFAULT_PROMPT1 "%/%R%# " -#define DEFAULT_PROMPT2 "%/%R%# " -#define DEFAULT_PROMPT3 ">> " - -/* - * Note: these enums should generally be chosen so that zero corresponds - * to the default behavior. - */ - -typedef enum -{ - PSQL_ECHO_NONE, - PSQL_ECHO_QUERIES, - PSQL_ECHO_ERRORS, - PSQL_ECHO_ALL -} PSQL_ECHO; - -typedef enum -{ - PSQL_ECHO_HIDDEN_OFF, - PSQL_ECHO_HIDDEN_ON, - PSQL_ECHO_HIDDEN_NOEXEC -} PSQL_ECHO_HIDDEN; - -typedef enum -{ - PSQL_ERROR_ROLLBACK_OFF, - PSQL_ERROR_ROLLBACK_INTERACTIVE, - PSQL_ERROR_ROLLBACK_ON -} PSQL_ERROR_ROLLBACK; - -typedef enum -{ - PSQL_COMP_CASE_PRESERVE_UPPER, - PSQL_COMP_CASE_PRESERVE_LOWER, - PSQL_COMP_CASE_UPPER, - PSQL_COMP_CASE_LOWER -} PSQL_COMP_CASE; - -typedef enum -{ - hctl_none = 0, - hctl_ignorespace = 1, - hctl_ignoredups = 2, - hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups -} HistControl; - -enum trivalue -{ - TRI_DEFAULT, - TRI_NO, - TRI_YES -}; - -typedef struct _psqlSettings -{ - PGconn *db; /* connection to backend */ - int encoding; /* client_encoding */ - FILE *queryFout; /* where to send the query results */ - bool queryFoutPipe; /* queryFout is from a popen() */ - - FILE *copyStream; /* Stream to read/write for \copy command */ - - printQueryOpt popt; - - char *gfname; /* one-shot file output argument for \g */ - char *gset_prefix; /* one-shot prefix argument for \gset */ - - bool notty; /* stdin or stdout is not a tty (as determined - * on startup) */ - enum trivalue getPassword; /* prompt the user for a username and password */ - FILE *cur_cmd_source; /* describe the status of the current main - * loop */ - bool cur_cmd_interactive; - int sversion; /* backend server version */ - const char *progname; /* in case you renamed psql */ - char *inputfile; /* file being currently processed, if any */ - uint64 lineno; /* also for error reporting */ - uint64 stmt_lineno; /* line number inside the current statement */ - - bool timing; /* enable timing of all queries */ - - FILE *logfile; /* session log file handle */ - - VariableSpace vars; /* "shell variable" repository */ - - /* - * The remaining fields are set by assign hooks associated with entries in - * "vars". They should not be set directly except by those hook - * functions. - */ - bool autocommit; - bool on_error_stop; - bool quiet; - bool singleline; - bool singlestep; - int fetch_count; - PSQL_ECHO echo; - PSQL_ECHO_HIDDEN echo_hidden; - PSQL_ERROR_ROLLBACK on_error_rollback; - PSQL_COMP_CASE comp_case; - HistControl histcontrol; - const char *prompt1; - const char *prompt2; - const char *prompt3; - PGVerbosity verbosity; /* current error verbosity level */ -} PsqlSettings; - -extern PsqlSettings pset; - - -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif - -#ifndef EXIT_FAILURE -#define EXIT_FAILURE 1 -#endif - -#define EXIT_BADCONN 2 - -#define EXIT_USER 3 - -#endif diff --git a/src/bin/csql/sql_help.c b/src/bin/csql/sql_help.c deleted file mode 100644 index 02b05e676..000000000 --- a/src/bin/csql/sql_help.c +++ /dev/null @@ -1,4052 +0,0 @@ -/* - * *** Do not change this file by hand. It is automatically - * *** generated from the DocBook documentation. - * - * generated by - * /usr/bin/perl create_help.pl ../../../doc/src/sgml/ref sql_help - * - */ - -#include "sql_help.h" - -void -sql_help_ABORT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ABORT [ WORK | TRANSACTION ]"); -} - -void -sql_help_ALTER_AGGREGATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER AGGREGATE %s ( %s ) RENAME TO %s\n" - "ALTER AGGREGATE %s ( %s )\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER AGGREGATE %s ( %s ) SET SCHEMA %s\n" - "\n" - "%s\n" - "\n" - "* |\n" - "[ %s ] [ %s ] %s [ , ... ] |\n" - "[ [ %s ] [ %s ] %s [ , ... ] ] ORDER BY [ %s ] [ %s ] %s [ , ... ]", - _("name"), - _("aggregate_signature"), - _("new_name"), - _("name"), - _("aggregate_signature"), - _("new_owner"), - _("name"), - _("aggregate_signature"), - _("new_schema"), - _("where aggregate_signature is:"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_ALTER_COLLATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER COLLATION %s RENAME TO %s\n" - "ALTER COLLATION %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER COLLATION %s SET SCHEMA %s", - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_CONVERSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER CONVERSION %s RENAME TO %s\n" - "ALTER CONVERSION %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER CONVERSION %s SET SCHEMA %s", - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_DATABASE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER DATABASE %s [ [ WITH ] %s [ ... ] ]\n" - "\n" - "%s\n" - "\n" - " ALLOW_CONNECTIONS %s\n" - " CONNECTION LIMIT %s\n" - " IS_TEMPLATE %s\n" - "\n" - "ALTER DATABASE %s RENAME TO %s\n" - "\n" - "ALTER DATABASE %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "\n" - "ALTER DATABASE %s SET TABLESPACE %s\n" - "\n" - "ALTER DATABASE %s SET %s { TO | = } { %s | DEFAULT }\n" - "ALTER DATABASE %s SET %s FROM CURRENT\n" - "ALTER DATABASE %s RESET %s\n" - "ALTER DATABASE %s RESET ALL", - _("name"), - _("option"), - _("where option can be:"), - _("allowconn"), - _("connlimit"), - _("istemplate"), - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_tablespace"), - _("name"), - _("configuration_parameter"), - _("value"), - _("name"), - _("configuration_parameter"), - _("name"), - _("configuration_parameter"), - _("name")); -} - -void -sql_help_ALTER_DEFAULT_PRIVILEGES(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER DEFAULT PRIVILEGES\n" - " [ FOR { ROLE | USER } %s [, ...] ]\n" - " [ IN SCHEMA %s [, ...] ]\n" - " %s\n" - "\n" - "%s\n" - "\n" - "GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON TABLES\n" - " TO { [ GROUP ] %s | PUBLIC } [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { USAGE | SELECT | UPDATE }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON SEQUENCES\n" - " TO { [ GROUP ] %s | PUBLIC } [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { EXECUTE | ALL [ PRIVILEGES ] }\n" - " ON FUNCTIONS\n" - " TO { [ GROUP ] %s | PUBLIC } [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON TYPES\n" - " TO { [ GROUP ] %s | PUBLIC } [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON TABLES\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { USAGE | SELECT | UPDATE }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON SEQUENCES\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { EXECUTE | ALL [ PRIVILEGES ] }\n" - " ON FUNCTIONS\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON TYPES\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]", - _("target_role"), - _("schema_name"), - _("abbreviated_grant_or_revoke"), - _("where abbreviated_grant_or_revoke is one of:"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name")); -} - -void -sql_help_ALTER_DOMAIN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER DOMAIN %s\n" - " { SET DEFAULT %s | DROP DEFAULT }\n" - "ALTER DOMAIN %s\n" - " { SET | DROP } NOT NULL\n" - "ALTER DOMAIN %s\n" - " ADD %s [ NOT VALID ]\n" - "ALTER DOMAIN %s\n" - " DROP CONSTRAINT [ IF EXISTS ] %s [ RESTRICT | CASCADE ]\n" - "ALTER DOMAIN %s\n" - " RENAME CONSTRAINT %s TO %s\n" - "ALTER DOMAIN %s\n" - " VALIDATE CONSTRAINT %s\n" - "ALTER DOMAIN %s\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER DOMAIN %s\n" - " RENAME TO %s\n" - "ALTER DOMAIN %s\n" - " SET SCHEMA %s", - _("name"), - _("expression"), - _("name"), - _("name"), - _("domain_constraint"), - _("name"), - _("constraint_name"), - _("name"), - _("constraint_name"), - _("new_constraint_name"), - _("name"), - _("constraint_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_name"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_EVENT_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER EVENT TRIGGER %s DISABLE\n" - "ALTER EVENT TRIGGER %s ENABLE [ REPLICA | ALWAYS ]\n" - "ALTER EVENT TRIGGER %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER EVENT TRIGGER %s RENAME TO %s", - _("name"), - _("name"), - _("name"), - _("new_owner"), - _("name"), - _("new_name")); -} - -void -sql_help_ALTER_EXTENSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER EXTENSION %s UPDATE [ TO %s ]\n" - "ALTER EXTENSION %s SET SCHEMA %s\n" - "ALTER EXTENSION %s ADD %s\n" - "ALTER EXTENSION %s DROP %s\n" - "\n" - "%s\n" - "\n" - " AGGREGATE %s ( %s ) |\n" - " CAST (%s AS %s) |\n" - " COLLATION %s |\n" - " CONVERSION %s |\n" - " DOMAIN %s |\n" - " EVENT TRIGGER %s |\n" - " FOREIGN DATA WRAPPER %s |\n" - " FOREIGN TABLE %s |\n" - " FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] ) |\n" - " MATERIALIZED VIEW %s |\n" - " OPERATOR %s (%s, %s) |\n" - " OPERATOR CLASS %s USING %s |\n" - " OPERATOR FAMILY %s USING %s |\n" - " [ PROCEDURAL ] LANGUAGE %s |\n" - " SCHEMA %s |\n" - " SEQUENCE %s |\n" - " SERVER %s |\n" - " TABLE %s |\n" - " TEXT SEARCH CONFIGURATION %s |\n" - " TEXT SEARCH DICTIONARY %s |\n" - " TEXT SEARCH PARSER %s |\n" - " TEXT SEARCH TEMPLATE %s |\n" - " TRANSFORM FOR %s LANGUAGE %s |\n" - " TYPE %s |\n" - " VIEW %s\n" - "\n" - "%s\n" - "\n" - "* |\n" - "[ %s ] [ %s ] %s [ , ... ] |\n" - "[ [ %s ] [ %s ] %s [ , ... ] ] ORDER BY [ %s ] [ %s ] %s [ , ... ]", - _("name"), - _("new_version"), - _("name"), - _("new_schema"), - _("name"), - _("member_object"), - _("name"), - _("member_object"), - _("where member_object is:"), - _("aggregate_name"), - _("aggregate_signature"), - _("source_type"), - _("target_type"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("function_name"), - _("argmode"), - _("argname"), - _("argtype"), - _("object_name"), - _("operator_name"), - _("left_type"), - _("right_type"), - _("object_name"), - _("index_method"), - _("object_name"), - _("index_method"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("type_name"), - _("lang_name"), - _("object_name"), - _("object_name"), - _("and aggregate_signature is:"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_ALTER_FOREIGN_DATA_WRAPPER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER FOREIGN DATA WRAPPER %s\n" - " [ HANDLER %s | NO HANDLER ]\n" - " [ VALIDATOR %s | NO VALIDATOR ]\n" - " [ OPTIONS ( [ ADD | SET | DROP ] %s ['%s'] [, ... ]) ]\n" - "ALTER FOREIGN DATA WRAPPER %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER FOREIGN DATA WRAPPER %s RENAME TO %s", - _("name"), - _("handler_function"), - _("validator_function"), - _("option"), - _("value"), - _("name"), - _("new_owner"), - _("name"), - _("new_name")); -} - -void -sql_help_ALTER_FOREIGN_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] %s [ * ]\n" - " %s [, ... ]\n" - "ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] %s [ * ]\n" - " RENAME [ COLUMN ] %s TO %s\n" - "ALTER FOREIGN TABLE [ IF EXISTS ] %s\n" - " RENAME TO %s\n" - "ALTER FOREIGN TABLE [ IF EXISTS ] %s\n" - " SET SCHEMA %s\n" - "\n" - "%s\n" - "\n" - " ADD [ COLUMN ] %s %s [ COLLATE %s ] [ %s [ ... ] ]\n" - " DROP [ COLUMN ] [ IF EXISTS ] %s [ RESTRICT | CASCADE ]\n" - " ALTER [ COLUMN ] %s [ SET DATA ] TYPE %s [ COLLATE %s ]\n" - " ALTER [ COLUMN ] %s SET DEFAULT %s\n" - " ALTER [ COLUMN ] %s DROP DEFAULT\n" - " ALTER [ COLUMN ] %s { SET | DROP } NOT NULL\n" - " ALTER [ COLUMN ] %s SET STATISTICS %s\n" - " ALTER [ COLUMN ] %s SET ( %s = %s [, ... ] )\n" - " ALTER [ COLUMN ] %s RESET ( %s [, ... ] )\n" - " ALTER [ COLUMN ] %s SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }\n" - " ALTER [ COLUMN ] %s OPTIONS ( [ ADD | SET | DROP ] %s ['%s'] [, ... ])\n" - " ADD %s [ NOT VALID ]\n" - " VALIDATE CONSTRAINT %s\n" - " DROP CONSTRAINT [ IF EXISTS ] %s [ RESTRICT | CASCADE ]\n" - " DISABLE TRIGGER [ %s | ALL | USER ]\n" - " ENABLE TRIGGER [ %s | ALL | USER ]\n" - " ENABLE REPLICA TRIGGER %s\n" - " ENABLE ALWAYS TRIGGER %s\n" - " SET WITH OIDS\n" - " SET WITHOUT OIDS\n" - " INHERIT %s\n" - " NO INHERIT %s\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - " OPTIONS ( [ ADD | SET | DROP ] %s ['%s'] [, ... ])", - _("name"), - _("action"), - _("name"), - _("column_name"), - _("new_column_name"), - _("name"), - _("new_name"), - _("name"), - _("new_schema"), - _("where action is one of:"), - _("column_name"), - _("data_type"), - _("collation"), - _("column_constraint"), - _("column_name"), - _("column_name"), - _("data_type"), - _("collation"), - _("column_name"), - _("expression"), - _("column_name"), - _("column_name"), - _("column_name"), - _("integer"), - _("column_name"), - _("attribute_option"), - _("value"), - _("column_name"), - _("attribute_option"), - _("column_name"), - _("column_name"), - _("option"), - _("value"), - _("table_constraint"), - _("constraint_name"), - _("constraint_name"), - _("trigger_name"), - _("trigger_name"), - _("trigger_name"), - _("trigger_name"), - _("parent_table"), - _("parent_table"), - _("new_owner"), - _("option"), - _("value")); -} - -void -sql_help_ALTER_FUNCTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] )\n" - " %s [ ... ] [ RESTRICT ]\n" - "ALTER FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] )\n" - " RENAME TO %s\n" - "ALTER FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] )\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] )\n" - " SET SCHEMA %s\n" - "\n" - "%s\n" - "\n" - " CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT\n" - " IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF\n" - " [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER\n" - " COST %s\n" - " ROWS %s\n" - " SET %s { TO | = } { %s | DEFAULT }\n" - " SET %s FROM CURRENT\n" - " RESET %s\n" - " RESET ALL", - _("name"), - _("argmode"), - _("argname"), - _("argtype"), - _("action"), - _("name"), - _("argmode"), - _("argname"), - _("argtype"), - _("new_name"), - _("name"), - _("argmode"), - _("argname"), - _("argtype"), - _("new_owner"), - _("name"), - _("argmode"), - _("argname"), - _("argtype"), - _("new_schema"), - _("where action is one of:"), - _("execution_cost"), - _("result_rows"), - _("configuration_parameter"), - _("value"), - _("configuration_parameter"), - _("configuration_parameter")); -} - -void -sql_help_ALTER_GROUP(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER GROUP %s ADD USER %s [, ... ]\n" - "ALTER GROUP %s DROP USER %s [, ... ]\n" - "\n" - "%s\n" - "\n" - " %s\n" - " | CURRENT_USER\n" - " | SESSION_USER\n" - "\n" - "ALTER GROUP %s RENAME TO %s", - _("role_specification"), - _("user_name"), - _("role_specification"), - _("user_name"), - _("where role_specification can be:"), - _("role_name"), - _("group_name"), - _("new_name")); -} - -void -sql_help_ALTER_INDEX(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER INDEX [ IF EXISTS ] %s RENAME TO %s\n" - "ALTER INDEX [ IF EXISTS ] %s SET TABLESPACE %s\n" - "ALTER INDEX [ IF EXISTS ] %s SET ( %s = %s [, ... ] )\n" - "ALTER INDEX [ IF EXISTS ] %s RESET ( %s [, ... ] )\n" - "ALTER INDEX ALL IN TABLESPACE %s [ OWNED BY %s [, ... ] ]\n" - " SET TABLESPACE %s [ NOWAIT ]", - _("name"), - _("new_name"), - _("name"), - _("tablespace_name"), - _("name"), - _("storage_parameter"), - _("value"), - _("name"), - _("storage_parameter"), - _("name"), - _("role_name"), - _("new_tablespace")); -} - -void -sql_help_ALTER_LANGUAGE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER [ PROCEDURAL ] LANGUAGE %s RENAME TO %s\n" - "ALTER [ PROCEDURAL ] LANGUAGE %s OWNER TO { %s | CURRENT_USER | SESSION_USER }", - _("name"), - _("new_name"), - _("name"), - _("new_owner")); -} - -void -sql_help_ALTER_LARGE_OBJECT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER LARGE OBJECT %s OWNER TO { %s | CURRENT_USER | SESSION_USER }", - _("large_object_oid"), - _("new_owner")); -} - -void -sql_help_ALTER_MATERIALIZED_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER MATERIALIZED VIEW [ IF EXISTS ] %s\n" - " %s [, ... ]\n" - "ALTER MATERIALIZED VIEW [ IF EXISTS ] %s\n" - " RENAME [ COLUMN ] %s TO %s\n" - "ALTER MATERIALIZED VIEW [ IF EXISTS ] %s\n" - " RENAME TO %s\n" - "ALTER MATERIALIZED VIEW [ IF EXISTS ] %s\n" - " SET SCHEMA %s\n" - "ALTER MATERIALIZED VIEW ALL IN TABLESPACE %s [ OWNED BY %s [, ... ] ]\n" - " SET TABLESPACE %s [ NOWAIT ]\n" - "\n" - "%s\n" - "\n" - " ALTER [ COLUMN ] %s SET STATISTICS %s\n" - " ALTER [ COLUMN ] %s SET ( %s = %s [, ... ] )\n" - " ALTER [ COLUMN ] %s RESET ( %s [, ... ] )\n" - " ALTER [ COLUMN ] %s SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }\n" - " CLUSTER ON %s\n" - " SET WITHOUT CLUSTER\n" - " SET ( %s = %s [, ... ] )\n" - " RESET ( %s [, ... ] )\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - " SET TABLESPACE %s", - _("name"), - _("action"), - _("name"), - _("column_name"), - _("new_column_name"), - _("name"), - _("new_name"), - _("name"), - _("new_schema"), - _("name"), - _("role_name"), - _("new_tablespace"), - _("where action is one of:"), - _("column_name"), - _("integer"), - _("column_name"), - _("attribute_option"), - _("value"), - _("column_name"), - _("attribute_option"), - _("column_name"), - _("index_name"), - _("storage_parameter"), - _("value"), - _("storage_parameter"), - _("new_owner"), - _("new_tablespace")); -} - -void -sql_help_ALTER_OPERATOR(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER OPERATOR %s ( { %s | NONE } , { %s | NONE } )\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "\n" - "ALTER OPERATOR %s ( { %s | NONE } , { %s | NONE } )\n" - " SET SCHEMA %s", - _("name"), - _("left_type"), - _("right_type"), - _("new_owner"), - _("name"), - _("left_type"), - _("right_type"), - _("new_schema")); -} - -void -sql_help_ALTER_OPERATOR_CLASS(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER OPERATOR CLASS %s USING %s\n" - " RENAME TO %s\n" - "\n" - "ALTER OPERATOR CLASS %s USING %s\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "\n" - "ALTER OPERATOR CLASS %s USING %s\n" - " SET SCHEMA %s", - _("name"), - _("index_method"), - _("new_name"), - _("name"), - _("index_method"), - _("new_owner"), - _("name"), - _("index_method"), - _("new_schema")); -} - -void -sql_help_ALTER_OPERATOR_FAMILY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER OPERATOR FAMILY %s USING %s ADD\n" - " { OPERATOR %s %s ( %s, %s )\n" - " [ FOR SEARCH | FOR ORDER BY %s ]\n" - " | FUNCTION %s [ ( %s [ , %s ] ) ]\n" - " %s ( %s [, ...] )\n" - " } [, ... ]\n" - "\n" - "ALTER OPERATOR FAMILY %s USING %s DROP\n" - " { OPERATOR %s ( %s [ , %s ] )\n" - " | FUNCTION %s ( %s [ , %s ] )\n" - " } [, ... ]\n" - "\n" - "ALTER OPERATOR FAMILY %s USING %s\n" - " RENAME TO %s\n" - "\n" - "ALTER OPERATOR FAMILY %s USING %s\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "\n" - "ALTER OPERATOR FAMILY %s USING %s\n" - " SET SCHEMA %s", - _("name"), - _("index_method"), - _("strategy_number"), - _("operator_name"), - _("op_type"), - _("op_type"), - _("sort_family_name"), - _("support_number"), - _("op_type"), - _("op_type"), - _("function_name"), - _("argument_type"), - _("name"), - _("index_method"), - _("strategy_number"), - _("op_type"), - _("op_type"), - _("support_number"), - _("op_type"), - _("op_type"), - _("name"), - _("index_method"), - _("new_name"), - _("name"), - _("index_method"), - _("new_owner"), - _("name"), - _("index_method"), - _("new_schema")); -} - -void -sql_help_ALTER_POLICY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER POLICY %s ON %s RENAME TO %s\n" - "\n" - "ALTER POLICY %s ON %s\n" - " [ TO { %s | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]\n" - " [ USING ( %s ) ]\n" - " [ WITH CHECK ( %s ) ]", - _("name"), - _("table_name"), - _("new_name"), - _("name"), - _("table_name"), - _("role_name"), - _("using_expression"), - _("check_expression")); -} - -void -sql_help_ALTER_ROLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER ROLE %s [ WITH ] %s [ ... ]\n" - "\n" - "%s\n" - "\n" - " SUPERUSER | NOSUPERUSER\n" - " | CREATEDB | NOCREATEDB\n" - " | CREATEROLE | NOCREATEROLE\n" - " | CREATEUSER | NOCREATEUSER\n" - " | INHERIT | NOINHERIT\n" - " | LOGIN | NOLOGIN\n" - " | REPLICATION | NOREPLICATION\n" - " | BYPASSRLS | NOBYPASSRLS\n" - " | CONNECTION LIMIT %s\n" - " | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '%s'\n" - " | VALID UNTIL '%s'\n" - "\n" - "ALTER ROLE %s RENAME TO %s\n" - "\n" - "ALTER ROLE { %s | ALL } [ IN DATABASE %s ] SET %s { TO | = } { %s | DEFAULT }\n" - "ALTER ROLE { %s | ALL } [ IN DATABASE %s ] SET %s FROM CURRENT\n" - "ALTER ROLE { %s | ALL } [ IN DATABASE %s ] RESET %s\n" - "ALTER ROLE { %s | ALL } [ IN DATABASE %s ] RESET ALL\n" - "\n" - "%s\n" - "\n" - " [ GROUP ] %s\n" - " | CURRENT_USER\n" - " | SESSION_USER", - _("role_specification"), - _("option"), - _("where option can be:"), - _("connlimit"), - _("password"), - _("timestamp"), - _("name"), - _("new_name"), - _("role_specification"), - _("database_name"), - _("configuration_parameter"), - _("value"), - _("role_specification"), - _("database_name"), - _("configuration_parameter"), - _("role_specification"), - _("database_name"), - _("configuration_parameter"), - _("role_specification"), - _("database_name"), - _("where role_specification can be:"), - _("role_name")); -} - -void -sql_help_ALTER_RULE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER RULE %s ON %s RENAME TO %s", - _("name"), - _("table_name"), - _("new_name")); -} - -void -sql_help_ALTER_SCHEMA(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER SCHEMA %s RENAME TO %s\n" - "ALTER SCHEMA %s OWNER TO { %s | CURRENT_USER | SESSION_USER }", - _("name"), - _("new_name"), - _("name"), - _("new_owner")); -} - -void -sql_help_ALTER_SEQUENCE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER SEQUENCE [ IF EXISTS ] %s [ INCREMENT [ BY ] %s ]\n" - " [ MINVALUE %s | NO MINVALUE ] [ MAXVALUE %s | NO MAXVALUE ]\n" - " [ START [ WITH ] %s ]\n" - " [ RESTART [ [ WITH ] %s ] ]\n" - " [ CACHE %s ] [ [ NO ] CYCLE ]\n" - " [ OWNED BY { %s.%s | NONE } ]\n" - "ALTER SEQUENCE [ IF EXISTS ] %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER SEQUENCE [ IF EXISTS ] %s RENAME TO %s\n" - "ALTER SEQUENCE [ IF EXISTS ] %s SET SCHEMA %s", - _("name"), - _("increment"), - _("minvalue"), - _("maxvalue"), - _("start"), - _("restart"), - _("cache"), - _("table_name"), - _("column_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_name"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_SERVER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER SERVER %s [ VERSION '%s' ]\n" - " [ OPTIONS ( [ ADD | SET | DROP ] %s ['%s'] [, ... ] ) ]\n" - "ALTER SERVER %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER SERVER %s RENAME TO %s", - _("name"), - _("new_version"), - _("option"), - _("value"), - _("name"), - _("new_owner"), - _("name"), - _("new_name")); -} - -void -sql_help_ALTER_SYSTEM(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER SYSTEM SET %s { TO | = } { %s | '%s' | DEFAULT }\n" - "\n" - "ALTER SYSTEM RESET %s\n" - "ALTER SYSTEM RESET ALL", - _("configuration_parameter"), - _("value"), - _("value"), - _("configuration_parameter")); -} - -void -sql_help_ALTER_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TABLE [ IF EXISTS ] [ ONLY ] %s [ * ]\n" - " %s [, ... ]\n" - "ALTER TABLE [ IF EXISTS ] [ ONLY ] %s [ * ]\n" - " RENAME [ COLUMN ] %s TO %s\n" - "ALTER TABLE [ IF EXISTS ] [ ONLY ] %s [ * ]\n" - " RENAME CONSTRAINT %s TO %s\n" - "ALTER TABLE [ IF EXISTS ] %s\n" - " RENAME TO %s\n" - "ALTER TABLE [ IF EXISTS ] %s\n" - " SET SCHEMA %s\n" - "ALTER TABLE ALL IN TABLESPACE %s [ OWNED BY %s [, ... ] ]\n" - " SET TABLESPACE %s [ NOWAIT ]\n" - "\n" - "%s\n" - "\n" - " ADD [ COLUMN ] %s %s [ COLLATE %s ] [ %s [ ... ] ]\n" - " DROP [ COLUMN ] [ IF EXISTS ] %s [ RESTRICT | CASCADE ]\n" - " ALTER [ COLUMN ] %s [ SET DATA ] TYPE %s [ COLLATE %s ] [ USING %s ]\n" - " ALTER [ COLUMN ] %s SET DEFAULT %s\n" - " ALTER [ COLUMN ] %s DROP DEFAULT\n" - " ALTER [ COLUMN ] %s { SET | DROP } NOT NULL\n" - " ALTER [ COLUMN ] %s SET STATISTICS %s\n" - " ALTER [ COLUMN ] %s SET ( %s = %s [, ... ] )\n" - " ALTER [ COLUMN ] %s RESET ( %s [, ... ] )\n" - " ALTER [ COLUMN ] %s SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }\n" - " ADD %s [ NOT VALID ]\n" - " ADD %s\n" - " ALTER CONSTRAINT %s [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]\n" - " VALIDATE CONSTRAINT %s\n" - " DROP CONSTRAINT [ IF EXISTS ] %s [ RESTRICT | CASCADE ]\n" - " DISABLE TRIGGER [ %s | ALL | USER ]\n" - " ENABLE TRIGGER [ %s | ALL | USER ]\n" - " ENABLE REPLICA TRIGGER %s\n" - " ENABLE ALWAYS TRIGGER %s\n" - " DISABLE RULE %s\n" - " ENABLE RULE %s\n" - " ENABLE REPLICA RULE %s\n" - " ENABLE ALWAYS RULE %s\n" - " DISABLE ROW LEVEL SECURITY\n" - " ENABLE ROW LEVEL SECURITY\n" - " FORCE ROW LEVEL SECURITY\n" - " NO FORCE ROW LEVEL SECURITY\n" - " CLUSTER ON %s\n" - " SET WITHOUT CLUSTER\n" - " SET WITH OIDS\n" - " SET WITHOUT OIDS\n" - " SET TABLESPACE %s\n" - " SET { LOGGED | UNLOGGED }\n" - " SET ( %s = %s [, ... ] )\n" - " RESET ( %s [, ... ] )\n" - " INHERIT %s\n" - " NO INHERIT %s\n" - " OF %s\n" - " NOT OF\n" - " OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - " REPLICA IDENTITY { DEFAULT | USING INDEX %s | FULL | NOTHING }\n" - "\n" - "%s\n" - "\n" - " [ CONSTRAINT %s ]\n" - " { UNIQUE | PRIMARY KEY } USING INDEX %s\n" - " [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]", - _("name"), - _("action"), - _("name"), - _("column_name"), - _("new_column_name"), - _("name"), - _("constraint_name"), - _("new_constraint_name"), - _("name"), - _("new_name"), - _("name"), - _("new_schema"), - _("name"), - _("role_name"), - _("new_tablespace"), - _("where action is one of:"), - _("column_name"), - _("data_type"), - _("collation"), - _("column_constraint"), - _("column_name"), - _("column_name"), - _("data_type"), - _("collation"), - _("expression"), - _("column_name"), - _("expression"), - _("column_name"), - _("column_name"), - _("column_name"), - _("integer"), - _("column_name"), - _("attribute_option"), - _("value"), - _("column_name"), - _("attribute_option"), - _("column_name"), - _("table_constraint"), - _("table_constraint_using_index"), - _("constraint_name"), - _("constraint_name"), - _("constraint_name"), - _("trigger_name"), - _("trigger_name"), - _("trigger_name"), - _("trigger_name"), - _("rewrite_rule_name"), - _("rewrite_rule_name"), - _("rewrite_rule_name"), - _("rewrite_rule_name"), - _("index_name"), - _("new_tablespace"), - _("storage_parameter"), - _("value"), - _("storage_parameter"), - _("parent_table"), - _("parent_table"), - _("type_name"), - _("new_owner"), - _("index_name"), - _("and table_constraint_using_index is:"), - _("constraint_name"), - _("index_name")); -} - -void -sql_help_ALTER_TABLESPACE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TABLESPACE %s RENAME TO %s\n" - "ALTER TABLESPACE %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER TABLESPACE %s SET ( %s = %s [, ... ] )\n" - "ALTER TABLESPACE %s RESET ( %s [, ... ] )", - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("tablespace_option"), - _("value"), - _("name"), - _("tablespace_option")); -} - -void -sql_help_ALTER_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TEXT SEARCH CONFIGURATION %s\n" - " ADD MAPPING FOR %s [, ... ] WITH %s [, ... ]\n" - "ALTER TEXT SEARCH CONFIGURATION %s\n" - " ALTER MAPPING FOR %s [, ... ] WITH %s [, ... ]\n" - "ALTER TEXT SEARCH CONFIGURATION %s\n" - " ALTER MAPPING REPLACE %s WITH %s\n" - "ALTER TEXT SEARCH CONFIGURATION %s\n" - " ALTER MAPPING FOR %s [, ... ] REPLACE %s WITH %s\n" - "ALTER TEXT SEARCH CONFIGURATION %s\n" - " DROP MAPPING [ IF EXISTS ] FOR %s [, ... ]\n" - "ALTER TEXT SEARCH CONFIGURATION %s RENAME TO %s\n" - "ALTER TEXT SEARCH CONFIGURATION %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER TEXT SEARCH CONFIGURATION %s SET SCHEMA %s", - _("name"), - _("token_type"), - _("dictionary_name"), - _("name"), - _("token_type"), - _("dictionary_name"), - _("name"), - _("old_dictionary"), - _("new_dictionary"), - _("name"), - _("token_type"), - _("old_dictionary"), - _("new_dictionary"), - _("name"), - _("token_type"), - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TEXT SEARCH DICTIONARY %s (\n" - " %s [ = %s ] [, ... ]\n" - ")\n" - "ALTER TEXT SEARCH DICTIONARY %s RENAME TO %s\n" - "ALTER TEXT SEARCH DICTIONARY %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER TEXT SEARCH DICTIONARY %s SET SCHEMA %s", - _("name"), - _("option"), - _("value"), - _("name"), - _("new_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_TEXT_SEARCH_PARSER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TEXT SEARCH PARSER %s RENAME TO %s\n" - "ALTER TEXT SEARCH PARSER %s SET SCHEMA %s", - _("name"), - _("new_name"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TEXT SEARCH TEMPLATE %s RENAME TO %s\n" - "ALTER TEXT SEARCH TEMPLATE %s SET SCHEMA %s", - _("name"), - _("new_name"), - _("name"), - _("new_schema")); -} - -void -sql_help_ALTER_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TRIGGER %s ON %s RENAME TO %s", - _("name"), - _("table_name"), - _("new_name")); -} - -void -sql_help_ALTER_TYPE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER TYPE %s %s [, ... ]\n" - "ALTER TYPE %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER TYPE %s RENAME ATTRIBUTE %s TO %s [ CASCADE | RESTRICT ]\n" - "ALTER TYPE %s RENAME TO %s\n" - "ALTER TYPE %s SET SCHEMA %s\n" - "ALTER TYPE %s ADD VALUE [ IF NOT EXISTS ] %s [ { BEFORE | AFTER } %s ]\n" - "\n" - "%s\n" - "\n" - " ADD ATTRIBUTE %s %s [ COLLATE %s ] [ CASCADE | RESTRICT ]\n" - " DROP ATTRIBUTE [ IF EXISTS ] %s [ CASCADE | RESTRICT ]\n" - " ALTER ATTRIBUTE %s [ SET DATA ] TYPE %s [ COLLATE %s ] [ CASCADE | RESTRICT ]", - _("name"), - _("action"), - _("name"), - _("new_owner"), - _("name"), - _("attribute_name"), - _("new_attribute_name"), - _("name"), - _("new_name"), - _("name"), - _("new_schema"), - _("name"), - _("new_enum_value"), - _("existing_enum_value"), - _("where action is one of:"), - _("attribute_name"), - _("data_type"), - _("collation"), - _("attribute_name"), - _("attribute_name"), - _("data_type"), - _("collation")); -} - -void -sql_help_ALTER_USER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER USER %s [ WITH ] %s [ ... ]\n" - "\n" - "%s\n" - "\n" - " SUPERUSER | NOSUPERUSER\n" - " | CREATEDB | NOCREATEDB\n" - " | CREATEROLE | NOCREATEROLE\n" - " | CREATEUSER | NOCREATEUSER\n" - " | INHERIT | NOINHERIT\n" - " | LOGIN | NOLOGIN\n" - " | REPLICATION | NOREPLICATION\n" - " | CONNECTION LIMIT %s\n" - " | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '%s'\n" - " | VALID UNTIL '%s'\n" - "\n" - "ALTER USER %s RENAME TO %s\n" - "\n" - "ALTER USER %s SET %s { TO | = } { %s | DEFAULT }\n" - "ALTER USER %s SET %s FROM CURRENT\n" - "ALTER USER %s RESET %s\n" - "ALTER USER %s RESET ALL\n" - "\n" - "%s\n" - "\n" - " [ GROUP ] %s\n" - " | CURRENT_USER\n" - " | SESSION_USER", - _("role_specification"), - _("option"), - _("where option can be:"), - _("connlimit"), - _("password"), - _("timestamp"), - _("name"), - _("new_name"), - _("role_specification"), - _("configuration_parameter"), - _("value"), - _("role_specification"), - _("configuration_parameter"), - _("role_specification"), - _("configuration_parameter"), - _("role_specification"), - _("where role_specification can be:"), - _("role_name")); -} - -void -sql_help_ALTER_USER_MAPPING(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER USER MAPPING FOR { %s | USER | CURRENT_USER | SESSION_USER | PUBLIC }\n" - " SERVER %s\n" - " OPTIONS ( [ ADD | SET | DROP ] %s ['%s'] [, ... ] )", - _("user_name"), - _("server_name"), - _("option"), - _("value")); -} - -void -sql_help_ALTER_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ALTER VIEW [ IF EXISTS ] %s ALTER [ COLUMN ] %s SET DEFAULT %s\n" - "ALTER VIEW [ IF EXISTS ] %s ALTER [ COLUMN ] %s DROP DEFAULT\n" - "ALTER VIEW [ IF EXISTS ] %s OWNER TO { %s | CURRENT_USER | SESSION_USER }\n" - "ALTER VIEW [ IF EXISTS ] %s RENAME TO %s\n" - "ALTER VIEW [ IF EXISTS ] %s SET SCHEMA %s\n" - "ALTER VIEW [ IF EXISTS ] %s SET ( %s [= %s] [, ... ] )\n" - "ALTER VIEW [ IF EXISTS ] %s RESET ( %s [, ... ] )", - _("name"), - _("column_name"), - _("expression"), - _("name"), - _("column_name"), - _("name"), - _("new_owner"), - _("name"), - _("new_name"), - _("name"), - _("new_schema"), - _("name"), - _("view_option_name"), - _("view_option_value"), - _("name"), - _("view_option_name")); -} - -void -sql_help_ANALYZE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ANALYZE [ VERBOSE ] [ %s [ ( %s [, ...] ) ] ]", - _("table_name"), - _("column_name")); -} - -void -sql_help_BEGIN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "BEGIN [ WORK | TRANSACTION ] [ %s [, ...] ]\n" - "\n" - "%s\n" - "\n" - " ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }\n" - " READ WRITE | READ ONLY\n" - " [ NOT ] DEFERRABLE", - _("transaction_mode"), - _("where transaction_mode is one of:")); -} - -void -sql_help_CHECKPOINT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CHECKPOINT"); -} - -void -sql_help_CLOSE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CLOSE { %s | ALL }", - _("name")); -} - -void -sql_help_CLUSTER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CLUSTER [VERBOSE] %s [ USING %s ]\n" - "CLUSTER [VERBOSE]", - _("table_name"), - _("index_name")); -} - -void -sql_help_COMMENT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "COMMENT ON\n" - "{\n" - " AGGREGATE %s ( %s ) |\n" - " CAST (%s AS %s) |\n" - " COLLATION %s |\n" - " COLUMN %s.%s |\n" - " CONSTRAINT %s ON %s |\n" - " CONSTRAINT %s ON DOMAIN %s |\n" - " CONVERSION %s |\n" - " DATABASE %s |\n" - " DOMAIN %s |\n" - " EXTENSION %s |\n" - " EVENT TRIGGER %s |\n" - " FOREIGN DATA WRAPPER %s |\n" - " FOREIGN TABLE %s |\n" - " FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] ) |\n" - " INDEX %s |\n" - " LARGE OBJECT %s |\n" - " MATERIALIZED VIEW %s |\n" - " OPERATOR %s (%s, %s) |\n" - " OPERATOR CLASS %s USING %s |\n" - " OPERATOR FAMILY %s USING %s |\n" - " POLICY %s ON %s |\n" - " [ PROCEDURAL ] LANGUAGE %s |\n" - " ROLE %s |\n" - " RULE %s ON %s |\n" - " SCHEMA %s |\n" - " SEQUENCE %s |\n" - " SERVER %s |\n" - " TABLE %s |\n" - " TABLESPACE %s |\n" - " TEXT SEARCH CONFIGURATION %s |\n" - " TEXT SEARCH DICTIONARY %s |\n" - " TEXT SEARCH PARSER %s |\n" - " TEXT SEARCH TEMPLATE %s |\n" - " TRANSFORM FOR %s LANGUAGE %s |\n" - " TRIGGER %s ON %s |\n" - " TYPE %s |\n" - " VIEW %s\n" - "} IS '%s'\n" - "\n" - "%s\n" - "\n" - "* |\n" - "[ %s ] [ %s ] %s [ , ... ] |\n" - "[ [ %s ] [ %s ] %s [ , ... ] ] ORDER BY [ %s ] [ %s ] %s [ , ... ]", - _("aggregate_name"), - _("aggregate_signature"), - _("source_type"), - _("target_type"), - _("object_name"), - _("relation_name"), - _("column_name"), - _("constraint_name"), - _("table_name"), - _("constraint_name"), - _("domain_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("function_name"), - _("argmode"), - _("argname"), - _("argtype"), - _("object_name"), - _("large_object_oid"), - _("object_name"), - _("operator_name"), - _("left_type"), - _("right_type"), - _("object_name"), - _("index_method"), - _("object_name"), - _("index_method"), - _("policy_name"), - _("table_name"), - _("object_name"), - _("object_name"), - _("rule_name"), - _("table_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("type_name"), - _("lang_name"), - _("trigger_name"), - _("table_name"), - _("object_name"), - _("object_name"), - _("text"), - _("where aggregate_signature is:"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_COMMIT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "COMMIT [ WORK | TRANSACTION ]"); -} - -void -sql_help_COMMIT_PREPARED(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "COMMIT PREPARED %s", - _("transaction_id")); -} - -void -sql_help_COPY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "COPY %s [ ( %s [, ...] ) ]\n" - " FROM { '%s' | PROGRAM '%s' | STDIN }\n" - " [ [ WITH ] ( %s [, ...] ) ]\n" - "\n" - "COPY { %s [ ( %s [, ...] ) ] | ( %s ) }\n" - " TO { '%s' | PROGRAM '%s' | STDOUT }\n" - " [ [ WITH ] ( %s [, ...] ) ]\n" - "\n" - "%s\n" - "\n" - " FORMAT %s\n" - " OIDS [ %s ]\n" - " FREEZE [ %s ]\n" - " DELIMITER '%s'\n" - " NULL '%s'\n" - " HEADER [ %s ]\n" - " QUOTE '%s'\n" - " ESCAPE '%s'\n" - " FORCE_QUOTE { ( %s [, ...] ) | * }\n" - " FORCE_NOT_NULL ( %s [, ...] )\n" - " FORCE_NULL ( %s [, ...] )\n" - " ENCODING '%s'", - _("table_name"), - _("column_name"), - _("filename"), - _("command"), - _("option"), - _("table_name"), - _("column_name"), - _("query"), - _("filename"), - _("command"), - _("option"), - _("where option can be one of:"), - _("format_name"), - _("boolean"), - _("boolean"), - _("delimiter_character"), - _("null_string"), - _("boolean"), - _("quote_character"), - _("escape_character"), - _("column_name"), - _("column_name"), - _("column_name"), - _("encoding_name")); -} - -void -sql_help_CREATE_AGGREGATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE AGGREGATE %s ( [ %s ] [ %s ] %s [ , ... ] ) (\n" - " SFUNC = %s,\n" - " STYPE = %s\n" - " [ , SSPACE = %s ]\n" - " [ , FINALFUNC = %s ]\n" - " [ , FINALFUNC_EXTRA ]\n" - " [ , INITCOND = %s ]\n" - " [ , MSFUNC = %s ]\n" - " [ , MINVFUNC = %s ]\n" - " [ , MSTYPE = %s ]\n" - " [ , MSSPACE = %s ]\n" - " [ , MFINALFUNC = %s ]\n" - " [ , MFINALFUNC_EXTRA ]\n" - " [ , MINITCOND = %s ]\n" - " [ , SORTOP = %s ]\n" - ")\n" - "\n" - "CREATE AGGREGATE %s ( [ [ %s ] [ %s ] %s [ , ... ] ]\n" - " ORDER BY [ %s ] [ %s ] %s [ , ... ] ) (\n" - " SFUNC = %s,\n" - " STYPE = %s\n" - " [ , SSPACE = %s ]\n" - " [ , FINALFUNC = %s ]\n" - " [ , FINALFUNC_EXTRA ]\n" - " [ , INITCOND = %s ]\n" - " [ , HYPOTHETICAL ]\n" - ")\n" - "\n" - "%s\n" - "\n" - "CREATE AGGREGATE %s (\n" - " BASETYPE = %s,\n" - " SFUNC = %s,\n" - " STYPE = %s\n" - " [ , SSPACE = %s ]\n" - " [ , FINALFUNC = %s ]\n" - " [ , FINALFUNC_EXTRA ]\n" - " [ , INITCOND = %s ]\n" - " [ , MSFUNC = %s ]\n" - " [ , MINVFUNC = %s ]\n" - " [ , MSTYPE = %s ]\n" - " [ , MSSPACE = %s ]\n" - " [ , MFINALFUNC = %s ]\n" - " [ , MFINALFUNC_EXTRA ]\n" - " [ , MINITCOND = %s ]\n" - " [ , SORTOP = %s ]\n" - ")", - _("name"), - _("argmode"), - _("argname"), - _("arg_data_type"), - _("sfunc"), - _("state_data_type"), - _("state_data_size"), - _("ffunc"), - _("initial_condition"), - _("msfunc"), - _("minvfunc"), - _("mstate_data_type"), - _("mstate_data_size"), - _("mffunc"), - _("minitial_condition"), - _("sort_operator"), - _("name"), - _("argmode"), - _("argname"), - _("arg_data_type"), - _("argmode"), - _("argname"), - _("arg_data_type"), - _("sfunc"), - _("state_data_type"), - _("state_data_size"), - _("ffunc"), - _("initial_condition"), - _("or the old syntax"), - _("name"), - _("base_type"), - _("sfunc"), - _("state_data_type"), - _("state_data_size"), - _("ffunc"), - _("initial_condition"), - _("msfunc"), - _("minvfunc"), - _("mstate_data_type"), - _("mstate_data_size"), - _("mffunc"), - _("minitial_condition"), - _("sort_operator")); -} - -void -sql_help_CREATE_CAST(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE CAST (%s AS %s)\n" - " WITH FUNCTION %s (%s [, ...])\n" - " [ AS ASSIGNMENT | AS IMPLICIT ]\n" - "\n" - "CREATE CAST (%s AS %s)\n" - " WITHOUT FUNCTION\n" - " [ AS ASSIGNMENT | AS IMPLICIT ]\n" - "\n" - "CREATE CAST (%s AS %s)\n" - " WITH INOUT\n" - " [ AS ASSIGNMENT | AS IMPLICIT ]", - _("source_type"), - _("target_type"), - _("function_name"), - _("argument_type"), - _("source_type"), - _("target_type"), - _("source_type"), - _("target_type")); -} - -void -sql_help_CREATE_COLLATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE COLLATION %s (\n" - " [ LOCALE = %s, ]\n" - " [ LC_COLLATE = %s, ]\n" - " [ LC_CTYPE = %s ]\n" - ")\n" - "CREATE COLLATION %s FROM %s", - _("name"), - _("locale"), - _("lc_collate"), - _("lc_ctype"), - _("name"), - _("existing_collation")); -} - -void -sql_help_CREATE_CONVERSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ DEFAULT ] CONVERSION %s\n" - " FOR %s TO %s FROM %s", - _("name"), - _("source_encoding"), - _("dest_encoding"), - _("function_name")); -} - -void -sql_help_CREATE_DATABASE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE DATABASE %s\n" - " [ [ WITH ] [ OWNER [=] %s ]\n" - " [ TEMPLATE [=] %s ]\n" - " [ ENCODING [=] %s ]\n" - " [ LC_COLLATE [=] %s ]\n" - " [ LC_CTYPE [=] %s ]\n" - " [ TABLESPACE [=] %s ]\n" - " [ ALLOW_CONNECTIONS [=] %s ]\n" - " [ CONNECTION LIMIT [=] %s ] ]\n" - " [ IS_TEMPLATE [=] %s ]", - _("name"), - _("user_name"), - _("template"), - _("encoding"), - _("lc_collate"), - _("lc_ctype"), - _("tablespace_name"), - _("allowconn"), - _("connlimit"), - _("istemplate")); -} - -void -sql_help_CREATE_DOMAIN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE DOMAIN %s [ AS ] %s\n" - " [ COLLATE %s ]\n" - " [ DEFAULT %s ]\n" - " [ %s [ ... ] ]\n" - "\n" - "%s\n" - "\n" - "[ CONSTRAINT %s ]\n" - "{ NOT NULL | NULL | CHECK (%s) }", - _("name"), - _("data_type"), - _("collation"), - _("expression"), - _("constraint"), - _("where constraint is:"), - _("constraint_name"), - _("expression")); -} - -void -sql_help_CREATE_EVENT_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE EVENT TRIGGER %s\n" - " ON %s\n" - " [ WHEN %s IN (filter_value [, ... ]) [ AND ... ] ]\n" - " EXECUTE PROCEDURE %s()", - _("name"), - _("event"), - _("filter_variable"), - _("function_name")); -} - -void -sql_help_CREATE_EXTENSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE EXTENSION [ IF NOT EXISTS ] %s\n" - " [ WITH ] [ SCHEMA %s ]\n" - " [ VERSION %s ]\n" - " [ FROM %s ]", - _("extension_name"), - _("schema_name"), - _("version"), - _("old_version")); -} - -void -sql_help_CREATE_FOREIGN_DATA_WRAPPER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE FOREIGN DATA WRAPPER %s\n" - " [ HANDLER %s | NO HANDLER ]\n" - " [ VALIDATOR %s | NO VALIDATOR ]\n" - " [ OPTIONS ( %s '%s' [, ... ] ) ]", - _("name"), - _("handler_function"), - _("validator_function"), - _("option"), - _("value")); -} - -void -sql_help_CREATE_FOREIGN_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE FOREIGN TABLE [ IF NOT EXISTS ] %s ( [\n" - " { %s %s [ OPTIONS ( %s '%s' [, ... ] ) ] [ COLLATE %s ] [ %s [ ... ] ]\n" - " | %s }\n" - " [, ... ]\n" - "] )\n" - "[ INHERITS ( %s [, ... ] ) ]\n" - " SERVER %s\n" - "[ OPTIONS ( %s '%s' [, ... ] ) ]\n" - "\n" - "%s\n" - "\n" - "[ CONSTRAINT %s ]\n" - "{ NOT NULL |\n" - " NULL |\n" - " CHECK ( %s ) [ NO INHERIT ] |\n" - " DEFAULT %s }\n" - "\n" - "%s\n" - "\n" - "[ CONSTRAINT %s ]\n" - "CHECK ( %s ) [ NO INHERIT ]", - _("table_name"), - _("column_name"), - _("data_type"), - _("option"), - _("value"), - _("collation"), - _("column_constraint"), - _("table_constraint"), - _("parent_table"), - _("server_name"), - _("option"), - _("value"), - _("where column_constraint is:"), - _("constraint_name"), - _("expression"), - _("default_expr"), - _("and table_constraint is:"), - _("constraint_name"), - _("expression")); -} - -void -sql_help_CREATE_FUNCTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ OR REPLACE ] FUNCTION\n" - " %s ( [ [ %s ] [ %s ] %s [ { DEFAULT | = } %s ] [, ...] ] )\n" - " [ RETURNS %s\n" - " | RETURNS TABLE ( %s %s [, ...] ) ]\n" - " { LANGUAGE %s\n" - " | TRANSFORM { FOR TYPE %s } [, ... ]\n" - " | WINDOW\n" - " | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF\n" - " | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT\n" - " | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER\n" - " | COST %s\n" - " | ROWS %s\n" - " | SET %s { TO %s | = %s | FROM CURRENT }\n" - " | AS '%s'\n" - " | AS '%s', '%s'\n" - " } ...\n" - " [ WITH ( %s [, ...] ) ]", - _("name"), - _("argmode"), - _("argname"), - _("argtype"), - _("default_expr"), - _("rettype"), - _("column_name"), - _("column_type"), - _("lang_name"), - _("type_name"), - _("execution_cost"), - _("result_rows"), - _("configuration_parameter"), - _("value"), - _("value"), - _("definition"), - _("obj_file"), - _("link_symbol"), - _("attribute")); -} - -void -sql_help_CREATE_GROUP(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE GROUP %s [ [ WITH ] %s [ ... ] ]\n" - "\n" - "%s\n" - "\n" - " SUPERUSER | NOSUPERUSER\n" - " | CREATEDB | NOCREATEDB\n" - " | CREATEROLE | NOCREATEROLE\n" - " | CREATEUSER | NOCREATEUSER\n" - " | INHERIT | NOINHERIT\n" - " | LOGIN | NOLOGIN\n" - " | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '%s'\n" - " | VALID UNTIL '%s'\n" - " | IN ROLE %s [, ...]\n" - " | IN GROUP %s [, ...]\n" - " | ROLE %s [, ...]\n" - " | ADMIN %s [, ...]\n" - " | USER %s [, ...]\n" - " | SYSID %s", - _("name"), - _("option"), - _("where option can be:"), - _("password"), - _("timestamp"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("uid")); -} - -void -sql_help_CREATE_INDEX(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] %s ] ON %s [ USING %s ]\n" - " ( { %s | ( %s ) } [ COLLATE %s ] [ %s ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )\n" - " [ WITH ( %s = %s [, ... ] ) ]\n" - " [ TABLESPACE %s ]\n" - " [ WHERE %s ]", - _("name"), - _("table_name"), - _("method"), - _("column_name"), - _("expression"), - _("collation"), - _("opclass"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("predicate")); -} - -void -sql_help_CREATE_LANGUAGE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ OR REPLACE ] [ PROCEDURAL ] LANGUAGE %s\n" - "CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE %s\n" - " HANDLER %s [ INLINE %s ] [ VALIDATOR %s ]", - _("name"), - _("name"), - _("call_handler"), - _("inline_handler"), - _("valfunction")); -} - -void -sql_help_CREATE_MATERIALIZED_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] %s\n" - " [ (%s [, ...] ) ]\n" - " [ WITH ( %s [= %s] [, ... ] ) ]\n" - " [ TABLESPACE %s ]\n" - " AS %s\n" - " [ WITH [ NO ] DATA ]", - _("table_name"), - _("column_name"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("query")); -} - -void -sql_help_CREATE_OPERATOR(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE OPERATOR %s (\n" - " PROCEDURE = %s\n" - " [, LEFTARG = %s ] [, RIGHTARG = %s ]\n" - " [, COMMUTATOR = %s ] [, NEGATOR = %s ]\n" - " [, RESTRICT = %s ] [, JOIN = %s ]\n" - " [, HASHES ] [, MERGES ]\n" - ")", - _("name"), - _("function_name"), - _("left_type"), - _("right_type"), - _("com_op"), - _("neg_op"), - _("res_proc"), - _("join_proc")); -} - -void -sql_help_CREATE_OPERATOR_CLASS(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE OPERATOR CLASS %s [ DEFAULT ] FOR TYPE %s\n" - " USING %s [ FAMILY %s ] AS\n" - " { OPERATOR %s %s [ ( %s, %s ) ] [ FOR SEARCH | FOR ORDER BY %s ]\n" - " | FUNCTION %s [ ( %s [ , %s ] ) ] %s ( %s [, ...] )\n" - " | STORAGE %s\n" - " } [, ... ]", - _("name"), - _("data_type"), - _("index_method"), - _("family_name"), - _("strategy_number"), - _("operator_name"), - _("op_type"), - _("op_type"), - _("sort_family_name"), - _("support_number"), - _("op_type"), - _("op_type"), - _("function_name"), - _("argument_type"), - _("storage_type")); -} - -void -sql_help_CREATE_OPERATOR_FAMILY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE OPERATOR FAMILY %s USING %s", - _("name"), - _("index_method")); -} - -void -sql_help_CREATE_POLICY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE POLICY %s ON %s\n" - " [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]\n" - " [ TO { %s | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]\n" - " [ USING ( %s ) ]\n" - " [ WITH CHECK ( %s ) ]", - _("name"), - _("table_name"), - _("role_name"), - _("using_expression"), - _("check_expression")); -} - -void -sql_help_CREATE_ROLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE ROLE %s [ [ WITH ] %s [ ... ] ]\n" - "\n" - "%s\n" - "\n" - " SUPERUSER | NOSUPERUSER\n" - " | CREATEDB | NOCREATEDB\n" - " | CREATEROLE | NOCREATEROLE\n" - " | CREATEUSER | NOCREATEUSER\n" - " | INHERIT | NOINHERIT\n" - " | LOGIN | NOLOGIN\n" - " | REPLICATION | NOREPLICATION\n" - " | BYPASSRLS | NOBYPASSRLS\n" - " | CONNECTION LIMIT %s\n" - " | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '%s'\n" - " | VALID UNTIL '%s'\n" - " | IN ROLE %s [, ...]\n" - " | IN GROUP %s [, ...]\n" - " | ROLE %s [, ...]\n" - " | ADMIN %s [, ...]\n" - " | USER %s [, ...]\n" - " | SYSID %s", - _("name"), - _("option"), - _("where option can be:"), - _("connlimit"), - _("password"), - _("timestamp"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("uid")); -} - -void -sql_help_CREATE_RULE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ OR REPLACE ] RULE %s AS ON %s\n" - " TO %s [ WHERE %s ]\n" - " DO [ ALSO | INSTEAD ] { NOTHING | %s | ( %s ; %s ... ) }\n" - "\n" - "%s\n" - "\n" - " SELECT | INSERT | UPDATE | DELETE", - _("name"), - _("event"), - _("table_name"), - _("condition"), - _("command"), - _("command"), - _("command"), - _("where event can be one of:")); -} - -void -sql_help_CREATE_SCHEMA(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE SCHEMA %s [ AUTHORIZATION %s ] [ %s [ ... ] ]\n" - "CREATE SCHEMA AUTHORIZATION %s [ %s [ ... ] ]\n" - "CREATE SCHEMA IF NOT EXISTS %s [ AUTHORIZATION %s ]\n" - "CREATE SCHEMA IF NOT EXISTS AUTHORIZATION %s\n" - "\n" - "%s\n" - "\n" - " [ GROUP ] %s\n" - " | CURRENT_USER\n" - " | SESSION_USER", - _("schema_name"), - _("role_specification"), - _("schema_element"), - _("role_specification"), - _("schema_element"), - _("schema_name"), - _("role_specification"), - _("role_specification"), - _("where role_specification can be:"), - _("user_name")); -} - -void -sql_help_CREATE_SEQUENCE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] %s [ INCREMENT [ BY ] %s ]\n" - " [ MINVALUE %s | NO MINVALUE ] [ MAXVALUE %s | NO MAXVALUE ]\n" - " [ START [ WITH ] %s ] [ CACHE %s ] [ [ NO ] CYCLE ]\n" - " [ OWNED BY { %s.%s | NONE } ]", - _("name"), - _("increment"), - _("minvalue"), - _("maxvalue"), - _("start"), - _("cache"), - _("table_name"), - _("column_name")); -} - -void -sql_help_CREATE_SERVER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE SERVER %s [ TYPE '%s' ] [ VERSION '%s' ]\n" - " FOREIGN DATA WRAPPER %s\n" - " [ OPTIONS ( %s '%s' [, ... ] ) ]", - _("server_name"), - _("server_type"), - _("server_version"), - _("fdw_name"), - _("option"), - _("value")); -} - -void -sql_help_CREATE_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] %s ( [\n" - " { %s %s [ COLLATE %s ] [ %s [ ... ] ]\n" - " | %s\n" - " | LIKE %s [ %s ... ] }\n" - " [, ... ]\n" - "] )\n" - "[ INHERITS ( %s [, ... ] ) ]\n" - "[ WITH ( %s [= %s] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]\n" - "[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]\n" - "[ TABLESPACE %s ]\n" - "\n" - "CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] %s\n" - " OF %s [ (\n" - " { %s WITH OPTIONS [ %s [ ... ] ]\n" - " | %s }\n" - " [, ... ]\n" - ") ]\n" - "[ WITH ( %s [= %s] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]\n" - "[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]\n" - "[ TABLESPACE %s ]\n" - "\n" - "%s\n" - "\n" - "[ CONSTRAINT %s ]\n" - "{ NOT NULL |\n" - " NULL |\n" - " CHECK ( %s ) [ NO INHERIT ] |\n" - " DEFAULT %s |\n" - " UNIQUE %s |\n" - " PRIMARY KEY %s |\n" - " REFERENCES %s [ ( %s ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]\n" - " [ ON DELETE %s ] [ ON UPDATE %s ] }\n" - "[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]\n" - "\n" - "%s\n" - "\n" - "[ CONSTRAINT %s ]\n" - "{ CHECK ( %s ) [ NO INHERIT ] |\n" - " UNIQUE ( %s [, ... ] ) %s |\n" - " PRIMARY KEY ( %s [, ... ] ) %s |\n" - " EXCLUDE [ USING %s ] ( %s WITH %s [, ... ] ) %s [ WHERE ( %s ) ] |\n" - " FOREIGN KEY ( %s [, ... ] ) REFERENCES %s [ ( %s [, ... ] ) ]\n" - " [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE %s ] [ ON UPDATE %s ] }\n" - "[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]\n" - "\n" - "%s\n" - "\n" - "{ INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL }\n" - "\n" - "%s\n" - "\n" - "[ WITH ( %s [= %s] [, ... ] ) ]\n" - "[ USING INDEX TABLESPACE %s ]\n" - "\n" - "%s\n" - "\n" - "{ %s | ( %s ) } [ %s ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]", - _("table_name"), - _("column_name"), - _("data_type"), - _("collation"), - _("column_constraint"), - _("table_constraint"), - _("source_table"), - _("like_option"), - _("parent_table"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("table_name"), - _("type_name"), - _("column_name"), - _("column_constraint"), - _("table_constraint"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("where column_constraint is:"), - _("constraint_name"), - _("expression"), - _("default_expr"), - _("index_parameters"), - _("index_parameters"), - _("reftable"), - _("refcolumn"), - _("action"), - _("action"), - _("and table_constraint is:"), - _("constraint_name"), - _("expression"), - _("column_name"), - _("index_parameters"), - _("column_name"), - _("index_parameters"), - _("index_method"), - _("exclude_element"), - _("operator"), - _("index_parameters"), - _("predicate"), - _("column_name"), - _("reftable"), - _("refcolumn"), - _("action"), - _("action"), - _("and like_option is:"), - _("index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are:"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("exclude_element in an EXCLUDE constraint is:"), - _("column_name"), - _("expression"), - _("opclass")); -} - -void -sql_help_CREATE_TABLE_AS(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] %s\n" - " [ (%s [, ...] ) ]\n" - " [ WITH ( %s [= %s] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]\n" - " [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]\n" - " [ TABLESPACE %s ]\n" - " AS %s\n" - " [ WITH [ NO ] DATA ]", - _("table_name"), - _("column_name"), - _("storage_parameter"), - _("value"), - _("tablespace_name"), - _("query")); -} - -void -sql_help_CREATE_TABLESPACE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TABLESPACE %s\n" - " [ OWNER { %s | CURRENT_USER | SESSION_USER } ]\n" - " LOCATION '%s'\n" - " [ WITH ( %s = %s [, ... ] ) ]", - _("tablespace_name"), - _("new_owner"), - _("directory"), - _("tablespace_option"), - _("value")); -} - -void -sql_help_CREATE_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TEXT SEARCH CONFIGURATION %s (\n" - " PARSER = %s |\n" - " COPY = %s\n" - ")", - _("name"), - _("parser_name"), - _("source_config")); -} - -void -sql_help_CREATE_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TEXT SEARCH DICTIONARY %s (\n" - " TEMPLATE = %s\n" - " [, %s = %s [, ... ]]\n" - ")", - _("name"), - _("template"), - _("option"), - _("value")); -} - -void -sql_help_CREATE_TEXT_SEARCH_PARSER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TEXT SEARCH PARSER %s (\n" - " START = %s ,\n" - " GETTOKEN = %s ,\n" - " END = %s ,\n" - " LEXTYPES = %s\n" - " [, HEADLINE = %s ]\n" - ")", - _("name"), - _("start_function"), - _("gettoken_function"), - _("end_function"), - _("lextypes_function"), - _("headline_function")); -} - -void -sql_help_CREATE_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TEXT SEARCH TEMPLATE %s (\n" - " [ INIT = %s , ]\n" - " LEXIZE = %s\n" - ")", - _("name"), - _("init_function"), - _("lexize_function")); -} - -void -sql_help_CREATE_TRANSFORM(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ OR REPLACE ] TRANSFORM FOR %s LANGUAGE %s (\n" - " FROM SQL WITH FUNCTION %s (%s [, ...]),\n" - " TO SQL WITH FUNCTION %s (%s [, ...])\n" - ");", - _("type_name"), - _("lang_name"), - _("from_sql_function_name"), - _("argument_type"), - _("to_sql_function_name"), - _("argument_type")); -} - -void -sql_help_CREATE_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ CONSTRAINT ] TRIGGER %s { BEFORE | AFTER | INSTEAD OF } { %s [ OR ... ] }\n" - " ON %s\n" - " [ FROM %s ]\n" - " [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n" - " [ FOR [ EACH ] { ROW | STATEMENT } ]\n" - " [ WHEN ( %s ) ]\n" - " EXECUTE PROCEDURE %s ( %s )\n" - "\n" - "%s\n" - "\n" - " INSERT\n" - " UPDATE [ OF %s [, ... ] ]\n" - " DELETE\n" - " TRUNCATE", - _("name"), - _("event"), - _("table_name"), - _("referenced_table_name"), - _("condition"), - _("function_name"), - _("arguments"), - _("where event can be one of:"), - _("column_name")); -} - -void -sql_help_CREATE_TYPE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE TYPE %s AS\n" - " ( [ %s %s [ COLLATE %s ] [, ... ] ] )\n" - "\n" - "CREATE TYPE %s AS ENUM\n" - " ( [ '%s' [, ... ] ] )\n" - "\n" - "CREATE TYPE %s AS RANGE (\n" - " SUBTYPE = %s\n" - " [ , SUBTYPE_OPCLASS = %s ]\n" - " [ , COLLATION = %s ]\n" - " [ , CANONICAL = %s ]\n" - " [ , SUBTYPE_DIFF = %s ]\n" - ")\n" - "\n" - "CREATE TYPE %s (\n" - " INPUT = %s,\n" - " OUTPUT = %s\n" - " [ , RECEIVE = %s ]\n" - " [ , SEND = %s ]\n" - " [ , TYPMOD_IN = %s ]\n" - " [ , TYPMOD_OUT = %s ]\n" - " [ , ANALYZE = %s ]\n" - " [ , INTERNALLENGTH = { %s | VARIABLE } ]\n" - " [ , PASSEDBYVALUE ]\n" - " [ , ALIGNMENT = %s ]\n" - " [ , STORAGE = %s ]\n" - " [ , LIKE = %s ]\n" - " [ , CATEGORY = %s ]\n" - " [ , PREFERRED = %s ]\n" - " [ , DEFAULT = %s ]\n" - " [ , ELEMENT = %s ]\n" - " [ , DELIMITER = %s ]\n" - " [ , COLLATABLE = %s ]\n" - ")\n" - "\n" - "CREATE TYPE %s", - _("name"), - _("attribute_name"), - _("data_type"), - _("collation"), - _("name"), - _("label"), - _("name"), - _("subtype"), - _("subtype_operator_class"), - _("collation"), - _("canonical_function"), - _("subtype_diff_function"), - _("name"), - _("input_function"), - _("output_function"), - _("receive_function"), - _("send_function"), - _("type_modifier_input_function"), - _("type_modifier_output_function"), - _("analyze_function"), - _("internallength"), - _("alignment"), - _("storage"), - _("like_type"), - _("category"), - _("preferred"), - _("default"), - _("element"), - _("delimiter"), - _("collatable"), - _("name")); -} - -void -sql_help_CREATE_USER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE USER %s [ [ WITH ] %s [ ... ] ]\n" - "\n" - "%s\n" - "\n" - " SUPERUSER | NOSUPERUSER\n" - " | CREATEDB | NOCREATEDB\n" - " | CREATEROLE | NOCREATEROLE\n" - " | CREATEUSER | NOCREATEUSER\n" - " | INHERIT | NOINHERIT\n" - " | LOGIN | NOLOGIN\n" - " | REPLICATION | NOREPLICATION\n" - " | CONNECTION LIMIT %s\n" - " | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '%s'\n" - " | VALID UNTIL '%s'\n" - " | IN ROLE %s [, ...]\n" - " | IN GROUP %s [, ...]\n" - " | ROLE %s [, ...]\n" - " | ADMIN %s [, ...]\n" - " | USER %s [, ...]\n" - " | SYSID %s", - _("name"), - _("option"), - _("where option can be:"), - _("connlimit"), - _("password"), - _("timestamp"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("role_name"), - _("uid")); -} - -void -sql_help_CREATE_USER_MAPPING(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE USER MAPPING FOR { %s | USER | CURRENT_USER | PUBLIC }\n" - " SERVER %s\n" - " [ OPTIONS ( %s '%s' [ , ... ] ) ]", - _("user_name"), - _("server_name"), - _("option"), - _("value")); -} - -void -sql_help_CREATE_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] VIEW %s [ ( %s [, ...] ) ]\n" - " [ WITH ( %s [= %s] [, ... ] ) ]\n" - " AS %s\n" - " [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]", - _("name"), - _("column_name"), - _("view_option_name"), - _("view_option_value"), - _("query")); -} - -void -sql_help_DEALLOCATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DEALLOCATE [ PREPARE ] { %s | ALL }", - _("name")); -} - -void -sql_help_DECLARE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DECLARE %s [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]\n" - " CURSOR [ { WITH | WITHOUT } HOLD ] FOR %s", - _("name"), - _("query")); -} - -void -sql_help_DELETE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "DELETE FROM [ ONLY ] %s [ * ] [ [ AS ] %s ]\n" - " [ USING %s ]\n" - " [ WHERE %s | WHERE CURRENT OF %s ]\n" - " [ RETURNING * | %s [ [ AS ] %s ] [, ...] ]", - _("with_query"), - _("table_name"), - _("alias"), - _("using_list"), - _("condition"), - _("cursor_name"), - _("output_expression"), - _("output_name")); -} - -void -sql_help_DISCARD(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }"); -} - -void -sql_help_DO(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DO [ LANGUAGE %s ] %s", - _("lang_name"), - _("code")); -} - -void -sql_help_DROP_AGGREGATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP AGGREGATE [ IF EXISTS ] %s ( %s ) [ CASCADE | RESTRICT ]\n" - "\n" - "%s\n" - "\n" - "* |\n" - "[ %s ] [ %s ] %s [ , ... ] |\n" - "[ [ %s ] [ %s ] %s [ , ... ] ] ORDER BY [ %s ] [ %s ] %s [ , ... ]", - _("name"), - _("aggregate_signature"), - _("where aggregate_signature is:"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_DROP_CAST(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP CAST [ IF EXISTS ] (%s AS %s) [ CASCADE | RESTRICT ]", - _("source_type"), - _("target_type")); -} - -void -sql_help_DROP_COLLATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP COLLATION [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_CONVERSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP CONVERSION [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_DATABASE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP DATABASE [ IF EXISTS ] %s", - _("name")); -} - -void -sql_help_DROP_DOMAIN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP DOMAIN [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_EVENT_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP EVENT TRIGGER [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_EXTENSION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP EXTENSION [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_FOREIGN_DATA_WRAPPER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP FOREIGN DATA WRAPPER [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_FOREIGN_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP FOREIGN TABLE [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_FUNCTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP FUNCTION [ IF EXISTS ] %s ( [ [ %s ] [ %s ] %s [, ...] ] )\n" - " [ CASCADE | RESTRICT ]", - _("name"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_DROP_GROUP(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP GROUP [ IF EXISTS ] %s [, ...]", - _("name")); -} - -void -sql_help_DROP_INDEX(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_LANGUAGE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP [ PROCEDURAL ] LANGUAGE [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_MATERIALIZED_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP MATERIALIZED VIEW [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_OPERATOR(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP OPERATOR [ IF EXISTS ] %s ( { %s | NONE } , { %s | NONE } ) [ CASCADE | RESTRICT ]", - _("name"), - _("left_type"), - _("right_type")); -} - -void -sql_help_DROP_OPERATOR_CLASS(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP OPERATOR CLASS [ IF EXISTS ] %s USING %s [ CASCADE | RESTRICT ]", - _("name"), - _("index_method")); -} - -void -sql_help_DROP_OPERATOR_FAMILY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP OPERATOR FAMILY [ IF EXISTS ] %s USING %s [ CASCADE | RESTRICT ]", - _("name"), - _("index_method")); -} - -void -sql_help_DROP_OWNED(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP OWNED BY { %s | CURRENT_USER | SESSION_USER } [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_POLICY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP POLICY [ IF EXISTS ] %s ON %s", - _("name"), - _("table_name")); -} - -void -sql_help_DROP_ROLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP ROLE [ IF EXISTS ] %s [, ...]", - _("name")); -} - -void -sql_help_DROP_RULE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP RULE [ IF EXISTS ] %s ON %s [ CASCADE | RESTRICT ]", - _("name"), - _("table_name")); -} - -void -sql_help_DROP_SCHEMA(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP SCHEMA [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_SEQUENCE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP SEQUENCE [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_SERVER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP SERVER [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TABLE [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TABLESPACE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TABLESPACE [ IF EXISTS ] %s", - _("name")); -} - -void -sql_help_DROP_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TEXT SEARCH CONFIGURATION [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TEXT SEARCH DICTIONARY [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TEXT_SEARCH_PARSER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TEXT SEARCH PARSER [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TEXT SEARCH TEMPLATE [ IF EXISTS ] %s [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_TRANSFORM(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TRANSFORM [ IF EXISTS ] FOR %s LANGUAGE %s", - _("type_name"), - _("lang_name")); -} - -void -sql_help_DROP_TRIGGER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TRIGGER [ IF EXISTS ] %s ON %s [ CASCADE | RESTRICT ]", - _("name"), - _("table_name")); -} - -void -sql_help_DROP_TYPE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP TYPE [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_DROP_USER(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP USER [ IF EXISTS ] %s [, ...]", - _("name")); -} - -void -sql_help_DROP_USER_MAPPING(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP USER MAPPING [ IF EXISTS ] FOR { %s | USER | CURRENT_USER | PUBLIC } SERVER %s", - _("user_name"), - _("server_name")); -} - -void -sql_help_DROP_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "DROP VIEW [ IF EXISTS ] %s [, ...] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_END(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "END [ WORK | TRANSACTION ]"); -} - -void -sql_help_EXECUTE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "EXECUTE %s [ ( %s [, ...] ) ]", - _("name"), - _("parameter")); -} - -void -sql_help_EXPLAIN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "EXPLAIN [ ( %s [, ...] ) ] %s\n" - "EXPLAIN [ ANALYZE ] [ VERBOSE ] %s\n" - "\n" - "%s\n" - "\n" - " ANALYZE [ %s ]\n" - " VERBOSE [ %s ]\n" - " COSTS [ %s ]\n" - " BUFFERS [ %s ]\n" - " TIMING [ %s ]\n" - " FORMAT { TEXT | XML | JSON | YAML }", - _("option"), - _("statement"), - _("statement"), - _("where option can be one of:"), - _("boolean"), - _("boolean"), - _("boolean"), - _("boolean"), - _("boolean")); -} - -void -sql_help_FETCH(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "FETCH [ %s [ FROM | IN ] ] %s\n" - "\n" - "%s\n" - "\n" - " NEXT\n" - " PRIOR\n" - " FIRST\n" - " LAST\n" - " ABSOLUTE %s\n" - " RELATIVE %s\n" - " %s\n" - " ALL\n" - " FORWARD\n" - " FORWARD %s\n" - " FORWARD ALL\n" - " BACKWARD\n" - " BACKWARD %s\n" - " BACKWARD ALL", - _("direction"), - _("cursor_name"), - _("where direction can be empty or one of:"), - _("count"), - _("count"), - _("count"), - _("count"), - _("count")); -} - -void -sql_help_GRANT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON { [ TABLE ] %s [, ...]\n" - " | ALL TABLES IN SCHEMA %s [, ...] }\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( %s [, ...] )\n" - " [, ...] | ALL [ PRIVILEGES ] ( %s [, ...] ) }\n" - " ON [ TABLE ] %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { USAGE | SELECT | UPDATE }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON { SEQUENCE %s [, ...]\n" - " | ALL SEQUENCES IN SCHEMA %s [, ...] }\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON DATABASE %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON DOMAIN %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON FOREIGN DATA WRAPPER %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON FOREIGN SERVER %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { EXECUTE | ALL [ PRIVILEGES ] }\n" - " ON { FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] ) [, ...]\n" - " | ALL FUNCTIONS IN SCHEMA %s [, ...] }\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON LANGUAGE %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON LARGE OBJECT %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON SCHEMA %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { CREATE | ALL [ PRIVILEGES ] }\n" - " ON TABLESPACE %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "GRANT { USAGE | ALL [ PRIVILEGES ] }\n" - " ON TYPE %s [, ...]\n" - " TO %s [, ...] [ WITH GRANT OPTION ]\n" - "\n" - "%s\n" - "\n" - " [ GROUP ] %s\n" - " | PUBLIC\n" - " | CURRENT_USER\n" - " | SESSION_USER\n" - "\n" - "GRANT %s [, ...] TO %s [, ...] [ WITH ADMIN OPTION ]", - _("table_name"), - _("schema_name"), - _("role_specification"), - _("column_name"), - _("column_name"), - _("table_name"), - _("role_specification"), - _("sequence_name"), - _("schema_name"), - _("role_specification"), - _("database_name"), - _("role_specification"), - _("domain_name"), - _("role_specification"), - _("fdw_name"), - _("role_specification"), - _("server_name"), - _("role_specification"), - _("function_name"), - _("argmode"), - _("arg_name"), - _("arg_type"), - _("schema_name"), - _("role_specification"), - _("lang_name"), - _("role_specification"), - _("loid"), - _("role_specification"), - _("schema_name"), - _("role_specification"), - _("tablespace_name"), - _("role_specification"), - _("type_name"), - _("role_specification"), - _("where role_specification can be:"), - _("role_name"), - _("role_name"), - _("role_name")); -} - -void -sql_help_IMPORT_FOREIGN_SCHEMA(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "IMPORT FOREIGN SCHEMA %s\n" - " [ { LIMIT TO | EXCEPT } ( %s [, ...] ) ]\n" - " FROM SERVER %s\n" - " INTO %s\n" - " [ OPTIONS ( %s '%s' [, ... ] ) ]", - _("remote_schema"), - _("table_name"), - _("server_name"), - _("local_schema"), - _("option"), - _("value")); -} - -void -sql_help_INSERT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "INSERT INTO %s [ AS %s ] [ ( %s [, ...] ) ]\n" - " { DEFAULT VALUES | VALUES ( { %s | DEFAULT } [, ...] ) [, ...] | %s }\n" - " [ ON CONFLICT [ %s ] %s ]\n" - " [ RETURNING * | %s [ [ AS ] %s ] [, ...] ]\n" - "\n" - "%s\n" - "\n" - " ( { %s | ( %s ) } [ COLLATE %s ] [ %s ] [, ...] ) [ WHERE %s ]\n" - " ON CONSTRAINT %s\n" - "\n" - "%s\n" - "\n" - " DO NOTHING\n" - " DO UPDATE SET { %s = { %s | DEFAULT } |\n" - " ( %s [, ...] ) = ( { %s | DEFAULT } [, ...] ) |\n" - " ( %s [, ...] ) = ( %s )\n" - " } [, ...]\n" - " [ WHERE %s ]", - _("with_query"), - _("table_name"), - _("alias"), - _("column_name"), - _("expression"), - _("query"), - _("conflict_target"), - _("conflict_action"), - _("output_expression"), - _("output_name"), - _("where conflict_target can be one of:"), - _("index_column_name"), - _("index_expression"), - _("collation"), - _("opclass"), - _("index_predicate"), - _("constraint_name"), - _("and conflict_action is one of:"), - _("column_name"), - _("expression"), - _("column_name"), - _("expression"), - _("column_name"), - _("sub-SELECT"), - _("condition")); -} - -void -sql_help_LISTEN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "LISTEN %s", - _("channel")); -} - -void -sql_help_LOAD(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "LOAD '%s'", - _("filename")); -} - -void -sql_help_LOCK(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "LOCK [ TABLE ] [ ONLY ] %s [ * ] [, ...] [ IN %s MODE ] [ NOWAIT ]\n" - "\n" - "%s\n" - "\n" - " ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE\n" - " | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE", - _("name"), - _("lockmode"), - _("where lockmode is one of:")); -} - -void -sql_help_MOVE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "MOVE [ %s [ FROM | IN ] ] %s\n" - "\n" - "%s\n" - "\n" - " NEXT\n" - " PRIOR\n" - " FIRST\n" - " LAST\n" - " ABSOLUTE %s\n" - " RELATIVE %s\n" - " %s\n" - " ALL\n" - " FORWARD\n" - " FORWARD %s\n" - " FORWARD ALL\n" - " BACKWARD\n" - " BACKWARD %s\n" - " BACKWARD ALL", - _("direction"), - _("cursor_name"), - _("where direction can be empty or one of:"), - _("count"), - _("count"), - _("count"), - _("count"), - _("count")); -} - -void -sql_help_NOTIFY(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "NOTIFY %s [ , %s ]", - _("channel"), - _("payload")); -} - -void -sql_help_PREPARE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "PREPARE %s [ ( %s [, ...] ) ] AS %s", - _("name"), - _("data_type"), - _("statement")); -} - -void -sql_help_PREPARE_TRANSACTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "PREPARE TRANSACTION %s", - _("transaction_id")); -} - -void -sql_help_REASSIGN_OWNED(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "REASSIGN OWNED BY { %s | CURRENT_USER | SESSION_USER } [, ...]\n" - " TO { %s | CURRENT_USER | SESSION_USER }", - _("old_role"), - _("new_role")); -} - -void -sql_help_REFRESH_MATERIALIZED_VIEW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] %s\n" - " [ WITH [ NO ] DATA ]", - _("name")); -} - -void -sql_help_REINDEX(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "REINDEX [ ( { VERBOSE } [, ...] ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } %s", - _("name")); -} - -void -sql_help_RELEASE_SAVEPOINT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "RELEASE [ SAVEPOINT ] %s", - _("savepoint_name")); -} - -void -sql_help_RESET(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "RESET %s\n" - "RESET ALL", - _("configuration_parameter")); -} - -void -sql_help_REVOKE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "REVOKE [ GRANT OPTION FOR ]\n" - " { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON { [ TABLE ] %s [, ...]\n" - " | ALL TABLES IN SCHEMA %s [, ...] }\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { SELECT | INSERT | UPDATE | REFERENCES } ( %s [, ...] )\n" - " [, ...] | ALL [ PRIVILEGES ] ( %s [, ...] ) }\n" - " ON [ TABLE ] %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { USAGE | SELECT | UPDATE }\n" - " [, ...] | ALL [ PRIVILEGES ] }\n" - " ON { SEQUENCE %s [, ...]\n" - " | ALL SEQUENCES IN SCHEMA %s [, ...] }\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON DATABASE %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON DOMAIN %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON FOREIGN DATA WRAPPER %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON FOREIGN SERVER %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { EXECUTE | ALL [ PRIVILEGES ] }\n" - " ON { FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] ) [, ...]\n" - " | ALL FUNCTIONS IN SCHEMA %s [, ...] }\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON LANGUAGE %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON LARGE OBJECT %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }\n" - " ON SCHEMA %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { CREATE | ALL [ PRIVILEGES ] }\n" - " ON TABLESPACE %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ GRANT OPTION FOR ]\n" - " { USAGE | ALL [ PRIVILEGES ] }\n" - " ON TYPE %s [, ...]\n" - " FROM { [ GROUP ] %s | PUBLIC } [, ...]\n" - " [ CASCADE | RESTRICT ]\n" - "\n" - "REVOKE [ ADMIN OPTION FOR ]\n" - " %s [, ...] FROM %s [, ...]\n" - " [ CASCADE | RESTRICT ]", - _("table_name"), - _("schema_name"), - _("role_name"), - _("column_name"), - _("column_name"), - _("table_name"), - _("role_name"), - _("sequence_name"), - _("schema_name"), - _("role_name"), - _("database_name"), - _("role_name"), - _("domain_name"), - _("role_name"), - _("fdw_name"), - _("role_name"), - _("server_name"), - _("role_name"), - _("function_name"), - _("argmode"), - _("arg_name"), - _("arg_type"), - _("schema_name"), - _("role_name"), - _("lang_name"), - _("role_name"), - _("loid"), - _("role_name"), - _("schema_name"), - _("role_name"), - _("tablespace_name"), - _("role_name"), - _("type_name"), - _("role_name"), - _("role_name"), - _("role_name")); -} - -void -sql_help_ROLLBACK(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ROLLBACK [ WORK | TRANSACTION ]"); -} - -void -sql_help_ROLLBACK_PREPARED(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ROLLBACK PREPARED %s", - _("transaction_id")); -} - -void -sql_help_ROLLBACK_TO_SAVEPOINT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] %s", - _("savepoint_name")); -} - -void -sql_help_SAVEPOINT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SAVEPOINT %s", - _("savepoint_name")); -} - -void -sql_help_SECURITY_LABEL(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SECURITY LABEL [ FOR %s ] ON\n" - "{\n" - " TABLE %s |\n" - " COLUMN %s.%s |\n" - " AGGREGATE %s ( %s ) |\n" - " DATABASE %s |\n" - " DOMAIN %s |\n" - " EVENT TRIGGER %s |\n" - " FOREIGN TABLE %s\n" - " FUNCTION %s ( [ [ %s ] [ %s ] %s [, ...] ] ) |\n" - " LARGE OBJECT %s |\n" - " MATERIALIZED VIEW %s |\n" - " [ PROCEDURAL ] LANGUAGE %s |\n" - " ROLE %s |\n" - " SCHEMA %s |\n" - " SEQUENCE %s |\n" - " TABLESPACE %s |\n" - " TYPE %s |\n" - " VIEW %s\n" - "} IS '%s'\n" - "\n" - "%s\n" - "\n" - "* |\n" - "[ %s ] [ %s ] %s [ , ... ] |\n" - "[ [ %s ] [ %s ] %s [ , ... ] ] ORDER BY [ %s ] [ %s ] %s [ , ... ]", - _("provider"), - _("object_name"), - _("table_name"), - _("column_name"), - _("aggregate_name"), - _("aggregate_signature"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("function_name"), - _("argmode"), - _("argname"), - _("argtype"), - _("large_object_oid"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("object_name"), - _("label"), - _("where aggregate_signature is:"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype"), - _("argmode"), - _("argname"), - _("argtype")); -} - -void -sql_help_SELECT(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "SELECT [ ALL | DISTINCT [ ON ( %s [, ...] ) ] ]\n" - " [ * | %s [ [ AS ] %s ] [, ...] ]\n" - " [ FROM %s [, ...] ]\n" - " [ WHERE %s ]\n" - " [ GROUP BY %s [, ...] ]\n" - " [ HAVING %s [, ...] ]\n" - " [ WINDOW %s AS ( %s ) [, ...] ]\n" - " [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] %s ]\n" - " [ ORDER BY %s [ ASC | DESC | USING %s ] [ NULLS { FIRST | LAST } ] [, ...] ]\n" - " [ LIMIT { %s | ALL } ]\n" - " [ OFFSET %s [ ROW | ROWS ] ]\n" - " [ FETCH { FIRST | NEXT } [ %s ] { ROW | ROWS } ONLY ]\n" - " [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF %s [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]\n" - "\n" - "%s\n" - "\n" - " [ ONLY ] %s [ * ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ TABLESAMPLE %s ( %s [, ...] ) [ REPEATABLE ( %s ) ] ]\n" - " [ LATERAL ] ( %s ) [ AS ] %s [ ( %s [, ...] ) ]\n" - " %s [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) [ AS ] %s ( %s [, ...] )\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) AS ( %s [, ...] )\n" - " [ LATERAL ] ROWS FROM( %s ( [ %s [, ...] ] ) [ AS ( %s [, ...] ) ] [, ...] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " %s [ NATURAL ] %s %s [ ON %s | USING ( %s [, ...] ) ]\n" - "\n" - "%s\n" - "\n" - " ( )\n" - " %s\n" - " ( %s [, ...] )\n" - " ROLLUP ( { %s | ( %s [, ...] ) } [, ...] )\n" - " CUBE ( { %s | ( %s [, ...] ) } [, ...] )\n" - " GROUPING SETS ( %s [, ...] )\n" - "\n" - "%s\n" - "\n" - " %s [ ( %s [, ...] ) ] AS ( %s | %s | %s | %s | %s )\n" - "\n" - "TABLE [ ONLY ] %s [ * ]", - _("with_query"), - _("expression"), - _("expression"), - _("output_name"), - _("from_item"), - _("condition"), - _("grouping_element"), - _("condition"), - _("window_name"), - _("window_definition"), - _("select"), - _("expression"), - _("operator"), - _("count"), - _("start"), - _("count"), - _("table_name"), - _("where from_item can be one of:"), - _("table_name"), - _("alias"), - _("column_alias"), - _("sampling_method"), - _("argument"), - _("seed"), - _("select"), - _("alias"), - _("column_alias"), - _("with_query_name"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("alias"), - _("column_alias"), - _("from_item"), - _("join_type"), - _("from_item"), - _("join_condition"), - _("join_column"), - _("and grouping_element can be one of:"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("grouping_element"), - _("and with_query is:"), - _("with_query_name"), - _("column_name"), - _("select"), - _("values"), - _("insert"), - _("update"), - _("delete"), - _("table_name")); -} - -void -sql_help_SELECT_INTO(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "SELECT [ ALL | DISTINCT [ ON ( %s [, ...] ) ] ]\n" - " * | %s [ [ AS ] %s ] [, ...]\n" - " INTO [ TEMPORARY | TEMP | UNLOGGED ] [ TABLE ] %s\n" - " [ FROM %s [, ...] ]\n" - " [ WHERE %s ]\n" - " [ GROUP BY %s [, ...] ]\n" - " [ HAVING %s [, ...] ]\n" - " [ WINDOW %s AS ( %s ) [, ...] ]\n" - " [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] %s ]\n" - " [ ORDER BY %s [ ASC | DESC | USING %s ] [ NULLS { FIRST | LAST } ] [, ...] ]\n" - " [ LIMIT { %s | ALL } ]\n" - " [ OFFSET %s [ ROW | ROWS ] ]\n" - " [ FETCH { FIRST | NEXT } [ %s ] { ROW | ROWS } ONLY ]\n" - " [ FOR { UPDATE | SHARE } [ OF %s [, ...] ] [ NOWAIT ] [...] ]", - _("with_query"), - _("expression"), - _("expression"), - _("output_name"), - _("new_table"), - _("from_item"), - _("condition"), - _("expression"), - _("condition"), - _("window_name"), - _("window_definition"), - _("select"), - _("expression"), - _("operator"), - _("count"), - _("start"), - _("count"), - _("table_name")); -} - -void -sql_help_SET(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SET [ SESSION | LOCAL ] %s { TO | = } { %s | '%s' | DEFAULT }\n" - "SET [ SESSION | LOCAL ] TIME ZONE { %s | LOCAL | DEFAULT }", - _("configuration_parameter"), - _("value"), - _("value"), - _("timezone")); -} - -void -sql_help_SET_CONSTRAINTS(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SET CONSTRAINTS { ALL | %s [, ...] } { DEFERRED | IMMEDIATE }", - _("name")); -} - -void -sql_help_SET_ROLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SET [ SESSION | LOCAL ] ROLE %s\n" - "SET [ SESSION | LOCAL ] ROLE NONE\n" - "RESET ROLE", - _("role_name")); -} - -void -sql_help_SET_SESSION_AUTHORIZATION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SET [ SESSION | LOCAL ] SESSION AUTHORIZATION %s\n" - "SET [ SESSION | LOCAL ] SESSION AUTHORIZATION DEFAULT\n" - "RESET SESSION AUTHORIZATION", - _("user_name")); -} - -void -sql_help_SET_TRANSACTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SET TRANSACTION %s [, ...]\n" - "SET TRANSACTION SNAPSHOT %s\n" - "SET SESSION CHARACTERISTICS AS TRANSACTION %s [, ...]\n" - "\n" - "%s\n" - "\n" - " ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }\n" - " READ WRITE | READ ONLY\n" - " [ NOT ] DEFERRABLE", - _("transaction_mode"), - _("snapshot_id"), - _("transaction_mode"), - _("where transaction_mode is one of:")); -} - -void -sql_help_SHOW(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "SHOW %s\n" - "SHOW ALL", - _("name")); -} - -void -sql_help_START_TRANSACTION(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "START TRANSACTION [ %s [, ...] ]\n" - "\n" - "%s\n" - "\n" - " ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }\n" - " READ WRITE | READ ONLY\n" - " [ NOT ] DEFERRABLE", - _("transaction_mode"), - _("where transaction_mode is one of:")); -} - -void -sql_help_TABLE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "SELECT [ ALL | DISTINCT [ ON ( %s [, ...] ) ] ]\n" - " [ * | %s [ [ AS ] %s ] [, ...] ]\n" - " [ FROM %s [, ...] ]\n" - " [ WHERE %s ]\n" - " [ GROUP BY %s [, ...] ]\n" - " [ HAVING %s [, ...] ]\n" - " [ WINDOW %s AS ( %s ) [, ...] ]\n" - " [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] %s ]\n" - " [ ORDER BY %s [ ASC | DESC | USING %s ] [ NULLS { FIRST | LAST } ] [, ...] ]\n" - " [ LIMIT { %s | ALL } ]\n" - " [ OFFSET %s [ ROW | ROWS ] ]\n" - " [ FETCH { FIRST | NEXT } [ %s ] { ROW | ROWS } ONLY ]\n" - " [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF %s [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]\n" - "\n" - "%s\n" - "\n" - " [ ONLY ] %s [ * ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ TABLESAMPLE %s ( %s [, ...] ) [ REPEATABLE ( %s ) ] ]\n" - " [ LATERAL ] ( %s ) [ AS ] %s [ ( %s [, ...] ) ]\n" - " %s [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) [ AS ] %s ( %s [, ...] )\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) AS ( %s [, ...] )\n" - " [ LATERAL ] ROWS FROM( %s ( [ %s [, ...] ] ) [ AS ( %s [, ...] ) ] [, ...] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " %s [ NATURAL ] %s %s [ ON %s | USING ( %s [, ...] ) ]\n" - "\n" - "%s\n" - "\n" - " ( )\n" - " %s\n" - " ( %s [, ...] )\n" - " ROLLUP ( { %s | ( %s [, ...] ) } [, ...] )\n" - " CUBE ( { %s | ( %s [, ...] ) } [, ...] )\n" - " GROUPING SETS ( %s [, ...] )\n" - "\n" - "%s\n" - "\n" - " %s [ ( %s [, ...] ) ] AS ( %s | %s | %s | %s | %s )\n" - "\n" - "TABLE [ ONLY ] %s [ * ]", - _("with_query"), - _("expression"), - _("expression"), - _("output_name"), - _("from_item"), - _("condition"), - _("grouping_element"), - _("condition"), - _("window_name"), - _("window_definition"), - _("select"), - _("expression"), - _("operator"), - _("count"), - _("start"), - _("count"), - _("table_name"), - _("where from_item can be one of:"), - _("table_name"), - _("alias"), - _("column_alias"), - _("sampling_method"), - _("argument"), - _("seed"), - _("select"), - _("alias"), - _("column_alias"), - _("with_query_name"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("alias"), - _("column_alias"), - _("from_item"), - _("join_type"), - _("from_item"), - _("join_condition"), - _("join_column"), - _("and grouping_element can be one of:"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("grouping_element"), - _("and with_query is:"), - _("with_query_name"), - _("column_name"), - _("select"), - _("values"), - _("insert"), - _("update"), - _("delete"), - _("table_name")); -} - -void -sql_help_TRUNCATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "TRUNCATE [ TABLE ] [ ONLY ] %s [ * ] [, ... ]\n" - " [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]", - _("name")); -} - -void -sql_help_UNLISTEN(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "UNLISTEN { %s | * }", - _("channel")); -} - -void -sql_help_UPDATE(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "UPDATE [ ONLY ] %s [ * ] [ [ AS ] %s ]\n" - " SET { %s = { %s | DEFAULT } |\n" - " ( %s [, ...] ) = ( { %s | DEFAULT } [, ...] ) |\n" - " ( %s [, ...] ) = ( %s )\n" - " } [, ...]\n" - " [ FROM %s ]\n" - " [ WHERE %s | WHERE CURRENT OF %s ]\n" - " [ RETURNING * | %s [ [ AS ] %s ] [, ...] ]", - _("with_query"), - _("table_name"), - _("alias"), - _("column_name"), - _("expression"), - _("column_name"), - _("expression"), - _("column_name"), - _("sub-SELECT"), - _("from_list"), - _("condition"), - _("cursor_name"), - _("output_expression"), - _("output_name")); -} - -void -sql_help_VACUUM(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ %s [ (%s [, ...] ) ] ]\n" - "VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ %s ]\n" - "VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ %s [ (%s [, ...] ) ] ]", - _("table_name"), - _("column_name"), - _("table_name"), - _("table_name"), - _("column_name")); -} - -void -sql_help_VALUES(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "VALUES ( %s [, ...] ) [, ...]\n" - " [ ORDER BY %s [ ASC | DESC | USING %s ] [, ...] ]\n" - " [ LIMIT { %s | ALL } ]\n" - " [ OFFSET %s [ ROW | ROWS ] ]\n" - " [ FETCH { FIRST | NEXT } [ %s ] { ROW | ROWS } ONLY ]", - _("expression"), - _("sort_expression"), - _("operator"), - _("count"), - _("start"), - _("count")); -} - -void -sql_help_WITH(PQExpBuffer buf) -{ - appendPQExpBuffer(buf, - "[ WITH [ RECURSIVE ] %s [, ...] ]\n" - "SELECT [ ALL | DISTINCT [ ON ( %s [, ...] ) ] ]\n" - " [ * | %s [ [ AS ] %s ] [, ...] ]\n" - " [ FROM %s [, ...] ]\n" - " [ WHERE %s ]\n" - " [ GROUP BY %s [, ...] ]\n" - " [ HAVING %s [, ...] ]\n" - " [ WINDOW %s AS ( %s ) [, ...] ]\n" - " [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] %s ]\n" - " [ ORDER BY %s [ ASC | DESC | USING %s ] [ NULLS { FIRST | LAST } ] [, ...] ]\n" - " [ LIMIT { %s | ALL } ]\n" - " [ OFFSET %s [ ROW | ROWS ] ]\n" - " [ FETCH { FIRST | NEXT } [ %s ] { ROW | ROWS } ONLY ]\n" - " [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF %s [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]\n" - "\n" - "%s\n" - "\n" - " [ ONLY ] %s [ * ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ TABLESAMPLE %s ( %s [, ...] ) [ REPEATABLE ( %s ) ] ]\n" - " [ LATERAL ] ( %s ) [ AS ] %s [ ( %s [, ...] ) ]\n" - " %s [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) [ AS ] %s ( %s [, ...] )\n" - " [ LATERAL ] %s ( [ %s [, ...] ] ) AS ( %s [, ...] )\n" - " [ LATERAL ] ROWS FROM( %s ( [ %s [, ...] ] ) [ AS ( %s [, ...] ) ] [, ...] )\n" - " [ WITH ORDINALITY ] [ [ AS ] %s [ ( %s [, ...] ) ] ]\n" - " %s [ NATURAL ] %s %s [ ON %s | USING ( %s [, ...] ) ]\n" - "\n" - "%s\n" - "\n" - " ( )\n" - " %s\n" - " ( %s [, ...] )\n" - " ROLLUP ( { %s | ( %s [, ...] ) } [, ...] )\n" - " CUBE ( { %s | ( %s [, ...] ) } [, ...] )\n" - " GROUPING SETS ( %s [, ...] )\n" - "\n" - "%s\n" - "\n" - " %s [ ( %s [, ...] ) ] AS ( %s | %s | %s | %s | %s )\n" - "\n" - "TABLE [ ONLY ] %s [ * ]", - _("with_query"), - _("expression"), - _("expression"), - _("output_name"), - _("from_item"), - _("condition"), - _("grouping_element"), - _("condition"), - _("window_name"), - _("window_definition"), - _("select"), - _("expression"), - _("operator"), - _("count"), - _("start"), - _("count"), - _("table_name"), - _("where from_item can be one of:"), - _("table_name"), - _("alias"), - _("column_alias"), - _("sampling_method"), - _("argument"), - _("seed"), - _("select"), - _("alias"), - _("column_alias"), - _("with_query_name"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_alias"), - _("function_name"), - _("argument"), - _("alias"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("function_name"), - _("argument"), - _("column_definition"), - _("alias"), - _("column_alias"), - _("from_item"), - _("join_type"), - _("from_item"), - _("join_condition"), - _("join_column"), - _("and grouping_element can be one of:"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("expression"), - _("grouping_element"), - _("and with_query is:"), - _("with_query_name"), - _("column_name"), - _("select"), - _("values"), - _("insert"), - _("update"), - _("delete"), - _("table_name")); -} - diff --git a/src/bin/csql/sql_help.h b/src/bin/csql/sql_help.h deleted file mode 100644 index 3aac3ce7a..000000000 --- a/src/bin/csql/sql_help.h +++ /dev/null @@ -1,1040 +0,0 @@ -/* - * *** Do not change this file by hand. It is automatically - * *** generated from the DocBook documentation. - * - * generated by - * /usr/bin/perl create_help.pl ../../../doc/src/sgml/ref sql_help - * - */ - -#ifndef SQL_HELP_H -#define SQL_HELP_H - -#define N_(x) (x) /* gettext noop */ - -#include "postgres_fe.h" -#include "pqexpbuffer.h" - -struct _helpStruct -{ - const char *cmd; /* the command name */ - const char *help; /* the help associated with it */ - void (*syntaxfunc)(PQExpBuffer); /* function that prints the syntax associated with it */ - int nl_count; /* number of newlines in syntax (for pager) */ -}; - -extern void sql_help_ABORT(PQExpBuffer buf); -extern void sql_help_ALTER_AGGREGATE(PQExpBuffer buf); -extern void sql_help_ALTER_COLLATION(PQExpBuffer buf); -extern void sql_help_ALTER_CONVERSION(PQExpBuffer buf); -extern void sql_help_ALTER_DATABASE(PQExpBuffer buf); -extern void sql_help_ALTER_DEFAULT_PRIVILEGES(PQExpBuffer buf); -extern void sql_help_ALTER_DOMAIN(PQExpBuffer buf); -extern void sql_help_ALTER_EVENT_TRIGGER(PQExpBuffer buf); -extern void sql_help_ALTER_EXTENSION(PQExpBuffer buf); -extern void sql_help_ALTER_FOREIGN_DATA_WRAPPER(PQExpBuffer buf); -extern void sql_help_ALTER_FOREIGN_TABLE(PQExpBuffer buf); -extern void sql_help_ALTER_FUNCTION(PQExpBuffer buf); -extern void sql_help_ALTER_GROUP(PQExpBuffer buf); -extern void sql_help_ALTER_INDEX(PQExpBuffer buf); -extern void sql_help_ALTER_LANGUAGE(PQExpBuffer buf); -extern void sql_help_ALTER_LARGE_OBJECT(PQExpBuffer buf); -extern void sql_help_ALTER_MATERIALIZED_VIEW(PQExpBuffer buf); -extern void sql_help_ALTER_OPERATOR(PQExpBuffer buf); -extern void sql_help_ALTER_OPERATOR_CLASS(PQExpBuffer buf); -extern void sql_help_ALTER_OPERATOR_FAMILY(PQExpBuffer buf); -extern void sql_help_ALTER_POLICY(PQExpBuffer buf); -extern void sql_help_ALTER_ROLE(PQExpBuffer buf); -extern void sql_help_ALTER_RULE(PQExpBuffer buf); -extern void sql_help_ALTER_SCHEMA(PQExpBuffer buf); -extern void sql_help_ALTER_SEQUENCE(PQExpBuffer buf); -extern void sql_help_ALTER_SERVER(PQExpBuffer buf); -extern void sql_help_ALTER_SYSTEM(PQExpBuffer buf); -extern void sql_help_ALTER_TABLE(PQExpBuffer buf); -extern void sql_help_ALTER_TABLESPACE(PQExpBuffer buf); -extern void sql_help_ALTER_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf); -extern void sql_help_ALTER_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf); -extern void sql_help_ALTER_TEXT_SEARCH_PARSER(PQExpBuffer buf); -extern void sql_help_ALTER_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf); -extern void sql_help_ALTER_TRIGGER(PQExpBuffer buf); -extern void sql_help_ALTER_TYPE(PQExpBuffer buf); -extern void sql_help_ALTER_USER(PQExpBuffer buf); -extern void sql_help_ALTER_USER_MAPPING(PQExpBuffer buf); -extern void sql_help_ALTER_VIEW(PQExpBuffer buf); -extern void sql_help_ANALYZE(PQExpBuffer buf); -extern void sql_help_BEGIN(PQExpBuffer buf); -extern void sql_help_CHECKPOINT(PQExpBuffer buf); -extern void sql_help_CLOSE(PQExpBuffer buf); -extern void sql_help_CLUSTER(PQExpBuffer buf); -extern void sql_help_COMMENT(PQExpBuffer buf); -extern void sql_help_COMMIT(PQExpBuffer buf); -extern void sql_help_COMMIT_PREPARED(PQExpBuffer buf); -extern void sql_help_COPY(PQExpBuffer buf); -extern void sql_help_CREATE_AGGREGATE(PQExpBuffer buf); -extern void sql_help_CREATE_CAST(PQExpBuffer buf); -extern void sql_help_CREATE_COLLATION(PQExpBuffer buf); -extern void sql_help_CREATE_CONVERSION(PQExpBuffer buf); -extern void sql_help_CREATE_DATABASE(PQExpBuffer buf); -extern void sql_help_CREATE_DOMAIN(PQExpBuffer buf); -extern void sql_help_CREATE_EVENT_TRIGGER(PQExpBuffer buf); -extern void sql_help_CREATE_EXTENSION(PQExpBuffer buf); -extern void sql_help_CREATE_FOREIGN_DATA_WRAPPER(PQExpBuffer buf); -extern void sql_help_CREATE_FOREIGN_TABLE(PQExpBuffer buf); -extern void sql_help_CREATE_FUNCTION(PQExpBuffer buf); -extern void sql_help_CREATE_GROUP(PQExpBuffer buf); -extern void sql_help_CREATE_INDEX(PQExpBuffer buf); -extern void sql_help_CREATE_LANGUAGE(PQExpBuffer buf); -extern void sql_help_CREATE_MATERIALIZED_VIEW(PQExpBuffer buf); -extern void sql_help_CREATE_OPERATOR(PQExpBuffer buf); -extern void sql_help_CREATE_OPERATOR_CLASS(PQExpBuffer buf); -extern void sql_help_CREATE_OPERATOR_FAMILY(PQExpBuffer buf); -extern void sql_help_CREATE_POLICY(PQExpBuffer buf); -extern void sql_help_CREATE_ROLE(PQExpBuffer buf); -extern void sql_help_CREATE_RULE(PQExpBuffer buf); -extern void sql_help_CREATE_SCHEMA(PQExpBuffer buf); -extern void sql_help_CREATE_SEQUENCE(PQExpBuffer buf); -extern void sql_help_CREATE_SERVER(PQExpBuffer buf); -extern void sql_help_CREATE_TABLE(PQExpBuffer buf); -extern void sql_help_CREATE_TABLE_AS(PQExpBuffer buf); -extern void sql_help_CREATE_TABLESPACE(PQExpBuffer buf); -extern void sql_help_CREATE_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf); -extern void sql_help_CREATE_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf); -extern void sql_help_CREATE_TEXT_SEARCH_PARSER(PQExpBuffer buf); -extern void sql_help_CREATE_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf); -extern void sql_help_CREATE_TRANSFORM(PQExpBuffer buf); -extern void sql_help_CREATE_TRIGGER(PQExpBuffer buf); -extern void sql_help_CREATE_TYPE(PQExpBuffer buf); -extern void sql_help_CREATE_USER(PQExpBuffer buf); -extern void sql_help_CREATE_USER_MAPPING(PQExpBuffer buf); -extern void sql_help_CREATE_VIEW(PQExpBuffer buf); -extern void sql_help_DEALLOCATE(PQExpBuffer buf); -extern void sql_help_DECLARE(PQExpBuffer buf); -extern void sql_help_DELETE(PQExpBuffer buf); -extern void sql_help_DISCARD(PQExpBuffer buf); -extern void sql_help_DO(PQExpBuffer buf); -extern void sql_help_DROP_AGGREGATE(PQExpBuffer buf); -extern void sql_help_DROP_CAST(PQExpBuffer buf); -extern void sql_help_DROP_COLLATION(PQExpBuffer buf); -extern void sql_help_DROP_CONVERSION(PQExpBuffer buf); -extern void sql_help_DROP_DATABASE(PQExpBuffer buf); -extern void sql_help_DROP_DOMAIN(PQExpBuffer buf); -extern void sql_help_DROP_EVENT_TRIGGER(PQExpBuffer buf); -extern void sql_help_DROP_EXTENSION(PQExpBuffer buf); -extern void sql_help_DROP_FOREIGN_DATA_WRAPPER(PQExpBuffer buf); -extern void sql_help_DROP_FOREIGN_TABLE(PQExpBuffer buf); -extern void sql_help_DROP_FUNCTION(PQExpBuffer buf); -extern void sql_help_DROP_GROUP(PQExpBuffer buf); -extern void sql_help_DROP_INDEX(PQExpBuffer buf); -extern void sql_help_DROP_LANGUAGE(PQExpBuffer buf); -extern void sql_help_DROP_MATERIALIZED_VIEW(PQExpBuffer buf); -extern void sql_help_DROP_OPERATOR(PQExpBuffer buf); -extern void sql_help_DROP_OPERATOR_CLASS(PQExpBuffer buf); -extern void sql_help_DROP_OPERATOR_FAMILY(PQExpBuffer buf); -extern void sql_help_DROP_OWNED(PQExpBuffer buf); -extern void sql_help_DROP_POLICY(PQExpBuffer buf); -extern void sql_help_DROP_ROLE(PQExpBuffer buf); -extern void sql_help_DROP_RULE(PQExpBuffer buf); -extern void sql_help_DROP_SCHEMA(PQExpBuffer buf); -extern void sql_help_DROP_SEQUENCE(PQExpBuffer buf); -extern void sql_help_DROP_SERVER(PQExpBuffer buf); -extern void sql_help_DROP_TABLE(PQExpBuffer buf); -extern void sql_help_DROP_TABLESPACE(PQExpBuffer buf); -extern void sql_help_DROP_TEXT_SEARCH_CONFIGURATION(PQExpBuffer buf); -extern void sql_help_DROP_TEXT_SEARCH_DICTIONARY(PQExpBuffer buf); -extern void sql_help_DROP_TEXT_SEARCH_PARSER(PQExpBuffer buf); -extern void sql_help_DROP_TEXT_SEARCH_TEMPLATE(PQExpBuffer buf); -extern void sql_help_DROP_TRANSFORM(PQExpBuffer buf); -extern void sql_help_DROP_TRIGGER(PQExpBuffer buf); -extern void sql_help_DROP_TYPE(PQExpBuffer buf); -extern void sql_help_DROP_USER(PQExpBuffer buf); -extern void sql_help_DROP_USER_MAPPING(PQExpBuffer buf); -extern void sql_help_DROP_VIEW(PQExpBuffer buf); -extern void sql_help_END(PQExpBuffer buf); -extern void sql_help_EXECUTE(PQExpBuffer buf); -extern void sql_help_EXPLAIN(PQExpBuffer buf); -extern void sql_help_FETCH(PQExpBuffer buf); -extern void sql_help_GRANT(PQExpBuffer buf); -extern void sql_help_IMPORT_FOREIGN_SCHEMA(PQExpBuffer buf); -extern void sql_help_INSERT(PQExpBuffer buf); -extern void sql_help_LISTEN(PQExpBuffer buf); -extern void sql_help_LOAD(PQExpBuffer buf); -extern void sql_help_LOCK(PQExpBuffer buf); -extern void sql_help_MOVE(PQExpBuffer buf); -extern void sql_help_NOTIFY(PQExpBuffer buf); -extern void sql_help_PREPARE(PQExpBuffer buf); -extern void sql_help_PREPARE_TRANSACTION(PQExpBuffer buf); -extern void sql_help_REASSIGN_OWNED(PQExpBuffer buf); -extern void sql_help_REFRESH_MATERIALIZED_VIEW(PQExpBuffer buf); -extern void sql_help_REINDEX(PQExpBuffer buf); -extern void sql_help_RELEASE_SAVEPOINT(PQExpBuffer buf); -extern void sql_help_RESET(PQExpBuffer buf); -extern void sql_help_REVOKE(PQExpBuffer buf); -extern void sql_help_ROLLBACK(PQExpBuffer buf); -extern void sql_help_ROLLBACK_PREPARED(PQExpBuffer buf); -extern void sql_help_ROLLBACK_TO_SAVEPOINT(PQExpBuffer buf); -extern void sql_help_SAVEPOINT(PQExpBuffer buf); -extern void sql_help_SECURITY_LABEL(PQExpBuffer buf); -extern void sql_help_SELECT(PQExpBuffer buf); -extern void sql_help_SELECT_INTO(PQExpBuffer buf); -extern void sql_help_SET(PQExpBuffer buf); -extern void sql_help_SET_CONSTRAINTS(PQExpBuffer buf); -extern void sql_help_SET_ROLE(PQExpBuffer buf); -extern void sql_help_SET_SESSION_AUTHORIZATION(PQExpBuffer buf); -extern void sql_help_SET_TRANSACTION(PQExpBuffer buf); -extern void sql_help_SHOW(PQExpBuffer buf); -extern void sql_help_START_TRANSACTION(PQExpBuffer buf); -extern void sql_help_TABLE(PQExpBuffer buf); -extern void sql_help_TRUNCATE(PQExpBuffer buf); -extern void sql_help_UNLISTEN(PQExpBuffer buf); -extern void sql_help_UPDATE(PQExpBuffer buf); -extern void sql_help_VACUUM(PQExpBuffer buf); -extern void sql_help_VALUES(PQExpBuffer buf); -extern void sql_help_WITH(PQExpBuffer buf); - - -static const struct _helpStruct QL_HELP[] = { - { "ABORT", - N_("abort the current transaction"), - sql_help_ABORT, - 0 }, - - { "ALTER AGGREGATE", - N_("change the definition of an aggregate function"), - sql_help_ALTER_AGGREGATE, - 9 }, - - { "ALTER COLLATION", - N_("change the definition of a collation"), - sql_help_ALTER_COLLATION, - 2 }, - - { "ALTER CONVERSION", - N_("change the definition of a conversion"), - sql_help_ALTER_CONVERSION, - 2 }, - - { "ALTER DATABASE", - N_("change a database"), - sql_help_ALTER_DATABASE, - 17 }, - - { "ALTER DEFAULT PRIVILEGES", - N_("define default access privileges"), - sql_help_ALTER_DEFAULT_PRIVILEGES, - 49 }, - - { "ALTER DOMAIN", - N_("change the definition of a domain"), - sql_help_ALTER_DOMAIN, - 17 }, - - { "ALTER EVENT TRIGGER", - N_("change the definition of an event trigger"), - sql_help_ALTER_EVENT_TRIGGER, - 3 }, - - { "ALTER EXTENSION", - N_("change the definition of an extension"), - sql_help_ALTER_EXTENSION, - 37 }, - - { "ALTER FOREIGN DATA WRAPPER", - N_("change the definition of a foreign-data wrapper"), - sql_help_ALTER_FOREIGN_DATA_WRAPPER, - 5 }, - - { "ALTER FOREIGN TABLE", - N_("change the definition of a foreign table"), - sql_help_ALTER_FOREIGN_TABLE, - 34 }, - - { "ALTER FUNCTION", - N_("change the definition of a function"), - sql_help_ALTER_FUNCTION, - 19 }, - - { "ALTER GROUP", - N_("change role name or membership"), - sql_help_ALTER_GROUP, - 9 }, - - { "ALTER INDEX", - N_("change the definition of an index"), - sql_help_ALTER_INDEX, - 5 }, - - { "ALTER LANGUAGE", - N_("change the definition of a procedural language"), - sql_help_ALTER_LANGUAGE, - 1 }, - - { "ALTER LARGE OBJECT", - N_("change the definition of a large object"), - sql_help_ALTER_LARGE_OBJECT, - 0 }, - - { "ALTER MATERIALIZED VIEW", - N_("change the definition of a materialized view"), - sql_help_ALTER_MATERIALIZED_VIEW, - 22 }, - - { "ALTER OPERATOR", - N_("change the definition of an operator"), - sql_help_ALTER_OPERATOR, - 4 }, - - { "ALTER OPERATOR CLASS", - N_("change the definition of an operator class"), - sql_help_ALTER_OPERATOR_CLASS, - 7 }, - - { "ALTER OPERATOR FAMILY", - N_("change the definition of an operator family"), - sql_help_ALTER_OPERATOR_FAMILY, - 19 }, - - { "ALTER POLICY", - N_("change the definition of a row level security policy"), - sql_help_ALTER_POLICY, - 5 }, - - { "ALTER ROLE", - N_("change a database role"), - sql_help_ALTER_ROLE, - 27 }, - - { "ALTER RULE", - N_("change the definition of a rule"), - sql_help_ALTER_RULE, - 0 }, - - { "ALTER SCHEMA", - N_("change the definition of a schema"), - sql_help_ALTER_SCHEMA, - 1 }, - - { "ALTER SEQUENCE", - N_("change the definition of a sequence generator"), - sql_help_ALTER_SEQUENCE, - 8 }, - - { "ALTER SERVER", - N_("change the definition of a foreign server"), - sql_help_ALTER_SERVER, - 3 }, - - { "ALTER SYSTEM", - N_("change a server configuration parameter"), - sql_help_ALTER_SYSTEM, - 3 }, - - { "ALTER TABLE", - N_("change the definition of a table"), - sql_help_ALTER_TABLE, - 61 }, - - { "ALTER TABLESPACE", - N_("change the definition of a tablespace"), - sql_help_ALTER_TABLESPACE, - 3 }, - - { "ALTER TEXT SEARCH CONFIGURATION", - N_("change the definition of a text search configuration"), - sql_help_ALTER_TEXT_SEARCH_CONFIGURATION, - 12 }, - - { "ALTER TEXT SEARCH DICTIONARY", - N_("change the definition of a text search dictionary"), - sql_help_ALTER_TEXT_SEARCH_DICTIONARY, - 5 }, - - { "ALTER TEXT SEARCH PARSER", - N_("change the definition of a text search parser"), - sql_help_ALTER_TEXT_SEARCH_PARSER, - 1 }, - - { "ALTER TEXT SEARCH TEMPLATE", - N_("change the definition of a text search template"), - sql_help_ALTER_TEXT_SEARCH_TEMPLATE, - 1 }, - - { "ALTER TRIGGER", - N_("change the definition of a trigger"), - sql_help_ALTER_TRIGGER, - 0 }, - - { "ALTER TYPE", - N_("change the definition of a type"), - sql_help_ALTER_TYPE, - 11 }, - - { "ALTER USER", - N_("change a database role"), - sql_help_ALTER_USER, - 26 }, - - { "ALTER USER MAPPING", - N_("change the definition of a user mapping"), - sql_help_ALTER_USER_MAPPING, - 2 }, - - { "ALTER VIEW", - N_("change the definition of a view"), - sql_help_ALTER_VIEW, - 6 }, - - { "ANALYZE", - N_("collect statistics about a database"), - sql_help_ANALYZE, - 0 }, - - { "BEGIN", - N_("start a transaction block"), - sql_help_BEGIN, - 6 }, - - { "CHECKPOINT", - N_("force a transaction log checkpoint"), - sql_help_CHECKPOINT, - 0 }, - - { "CLOSE", - N_("close a cursor"), - sql_help_CLOSE, - 0 }, - - { "CLUSTER", - N_("cluster a table according to an index"), - sql_help_CLUSTER, - 1 }, - - { "COMMENT", - N_("define or change the comment of an object"), - sql_help_COMMENT, - 45 }, - - { "COMMIT", - N_("commit the current transaction"), - sql_help_COMMIT, - 0 }, - - { "COMMIT PREPARED", - N_("commit a transaction that was earlier prepared for two-phase commit"), - sql_help_COMMIT_PREPARED, - 0 }, - - { "COPY", - N_("copy data between a file and a table"), - sql_help_COPY, - 21 }, - - { "CREATE AGGREGATE", - N_("define a new aggregate function"), - sql_help_CREATE_AGGREGATE, - 46 }, - - { "CREATE CAST", - N_("define a new cast"), - sql_help_CREATE_CAST, - 10 }, - - { "CREATE COLLATION", - N_("define a new collation"), - sql_help_CREATE_COLLATION, - 5 }, - - { "CREATE CONVERSION", - N_("define a new encoding conversion"), - sql_help_CREATE_CONVERSION, - 1 }, - - { "CREATE DATABASE", - N_("create a new database"), - sql_help_CREATE_DATABASE, - 9 }, - - { "CREATE DOMAIN", - N_("define a new domain"), - sql_help_CREATE_DOMAIN, - 8 }, - - { "CREATE EVENT TRIGGER", - N_("define a new event trigger"), - sql_help_CREATE_EVENT_TRIGGER, - 3 }, - - { "CREATE EXTENSION", - N_("install an extension"), - sql_help_CREATE_EXTENSION, - 3 }, - - { "CREATE FOREIGN DATA WRAPPER", - N_("define a new foreign-data wrapper"), - sql_help_CREATE_FOREIGN_DATA_WRAPPER, - 3 }, - - { "CREATE FOREIGN TABLE", - N_("define a new foreign table"), - sql_help_CREATE_FOREIGN_TABLE, - 20 }, - - { "CREATE FUNCTION", - N_("define a new function"), - sql_help_CREATE_FUNCTION, - 16 }, - - { "CREATE GROUP", - N_("define a new database role"), - sql_help_CREATE_GROUP, - 17 }, - - { "CREATE INDEX", - N_("define a new index"), - sql_help_CREATE_INDEX, - 4 }, - - { "CREATE LANGUAGE", - N_("define a new procedural language"), - sql_help_CREATE_LANGUAGE, - 2 }, - - { "CREATE MATERIALIZED VIEW", - N_("define a new materialized view"), - sql_help_CREATE_MATERIALIZED_VIEW, - 5 }, - - { "CREATE OPERATOR", - N_("define a new operator"), - sql_help_CREATE_OPERATOR, - 6 }, - - { "CREATE OPERATOR CLASS", - N_("define a new operator class"), - sql_help_CREATE_OPERATOR_CLASS, - 5 }, - - { "CREATE OPERATOR FAMILY", - N_("define a new operator family"), - sql_help_CREATE_OPERATOR_FAMILY, - 0 }, - - { "CREATE POLICY", - N_("define a new row level security policy for a table"), - sql_help_CREATE_POLICY, - 4 }, - - { "CREATE ROLE", - N_("define a new database role"), - sql_help_CREATE_ROLE, - 20 }, - - { "CREATE RULE", - N_("define a new rewrite rule"), - sql_help_CREATE_RULE, - 6 }, - - { "CREATE SCHEMA", - N_("define a new schema"), - sql_help_CREATE_SCHEMA, - 9 }, - - { "CREATE SEQUENCE", - N_("define a new sequence generator"), - sql_help_CREATE_SEQUENCE, - 3 }, - - { "CREATE SERVER", - N_("define a new foreign server"), - sql_help_CREATE_SERVER, - 2 }, - - { "CREATE TABLE", - N_("define a new table"), - sql_help_CREATE_TABLE, - 56 }, - - { "CREATE TABLE AS", - N_("define a new table from the results of a query"), - sql_help_CREATE_TABLE_AS, - 6 }, - - { "CREATE TABLESPACE", - N_("define a new tablespace"), - sql_help_CREATE_TABLESPACE, - 3 }, - - { "CREATE TEXT SEARCH CONFIGURATION", - N_("define a new text search configuration"), - sql_help_CREATE_TEXT_SEARCH_CONFIGURATION, - 3 }, - - { "CREATE TEXT SEARCH DICTIONARY", - N_("define a new text search dictionary"), - sql_help_CREATE_TEXT_SEARCH_DICTIONARY, - 3 }, - - { "CREATE TEXT SEARCH PARSER", - N_("define a new text search parser"), - sql_help_CREATE_TEXT_SEARCH_PARSER, - 6 }, - - { "CREATE TEXT SEARCH TEMPLATE", - N_("define a new text search template"), - sql_help_CREATE_TEXT_SEARCH_TEMPLATE, - 3 }, - - { "CREATE TRANSFORM", - N_("define a new transform"), - sql_help_CREATE_TRANSFORM, - 3 }, - - { "CREATE TRIGGER", - N_("define a new trigger"), - sql_help_CREATE_TRIGGER, - 13 }, - - { "CREATE TYPE", - N_("define a new data type"), - sql_help_CREATE_TYPE, - 35 }, - - { "CREATE USER", - N_("define a new database role"), - sql_help_CREATE_USER, - 19 }, - - { "CREATE USER MAPPING", - N_("define a new mapping of a user to a foreign server"), - sql_help_CREATE_USER_MAPPING, - 2 }, - - { "CREATE VIEW", - N_("define a new view"), - sql_help_CREATE_VIEW, - 3 }, - - { "DEALLOCATE", - N_("deallocate a prepared statement"), - sql_help_DEALLOCATE, - 0 }, - - { "DECLARE", - N_("define a cursor"), - sql_help_DECLARE, - 1 }, - - { "DELETE", - N_("delete rows of a table"), - sql_help_DELETE, - 4 }, - - { "DISCARD", - N_("discard session state"), - sql_help_DISCARD, - 0 }, - - { "DO", - N_("execute an anonymous code block"), - sql_help_DO, - 0 }, - - { "DROP AGGREGATE", - N_("remove an aggregate function"), - sql_help_DROP_AGGREGATE, - 6 }, - - { "DROP CAST", - N_("remove a cast"), - sql_help_DROP_CAST, - 0 }, - - { "DROP COLLATION", - N_("remove a collation"), - sql_help_DROP_COLLATION, - 0 }, - - { "DROP CONVERSION", - N_("remove a conversion"), - sql_help_DROP_CONVERSION, - 0 }, - - { "DROP DATABASE", - N_("remove a database"), - sql_help_DROP_DATABASE, - 0 }, - - { "DROP DOMAIN", - N_("remove a domain"), - sql_help_DROP_DOMAIN, - 0 }, - - { "DROP EVENT TRIGGER", - N_("remove an event trigger"), - sql_help_DROP_EVENT_TRIGGER, - 0 }, - - { "DROP EXTENSION", - N_("remove an extension"), - sql_help_DROP_EXTENSION, - 0 }, - - { "DROP FOREIGN DATA WRAPPER", - N_("remove a foreign-data wrapper"), - sql_help_DROP_FOREIGN_DATA_WRAPPER, - 0 }, - - { "DROP FOREIGN TABLE", - N_("remove a foreign table"), - sql_help_DROP_FOREIGN_TABLE, - 0 }, - - { "DROP FUNCTION", - N_("remove a function"), - sql_help_DROP_FUNCTION, - 1 }, - - { "DROP GROUP", - N_("remove a database role"), - sql_help_DROP_GROUP, - 0 }, - - { "DROP INDEX", - N_("remove an index"), - sql_help_DROP_INDEX, - 0 }, - - { "DROP LANGUAGE", - N_("remove a procedural language"), - sql_help_DROP_LANGUAGE, - 0 }, - - { "DROP MATERIALIZED VIEW", - N_("remove a materialized view"), - sql_help_DROP_MATERIALIZED_VIEW, - 0 }, - - { "DROP OPERATOR", - N_("remove an operator"), - sql_help_DROP_OPERATOR, - 0 }, - - { "DROP OPERATOR CLASS", - N_("remove an operator class"), - sql_help_DROP_OPERATOR_CLASS, - 0 }, - - { "DROP OPERATOR FAMILY", - N_("remove an operator family"), - sql_help_DROP_OPERATOR_FAMILY, - 0 }, - - { "DROP OWNED", - N_("remove database objects owned by a database role"), - sql_help_DROP_OWNED, - 0 }, - - { "DROP POLICY", - N_("remove a row level security policy from a table"), - sql_help_DROP_POLICY, - 0 }, - - { "DROP ROLE", - N_("remove a database role"), - sql_help_DROP_ROLE, - 0 }, - - { "DROP RULE", - N_("remove a rewrite rule"), - sql_help_DROP_RULE, - 0 }, - - { "DROP SCHEMA", - N_("remove a schema"), - sql_help_DROP_SCHEMA, - 0 }, - - { "DROP SEQUENCE", - N_("remove a sequence"), - sql_help_DROP_SEQUENCE, - 0 }, - - { "DROP SERVER", - N_("remove a foreign server descriptor"), - sql_help_DROP_SERVER, - 0 }, - - { "DROP TABLE", - N_("remove a table"), - sql_help_DROP_TABLE, - 0 }, - - { "DROP TABLESPACE", - N_("remove a tablespace"), - sql_help_DROP_TABLESPACE, - 0 }, - - { "DROP TEXT SEARCH CONFIGURATION", - N_("remove a text search configuration"), - sql_help_DROP_TEXT_SEARCH_CONFIGURATION, - 0 }, - - { "DROP TEXT SEARCH DICTIONARY", - N_("remove a text search dictionary"), - sql_help_DROP_TEXT_SEARCH_DICTIONARY, - 0 }, - - { "DROP TEXT SEARCH PARSER", - N_("remove a text search parser"), - sql_help_DROP_TEXT_SEARCH_PARSER, - 0 }, - - { "DROP TEXT SEARCH TEMPLATE", - N_("remove a text search template"), - sql_help_DROP_TEXT_SEARCH_TEMPLATE, - 0 }, - - { "DROP TRANSFORM", - N_("remove a transform"), - sql_help_DROP_TRANSFORM, - 0 }, - - { "DROP TRIGGER", - N_("remove a trigger"), - sql_help_DROP_TRIGGER, - 0 }, - - { "DROP TYPE", - N_("remove a data type"), - sql_help_DROP_TYPE, - 0 }, - - { "DROP USER", - N_("remove a database role"), - sql_help_DROP_USER, - 0 }, - - { "DROP USER MAPPING", - N_("remove a user mapping for a foreign server"), - sql_help_DROP_USER_MAPPING, - 0 }, - - { "DROP VIEW", - N_("remove a view"), - sql_help_DROP_VIEW, - 0 }, - - { "END", - N_("commit the current transaction"), - sql_help_END, - 0 }, - - { "EXECUTE", - N_("execute a prepared statement"), - sql_help_EXECUTE, - 0 }, - - { "EXPLAIN", - N_("show the execution plan of a statement"), - sql_help_EXPLAIN, - 10 }, - - { "FETCH", - N_("retrieve rows from a query using a cursor"), - sql_help_FETCH, - 17 }, - - { "GRANT", - N_("define access privileges"), - sql_help_GRANT, - 65 }, - - { "IMPORT FOREIGN SCHEMA", - N_("import table definitions from a foreign server"), - sql_help_IMPORT_FOREIGN_SCHEMA, - 4 }, - - { "INSERT", - N_("create new rows in a table"), - sql_help_INSERT, - 18 }, - - { "LISTEN", - N_("listen for a notification"), - sql_help_LISTEN, - 0 }, - - { "LOAD", - N_("load a shared library file"), - sql_help_LOAD, - 0 }, - - { "LOCK", - N_("lock a table"), - sql_help_LOCK, - 5 }, - - { "MOVE", - N_("position a cursor"), - sql_help_MOVE, - 17 }, - - { "NOTIFY", - N_("generate a notification"), - sql_help_NOTIFY, - 0 }, - - { "PREPARE", - N_("prepare a statement for execution"), - sql_help_PREPARE, - 0 }, - - { "PREPARE TRANSACTION", - N_("prepare the current transaction for two-phase commit"), - sql_help_PREPARE_TRANSACTION, - 0 }, - - { "REASSIGN OWNED", - N_("change the ownership of database objects owned by a database role"), - sql_help_REASSIGN_OWNED, - 1 }, - - { "REFRESH MATERIALIZED VIEW", - N_("replace the contents of a materialized view"), - sql_help_REFRESH_MATERIALIZED_VIEW, - 1 }, - - { "REINDEX", - N_("rebuild indexes"), - sql_help_REINDEX, - 0 }, - - { "RELEASE SAVEPOINT", - N_("destroy a previously defined savepoint"), - sql_help_RELEASE_SAVEPOINT, - 0 }, - - { "RESET", - N_("restore the value of a run-time parameter to the default value"), - sql_help_RESET, - 1 }, - - { "REVOKE", - N_("remove access privileges"), - sql_help_REVOKE, - 86 }, - - { "ROLLBACK", - N_("abort the current transaction"), - sql_help_ROLLBACK, - 0 }, - - { "ROLLBACK PREPARED", - N_("cancel a transaction that was earlier prepared for two-phase commit"), - sql_help_ROLLBACK_PREPARED, - 0 }, - - { "ROLLBACK TO SAVEPOINT", - N_("roll back to a savepoint"), - sql_help_ROLLBACK_TO_SAVEPOINT, - 0 }, - - { "SAVEPOINT", - N_("define a new savepoint within the current transaction"), - sql_help_SAVEPOINT, - 0 }, - - { "SECURITY LABEL", - N_("define or change a security label applied to an object"), - sql_help_SECURITY_LABEL, - 25 }, - - { "SELECT", - N_("retrieve rows from a table or view"), - sql_help_SELECT, - 42 }, - - { "SELECT INTO", - N_("define a new table from the results of a query"), - sql_help_SELECT_INTO, - 14 }, - - { "SET", - N_("change a run-time parameter"), - sql_help_SET, - 1 }, - - { "SET CONSTRAINTS", - N_("set constraint check timing for the current transaction"), - sql_help_SET_CONSTRAINTS, - 0 }, - - { "SET ROLE", - N_("set the current user identifier of the current session"), - sql_help_SET_ROLE, - 2 }, - - { "SET SESSION AUTHORIZATION", - N_("set the session user identifier and the current user identifier of the current session"), - sql_help_SET_SESSION_AUTHORIZATION, - 2 }, - - { "SET TRANSACTION", - N_("set the characteristics of the current transaction"), - sql_help_SET_TRANSACTION, - 8 }, - - { "SHOW", - N_("show the value of a run-time parameter"), - sql_help_SHOW, - 1 }, - - { "START TRANSACTION", - N_("start a transaction block"), - sql_help_START_TRANSACTION, - 6 }, - - { "TABLE", - N_("retrieve rows from a table or view"), - sql_help_TABLE, - 42 }, - - { "TRUNCATE", - N_("empty a table or set of tables"), - sql_help_TRUNCATE, - 1 }, - - { "UNLISTEN", - N_("stop listening for a notification"), - sql_help_UNLISTEN, - 0 }, - - { "UPDATE", - N_("update rows of a table"), - sql_help_UPDATE, - 8 }, - - { "VACUUM", - N_("garbage-collect and optionally analyze a database"), - sql_help_VACUUM, - 2 }, - - { "VALUES", - N_("compute a set of rows"), - sql_help_VALUES, - 4 }, - - { "WITH", - N_("retrieve rows from a table or view"), - sql_help_WITH, - 42 }, - - - { NULL, NULL, NULL } /* End of list marker */ -}; - - -#define QL_HELP_COUNT 167 /* number of help items */ -#define QL_MAX_CMD_LEN 32 /* largest strlen(cmd) */ - - -#endif /* SQL_HELP_H */ diff --git a/src/bin/csql/stage.c b/src/bin/csql/stage.c deleted file mode 100644 index eed017978..000000000 --- a/src/bin/csql/stage.c +++ /dev/null @@ -1,1787 +0,0 @@ -/* - * csql - the Citus interactive terminal - * stage.c - * Helper routines to execute the csql meta-command \stage. These routines - * communicate with the master and worker nodes; and create new shards and - * upload data into them. - * - * Copyright (c) 2012-2016, Citus Data, Inc. - * - * $Id$ - */ - -#include -#include - -#include "libpq-int.h" -#include "libpq/ip.h" -#include "common.h" -#include "copy.h" -#include "distributed/pg_dist_partition.h" -#include "settings.h" -#include "stage.h" - - -/* Local functions forward declarations */ -static bool FileSize(char *filename, uint64 *fileSize); -static PGconn * ConnectToWorkerNode(const char *nodeName, uint32 nodePort, - const char *nodeDatabase); -static PGresult * ExecuteRemoteCommand(PGconn *remoteConnection, - const char *remoteCommand, - const char **parameterValues, int parameterCount); -static TableMetadata * InitTableMetadata(const char *tableName); -static ShardMetadata * InitShardMetadata(int shardPlacementPolicy); -static void FreeTableMetadata(TableMetadata *tableMetadata); -static void FreeShardMetadata(ShardMetadata *shardMetadata); -static void FreeShardMetadataList(ShardMetadata **shardMetadataList); -static void FreeCommonStageData(copy_options *stageOptions, TableMetadata *tableMetadata, - ShardMetadata **shardMetadataList); -static bool ColumnarTableOptionsOK(Oid relationOid); -static char * ExtendTablename(const char *baseTablename, uint64 shardId); -static uint64 GetValueUint64(const PGresult *result, int rowNumber, int columnNumber); -static bool MasterGetTableMetadata(const char *tableName, TableMetadata *tableMetadata); -static bool MasterGetTableDDLEvents(const char *tableName, TableMetadata *tableMetadata); -static bool MasterGetNewShardId(ShardMetadata *shardMetadata); -static bool MasterGetCandidateNodes(ShardMetadata *shardMetadata, - int shardPlacementPolicy); -static bool MasterInsertShardRow(uint32 logicalRelid, char storageType, - const ShardMetadata *shardMetadata); -static bool MasterInsertPlacementRows(const ShardMetadata *shardMetadata); -static bool MasterInsertShardMetadata(uint32 logicalRelid, char storageType, - ShardMetadata **shardMetadataList); -static bool IssueTransactionCommand(PGconn *connection, const char *command); -static bool StageTableData(PGconn *workerNode, uint64 shardId, - TableMetadata *tableMetadata, copy_options *stageOptions, - uint64 currentFileOffset, uint64 *nextFileOffset); -static bool StageForeignData(PGconn *workerNode, uint64 shardId, - TableMetadata *tableMetaData, copy_options *stageOptions); -static bool CreateRegularTable(PGconn *workerNode, int64 shardId, - TableMetadata *tableMetadata); -static bool CreateForeignTable(PGconn *workerNode, uint64 shardId, - TableMetadata *tableMetaData, const char *tableName, - const char *filePath); -static bool ApplyShardDDLCommand(PGconn *workerNode, uint64 shardId, const char *command); -static bool TransmitTableData(PGconn *workerNode, uint64 shardId, - uint64 shardMaxSize, copy_options *stageOptions, - uint64 currentFileOffset, uint64 *nextFileOffset); -static bool TransmitFile(PGconn *workerNode, const char *localPath, - const char *remotePath); -static bool FileStreamOK(const copy_options *stageOptions); -static PQExpBuffer CreateCopyQueryString(const char *tableName, const char *columnList, - const char *afterToFrom); -static int64 ShardTableSize(PGconn *workerNode, const char *tablename, uint64 shardId); -static int64 ShardFileSize(PGconn *workerNode, const char *tablename, uint64 shardId); -static int64 ShardColumnarTableSize(PGconn *workerNode, const char *tablename, - uint64 shardId); -static bool ShardMinMaxValues(PGconn *workerNode, const char *tablename, - const char *partitionKey, ShardMetadata *shardMetadata); - - -/* - * DoStageData augments psql's copy meta-command with data loading functionality - * for sharded databases. The function parses given command options, determines - * the table name, and then retrieves table related metadata from the master. - * - * The function later breaks input data into shards of fixed sizes, and uploads - * these shards in a loop. In this loop, the function asks the master node to - * create metadata for a new shard and return shard metadata. The function then - * uses these metadata, contacts worker nodes, and creates shards on them. - * - * Once all shards are created and staged (and all input data consumed), the - * function finalizes shard metadata with the master, and returns true. In case - * of a failure, the function aborts from finalizing shard metadata, and returns - * false. - */ -bool -DoStageData(const char *stageCommand) -{ - TableMetadata *tableMetadata = NULL; - copy_options *copyOptions = NULL; - copy_options *stageOptions = NULL; - ShardMetadata *shardMetadataList[MAX_SHARD_UPLOADS]; - uint32 shardCount = 0; - uint32 shardIndex = 0; - uint64 fileSize = 0; - uint64 currentFileOffset = 0; - uint64 nextFileOffset = 0; - char tableStorageType = 0; - char partitionMethod = 0; - bool metadataOK = false; - bool fileOK = false; - - /* parse the stage command, and validate its options */ - copyOptions = parse_slash_copy(stageCommand); - if (copyOptions == NULL) - { - return false; - } - else if (copyOptions->from == false) - { - psql_error("\\stage: staging tablename to filename unsupported\n"); - free_copy_options(copyOptions); - - return false; - } - else if (copyOptions->file == NULL) - { - /* extending this function to read data from stdin should be easy */ - psql_error("\\stage: staging currently supports file inputs only\n"); - free_copy_options(copyOptions); - - return false; - } - - /* parse in the additional options needed for staging */ - stageOptions = ParseStageOptions(copyOptions); - - /* get file size */ - fileOK = FileSize(stageOptions->file, &fileSize); - if (!fileOK) - { - free_copy_options(stageOptions); - return false; - } - - /* allocate and retrieve table related metadata */ - tableMetadata = InitTableMetadata(stageOptions->tableName); - if (tableMetadata == NULL) - { - free_copy_options(stageOptions); - return false; - } - - /* check that options specified by the user are reasonable */ - tableStorageType = tableMetadata->tableStorageType; - if (tableStorageType == STORAGE_TYPE_FOREIGN) - { - if (stageOptions->after_tofrom != NULL) - { - psql_error("\\stage: options for foreign tables are not supported\n"); - free_copy_options(stageOptions); - FreeTableMetadata(tableMetadata); - - return false; - } - } - - /* check that we are not staging into a hash partitioned table */ - partitionMethod = tableMetadata->partitionMethod; - if (partitionMethod == DISTRIBUTE_BY_HASH) - { - psql_error("\\stage: staging data into hash partitioned tables is not " - "supported\n"); - free_copy_options(stageOptions); - FreeTableMetadata(tableMetadata); - - return false; - } - - /* check that the foreign table options are suitable for the \stage command */ - if (tableStorageType == STORAGE_TYPE_COLUMNAR) - { - bool tableOptionsOK = ColumnarTableOptionsOK(tableMetadata->logicalRelid); - if (!tableOptionsOK) - { - return false; /* error message already displayed */ - } - } - - memset(shardMetadataList, 0, sizeof(shardMetadataList)); - shardCount = (fileSize / tableMetadata->shardMaxSize) + 1; - - /* foreign files may be compressed, and we can't split them yet */ - if (tableStorageType == STORAGE_TYPE_FOREIGN) - { - shardCount = 1; - } - - if (shardCount > MAX_SHARD_UPLOADS) - { - psql_error("\\stage: cannot stage more than %u shards\n", MAX_SHARD_UPLOADS); - free_copy_options(stageOptions); - FreeTableMetadata(tableMetadata); - - return false; - } - - /* while more file data left, continue to create and upload shards */ - while (currentFileOffset < fileSize) - { - ShardMetadata *shardMetadata = NULL; - const char *tableName = stageOptions->tableName; - const char *dbName = PQdb(pset.db); - char shardIdString[MAXPGPATH]; - uint64 shardId = 0; - uint32 nodeIndex = 0; - uint32 stageCount = 0; - - /* - * Allocate and retrieve metadata for new shard on the basis of the shard - * placement policy. - */ - shardMetadata = InitShardMetadata(tableMetadata->shardPlacementPolicy); - if (shardMetadata == NULL) - { - /* - * For now, we simply abort staging by not finalizing shard metadata - * on the master. This leaves invisible shard data on worker nodes. - */ - FreeCommonStageData(stageOptions, tableMetadata, shardMetadataList); - - return false; /* abort immediately */ - } - - /* save allocated shard metadata */ - shardMetadataList[shardIndex] = shardMetadata; - shardIndex++; - - shardId = shardMetadata->shardId; - snprintf(shardIdString, MAXPGPATH, UINT64_FORMAT, shardId); - - /* - * We now have table and shard metadata, and can start uploading shard - * data to remote nodes. For this, we need to upload replicas to a - * predetermined number of remote nodes. If we fail to create and upload - * shards on a remote node, or to fetch shard statistics from it, we - * retry with the next node in the list. If we aren't able to stage to - * enough nodes, we error out. - */ - for (nodeIndex = 0; nodeIndex < shardMetadata->nodeCount; nodeIndex++) - { - char *remoteNodeName = shardMetadata->nodeNameList[nodeIndex]; - uint32 remoteNodePort = shardMetadata->nodePortList[nodeIndex]; - - PGconn *remoteNode = NULL; - bool shardReplicaCreated = false; - bool shardStageFailed = false; - bool minMaxOK = false; - int64 shardSize = 0; - - remoteNode = ConnectToWorkerNode(remoteNodeName, remoteNodePort, dbName); - if (remoteNode == NULL) - { - shardMetadata->nodeStageList[nodeIndex] = false; - continue; - } - - if (tableStorageType == STORAGE_TYPE_TABLE || - tableStorageType == STORAGE_TYPE_COLUMNAR) - { - shardReplicaCreated = StageTableData(remoteNode, shardId, tableMetadata, - stageOptions, currentFileOffset, - &nextFileOffset); - } - else if (tableStorageType == STORAGE_TYPE_FOREIGN) - { - shardReplicaCreated = StageForeignData(remoteNode, shardId, - tableMetadata, stageOptions); - nextFileOffset = fileSize; - } - - if (!shardReplicaCreated) - { - shardStageFailed = true; - } - - /* if this is the first successful shard replica, fetch stats from it */ - if (!shardStageFailed && stageCount == 0) - { - /* fetch shard size */ - if (tableStorageType == STORAGE_TYPE_TABLE) - { - shardSize = ShardTableSize(remoteNode, tableName, shardId); - } - else if (tableStorageType == STORAGE_TYPE_FOREIGN) - { - shardSize = ShardFileSize(remoteNode, tableName, shardId); - } - else if (tableStorageType == STORAGE_TYPE_COLUMNAR) - { - shardSize = ShardColumnarTableSize(remoteNode, tableName, shardId); - } - - /* fetch partition key's min/max values in shard */ - minMaxOK = ShardMinMaxValues(remoteNode, tableName, - tableMetadata->partitionKey, shardMetadata); - - if (shardSize >= 0 && minMaxOK) - { - shardMetadata->shardSize = shardSize; - } - else - { - shardStageFailed = true; - } - } - - if (shardStageFailed) - { - shardMetadata->nodeStageList[nodeIndex] = false; - } - else - { - shardMetadata->nodeStageList[nodeIndex] = true; - stageCount++; - } - - PQfinish(remoteNode); - - if (stageCount == tableMetadata->shardReplicaCount) - { - break; - } - } - - /* check that we staged data to enough nodes */ - if (stageCount < tableMetadata->shardReplicaCount) - { - psql_error("\\stage: failed to replicate shard to enough replicas\n"); - - FreeCommonStageData(stageOptions, tableMetadata, shardMetadataList); - - return false; - } - - /* update current file offset */ - currentFileOffset = nextFileOffset; - } /* while more file data left for sharding */ - - /* - * At this stage, we have all file data staged into shards. We now finalize - * these shards by uploading their metadata information to the master. - */ - metadataOK = MasterInsertShardMetadata(tableMetadata->logicalRelid, - tableStorageType, shardMetadataList); - - /* clean up */ - FreeCommonStageData(stageOptions, tableMetadata, shardMetadataList); - - return metadataOK; -} - - -/* Canonicalize given file name, and determine file's size. */ -static bool -FileSize(char *filename, uint64 *fileSize) -{ - struct stat fileStat; - - canonicalize_path(filename); - if (stat(filename, &fileStat) < 0) - { - psql_error("%s: %s\n", filename, strerror(errno)); - return false; - } - - (*fileSize) = fileStat.st_size; - - return true; -} - - -/* - * ConnectToWorkerNode establishes connection to the worker node, and returns - * the connection. If connection to the node fails, the function returns null. - */ -static PGconn * -ConnectToWorkerNode(const char *nodeName, uint32 nodePort, const char *nodeDatabase) -{ - PGconn *workerNode = NULL; - const char *nodeOptions = NULL; - const char *nodeTty = NULL; - char nodePortString[MAXPGPATH]; - char connInfoString[MAXPGPATH]; - - /* transcribe port number and connection info to their string values */ - snprintf(nodePortString, MAXPGPATH, "%u", nodePort); - snprintf(connInfoString, MAXPGPATH, CONN_INFO_TEMPLATE, - nodeDatabase, CLIENT_CONNECT_TIMEOUT); - - workerNode = PQsetdb(nodeName, nodePortString, nodeOptions, nodeTty, connInfoString); - - if (PQstatus(workerNode) != CONNECTION_OK) - { - psql_error("worker node connection failed with %s", PQerrorMessage(workerNode)); - - PQfinish(workerNode); - workerNode = NULL; - } - - return workerNode; -} - - -/* - * ExecuteRemoteCommand executes commands on given remote node. The function - * uses the text protocol both for parameters and results; and on success - * returns the results object. On failure, the function emits an error message, - * clears results and returns null. - */ -static PGresult * -ExecuteRemoteCommand(PGconn *remoteConnection, const char *remoteCommand, - const char **parameterValues, int parameterCount) -{ - PGresult *result = NULL; - - const Oid *parameterType = NULL; /* let the backend deduce type */ - const int *parameterLength = NULL; /* text params do not need length */ - const int *parameterFormat = NULL; /* text params have Null by default */ - const int resultFormat = 0; /* ask for results in text format */ - - result = PQexecParams(remoteConnection, remoteCommand, - parameterCount, parameterType, parameterValues, - parameterLength, parameterFormat, resultFormat); - - if (PQresultStatus(result) != PGRES_COMMAND_OK && - PQresultStatus(result) != PGRES_TUPLES_OK) - { - psql_error("remote command \"%s\" failed with %s", - remoteCommand, PQerrorMessage(remoteConnection)); - PQclear(result); - - return NULL; - } - - return result; -} - - -/* - * InitTableMetadata allocates memory for table related metadata, and then - * executes remote calls on the master to initialize these metadata. On success, - * the function returns the fully initialized table metadata; on failure, it - * returns null. - */ -static TableMetadata * -InitTableMetadata(const char *tableName) -{ - TableMetadata *tableMetadata = NULL; - bool commandOK = true; - - tableMetadata = (TableMetadata *) pg_malloc0(sizeof(TableMetadata)); - - commandOK = MasterGetTableMetadata(tableName, tableMetadata); - if (!commandOK) - { - FreeTableMetadata(tableMetadata); - return NULL; - } - - commandOK = MasterGetTableDDLEvents(tableName, tableMetadata); - if (!commandOK) - { - FreeTableMetadata(tableMetadata); - return NULL; - } - - return tableMetadata; -} - - -/* Frees memory allocated to table metadata structure. */ -static void -FreeTableMetadata(TableMetadata *tableMetadata) -{ - if (tableMetadata->ddlEventList != NULL) - { - uint32 eventIndex = 0; - uint32 eventCount = tableMetadata->ddlEventCount; - - for (eventIndex = 0; eventIndex < eventCount; eventIndex++) - { - char *ddlEvent = tableMetadata->ddlEventList[eventIndex]; - - free(ddlEvent); - ddlEvent = NULL; - } - } - - free(tableMetadata->ddlEventList); - free(tableMetadata->partitionKey); - - free(tableMetadata); -} - - -/* - * InitShardMetadata allocates memory for shard metadata, and then executes - * remote calls on the master to initialize these metadata. On success, the - * function returns the fully initialized shard metadata; on failure, it returns - * null. - */ -static ShardMetadata * -InitShardMetadata(int shardPlacementPolicy) -{ - ShardMetadata *shardMetadata = NULL; - uint32 nodeCount = 0; - bool commandOK = true; - - shardMetadata = (ShardMetadata *) pg_malloc0(sizeof(ShardMetadata)); - - commandOK = MasterGetNewShardId(shardMetadata); - if (!commandOK) - { - FreeShardMetadata(shardMetadata); - return NULL; - } - - commandOK = MasterGetCandidateNodes(shardMetadata, shardPlacementPolicy); - if (!commandOK) - { - FreeShardMetadata(shardMetadata); - return NULL; - } - - nodeCount = shardMetadata->nodeCount; - shardMetadata->nodeStageList = (bool *) pg_malloc0(nodeCount * sizeof(bool)); - shardMetadata->shardMinValue = NULL; - shardMetadata->shardMaxValue = NULL; - shardMetadata->shardSize = 0; - - return shardMetadata; -} - - -/* Frees memory allocated to shard metadata structure. */ -static void -FreeShardMetadata(ShardMetadata *shardMetadata) -{ - if (shardMetadata->nodeNameList != NULL) - { - uint32 nodeIndex = 0; - uint32 nodeCount = shardMetadata->nodeCount; - - for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) - { - char *nodeName = shardMetadata->nodeNameList[nodeIndex]; - - free(nodeName); - nodeName = NULL; - } - } - - free(shardMetadata->nodeNameList); - free(shardMetadata->nodePortList); - - free(shardMetadata->nodeStageList); - free(shardMetadata->shardMinValue); - free(shardMetadata->shardMaxValue); - - free(shardMetadata); -} - - -/* Frees memory allocated to all shard metadata structures in given list. */ -static void -FreeShardMetadataList(ShardMetadata **shardMetadataList) -{ - uint32 listIndex = 0; - for (listIndex = 0; listIndex < MAX_SHARD_UPLOADS; listIndex++) - { - ShardMetadata *shardMetadata = shardMetadataList[listIndex]; - if (shardMetadata != NULL) - { - FreeShardMetadata(shardMetadata); - shardMetadata = NULL; - } - } -} - - -/* Frees memory common to all staging operations. */ -static void -FreeCommonStageData(copy_options *stageOptions, TableMetadata *tableMetadata, - ShardMetadata **shardMetadataList) -{ - free_copy_options(stageOptions); - FreeTableMetadata(tableMetadata); - FreeShardMetadataList(shardMetadataList); -} - - -/* - * ColumnarTableOptionsOK checks if the foreign table options for the distributed - * cstore_fdw table are suitable for staging or not. It returns true if they are - * and false if they are not. - */ -static bool -ColumnarTableOptionsOK(Oid relationOid) -{ - PGconn *masterNode = pset.db; - PGresult *queryResult = NULL; - ExecStatusType queryStatus = 0; - bool optionsOK = true; - - PQExpBuffer queryString = createPQExpBuffer(); - appendPQExpBuffer(queryString, GET_COLUMNAR_TABLE_FILENAME_OPTION, relationOid); - - /* get the filename option for the given cstore_fdw table */ - queryResult = PQexec(masterNode, queryString->data); - - queryStatus = PQresultStatus(queryResult); - if (queryStatus == PGRES_TUPLES_OK) - { - int tupleCount = PQntuples(queryResult); - Assert(tupleCount <= 1); - if (tupleCount == 1) - { - /* - * Since the result has one row, it implies that the filename - * option was specified, which is not allowed for distributed - * cstore_fdw tables. So, we error out. - */ - psql_error("\\stage: filename option is not allowed for distributed " - "columnar tables\n"); - optionsOK = false; - } - } - else - { - psql_error("\\stage: %s", PQerrorMessage(masterNode)); - optionsOK = false; - } - - PQclear(queryResult); - destroyPQExpBuffer(queryString); - - return optionsOK; -} - - -/* - * ExtendTablename appends shardId to given tablename, and returns extended name - * in a dynamically allocated string. - */ -static char * -ExtendTablename(const char *baseTablename, uint64 shardId) -{ - char *extendedTablename = (char *) pg_malloc0(NAMEDATALEN); - - snprintf(extendedTablename, NAMEDATALEN, "%s%c" UINT64_FORMAT, - baseTablename, SHARD_NAME_SEPARATOR, shardId); - - return extendedTablename; -} - - -/* Helper method that converts result value string to 64-bit unsigned integer. */ -static uint64 -GetValueUint64(const PGresult *result, int rowNumber, int columnNumber) -{ - char *valueString = NULL; - char *valueStringEnd = NULL; - uint64 value = 0; - - valueString = PQgetvalue(result, rowNumber, columnNumber); - if (valueString == NULL || (*valueString) == '\0') - { - return INVALID_UINT64; - } - - errno = 0; - value = strtoull(valueString, &valueStringEnd, 0); - - if (errno != 0 || (*valueStringEnd) != '\0') - { - return INVALID_UINT64; - } - - /* Server returned values for staging should never equal to "0". */ - Assert(value != INVALID_UINT64); - - return value; -} - - -/* - * MasterGetTableMetadata fetches from the master metadata related to a - * particular table. The function then parses relevant fields, assigns them to - * master metadata, and on success returns true. On failure, the function - * returns false. - */ -static bool -MasterGetTableMetadata(const char *tableName, TableMetadata *tableMetadata) -{ - const char *remoteCommand = MASTER_GET_TABLE_METADATA; - const char *parameterValue[1] = { tableName }; - const int parameterCount = 1; - int logicalRelidIndex = 0; - int partStorageTypeIndex = 0; - int partMethodIndex = 0; - int partKeyIndex = 0; - int partReplicaCountIndex = 0; - int partMaxSizeIndex = 0; - int partPlacementPolicyIndex = 0; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - char *tableStorageType = NULL; - char *partitionMethod = NULL; - char *partitionKey = NULL; - int partitionKeyLength = 0; - uint64 logicalRelid = 0; - uint64 shardReplicaCount = 0; - uint64 shardMaxSize = 0; - uint64 shardPlacementPolicy = 0; - - /* fetch table metadata for partitioning */ - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - if (result == NULL) - { - return false; /* error message already displayed */ - } - - /* find column numbers associated with column names */ - logicalRelidIndex = PQfnumber(result, LOGICAL_RELID_FIELD); - partStorageTypeIndex = PQfnumber(result, PART_STORAGE_TYPE_FIELD); - partMethodIndex = PQfnumber(result, PART_METHOD_FIELD); - partKeyIndex = PQfnumber(result, PART_KEY_FIELD); - partReplicaCountIndex = PQfnumber(result, PART_REPLICA_COUNT_FIELD); - partMaxSizeIndex = PQfnumber(result, PART_MAX_SIZE_FIELD); - partPlacementPolicyIndex = PQfnumber(result, PART_PLACEMENT_POLICY_FIELD); - - /* fetch variable length response */ - partitionKey = PQgetvalue(result, 0, partKeyIndex); - partitionKeyLength = PQgetlength(result, 0, partKeyIndex); - - /* fetch fixed length responses and convert those that are integers */ - tableStorageType = PQgetvalue(result, 0, partStorageTypeIndex); - partitionMethod = PQgetvalue(result, 0, partMethodIndex); - logicalRelid = GetValueUint64(result, 0, logicalRelidIndex); - shardReplicaCount = GetValueUint64(result, 0, partReplicaCountIndex); - shardMaxSize = GetValueUint64(result, 0, partMaxSizeIndex); - shardPlacementPolicy = GetValueUint64(result, 0, partPlacementPolicyIndex); - - if (partitionKeyLength <= 0 || logicalRelid == INVALID_UINT64 || - shardReplicaCount == INVALID_UINT64 || shardMaxSize == INVALID_UINT64 || - shardPlacementPolicy == INVALID_UINT64) - { - psql_error("remote command \"%s:%s\" failed with invalid table metadata\n", - remoteCommand, tableName); - PQclear(result); - - return false; - } - - /* set metadata related to the table */ - tableMetadata->partitionKey = (char *) pg_malloc0(partitionKeyLength + 1); - strncpy(tableMetadata->partitionKey, partitionKey, partitionKeyLength + 1); - - tableMetadata->tableStorageType = tableStorageType[0]; - tableMetadata->partitionMethod = partitionMethod[0]; - tableMetadata->logicalRelid = (uint32) logicalRelid; - tableMetadata->shardReplicaCount = (uint32) shardReplicaCount; - tableMetadata->shardMaxSize = (uint64) shardMaxSize; - tableMetadata->shardPlacementPolicy = (uint32) shardPlacementPolicy; - - PQclear(result); - - return true; -} - - -/* - * MasterGetTableDDLEvents fetches from the master a list of DDL events that - * relate to a particular table. The function then deep copies these DDL events - * to metadata, and on success returns true. On failure, the function returns - * false. - */ -static bool -MasterGetTableDDLEvents(const char *tableName, TableMetadata *tableMetadata) -{ - const char *remoteCommand = MASTER_GET_TABLE_DDL_EVENTS; - const char *parameterValue[1] = { tableName }; - const int parameterCount = 1; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - int ddlEventCount = 0; - int ddlEventIndex = 0; - - /* fetch DDL events needed for table creation */ - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - if (result == NULL) - { - return false; - } - - /* check that we have at least one DDL event */ - ddlEventCount = PQntuples(result); - if (ddlEventCount <= 0) - { - psql_error("remote command \"%s:%s\" failed to fetch DDL events\n", - remoteCommand, tableName); - PQclear(result); - - return false; - } - - /* allocate memory for DDL event list in metadata */ - tableMetadata->ddlEventList = (char **) pg_malloc0(ddlEventCount * sizeof(char *)); - tableMetadata->ddlEventCount = ddlEventCount; - - /* walk over fetched DDL event list, and assign them to metadata */ - for (ddlEventIndex = 0; ddlEventIndex < ddlEventCount; ddlEventIndex++) - { - char *ddlEvent = NULL; - char *ddlEventValue = PQgetvalue(result, ddlEventIndex, 0); - int ddlEventLength = PQgetlength(result, ddlEventIndex, 0); - - if (ddlEventLength <= 0) - { - psql_error("remote command \"%s:%s\" fetched empty DDL event\n", - remoteCommand, tableName); - PQclear(result); - - return false; - } - - /* deep copy DDL event and assign to metadata */ - ddlEvent = (char *) pg_malloc0(ddlEventLength + 1); - strncpy(ddlEvent, ddlEventValue, ddlEventLength + 1); - - tableMetadata->ddlEventList[ddlEventIndex] = ddlEvent; - } - - PQclear(result); - - return true; -} - - -/* - * MasterGetNewShardId fetches from the master a global shardId for the new - * shard to be created. The function then sets the shardId field in metadata, - * and on success returns true. On failure, the function returns false. - */ -static bool -MasterGetNewShardId(ShardMetadata *shardMetadata) -{ - const char *remoteCommand = MASTER_GET_NEW_SHARDID; - const char **parameterValue = NULL; - const int parameterCount = 0; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - uint64 shardId = 0; - - /* fetch unique shardId for shard to be created */ - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - if (result == NULL) - { - return false; - } - - /* get shard value string and convert it to 64-bit integer */ - shardId = GetValueUint64(result, 0, 0); - if (shardId == INVALID_UINT64) - { - psql_error("remote command \"%s\" failed with invalid shardId\n", - remoteCommand); - PQclear(result); - - return false; - } - - /* set metadata shardId; clear results */ - shardMetadata->shardId = shardId; - PQclear(result); - - return true; -} - - -/* - * MasterGetCandidateNodes fetches from the master a list of worker node names - * and port numbers for staging (uploading) shard data on the basis of the - * shard placement policy passed to it. The function then parses and deep copies - * these node names and port numbers to metadata, and on success returns true. - * On failure, the function returns false. - */ -static bool -MasterGetCandidateNodes(ShardMetadata *shardMetadata, int shardPlacementPolicy) -{ - const char *remoteCommand = NULL; - const char **parameterValue = NULL; - int parameterCount = 0; - char shardIdString[NAMEDATALEN]; - int nodeNameIndex = 0; - int nodePortIndex = 0; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - int nodeCount = 0; - int nodeIndex = 0; - - /* - * We choose the remote command for fetching node names and node ports, and the - * parameters to be passed to it on the basis of the shard placement policy. - */ - Assert(shardPlacementPolicy == SHARD_PLACEMENT_LOCAL_NODE_FIRST || - shardPlacementPolicy == SHARD_PLACEMENT_ROUND_ROBIN); - if (shardPlacementPolicy == SHARD_PLACEMENT_LOCAL_NODE_FIRST) - { - remoteCommand = MASTER_GET_LOCAL_FIRST_CANDIDATE_NODES; - parameterCount = 0; - - /* - * The master uses its connection's remote socket address to determine - * the client's hostname. The master then uses this hostname to allocate - * the first candidate node in the local node first policy. For all of - * this to happen, we should have connected to the master over TCP/IP. - */ - if (masterNode->laddr.addr.ss_family != AF_INET && - masterNode->laddr.addr.ss_family != AF_INET6) - { - psql_error("remote command \"%s\" needs TCP/IP connection to master node\n", - remoteCommand); - - return false; - } - } - else if (shardPlacementPolicy == SHARD_PLACEMENT_ROUND_ROBIN) - { - remoteCommand = MASTER_GET_ROUND_ROBIN_CANDIDATE_NODES; - parameterCount = 1; - - /* convert parameter to its string representation */ - snprintf(shardIdString, NAMEDATALEN, UINT64_FORMAT, shardMetadata->shardId); - - parameterValue = (const char **) pg_malloc0(parameterCount * sizeof(char *)); - parameterValue[0] = shardIdString; - } - - /* fetch worker node name/port list for uploading shard data */ - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - - if (parameterValue != NULL) - { - free(parameterValue); - } - - if (result == NULL) - { - return false; - } - - /* find column numbers associated with column names */ - nodeNameIndex = PQfnumber(result, NODE_NAME_FIELD); - nodePortIndex = PQfnumber(result, NODE_PORT_FIELD); - if (nodeNameIndex < 0 || nodePortIndex < 0) - { - psql_error("remote command \"%s\" failed with invalid response\n", - remoteCommand); - PQclear(result); - - return false; - } - - nodeCount = PQntuples(result); - if (nodeCount <= 0) - { - psql_error("remote command \"%s\" failed to fetch worker nodes\n", - remoteCommand); - PQclear(result); - - return false; - } - - /* allocate memory for node name/port list in metadata */ - shardMetadata->nodeNameList = (char **) pg_malloc0(nodeCount * sizeof(char *)); - shardMetadata->nodePortList = (uint32 *) pg_malloc0(nodeCount * sizeof(uint32)); - shardMetadata->nodeCount = nodeCount; - - /* walk over fetched node name/port list, and assign them to metadata */ - for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) - { - char *nodeName = NULL; - uint64 nodePort = 0; - - char *nodeNameValue = PQgetvalue(result, nodeIndex, nodeNameIndex); - int nodeNameLength = PQgetlength(result, nodeIndex, nodeNameIndex); - - if (nodeNameLength <= 0) - { - psql_error("remote command \"%s\" fetched empty node name\n", - remoteCommand); - PQclear(result); - - return false; - } - - /* deep copy node name and assign to metadata */ - nodeName = (char *) pg_malloc0(nodeNameLength + 1); - strncpy(nodeName, nodeNameValue, nodeNameLength + 1); - - shardMetadata->nodeNameList[nodeIndex] = nodeName; - - /* convert port value string to 64-bit integer, and assign to metadata */ - nodePort = GetValueUint64(result, nodeIndex, nodePortIndex); - if (nodePort == INVALID_UINT64) - { - psql_error("remote command \"%s\" failed to fetch valid port number\n", - remoteCommand); - PQclear(result); - - return false; - } - - shardMetadata->nodePortList[nodeIndex] = (uint32) nodePort; - } - - PQclear(result); - - return true; -} - - -/* Executes command on the master to insert shard tuple to pg_dist_shard. */ -static bool -MasterInsertShardRow(uint32 logicalRelid, char storageType, - const ShardMetadata *shardMetadata) -{ - const char *remoteCommand = MASTER_INSERT_SHARD_ROW; - const char *parameterValue[5]; - const int parameterCount = 5; - char logicalRelidString[NAMEDATALEN]; - char shardIdString[NAMEDATALEN]; - char storageTypeString[NAMEDATALEN]; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - - /* convert parameters to their string representations */ - snprintf(logicalRelidString, NAMEDATALEN, "%u", logicalRelid); - snprintf(shardIdString, NAMEDATALEN, UINT64_FORMAT, shardMetadata->shardId); - snprintf(storageTypeString, NAMEDATALEN, "%c", storageType); - - parameterValue[0] = logicalRelidString; - parameterValue[1] = shardIdString; - parameterValue[2] = storageTypeString; - parameterValue[3] = shardMetadata->shardMinValue; - parameterValue[4] = shardMetadata->shardMaxValue; - - /* insert shard metadata to master's system catalogs */ - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - if (result == NULL) - { - return false; - } - - PQclear(result); - return true; -} - - -/* - * MasterInsertPlacementRows executes commands on the master node to insert - * shard placement tuples to pg_dist_shard_placement. - */ -static bool -MasterInsertPlacementRows(const ShardMetadata *shardMetadata) -{ - const char *remoteCommand = MASTER_INSERT_PLACEMENT_ROW; - const char *parameterValue[5]; - const int parameterCount = 5; - char shardIdString[NAMEDATALEN]; - char shardLengthString[NAMEDATALEN]; - char nodePortString[NAMEDATALEN]; - - PGconn *masterNode = pset.db; - PGresult *result = NULL; - uint32 nodeIndex = 0; - - /* convert parameters to their string representations */ - snprintf(shardIdString, NAMEDATALEN, UINT64_FORMAT, shardMetadata->shardId); - snprintf(shardLengthString, NAMEDATALEN, UINT64_FORMAT, shardMetadata->shardSize); - - parameterValue[0] = shardIdString; - parameterValue[1] = FILE_FINALIZED; - parameterValue[2] = shardLengthString; - - for (nodeIndex = 0; nodeIndex < shardMetadata->nodeCount; nodeIndex++) - { - bool staged = shardMetadata->nodeStageList[nodeIndex]; - if (staged) - { - char *nodeName = shardMetadata->nodeNameList[nodeIndex]; - uint32 nodePort = shardMetadata->nodePortList[nodeIndex]; - - /* convert parameter to its string representation */ - snprintf(nodePortString, NAMEDATALEN, "%u", nodePort); - - parameterValue[3] = nodeName; - parameterValue[4] = nodePortString; - - result = ExecuteRemoteCommand(masterNode, remoteCommand, - parameterValue, parameterCount); - if (result == NULL) - { - return false; - } - - PQclear(result); - } - } - - return true; -} - - -/* - * MasterInsertShardMetadata finalizes with the master metadata for all shards - * staged to worker nodes. The function executes shard metadata insert commands - * within a single transaction so that either all or none of the metadata are - * finalized. On success, the function commits the transaction and returns true. - * On failure, the function rolls back the transaction and returns false. - */ -static bool -MasterInsertShardMetadata(uint32 logicalRelid, char storageType, - ShardMetadata **shardMetadataList) -{ - PGconn *masterNode = pset.db; - uint32 listIndex = 0; - bool issued = true; - bool metadataOK = true; - - issued = IssueTransactionCommand(masterNode, BEGIN_COMMAND); - if (!issued) - { - return false; - } - - for (listIndex = 0; listIndex < MAX_SHARD_UPLOADS; listIndex++) - { - ShardMetadata *shardMetadata = shardMetadataList[listIndex]; - if (shardMetadata != NULL) - { - bool shardRowOK = MasterInsertShardRow(logicalRelid, storageType, - shardMetadata); - bool workerRowOK = MasterInsertPlacementRows(shardMetadata); - - if (!shardRowOK || !workerRowOK) - { - IssueTransactionCommand(masterNode, ROLLBACK_COMMAND); - - metadataOK = false; - break; - } - } - } - - if (metadataOK) - { - issued = IssueTransactionCommand(masterNode, COMMIT_COMMAND); - if (!issued) - { - return false; - } - } - - return metadataOK; -} - - -/* Issues given transaction command on the remote node. */ -static bool -IssueTransactionCommand(PGconn *connection, const char *command) -{ - PGresult *result = PQexec(connection, command); - ExecStatusType resultStatus = PQresultStatus(result); - - if (resultStatus != PGRES_COMMAND_OK) - { - psql_error("%s", PQerrorMessage(connection)); - PQclear(result); - - return false; - } - - PQclear(result); - return true; -} - - -/* - * StageTableData creates the table on the remote node, and then uses the - * copy protocol to upload data to the table. On success, the function updates - * given file offset, commits the transaction, and returns true. On failure, the - * function rolls back the transaction and returns false. - */ -static bool -StageTableData(PGconn *workerNode, uint64 shardId, - TableMetadata *tableMetadata, copy_options *stageOptions, - uint64 currentFileOffset, uint64 *nextFileOffset) -{ - bool issued = false; - bool createOK = false; - bool transmitOK = false; - - /* start explicit transaction; this also skips WAL-logging */ - issued = IssueTransactionCommand(workerNode, BEGIN_COMMAND); - if (!issued) - { - return false; - } - - createOK = CreateRegularTable(workerNode, shardId, tableMetadata); - if (!createOK) - { - IssueTransactionCommand(workerNode, ROLLBACK_COMMAND); - return false; - } - - transmitOK = TransmitTableData(workerNode, shardId, tableMetadata->shardMaxSize, - stageOptions, currentFileOffset, nextFileOffset); - if (!transmitOK) - { - IssueTransactionCommand(workerNode, ROLLBACK_COMMAND); - return false; - } - - issued = IssueTransactionCommand(workerNode, COMMIT_COMMAND); - if (!issued) - { - return false; - } - - return true; -} - - -/* - * StageForeignData determines the remote file path to upload the given file, - * creates the foreign table on the remote node, and uploads the file to the - * remote node at the determined path. Then, it commits the transaction and - * returns true. On failure, the function rolls back the transaction and - * returns false. - */ -static bool -StageForeignData(PGconn *workerNode, uint64 shardId, TableMetadata *tableMetadata, - copy_options *stageOptions) -{ - bool issued = false; - bool transmitOK = false; - bool createOK = false; - - char remoteFilePath[MAXPGPATH]; - char *extendedTableName = ExtendTablename(stageOptions->tableName, shardId); - snprintf(remoteFilePath, MAXPGPATH, "%s/%s", FOREIGN_CACHED_DIR, extendedTableName); - free(extendedTableName); - - /* start explicit transaction */ - issued = IssueTransactionCommand(workerNode, BEGIN_COMMAND); - if (!issued) - { - return false; - } - - createOK = CreateForeignTable(workerNode, shardId, tableMetadata, - stageOptions->tableName, remoteFilePath); - if (!createOK) - { - IssueTransactionCommand(workerNode, ROLLBACK_COMMAND); - return false; - } - - transmitOK = TransmitFile(workerNode, stageOptions->file, remoteFilePath); - if (!transmitOK) - { - IssueTransactionCommand(workerNode, ROLLBACK_COMMAND); - return false; - } - - issued = IssueTransactionCommand(workerNode, COMMIT_COMMAND); - if (!issued) - { - return false; - } - - return true; -} - - -/* - * CreateRegularTable executes DDL commands to create the table on the worker - * node. On success the function returns true, otherwise it returns false. - */ -static bool -CreateRegularTable(PGconn *workerNode, int64 shardId, TableMetadata *tableMetadata) -{ - bool createOK = true; - const uint32 ddlEventCount = tableMetadata->ddlEventCount; - uint32 ddlEventIndex = 0; - - /* execute DDL statements on remote node to create table and indexes */ - for (ddlEventIndex = 0; ddlEventIndex < ddlEventCount; ddlEventIndex++) - { - char *ddlEvent = tableMetadata->ddlEventList[ddlEventIndex]; - - bool ddlApplied = ApplyShardDDLCommand(workerNode, shardId, ddlEvent); - if (!ddlApplied) - { - createOK = false; - break; - } - } - - return createOK; -} - - -/* - * CreateForeignTable executes DDL commands to create the foreign table on the - * worker node, and then sets the foreign table's filename to the given file - * path. On success the function returns true, otherwise it returns false. - */ -static bool -CreateForeignTable(PGconn *workerNode, uint64 shardId, TableMetadata *tableMetaData, - const char *tableName, const char *filePath) -{ - bool createOK = false; - bool alterQueryOK = true; - PQExpBuffer alterQueryString = NULL; - uint32 ddlEventIndex = 0; - uint32 ddlEventCount = tableMetaData->ddlEventCount; - - /* replay DDL commands to create foreign table */ - for (ddlEventIndex = 0; ddlEventIndex < ddlEventCount; ddlEventIndex++) - { - char *ddlEvent = tableMetaData->ddlEventList[ddlEventIndex]; - - bool ddlApplied = ApplyShardDDLCommand(workerNode, shardId, ddlEvent); - if (!ddlApplied) - { - return false; - } - } - - /* then issue DDL command to set foreign table's file path */ - alterQueryString = createPQExpBuffer(); - appendPQExpBuffer(alterQueryString, SET_FOREIGN_TABLE_FILENAME, - tableName, filePath); - - alterQueryOK = ApplyShardDDLCommand(workerNode, shardId, alterQueryString->data); - if (alterQueryOK) - { - createOK = true; - } - - destroyPQExpBuffer(alterQueryString); - - return createOK; -} - - -/* - * ApplyShardDDLCommand calls remote function on the worker node to extend the - * given ddl command with the shardId, and apply this extended command on the - * worker database. The function then returns its success status. - */ -static bool -ApplyShardDDLCommand(PGconn *workerNode, uint64 shardId, const char *ddlCommand) -{ - const char *remoteCommand = APPLY_SHARD_DDL_COMMAND; - const char *parameterValue[2]; - const int parameterCount = 2; - PGresult *ddlResult = NULL; - - char shardIdString[NAMEDATALEN]; - snprintf(shardIdString, NAMEDATALEN, UINT64_FORMAT, shardId); - - parameterValue[0] = shardIdString; - parameterValue[1] = ddlCommand; - - ddlResult = ExecuteRemoteCommand(workerNode, remoteCommand, - parameterValue, parameterCount); - if (ddlResult == NULL) - { - return false; - } - - PQclear(ddlResult); - return true; -} - - -/* - * TransmitTableData uploads data from the local file to the remote table using - * the copy protocol. On success, the function updates given file offset and - * returns true. On failure, it returns false. - */ -static bool -TransmitTableData(PGconn *workerNode, uint64 shardId, - uint64 shardMaxSize, copy_options *stageOptions, - uint64 currentFileOffset, uint64 *nextFileOffset) -{ - bool transmitOK = true; - bool fileOK = true; - bool closeFileOK = true; - int seeked = 0; - - PQExpBuffer queryString = NULL; - PGresult *copyResult = NULL; - ExecStatusType copyStatus = 0; - bool copyIsBinary = false; - FILE *stageStream = NULL; - char *extendedTablename = NULL; - - /* if file doesn't exists, return immediately */ - fileOK = FileStreamOK((const copy_options *) stageOptions); - if (!fileOK) - { - return false; - } - - /* open a file stream to stage data from, and seek to given file offset */ - stageStream = OpenCopyStream(stageOptions); - if (stageStream == NULL) - { - return false; - } - - seeked = fseeko(stageStream, (off_t) (currentFileOffset), SEEK_SET); - if (seeked < 0) - { - psql_error("%s: %s\n", stageOptions->file, strerror(errno)); - return false; - } - - /* extend table name with shardId for the query */ - extendedTablename = ExtendTablename(stageOptions->tableName, shardId); - - /* - * Now start staging data to the worker table. For this, we first form the - * query string, and then execute the query over the copy protocol. - */ - queryString = CreateCopyQueryString(extendedTablename, stageOptions->columnList, - stageOptions->after_tofrom); - - copyResult = PQexec(workerNode, queryString->data); - - copyStatus = PQresultStatus(copyResult); - copyIsBinary = (bool) PQbinaryTuples(copyResult); - - transmitOK = HandleCopyData(workerNode, copyStatus, copyIsBinary, - stageStream, shardMaxSize); - - /* clean up after remote query */ - PQclear(copyResult); - destroyPQExpBuffer(queryString); - free(extendedTablename); - - /* determine new offset in file */ - if (transmitOK && stageOptions->file != NULL) - { - int64 nextStreamOffset = ftello(stageStream); - if (nextStreamOffset < 0) - { - psql_error("%s: %s\n", stageOptions->file, strerror(errno)); - transmitOK = false; - } - else - { - Assert(nextStreamOffset >= currentFileOffset); - (*nextFileOffset) = nextStreamOffset; - } - } - - closeFileOK = CloseCopyStream(stageOptions, stageStream); - if (!closeFileOK) - { - transmitOK = false; - } - - return transmitOK; -} - - -/* - * TransmitFile uploads file on the local path to the given remote file path on - * worker node. The function uses the copy protocol to upload data, and then - * returns its success status. - */ -static bool -TransmitFile(PGconn *workerNode, const char *localPath, const char *remotePath) -{ - const uint64 copySizeUnlimited = 0; - FILE *transmitStream = NULL; - PQExpBuffer queryString = NULL; - PGresult *copyResult = NULL; - ExecStatusType copyStatus = 0; - bool copyIsBinary = false; - bool transmitOK = false; - int closeStatus = 0; - - transmitStream = fopen(localPath, PG_BINARY_R); - if (transmitStream == NULL) - { - psql_error("%s: %s\n", localPath, strerror(errno)); - - return false; - } - - queryString = createPQExpBuffer(); - appendPQExpBuffer(queryString, TRANSMIT_REGULAR_COMMAND, remotePath); - - /* execute remote query to start uploading data over copy protocol */ - copyResult = PQexec(workerNode, queryString->data); - copyStatus = PQresultStatus(copyResult); - copyIsBinary = (bool) PQbinaryTuples(copyResult); - - PQclear(copyResult); - destroyPQExpBuffer(queryString); - - transmitOK = HandleCopyData(workerNode, copyStatus, copyIsBinary, - transmitStream, copySizeUnlimited); - if (!transmitOK) - { - return false; - } - - closeStatus = fclose(transmitStream); - if (closeStatus != 0) - { - psql_error("%s: %s\n", localPath, strerror(errno)); - - return false; - } - - return true; -} - - -/* Checks if a stream can be opened from the given stage options. */ -static bool -FileStreamOK(const copy_options *stageOptions) -{ - FILE *fileStream = OpenCopyStream(stageOptions); - if (fileStream == NULL) - { - return false; - } - - CloseCopyStream(stageOptions, fileStream); - - return true; -} - - -/* Creates query string for copy command from the given staging options. */ -static PQExpBuffer -CreateCopyQueryString(const char *tableName, const char *columnList, - const char *afterToFrom) -{ - PQExpBuffer queryString = createPQExpBuffer(); - - printfPQExpBuffer(queryString, "COPY "); - appendPQExpBuffer(queryString, "%s ", tableName); - - if (columnList != NULL) - { - appendPQExpBuffer(queryString, "%s ", columnList); - } - - appendPQExpBuffer(queryString, "FROM STDIN "); - - if (afterToFrom != NULL) - { - appendPQExpBufferStr(queryString, afterToFrom); - } - - return queryString; -} - - -/* - * ShardTableSize executes a command on the worker node to determine the size of - * the table representing the shard. On success, the function returns the table - * size on disk. On failure, the function returns -1. - */ -static int64 -ShardTableSize(PGconn *workerNode, const char *tablename, uint64 shardId) -{ - PGresult *result = NULL; - char remoteCommand[MAXPGPATH]; - char *extendedTablename = NULL; - int64 shardTableSize = -1; - - extendedTablename = ExtendTablename(tablename, shardId); - snprintf(remoteCommand, MAXPGPATH, SHARD_TABLE_SIZE_COMMAND, extendedTablename); - - result = PQexec(workerNode, remoteCommand); - if (result == NULL) - { - PQclear(result); - free(extendedTablename); - - return -1; - } - - shardTableSize = (int64) GetValueUint64(result, 0, 0); - if (shardTableSize <= 0) - { - psql_error("remote command \"%s\" fetched invalid table size\n", remoteCommand); - PQclear(result); - free(extendedTablename); - - return -1; - } - - PQclear(result); - free(extendedTablename); - - return shardTableSize; -} - - -/* - * ShardFileSize executes a command on the worker node to determine the size of - * the foreign file represneting the shard. On success, the function returns the - * file size on disk. On failure, the function returns -1. - */ -static int64 -ShardFileSize(PGconn *workerNode, const char *tablename, uint64 shardId) -{ - int64 tableSize = -1; - char remoteFilePath[MAXPGPATH]; - char remoteCommand[MAXPGPATH]; - PGresult *result = NULL; - - char *extendedTableName = ExtendTablename(tablename, shardId); - snprintf(remoteFilePath, MAXPGPATH, "%s/%s", FOREIGN_CACHED_DIR, extendedTableName); - free(extendedTableName); - - snprintf(remoteCommand, MAXPGPATH, REMOTE_FILE_SIZE_COMMAND, remoteFilePath); - - result = PQexec(workerNode, remoteCommand); - if (result == NULL) - { - PQclear(result); - return -1; - } - - tableSize = (int64) GetValueUint64(result, 0, 0); - if (tableSize <= 0) - { - psql_error("remote command \"%s\" fetched invalid table size\n", remoteCommand); - PQclear(result); - - return -1; - } - - PQclear(result); - - return tableSize; -} - - -/* - * ShardColumnarTableSize executes a command on the worker node to determine the - * size of the cstore_fdw table representing the shard. On success, the function - * returns the table size on disk. On failure, the function returns -1. - */ -static int64 -ShardColumnarTableSize(PGconn *workerNode, const char *tablename, uint64 shardId) -{ - PGresult *result = NULL; - char remoteCommand[MAXPGPATH]; - char *extendedTablename = NULL; - int64 shardTableSize = -1; - - extendedTablename = ExtendTablename(tablename, shardId); - snprintf(remoteCommand, MAXPGPATH, SHARD_COLUMNAR_TABLE_SIZE_COMMAND, - extendedTablename); - - result = PQexec(workerNode, remoteCommand); - if (result == NULL) - { - PQclear(result); - free(extendedTablename); - - return -1; - } - - shardTableSize = (int64) GetValueUint64(result, 0, 0); - if (shardTableSize <= 0) - { - psql_error("remote command \"%s\" fetched invalid table size\n", remoteCommand); - PQclear(result); - free(extendedTablename); - - return -1; - } - - PQclear(result); - free(extendedTablename); - - return shardTableSize; -} - - -/* - * ShardMinMaxValues executes command on the worker node to determine minimum - * and maximum values for partition key expression. On successful execution, the - * function sets min and max values in shard metadata and returns true. On - * failure, the function returns false. - */ -static bool -ShardMinMaxValues(PGconn *workerNode, const char *tablename, - const char *partitionKey, ShardMetadata *shardMetadata) -{ - const int MinValueIndex = 0; - const int MaxValueIndex = 1; - - PGresult *result = NULL; - char remoteCommand[MAXPGPATH]; - char *extendedTablename = NULL; - char *minValue = NULL; - char *maxValue = NULL; - int minValueLength = 0; - int maxValueLength = 0; - - extendedTablename = ExtendTablename(tablename, shardMetadata->shardId); - snprintf(remoteCommand, MAXPGPATH, SHARD_MIN_MAX_COMMAND, - partitionKey, partitionKey, extendedTablename); - - result = PQexec(workerNode, remoteCommand); - if (result == NULL) - { - PQclear(result); - free(extendedTablename); - - return false; - } - - minValue = PQgetvalue(result, 0, MinValueIndex); - maxValue = PQgetvalue(result, 0, MaxValueIndex); - - minValueLength = PQgetlength(result, 0, MinValueIndex); - maxValueLength = PQgetlength(result, 0, MaxValueIndex); - - if (minValueLength <= 0 || maxValueLength <= 0) - { - psql_error("remote command \"%s\" fetched empty min/max values\n", - remoteCommand); - PQclear(result); - free(extendedTablename); - - return false; - } - - shardMetadata->shardMinValue = (char *) pg_malloc0(minValueLength + 1); - shardMetadata->shardMaxValue = (char *) pg_malloc0(maxValueLength + 1); - - strncpy(shardMetadata->shardMinValue, minValue, minValueLength + 1); - strncpy(shardMetadata->shardMaxValue, maxValue, maxValueLength + 1); - - PQclear(result); - free(extendedTablename); - - return true; -} diff --git a/src/bin/csql/stage.h b/src/bin/csql/stage.h deleted file mode 100644 index 821d62ccd..000000000 --- a/src/bin/csql/stage.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * csql - the Citus interactive terminal - * stage.h - * Declarations for the csql meta-command \stage. These declarations define a - * protocol for the client to communicate to the master and worker nodes. - * - * Copyright (c) 2012-2016, Citus Data, Inc. - * - * $Id$ - */ - -#ifndef STAGE_H -#define STAGE_H - -#include "postgres_fe.h" - - -/* Server returned values never equal to zero. */ -#define INVALID_UINT64 0 -#define MAX_SHARD_UPLOADS 256 -#define SHARD_NAME_SEPARATOR '_' - -/* Connection parameters set to enable connect timeouts in seconds. */ -#define CONN_INFO_TEMPLATE "dbname=%s connect_timeout=%u" -#define CLIENT_CONNECT_TIMEOUT 20 - -/* Transaction related commands used in talking to the master and primary. */ -#define BEGIN_COMMAND "BEGIN" -#define COMMIT_COMMAND "COMMIT" -#define ROLLBACK_COMMAND "ROLLBACK" - -/* Names of remote function calls to execute on the master. */ -#define MASTER_GET_TABLE_METADATA "SELECT * FROM master_get_table_metadata($1::text)" -#define MASTER_GET_TABLE_DDL_EVENTS "SELECT * FROM master_get_table_ddl_events($1::text)" -#define MASTER_GET_NEW_SHARDID "SELECT * FROM master_get_new_shardid()" -#define MASTER_GET_LOCAL_FIRST_CANDIDATE_NODES \ - "SELECT * FROM master_get_local_first_candidate_nodes()" -#define MASTER_GET_ROUND_ROBIN_CANDIDATE_NODES \ - "SELECT * FROM master_get_round_robin_candidate_nodes($1::int8)" - -#define MASTER_INSERT_SHARD_ROW \ - "SELECT master_stage_shard_row(\ - $1::oid, $2::int8, $3::\"char\", $4::text, $5::text)" -#define MASTER_INSERT_PLACEMENT_ROW \ - "SELECT master_stage_shard_placement_row(\ - $1::int8, $2::int4, $3::int8, $4::text, $5::int4)" - -/* Column names used to identify response fields as returned from the master. */ -#define LOGICAL_RELID_FIELD "logical_relid" -#define PART_STORAGE_TYPE_FIELD "part_storage_type" -#define PART_METHOD_FIELD "part_method" -#define PART_KEY_FIELD "part_key" -#define PART_REPLICA_COUNT_FIELD "part_replica_count" -#define PART_MAX_SIZE_FIELD "part_max_size" -#define PART_PLACEMENT_POLICY_FIELD "part_placement_policy" -#define NODE_NAME_FIELD "node_name" -#define NODE_PORT_FIELD "node_port" - -/* the tablename in the overloaded COPY statement is the to-be-transferred file */ -#define TRANSMIT_REGULAR_COMMAND "COPY \"%s\" FROM STDIN WITH (format 'transmit')" -#define SHARD_MIN_MAX_COMMAND "SELECT min(%s), max(%s) FROM %s" -#define SHARD_TABLE_SIZE_COMMAND "SELECT pg_table_size('%s')" -#define SET_FOREIGN_TABLE_FILENAME "ALTER FOREIGN TABLE %s OPTIONS (SET filename '%s')" -#define GET_COLUMNAR_TABLE_FILENAME_OPTION \ - "SELECT * FROM (SELECT (pg_options_to_table(ftoptions)).* FROM pg_foreign_table " \ - "WHERE ftrelid = %u) AS Q WHERE option_name = 'filename';" -#define APPLY_SHARD_DDL_COMMAND \ - "SELECT * FROM worker_apply_shard_ddl_command ($1::int8, $2::text)" -#define REMOTE_FILE_SIZE_COMMAND "SELECT size FROM pg_stat_file('%s')" -#define SHARD_COLUMNAR_TABLE_SIZE_COMMAND "SELECT cstore_table_size('%s')" - -/* Types that define table storage type and shard state. */ -#define STORAGE_TYPE_TABLE 't' -#define STORAGE_TYPE_FOREIGN 'f' -#define STORAGE_TYPE_COLUMNAR 'c' -#define FILE_FINALIZED "1" - -/* Shard placement policy types. */ -#define SHARD_PLACEMENT_LOCAL_NODE_FIRST 1 -#define SHARD_PLACEMENT_ROUND_ROBIN 2 - -/* Directory to put foreign table files on secondary nodes. */ -#define FOREIGN_CACHED_DIR "pg_foreign_file/cached" - - -/* - * TableMetadata keeps table related metadata to which the user requested to - * stage data. These metadata are retrieved from the master as read-only; and - * are cached and reused as new shards are created. - */ -typedef struct TableMetadata -{ - uint32 logicalRelid; /* table's relationId on the master */ - char tableStorageType; /* relay file, foreign table, or table */ - char partitionMethod; /* table's partition method */ - char *partitionKey; /* partition key expression */ - uint32 shardReplicaCount; /* shard replication factor */ - uint64 shardMaxSize; /* create new shard when shard reaches max size */ - uint32 shardPlacementPolicy; /* policy to use when choosing nodes to place shards */ - - char **ddlEventList; /* DDL statements used for creating new shard */ - uint32 ddlEventCount; /* DDL statement count; statement list size */ -} TableMetadata; - - -/* - * ShardMetadata keeps metadata related to one shard for which we are currently - * staging data. Some parts of these metadata are retrieved from the master as - * read-only (shardId, node names and port numbers); and other parts are updated - * by the client as we stage data to clients (shard size and stage statuses). - */ -typedef struct ShardMetadata -{ - uint64 shardId; /* global shardId; created on the master node */ - - char **nodeNameList; /* candidate node name list for shard uploading */ - uint32 *nodePortList; /* candidate node port list for shard uploading */ - uint32 nodeCount; /* candidate node count; node list size */ - bool *nodeStageList; /* shard uploaded to corresponding candidate node? */ - - char *shardMinValue; /* partition key's minimum value in shard */ - char *shardMaxValue; /* partition key's maximum value in shard */ - uint64 shardSize; /* shard size; updated during staging */ -} ShardMetadata; - - -/* Function declaration for staging data to remote nodes */ -bool DoStageData(const char *stageCommand); - - -#endif /* STAGE_H */ diff --git a/src/bin/csql/startup.c b/src/bin/csql/startup.c deleted file mode 100644 index 8b1777e24..000000000 --- a/src/bin/csql/startup.c +++ /dev/null @@ -1,896 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/startup.c - */ -#include "postgres_fe.h" - -#include - -#ifndef WIN32 -#include -#else /* WIN32 */ -#include -#include -#endif /* WIN32 */ - -#include "getopt_long.h" - -#include - -#include "command.h" -#include "common.h" -#include "describe.h" -#include "help.h" -#include "input.h" -#include "mainloop.h" -#include "print.h" -#include "settings.h" - - - -/* - * Global psql options - */ -PsqlSettings pset; - -#ifndef WIN32 -#define SYSPSQLRC "psqlrc" -#define PSQLRC ".psqlrc" -#else -#define SYSPSQLRC "psqlrc" -#define PSQLRC "psqlrc.conf" -#endif - -/* - * Structures to pass information between the option parsing routine - * and the main function - */ -enum _actions -{ - ACT_NOTHING = 0, - ACT_SINGLE_SLASH, - ACT_LIST_DB, - ACT_SINGLE_QUERY, - ACT_FILE -}; - -struct adhoc_opts -{ - char *dbname; - char *host; - char *port; - char *username; - char *logfilename; - enum _actions action; - char *action_string; - bool no_readline; - bool no_psqlrc; - bool single_txn; -}; - -static void parse_psql_options(int argc, char *argv[], - struct adhoc_opts * options); -static void process_psqlrc(char *argv0); -static void process_psqlrc_file(char *filename); -static void showVersion(void); -static void EstablishVariableSpace(void); - -#define NOPAGER 0 - -/* - * - * main - * - */ -int -main(int argc, char *argv[]) -{ - struct adhoc_opts options; - int successResult; - char *password = NULL; - char *password_prompt = NULL; - bool new_pass; - - set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql")); - - if (argc > 1) - { - if ((strcmp(argv[1], "-?") == 0) || (argc == 2 && (strcmp(argv[1], "--help") == 0))) - { - usage(NOPAGER); - exit(EXIT_SUCCESS); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - showVersion(); - exit(EXIT_SUCCESS); - } - } - -#ifdef WIN32 - setvbuf(stderr, NULL, _IONBF, 0); -#endif - - pset.progname = get_progname(argv[0]); - - pset.db = NULL; - setDecimalLocale(); - pset.encoding = PQenv2encoding(); - pset.queryFout = stdout; - pset.queryFoutPipe = false; - pset.copyStream = NULL; - pset.cur_cmd_source = stdin; - pset.cur_cmd_interactive = false; - - /* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */ - pset.popt.topt.format = PRINT_ALIGNED; - pset.popt.topt.border = 1; - pset.popt.topt.pager = 1; - pset.popt.topt.pager_min_lines = 0; - pset.popt.topt.start_table = true; - pset.popt.topt.stop_table = true; - pset.popt.topt.default_footer = true; - - pset.popt.topt.unicode_border_linestyle = UNICODE_LINESTYLE_SINGLE; - pset.popt.topt.unicode_column_linestyle = UNICODE_LINESTYLE_SINGLE; - pset.popt.topt.unicode_header_linestyle = UNICODE_LINESTYLE_SINGLE; - - refresh_utf8format(&(pset.popt.topt)); - - /* We must get COLUMNS here before readline() sets it */ - pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0; - - pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); - - pset.getPassword = TRI_DEFAULT; - - EstablishVariableSpace(); - - SetVariable(pset.vars, "VERSION", PG_VERSION_STR); - - /* Default values for variables */ - SetVariableBool(pset.vars, "AUTOCOMMIT"); - SetVariable(pset.vars, "VERBOSITY", "default"); - SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); - SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); - SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); - - parse_psql_options(argc, argv, &options); - - /* - * If no action was specified and we're in non-interactive mode, treat it - * as if the user had specified "-f -". This lets single-transaction mode - * work in this case. - */ - if (options.action == ACT_NOTHING && pset.notty) - { - options.action = ACT_FILE; - options.action_string = NULL; - } - - /* Bail out if -1 was specified but will be ignored. */ - if (options.single_txn && options.action != ACT_FILE && options.action == ACT_NOTHING) - { - fprintf(stderr, _("%s: -1 can only be used in non-interactive mode\n"), pset.progname); - exit(EXIT_FAILURE); - } - - if (!pset.popt.topt.fieldSep.separator && - !pset.popt.topt.fieldSep.separator_zero) - { - pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP); - pset.popt.topt.fieldSep.separator_zero = false; - } - if (!pset.popt.topt.recordSep.separator && - !pset.popt.topt.recordSep.separator_zero) - { - pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP); - pset.popt.topt.recordSep.separator_zero = false; - } - - if (options.username == NULL) - password_prompt = pg_strdup(_("Password: ")); - else - password_prompt = psprintf(_("Password for user %s: "), - options.username); - - if (pset.getPassword == TRI_YES) - password = simple_prompt(password_prompt, 100, false); - - /* loop until we have a password if requested by backend */ - do - { -#define PARAMS_ARRAY_SIZE 8 - const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); - const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); - - keywords[0] = "host"; - values[0] = options.host; - keywords[1] = "port"; - values[1] = options.port; - keywords[2] = "user"; - values[2] = options.username; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = (options.action == ACT_LIST_DB && - options.dbname == NULL) ? - "postgres" : options.dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; - - new_pass = false; - pset.db = PQconnectdbParams(keywords, values, true); - free(keywords); - free(values); - - if (PQstatus(pset.db) == CONNECTION_BAD && - PQconnectionNeedsPassword(pset.db) && - password == NULL && - pset.getPassword != TRI_NO) - { - PQfinish(pset.db); - password = simple_prompt(password_prompt, 100, false); - new_pass = true; - } - } while (new_pass); - - free(password); - free(password_prompt); - - if (PQstatus(pset.db) == CONNECTION_BAD) - { - fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db)); - PQfinish(pset.db); - exit(EXIT_BADCONN); - } - - setup_cancel_handler(); - - PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); - - SyncVariables(); - - if (options.action == ACT_LIST_DB) - { - int success; - - if (!options.no_psqlrc) - process_psqlrc(argv[0]); - - success = listAllDbs(NULL, false); - PQfinish(pset.db); - exit(success ? EXIT_SUCCESS : EXIT_FAILURE); - } - - if (options.logfilename) - { - pset.logfile = fopen(options.logfilename, "a"); - if (!pset.logfile) - { - fprintf(stderr, _("%s: could not open log file \"%s\": %s\n"), - pset.progname, options.logfilename, strerror(errno)); - exit(EXIT_FAILURE); - } - } - - /* - * Now find something to do - */ - - /* - * process file given by -f - */ - if (options.action == ACT_FILE) - { - if (!options.no_psqlrc) - process_psqlrc(argv[0]); - - successResult = process_file(options.action_string, options.single_txn, false); - } - - /* - * process slash command if one was given to -c - */ - else if (options.action == ACT_SINGLE_SLASH) - { - PsqlScanState scan_state; - - if (pset.echo == PSQL_ECHO_ALL) - puts(options.action_string); - - scan_state = psql_scan_create(); - psql_scan_setup(scan_state, - options.action_string, - strlen(options.action_string)); - - successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR - ? EXIT_SUCCESS : EXIT_FAILURE; - - psql_scan_destroy(scan_state); - } - - /* - * If the query given to -c was a normal one, send it - */ - else if (options.action == ACT_SINGLE_QUERY) - { - if (pset.echo == PSQL_ECHO_ALL) - puts(options.action_string); - - successResult = SendQuery(options.action_string) - ? EXIT_SUCCESS : EXIT_FAILURE; - } - - /* - * or otherwise enter interactive main loop - */ - else - { - if (!options.no_psqlrc) - process_psqlrc(argv[0]); - - connection_warnings(true); - if (!pset.quiet) - printf(_("Type \"help\" for help.\n\n")); - initializeInput(options.no_readline ? 0 : 1); - successResult = MainLoop(stdin); - } - - /* clean up */ - if (pset.logfile) - fclose(pset.logfile); - PQfinish(pset.db); - setQFout(NULL); - - return successResult; -} - - -/* - * Parse command line options - */ - -static void -parse_psql_options(int argc, char *argv[], struct adhoc_opts * options) -{ - static struct option long_options[] = - { - {"echo-all", no_argument, NULL, 'a'}, - {"no-align", no_argument, NULL, 'A'}, - {"command", required_argument, NULL, 'c'}, - {"dbname", required_argument, NULL, 'd'}, - {"echo-queries", no_argument, NULL, 'e'}, - {"echo-errors", no_argument, NULL, 'b'}, - {"echo-hidden", no_argument, NULL, 'E'}, - {"file", required_argument, NULL, 'f'}, - {"field-separator", required_argument, NULL, 'F'}, - {"field-separator-zero", no_argument, NULL, 'z'}, - {"host", required_argument, NULL, 'h'}, - {"html", no_argument, NULL, 'H'}, - {"list", no_argument, NULL, 'l'}, - {"log-file", required_argument, NULL, 'L'}, - {"no-readline", no_argument, NULL, 'n'}, - {"single-transaction", no_argument, NULL, '1'}, - {"output", required_argument, NULL, 'o'}, - {"port", required_argument, NULL, 'p'}, - {"pset", required_argument, NULL, 'P'}, - {"quiet", no_argument, NULL, 'q'}, - {"record-separator", required_argument, NULL, 'R'}, - {"record-separator-zero", no_argument, NULL, '0'}, - {"single-step", no_argument, NULL, 's'}, - {"single-line", no_argument, NULL, 'S'}, - {"tuples-only", no_argument, NULL, 't'}, - {"table-attr", required_argument, NULL, 'T'}, - {"username", required_argument, NULL, 'U'}, - {"set", required_argument, NULL, 'v'}, - {"variable", required_argument, NULL, 'v'}, - {"version", no_argument, NULL, 'V'}, - {"no-password", no_argument, NULL, 'w'}, - {"password", no_argument, NULL, 'W'}, - {"expanded", no_argument, NULL, 'x'}, - {"no-psqlrc", no_argument, NULL, 'X'}, - {"help", optional_argument, NULL, 1}, - {NULL, 0, NULL, 0} - }; - - int optindex; - int c; - - memset(options, 0, sizeof *options); - - while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01", - long_options, &optindex)) != -1) - { - switch (c) - { - case 'a': - SetVariable(pset.vars, "ECHO", "all"); - break; - case 'A': - pset.popt.topt.format = PRINT_UNALIGNED; - break; - case 'b': - SetVariable(pset.vars, "ECHO", "errors"); - break; - case 'c': - options->action_string = pg_strdup(optarg); - if (optarg[0] == '\\') - { - options->action = ACT_SINGLE_SLASH; - options->action_string++; - } - else - options->action = ACT_SINGLE_QUERY; - break; - case 'd': - options->dbname = pg_strdup(optarg); - break; - case 'e': - SetVariable(pset.vars, "ECHO", "queries"); - break; - case 'E': - SetVariableBool(pset.vars, "ECHO_HIDDEN"); - break; - case 'f': - options->action = ACT_FILE; - options->action_string = pg_strdup(optarg); - break; - case 'F': - pset.popt.topt.fieldSep.separator = pg_strdup(optarg); - pset.popt.topt.fieldSep.separator_zero = false; - break; - case 'h': - options->host = pg_strdup(optarg); - break; - case 'H': - pset.popt.topt.format = PRINT_HTML; - break; - case 'l': - options->action = ACT_LIST_DB; - break; - case 'L': - options->logfilename = pg_strdup(optarg); - break; - case 'n': - options->no_readline = true; - break; - case 'o': - if (!setQFout(optarg)) - exit(EXIT_FAILURE); - break; - case 'p': - options->port = pg_strdup(optarg); - break; - case 'P': - { - char *value; - char *equal_loc; - bool result; - - value = pg_strdup(optarg); - equal_loc = strchr(value, '='); - if (!equal_loc) - result = do_pset(value, NULL, &pset.popt, true); - else - { - *equal_loc = '\0'; - result = do_pset(value, equal_loc + 1, &pset.popt, true); - } - - if (!result) - { - fprintf(stderr, _("%s: could not set printing parameter \"%s\"\n"), pset.progname, value); - exit(EXIT_FAILURE); - } - - free(value); - break; - } - case 'q': - SetVariableBool(pset.vars, "QUIET"); - break; - case 'R': - pset.popt.topt.recordSep.separator = pg_strdup(optarg); - pset.popt.topt.recordSep.separator_zero = false; - break; - case 's': - SetVariableBool(pset.vars, "SINGLESTEP"); - break; - case 'S': - SetVariableBool(pset.vars, "SINGLELINE"); - break; - case 't': - pset.popt.topt.tuples_only = true; - break; - case 'T': - pset.popt.topt.tableAttr = pg_strdup(optarg); - break; - case 'U': - options->username = pg_strdup(optarg); - break; - case 'v': - { - char *value; - char *equal_loc; - - value = pg_strdup(optarg); - equal_loc = strchr(value, '='); - if (!equal_loc) - { - if (!DeleteVariable(pset.vars, value)) - { - fprintf(stderr, _("%s: could not delete variable \"%s\"\n"), - pset.progname, value); - exit(EXIT_FAILURE); - } - } - else - { - *equal_loc = '\0'; - if (!SetVariable(pset.vars, value, equal_loc + 1)) - { - fprintf(stderr, _("%s: could not set variable \"%s\"\n"), - pset.progname, value); - exit(EXIT_FAILURE); - } - } - - free(value); - break; - } - case 'V': - showVersion(); - exit(EXIT_SUCCESS); - case 'w': - pset.getPassword = TRI_NO; - break; - case 'W': - pset.getPassword = TRI_YES; - break; - case 'x': - pset.popt.topt.expanded = true; - break; - case 'X': - options->no_psqlrc = true; - break; - case 'z': - pset.popt.topt.fieldSep.separator_zero = true; - break; - case '0': - pset.popt.topt.recordSep.separator_zero = true; - break; - case '1': - options->single_txn = true; - break; - case '?': - /* Actual help option given */ - if (strcmp(argv[optind - 1], "-?") == 0) - { - usage(NOPAGER); - exit(EXIT_SUCCESS); - } - /* unknown option reported by getopt */ - else - goto unknown_option; - break; - case 1: - { - if (!optarg || strcmp(optarg, "options") == 0) - usage(NOPAGER); - else if (optarg && strcmp(optarg, "commands") == 0) - slashUsage(NOPAGER); - else if (optarg && strcmp(optarg, "variables") == 0) - helpVariables(NOPAGER); - else - goto unknown_option; - - exit(EXIT_SUCCESS); - } - break; - default: - unknown_option: - fprintf(stderr, _("Try \"%s --help\" for more information.\n"), - pset.progname); - exit(EXIT_FAILURE); - break; - } - } - - /* - * if we still have arguments, use it as the database name and username - */ - while (argc - optind >= 1) - { - if (!options->dbname) - options->dbname = argv[optind]; - else if (!options->username) - options->username = argv[optind]; - else if (!pset.quiet) - fprintf(stderr, _("%s: warning: extra command-line argument \"%s\" ignored\n"), - pset.progname, argv[optind]); - - optind++; - } -} - - -/* - * Load .psqlrc file, if found. - */ -static void -process_psqlrc(char *argv0) -{ - char home[MAXPGPATH]; - char rc_file[MAXPGPATH]; - char my_exec_path[MAXPGPATH]; - char etc_path[MAXPGPATH]; - char *envrc = getenv("PSQLRC"); - - if (find_my_exec(argv0, my_exec_path) < 0) - { - fprintf(stderr, _("%s: could not find own program executable\n"), argv0); - exit(EXIT_FAILURE); - } - - get_etc_path(my_exec_path, etc_path); - - snprintf(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC); - process_psqlrc_file(rc_file); - - if (envrc != NULL && strlen(envrc) > 0) - { - /* might need to free() this */ - char *envrc_alloc = pstrdup(envrc); - - expand_tilde(&envrc_alloc); - process_psqlrc_file(envrc_alloc); - } - else if (get_home_path(home)) - { - snprintf(rc_file, MAXPGPATH, "%s/%s", home, PSQLRC); - process_psqlrc_file(rc_file); - } -} - - - -static void -process_psqlrc_file(char *filename) -{ - char *psqlrc_minor, - *psqlrc_major; - -#if defined(WIN32) && (!defined(__MINGW32__)) -#define R_OK 4 -#endif - - psqlrc_minor = psprintf("%s-%s", filename, PG_VERSION); - psqlrc_major = psprintf("%s-%s", filename, PG_MAJORVERSION); - - /* check for minor version first, then major, then no version */ - if (access(psqlrc_minor, R_OK) == 0) - (void) process_file(psqlrc_minor, false, false); - else if (access(psqlrc_major, R_OK) == 0) - (void) process_file(psqlrc_major, false, false); - else if (access(filename, R_OK) == 0) - (void) process_file(filename, false, false); - - free(psqlrc_minor); - free(psqlrc_major); -} - - - -/* showVersion - * - * This output format is intended to match GNU standards. - */ -static void -showVersion(void) -{ - puts("psql (PostgreSQL) " PG_VERSION); -} - - - -/* - * Assign hooks for psql variables. - * - * This isn't an amazingly good place for them, but neither is anywhere else. - */ - -static void -autocommit_hook(const char *newval) -{ - pset.autocommit = ParseVariableBool(newval, "AUTOCOMMIT"); -} - -static void -on_error_stop_hook(const char *newval) -{ - pset.on_error_stop = ParseVariableBool(newval, "ON_ERROR_STOP"); -} - -static void -quiet_hook(const char *newval) -{ - pset.quiet = ParseVariableBool(newval, "QUIET"); -} - -static void -singleline_hook(const char *newval) -{ - pset.singleline = ParseVariableBool(newval, "SINGLELINE"); -} - -static void -singlestep_hook(const char *newval) -{ - pset.singlestep = ParseVariableBool(newval, "SINGLESTEP"); -} - -static void -fetch_count_hook(const char *newval) -{ - pset.fetch_count = ParseVariableNum(newval, -1, -1, false); -} - -static void -echo_hook(const char *newval) -{ - if (newval == NULL) - pset.echo = PSQL_ECHO_NONE; - else if (pg_strcasecmp(newval, "queries") == 0) - pset.echo = PSQL_ECHO_QUERIES; - else if (pg_strcasecmp(newval, "errors") == 0) - pset.echo = PSQL_ECHO_ERRORS; - else if (pg_strcasecmp(newval, "all") == 0) - pset.echo = PSQL_ECHO_ALL; - else if (pg_strcasecmp(newval, "none") == 0) - pset.echo = PSQL_ECHO_NONE; - else - { - psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", - newval, "ECHO", "none"); - pset.echo = PSQL_ECHO_NONE; - } -} - -static void -echo_hidden_hook(const char *newval) -{ - if (newval == NULL) - pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; - else if (pg_strcasecmp(newval, "noexec") == 0) - pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC; - else if (ParseVariableBool(newval, "ECHO_HIDDEN")) - pset.echo_hidden = PSQL_ECHO_HIDDEN_ON; - else /* ParseVariableBool printed msg if needed */ - pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; -} - -static void -on_error_rollback_hook(const char *newval) -{ - if (newval == NULL) - pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; - else if (pg_strcasecmp(newval, "interactive") == 0) - pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE; - else if (ParseVariableBool(newval, "ON_ERROR_ROLLBACK")) - pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON; - else /* ParseVariableBool printed msg if needed */ - pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; -} - -static void -comp_keyword_case_hook(const char *newval) -{ - if (newval == NULL) - pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER; - else if (pg_strcasecmp(newval, "preserve-upper") == 0) - pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER; - else if (pg_strcasecmp(newval, "preserve-lower") == 0) - pset.comp_case = PSQL_COMP_CASE_PRESERVE_LOWER; - else if (pg_strcasecmp(newval, "upper") == 0) - pset.comp_case = PSQL_COMP_CASE_UPPER; - else if (pg_strcasecmp(newval, "lower") == 0) - pset.comp_case = PSQL_COMP_CASE_LOWER; - else - { - psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", - newval, "COMP_KEYWORD_CASE", "preserve-upper"); - pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER; - } -} - -static void -histcontrol_hook(const char *newval) -{ - if (newval == NULL) - pset.histcontrol = hctl_none; - else if (pg_strcasecmp(newval, "ignorespace") == 0) - pset.histcontrol = hctl_ignorespace; - else if (pg_strcasecmp(newval, "ignoredups") == 0) - pset.histcontrol = hctl_ignoredups; - else if (pg_strcasecmp(newval, "ignoreboth") == 0) - pset.histcontrol = hctl_ignoreboth; - else if (pg_strcasecmp(newval, "none") == 0) - pset.histcontrol = hctl_none; - else - { - psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", - newval, "HISTCONTROL", "none"); - pset.histcontrol = hctl_none; - } -} - -static void -prompt1_hook(const char *newval) -{ - pset.prompt1 = newval ? newval : ""; -} - -static void -prompt2_hook(const char *newval) -{ - pset.prompt2 = newval ? newval : ""; -} - -static void -prompt3_hook(const char *newval) -{ - pset.prompt3 = newval ? newval : ""; -} - -static void -verbosity_hook(const char *newval) -{ - if (newval == NULL) - pset.verbosity = PQERRORS_DEFAULT; - else if (pg_strcasecmp(newval, "default") == 0) - pset.verbosity = PQERRORS_DEFAULT; - else if (pg_strcasecmp(newval, "terse") == 0) - pset.verbosity = PQERRORS_TERSE; - else if (pg_strcasecmp(newval, "verbose") == 0) - pset.verbosity = PQERRORS_VERBOSE; - else - { - psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", - newval, "VERBOSITY", "default"); - pset.verbosity = PQERRORS_DEFAULT; - } - - if (pset.db) - PQsetErrorVerbosity(pset.db, pset.verbosity); -} - - -static void -EstablishVariableSpace(void) -{ - pset.vars = CreateVariableSpace(); - - SetVariableAssignHook(pset.vars, "AUTOCOMMIT", autocommit_hook); - SetVariableAssignHook(pset.vars, "ON_ERROR_STOP", on_error_stop_hook); - SetVariableAssignHook(pset.vars, "QUIET", quiet_hook); - SetVariableAssignHook(pset.vars, "SINGLELINE", singleline_hook); - SetVariableAssignHook(pset.vars, "SINGLESTEP", singlestep_hook); - SetVariableAssignHook(pset.vars, "FETCH_COUNT", fetch_count_hook); - SetVariableAssignHook(pset.vars, "ECHO", echo_hook); - SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook); - SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook); - SetVariableAssignHook(pset.vars, "COMP_KEYWORD_CASE", comp_keyword_case_hook); - SetVariableAssignHook(pset.vars, "HISTCONTROL", histcontrol_hook); - SetVariableAssignHook(pset.vars, "PROMPT1", prompt1_hook); - SetVariableAssignHook(pset.vars, "PROMPT2", prompt2_hook); - SetVariableAssignHook(pset.vars, "PROMPT3", prompt3_hook); - SetVariableAssignHook(pset.vars, "VERBOSITY", verbosity_hook); -} diff --git a/src/bin/csql/stringutils.c b/src/bin/csql/stringutils.c deleted file mode 100644 index 5d613eb38..000000000 --- a/src/bin/csql/stringutils.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/stringutils.c - */ -#include "postgres_fe.h" - -#include - -#include "common.h" -#include "stringutils.h" - - -/* - * Replacement for strtok() (a.k.a. poor man's flex) - * - * Splits a string into tokens, returning one token per call, then NULL - * when no more tokens exist in the given string. - * - * The calling convention is similar to that of strtok, but with more - * frammishes. - * - * s - string to parse, if NULL continue parsing the last string - * whitespace - set of whitespace characters that separate tokens - * delim - set of non-whitespace separator characters (or NULL) - * quote - set of characters that can quote a token (NULL if none) - * escape - character that can quote quotes (0 if none) - * e_strings - if TRUE, treat E'...' syntax as a valid token - * del_quotes - if TRUE, strip quotes from the returned token, else return - * it exactly as found in the string - * encoding - the active character-set encoding - * - * Characters in 'delim', if any, will be returned as single-character - * tokens unless part of a quoted token. - * - * Double occurrences of the quoting character are always taken to represent - * a single quote character in the data. If escape isn't 0, then escape - * followed by anything (except \0) is a data character too. - * - * The combination of e_strings and del_quotes both TRUE is not currently - * handled. This could be fixed but it's not needed anywhere at the moment. - * - * Note that the string s is _not_ overwritten in this implementation. - * - * NB: it's okay to vary delim, quote, and escape from one call to the - * next on a single source string, but changing whitespace is a bad idea - * since you might lose data. - */ -char * -strtokx(const char *s, - const char *whitespace, - const char *delim, - const char *quote, - char escape, - bool e_strings, - bool del_quotes, - int encoding) -{ - static char *storage = NULL;/* store the local copy of the users string - * here */ - static char *string = NULL; /* pointer into storage where to continue on - * next call */ - - /* variously abused variables: */ - unsigned int offset; - char *start; - char *p; - - if (s) - { - free(storage); - - /* - * We may need extra space to insert delimiter nulls for adjacent - * tokens. 2X the space is a gross overestimate, but it's unlikely - * that this code will be used on huge strings anyway. - */ - storage = pg_malloc(2 * strlen(s) + 1); - strcpy(storage, s); - string = storage; - } - - if (!storage) - return NULL; - - /* skip leading whitespace */ - offset = strspn(string, whitespace); - start = &string[offset]; - - /* end of string reached? */ - if (*start == '\0') - { - /* technically we don't need to free here, but we're nice */ - free(storage); - storage = NULL; - string = NULL; - return NULL; - } - - /* test if delimiter character */ - if (delim && strchr(delim, *start)) - { - /* - * If not at end of string, we need to insert a null to terminate the - * returned token. We can just overwrite the next character if it - * happens to be in the whitespace set ... otherwise move over the - * rest of the string to make room. (This is why we allocated extra - * space above). - */ - p = start + 1; - if (*p != '\0') - { - if (!strchr(whitespace, *p)) - memmove(p + 1, p, strlen(p) + 1); - *p = '\0'; - string = p + 1; - } - else - { - /* at end of string, so no extra work */ - string = p; - } - - return start; - } - - /* check for E string */ - p = start; - if (e_strings && - (*p == 'E' || *p == 'e') && - p[1] == '\'') - { - quote = "'"; - escape = '\\'; /* if std strings before, not any more */ - p++; - } - - /* test if quoting character */ - if (quote && strchr(quote, *p)) - { - /* okay, we have a quoted token, now scan for the closer */ - char thisquote = *p++; - - for (; *p; p += PQmblen(p, encoding)) - { - if (*p == escape && p[1] != '\0') - p++; /* process escaped anything */ - else if (*p == thisquote && p[1] == thisquote) - p++; /* process doubled quote */ - else if (*p == thisquote) - { - p++; /* skip trailing quote */ - break; - } - } - - /* - * If not at end of string, we need to insert a null to terminate the - * returned token. See notes above. - */ - if (*p != '\0') - { - if (!strchr(whitespace, *p)) - memmove(p + 1, p, strlen(p) + 1); - *p = '\0'; - string = p + 1; - } - else - { - /* at end of string, so no extra work */ - string = p; - } - - /* Clean up the token if caller wants that */ - if (del_quotes) - strip_quotes(start, thisquote, escape, encoding); - - return start; - } - - /* - * Otherwise no quoting character. Scan till next whitespace, delimiter - * or quote. NB: at this point, *start is known not to be '\0', - * whitespace, delim, or quote, so we will consume at least one character. - */ - offset = strcspn(start, whitespace); - - if (delim) - { - unsigned int offset2 = strcspn(start, delim); - - if (offset > offset2) - offset = offset2; - } - - if (quote) - { - unsigned int offset2 = strcspn(start, quote); - - if (offset > offset2) - offset = offset2; - } - - p = start + offset; - - /* - * If not at end of string, we need to insert a null to terminate the - * returned token. See notes above. - */ - if (*p != '\0') - { - if (!strchr(whitespace, *p)) - memmove(p + 1, p, strlen(p) + 1); - *p = '\0'; - string = p + 1; - } - else - { - /* at end of string, so no extra work */ - string = p; - } - - return start; -} - - -/* - * strip_quotes - * - * Remove quotes from the string at *source. Leading and trailing occurrences - * of 'quote' are removed; embedded double occurrences of 'quote' are reduced - * to single occurrences; if 'escape' is not 0 then 'escape' removes special - * significance of next character. - * - * Note that the source string is overwritten in-place. - */ -void -strip_quotes(char *source, char quote, char escape, int encoding) -{ - char *src; - char *dst; - - Assert(source != NULL); - Assert(quote != '\0'); - - src = dst = source; - - if (*src && *src == quote) - src++; /* skip leading quote */ - - while (*src) - { - char c = *src; - int i; - - if (c == quote && src[1] == '\0') - break; /* skip trailing quote */ - else if (c == quote && src[1] == quote) - src++; /* process doubled quote */ - else if (c == escape && src[1] != '\0') - src++; /* process escaped character */ - - i = PQmblen(src, encoding); - while (i--) - *dst++ = *src++; - } - - *dst = '\0'; -} - - -/* - * quote_if_needed - * - * Opposite of strip_quotes(). If "source" denotes itself literally without - * quoting or escaping, returns NULL. Otherwise, returns a malloc'd copy with - * quoting and escaping applied: - * - * source - string to parse - * entails_quote - any of these present? need outer quotes - * quote - doubled within string, affixed to both ends - * escape - doubled within string - * encoding - the active character-set encoding - * - * Do not use this as a substitute for PQescapeStringConn(). Use it for - * strings to be parsed by strtokx() or psql_scan_slash_option(). - */ -char * -quote_if_needed(const char *source, const char *entails_quote, - char quote, char escape, int encoding) -{ - const char *src; - char *ret; - char *dst; - bool need_quotes = false; - - Assert(source != NULL); - Assert(quote != '\0'); - - src = source; - dst = ret = pg_malloc(2 * strlen(src) + 3); /* excess */ - - *dst++ = quote; - - while (*src) - { - char c = *src; - int i; - - if (c == quote) - { - need_quotes = true; - *dst++ = quote; - } - else if (c == escape) - { - need_quotes = true; - *dst++ = escape; - } - else if (strchr(entails_quote, c)) - need_quotes = true; - - i = PQmblen(src, encoding); - while (i--) - *dst++ = *src++; - } - - *dst++ = quote; - *dst = '\0'; - - if (!need_quotes) - { - free(ret); - ret = NULL; - } - - return ret; -} diff --git a/src/bin/csql/stringutils.h b/src/bin/csql/stringutils.h deleted file mode 100644 index 562b3bd85..000000000 --- a/src/bin/csql/stringutils.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/stringutils.h - */ -#ifndef STRINGUTILS_H -#define STRINGUTILS_H - -/* The cooler version of strtok() which knows about quotes and doesn't - * overwrite your input */ -extern char *strtokx(const char *s, - const char *whitespace, - const char *delim, - const char *quote, - char escape, - bool e_strings, - bool del_quotes, - int encoding); - -extern void strip_quotes(char *source, char quote, char escape, int encoding); - -extern char *quote_if_needed(const char *source, const char *entails_quote, - char quote, char escape, int encoding); - -#endif /* STRINGUTILS_H */ diff --git a/src/bin/csql/tab-complete.c b/src/bin/csql/tab-complete.c deleted file mode 100644 index 44d7a4f05..000000000 --- a/src/bin/csql/tab-complete.c +++ /dev/null @@ -1,4719 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/tab-complete.c - */ - -/*---------------------------------------------------------------------- - * 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. Also, - * hence the - * - * BUGS: - * - * - If you split your queries across lines, this whole thing gets - * confused. (To fix this, one would have to read psql's query - * buffer rather than readline's line buffer, which would require - * some major revisions of things.) - * - * - Table or attribute names with spaces in it may confuse it. - * - * - Quotes, parenthesis, and other funny characters are not handled - * all that gracefully. - *---------------------------------------------------------------------- - */ - -#include "postgres_fe.h" -#include "tab-complete.h" -#include "input.h" - -/* If we don't have this, we might as well forget about the whole thing: */ -#ifdef USE_READLINE - -#include -#include "libpq-fe.h" -#include "pqexpbuffer.h" -#include "common.h" -#include "settings.h" -#include "stringutils.h" - -#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION -#define filename_completion_function rl_filename_completion_function -#else -/* missing in some header files */ -extern char *filename_completion_function(); -#endif - -#ifdef HAVE_RL_COMPLETION_MATCHES -#define completion_matches rl_completion_matches -#endif - -/* word break characters */ -#define WORD_BREAKS "\t\n@$><=;|&{() " - -/* - * 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(). - */ -typedef struct SchemaQuery -{ - /* - * Name of catalog or catalogs to be queried, with alias, eg. - * "pg_catalog.pg_class c". Note that "pg_namespace n" will be added. - */ - 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, "c.relkind = 'r'". 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)". - */ - const char *viscondition; - - /* - * Namespace --- name of field to join to pg_namespace.oid. For example, - * "c.relnamespace". - */ - const char *namespace; - - /* - * Result --- the appropriately-quoted name to return, in the case of an - * unqualified name. For example, "pg_catalog.quote_ident(c.relname)". - */ - const char *result; - - /* - * In some cases a different result must be used for qualified names. - * Enter that here, or write NULL if result can be used. - */ - const char *qualresult; -} 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 COMPLETE_WITH_FOO macros and then used by - * the completion callback functions. Ugly but there is no better way. - */ -static const char *completion_charp; /* to pass a string */ -static const char *const * completion_charpp; /* to pass a list of strings */ -static const char *completion_info_charp; /* to pass a second string */ -static const char *completion_info_charp2; /* to pass a third string */ -static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ -static bool completion_case_sensitive; /* completion is case sensitive */ - -/* - * A few macros to ease typing. You can use these to complete the given - * string with - * 1) The results from a query you pass it. (Perhaps one of those below?) - * 2) The results from a schema query you pass it. - * 3) The items from a null-pointer-terminated list. - * 4) A string constant. - * 5) The list of attributes of the given table (possibly schema-qualified). - * 6/ The list of arguments to the given function (possibly schema-qualified). - */ -#define COMPLETE_WITH_QUERY(query) \ -do { \ - completion_charp = query; \ - matches = completion_matches(text, complete_from_query); \ -} while (0) - -#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ -do { \ - completion_squery = &(query); \ - completion_charp = addon; \ - matches = completion_matches(text, complete_from_schema_query); \ -} while (0) - -#define COMPLETE_WITH_LIST_CS(list) \ -do { \ - completion_charpp = list; \ - completion_case_sensitive = true; \ - matches = completion_matches(text, complete_from_list); \ -} while (0) - -#define COMPLETE_WITH_LIST(list) \ -do { \ - completion_charpp = list; \ - completion_case_sensitive = false; \ - matches = completion_matches(text, complete_from_list); \ -} while (0) - -#define COMPLETE_WITH_CONST(string) \ -do { \ - completion_charp = string; \ - completion_case_sensitive = false; \ - matches = completion_matches(text, complete_from_const); \ -} while (0) - -#define COMPLETE_WITH_ATTR(relation, addon) \ -do { \ - char *_completion_schema; \ - char *_completion_table; \ -\ - _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - if (_completion_table == NULL) \ - { \ - completion_charp = Query_for_list_of_attributes addon; \ - completion_info_charp = relation; \ - } \ - else \ - { \ - completion_charp = Query_for_list_of_attributes_with_schema addon; \ - completion_info_charp = _completion_table; \ - completion_info_charp2 = _completion_schema; \ - } \ - matches = completion_matches(text, complete_from_query); \ -} while (0) - -#define COMPLETE_WITH_FUNCTION_ARG(function) \ -do { \ - char *_completion_schema; \ - char *_completion_function; \ -\ - _completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - _completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \ - false, false, pset.encoding); \ - if (_completion_function == NULL) \ - { \ - completion_charp = Query_for_list_of_arguments; \ - completion_info_charp = function; \ - } \ - else \ - { \ - completion_charp = Query_for_list_of_arguments_with_schema; \ - completion_info_charp = _completion_function; \ - completion_info_charp2 = _completion_schema; \ - } \ - matches = completion_matches(text, complete_from_query); \ -} while (0) - -/* - * Assembly instructions for schema queries - */ - -static const SchemaQuery Query_for_list_of_aggregates = { - /* catname */ - "pg_catalog.pg_proc p", - /* selcondition */ - "p.proisagg", - /* viscondition */ - "pg_catalog.pg_function_is_visible(p.oid)", - /* namespace */ - "p.pronamespace", - /* result */ - "pg_catalog.quote_ident(p.proname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_datatypes = { - /* catname */ - "pg_catalog.pg_type t", - /* selcondition --- ignore table rowtypes and array types */ - "(t.typrelid = 0 " - " OR (SELECT c.relkind = 'c' 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 */ - "pg_catalog.format_type(t.oid, NULL)", - /* qualresult */ - "pg_catalog.quote_ident(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 */ - "pg_catalog.quote_ident(t.typname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_functions = { - /* catname */ - "pg_catalog.pg_proc p", - /* selcondition */ - NULL, - /* viscondition */ - "pg_catalog.pg_function_is_visible(p.oid)", - /* namespace */ - "p.pronamespace", - /* result */ - "pg_catalog.quote_ident(p.proname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_indexes = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('i')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_sequences = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('S')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_foreign_tables = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('f')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_tables = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('r')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_constraints_with_schema = { - /* catname */ - "pg_catalog.pg_constraint c", - /* selcondition */ - "c.conrelid <> 0", - /* viscondition */ - "true", /* there is no pg_constraint_is_visible */ - /* namespace */ - "c.connamespace", - /* result */ - "pg_catalog.quote_ident(c.conname)", - /* qualresult */ - NULL -}; - -/* Relations supporting INSERT, UPDATE or DELETE */ -static const SchemaQuery Query_for_list_of_updatables = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_relations = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - NULL, - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_tsvmf = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_tmf = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('r', 'm', 'f')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_tm = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('r', 'm')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_views = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('v')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - -static const SchemaQuery Query_for_list_of_matviews = { - /* catname */ - "pg_catalog.pg_class c", - /* selcondition */ - "c.relkind IN ('m')", - /* viscondition */ - "pg_catalog.pg_table_is_visible(c.oid)", - /* namespace */ - "c.relnamespace", - /* result */ - "pg_catalog.quote_ident(c.relname)", - /* qualresult */ - NULL -}; - - -/* - * Queries to get lists of names of various kinds of things, possibly - * restricted to names matching a partially entered name. In these queries, - * the first %s will be replaced by the text entered so far (suitably escaped - * to become a SQL literal string). %d will be replaced by the length of the - * string (in unescaped form). A second and third %s, if present, will be - * replaced by a suitably-escaped version of the string provided in - * completion_info_charp. A fourth and fifth %s are similarly replaced by - * completion_info_charp2. - * - * Beware that the allowed sequences of %s and %d are determined by - * _complete_from_query(). - */ - -#define Query_for_list_of_attributes \ -"SELECT pg_catalog.quote_ident(attname) "\ -" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\ -" WHERE c.oid = a.attrelid "\ -" AND a.attnum > 0 "\ -" AND NOT a.attisdropped "\ -" AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ -" AND (pg_catalog.quote_ident(relname)='%s' "\ -" OR '\"' || relname || '\"'='%s') "\ -" AND pg_catalog.pg_table_is_visible(c.oid)" - -#define Query_for_list_of_attributes_with_schema \ -"SELECT pg_catalog.quote_ident(attname) "\ -" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\ -" WHERE c.oid = a.attrelid "\ -" AND n.oid = c.relnamespace "\ -" AND a.attnum > 0 "\ -" AND NOT a.attisdropped "\ -" AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ -" AND (pg_catalog.quote_ident(relname)='%s' "\ -" OR '\"' || relname || '\"' ='%s') "\ -" AND (pg_catalog.quote_ident(nspname)='%s' "\ -" OR '\"' || nspname || '\"' ='%s') " - -#define Query_for_list_of_template_databases \ -"SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ -" WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate" - -#define Query_for_list_of_databases \ -"SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ -" WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'" - -#define Query_for_list_of_tablespaces \ -"SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\ -" WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'" - -#define Query_for_list_of_encodings \ -" SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\ -" FROM pg_catalog.pg_conversion "\ -" WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')" - -#define Query_for_list_of_languages \ -"SELECT pg_catalog.quote_ident(lanname) "\ -" FROM pg_catalog.pg_language "\ -" WHERE lanname != 'internal' "\ -" AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'" - -#define Query_for_list_of_schemas \ -"SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\ -" WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'" - -#define Query_for_list_of_alter_system_set_vars \ -"SELECT name FROM "\ -" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ -" WHERE context != 'internal') ss "\ -" WHERE substring(name,1,%d)='%s'"\ -" UNION ALL SELECT 'all' ss" - -#define Query_for_list_of_set_vars \ -"SELECT name FROM "\ -" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ -" WHERE context IN ('user', 'superuser') "\ -" UNION ALL SELECT 'constraints' "\ -" UNION ALL SELECT 'transaction' "\ -" UNION ALL SELECT 'session' "\ -" UNION ALL SELECT 'role' "\ -" UNION ALL SELECT 'tablespace' "\ -" UNION ALL SELECT 'all') ss "\ -" WHERE substring(name,1,%d)='%s'" - -#define Query_for_list_of_show_vars \ -"SELECT name FROM "\ -" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ -" UNION ALL SELECT 'session authorization' "\ -" UNION ALL SELECT 'all') ss "\ -" WHERE substring(name,1,%d)='%s'" - -#define Query_for_list_of_roles \ -" SELECT pg_catalog.quote_ident(rolname) "\ -" FROM pg_catalog.pg_roles "\ -" WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" - -#define Query_for_list_of_grant_roles \ -" SELECT pg_catalog.quote_ident(rolname) "\ -" FROM pg_catalog.pg_roles "\ -" WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\ -" UNION ALL SELECT 'PUBLIC'" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_table_owning_index \ -"SELECT pg_catalog.quote_ident(c1.relname) "\ -" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\ -" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\ -" and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(c2.relname)='%s'"\ -" and pg_catalog.pg_table_is_visible(c2.oid)" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_index_of_table \ -"SELECT pg_catalog.quote_ident(c2.relname) "\ -" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\ -" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\ -" and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(c1.relname)='%s'"\ -" and pg_catalog.pg_table_is_visible(c2.oid)" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_constraint_of_table \ -"SELECT pg_catalog.quote_ident(conname) "\ -" FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\ -" WHERE c1.oid=conrelid and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(c1.relname)='%s'"\ -" and pg_catalog.pg_table_is_visible(c1.oid)" - -#define Query_for_all_table_constraints \ -"SELECT pg_catalog.quote_ident(conname) "\ -" FROM pg_catalog.pg_constraint c "\ -" WHERE c.conrelid <> 0 " - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_constraint_of_type \ -"SELECT pg_catalog.quote_ident(conname) "\ -" FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\ -" WHERE t.oid=contypid and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(t.typname)='%s'"\ -" and pg_catalog.pg_type_is_visible(t.oid)" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_tables_for_constraint \ -"SELECT pg_catalog.quote_ident(relname) "\ -" FROM pg_catalog.pg_class"\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND oid IN "\ -" (SELECT conrelid FROM pg_catalog.pg_constraint "\ -" WHERE pg_catalog.quote_ident(conname)='%s')" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_rule_of_table \ -"SELECT pg_catalog.quote_ident(rulename) "\ -" FROM pg_catalog.pg_class c1, pg_catalog.pg_rewrite "\ -" WHERE c1.oid=ev_class and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(c1.relname)='%s'"\ -" and pg_catalog.pg_table_is_visible(c1.oid)" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_tables_for_rule \ -"SELECT pg_catalog.quote_ident(relname) "\ -" FROM pg_catalog.pg_class"\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND oid IN "\ -" (SELECT ev_class FROM pg_catalog.pg_rewrite "\ -" WHERE pg_catalog.quote_ident(rulename)='%s')" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_trigger_of_table \ -"SELECT pg_catalog.quote_ident(tgname) "\ -" FROM pg_catalog.pg_class c1, pg_catalog.pg_trigger "\ -" WHERE c1.oid=tgrelid and (%d = pg_catalog.length('%s'))"\ -" and pg_catalog.quote_ident(c1.relname)='%s'"\ -" and pg_catalog.pg_table_is_visible(c1.oid)"\ -" and not tgisinternal" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_tables_for_trigger \ -"SELECT pg_catalog.quote_ident(relname) "\ -" FROM pg_catalog.pg_class"\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND oid IN "\ -" (SELECT tgrelid FROM pg_catalog.pg_trigger "\ -" WHERE pg_catalog.quote_ident(tgname)='%s')" - -#define Query_for_list_of_ts_configurations \ -"SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\ -" WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'" - -#define Query_for_list_of_ts_dictionaries \ -"SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\ -" WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'" - -#define Query_for_list_of_ts_parsers \ -"SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\ -" WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'" - -#define Query_for_list_of_ts_templates \ -"SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\ -" WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'" - -#define Query_for_list_of_fdws \ -" SELECT pg_catalog.quote_ident(fdwname) "\ -" FROM pg_catalog.pg_foreign_data_wrapper "\ -" WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'" - -#define Query_for_list_of_servers \ -" SELECT pg_catalog.quote_ident(srvname) "\ -" FROM pg_catalog.pg_foreign_server "\ -" WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'" - -#define Query_for_list_of_user_mappings \ -" SELECT pg_catalog.quote_ident(usename) "\ -" FROM pg_catalog.pg_user_mappings "\ -" WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" - -#define Query_for_list_of_access_methods \ -" SELECT pg_catalog.quote_ident(amname) "\ -" FROM pg_catalog.pg_am "\ -" WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_arguments \ -"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\ -" FROM pg_catalog.pg_proc "\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND (pg_catalog.quote_ident(proname)='%s'"\ -" OR '\"' || proname || '\"'='%s') "\ -" AND (pg_catalog.pg_function_is_visible(pg_proc.oid))" - -/* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_arguments_with_schema \ -"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\ -" FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND n.oid = p.pronamespace "\ -" AND (pg_catalog.quote_ident(proname)='%s' "\ -" OR '\"' || proname || '\"' ='%s') "\ -" AND (pg_catalog.quote_ident(nspname)='%s' "\ -" OR '\"' || nspname || '\"' ='%s') " - -#define Query_for_list_of_extensions \ -" SELECT pg_catalog.quote_ident(extname) "\ -" FROM pg_catalog.pg_extension "\ -" WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'" - -#define Query_for_list_of_available_extensions \ -" SELECT pg_catalog.quote_ident(name) "\ -" FROM pg_catalog.pg_available_extensions "\ -" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL" - -#define Query_for_list_of_prepared_statements \ -" SELECT pg_catalog.quote_ident(name) "\ -" FROM pg_catalog.pg_prepared_statements "\ -" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'" - -#define Query_for_list_of_event_triggers \ -" SELECT pg_catalog.quote_ident(evtname) "\ -" FROM pg_catalog.pg_event_trigger "\ -" WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'" - -#define Query_for_list_of_tablesample_methods \ -" SELECT pg_catalog.quote_ident(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 "\ -" substring(pg_catalog.quote_ident(proname),1,%d)='%s'" - -#define Query_for_list_of_policies \ -" SELECT pg_catalog.quote_ident(polname) "\ -" FROM pg_catalog.pg_policy "\ -" WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'" - -#define Query_for_list_of_tables_for_policy \ -"SELECT pg_catalog.quote_ident(relname) "\ -" FROM pg_catalog.pg_class"\ -" WHERE (%d = pg_catalog.length('%s'))"\ -" AND oid IN "\ -" (SELECT polrelid FROM pg_catalog.pg_policy "\ -" WHERE pg_catalog.quote_ident(polname)='%s')" - -/* - * This is a list of all "things" in Pgsql, which can show up after CREATE or - * DROP; and there is also a query to get a list of them. - */ - -typedef struct -{ - const char *name; - const char *query; /* simple query, or NULL */ - const SchemaQuery *squery; /* schema query, or NULL */ - 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_SHOW (THING_NO_CREATE | THING_NO_DROP) - -static const pgsql_thing_t words_after_create[] = { - {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, - {"CAST", NULL, NULL}, /* Casts have complex structures for names, so - * skip it */ - {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, - - /* - * CREATE CONSTRAINT TRIGGER is not supported here because it is designed - * to be used only by pg_dump. - */ - {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW}, - {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"}, - {"DATABASE", Query_for_list_of_databases}, - {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW}, - {"DOMAIN", NULL, &Query_for_list_of_domains}, - {"EVENT TRIGGER", NULL, NULL}, - {"EXTENSION", Query_for_list_of_extensions}, - {"FOREIGN DATA WRAPPER", NULL, NULL}, - {"FOREIGN TABLE", NULL, NULL}, - {"FUNCTION", NULL, &Query_for_list_of_functions}, - {"GROUP", Query_for_list_of_roles}, - {"LANGUAGE", Query_for_list_of_languages}, - {"INDEX", NULL, &Query_for_list_of_indexes}, - {"MATERIALIZED VIEW", NULL, NULL}, - {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a - * good idea. */ - {"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */ - {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW}, - {"POLICY", NULL, NULL}, - {"ROLE", Query_for_list_of_roles}, - {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"}, - {"SCHEMA", Query_for_list_of_schemas}, - {"SEQUENCE", NULL, &Query_for_list_of_sequences}, - {"SERVER", Query_for_list_of_servers}, - {"TABLE", NULL, &Query_for_list_of_tables}, - {"TABLESPACE", Query_for_list_of_tablespaces}, - {"TEMP", NULL, NULL, THING_NO_DROP}, /* for CREATE TEMP TABLE ... */ - {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW}, - {"TEXT SEARCH", NULL, NULL}, - {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"}, - {"TYPE", NULL, &Query_for_list_of_datatypes}, - {"UNIQUE", NULL, NULL, THING_NO_DROP}, /* for CREATE UNIQUE INDEX ... */ - {"UNLOGGED", NULL, NULL, THING_NO_DROP}, /* for CREATE UNLOGGED TABLE - * ... */ - {"USER", Query_for_list_of_roles}, - {"USER MAPPING FOR", NULL, NULL}, - {"VIEW", NULL, &Query_for_list_of_views}, - {NULL} /* end of list */ -}; - - -/* Forward declaration of functions */ -static char **psql_completion(const char *text, int start, int end); -static char *create_command_generator(const char *text, int state); -static char *drop_command_generator(const char *text, int state); -static char *complete_from_query(const char *text, int state); -static char *complete_from_schema_query(const char *text, int state); -static char *_complete_from_query(int is_schema_query, - const char *text, int state); -static char *complete_from_list(const char *text, int state); -static char *complete_from_const(const char *text, int state); -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 PGresult *exec_query(const char *query); - -static void get_previous_words(int point, char **previous_words, int nwords); - -#ifdef NOT_USED -static char *quote_file_name(char *text, int match_type, char *quote_pointer); -static char *dequote_file_name(char *text, char quote_char); -#endif - - -/* - * Initialize the readline library for our purposes. - */ -void -initialize_readline(void) -{ - rl_readline_name = (char *) pset.progname; - rl_attempted_completion_function = psql_completion; - - rl_basic_word_break_characters = WORD_BREAKS; - - completion_max_records = 1000; - - /* - * There is a variable rl_completion_query_items for this but apparently - * it's not defined everywhere. - */ -} - - -/* - * 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 - * 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; - - /* This array will contain some scannage of the input line. */ - char *previous_words[6]; - - /* For compactness, we use these macros to reference previous_words[]. */ -#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]) - - static const char *const sql_commands[] = { - "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", - "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", - "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", - "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK", - "MOVE", "NOTIFY", "PREPARE", - "REASSIGN", "REFRESH", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", - "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START", - "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH", - NULL - }; - - static const char *const backslash_commands[] = { - "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright", - "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD", - "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df", - "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL", - "\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\drds", "\\ds", "\\dS", - "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", - "\\e", "\\echo", "\\ef", "\\encoding", - "\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l", - "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", - "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", - "\\s", "\\set", "\\setenv", "\\sf", "\\t", "\\T", - "\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL - }; - - (void) end; /* not used */ - -#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER - rl_completion_append_character = ' '; -#endif - - /* Clear a few things. */ - completion_charp = NULL; - completion_charpp = NULL; - completion_info_charp = NULL; - completion_info_charp2 = NULL; - - /* - * Scan the input line before our current position for the last few words. - * According to those we'll make some smart decisions on what the user is - * probably intending to type. - */ - get_previous_words(start, previous_words, lengthof(previous_words)); - - /* If a backslash command was started, continue */ - if (text[0] == '\\') - COMPLETE_WITH_LIST_CS(backslash_commands); - - /* Variable interpolation */ - 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 - matches = complete_from_variables(text, ":", "", true); - } - - /* If no previous word, suggest one of the basic sql commands */ - else if (prev_wd[0] == '\0') - COMPLETE_WITH_LIST(sql_commands); - -/* CREATE */ - /* complete with something you can create */ - else if (pg_strcasecmp(prev_wd, "CREATE") == 0) - matches = completion_matches(text, create_command_generator); - -/* DROP, but not DROP embedded in other commands */ - /* complete with something you can drop */ - else if (pg_strcasecmp(prev_wd, "DROP") == 0 && - prev2_wd[0] == '\0') - matches = completion_matches(text, drop_command_generator); - -/* ALTER */ - - /* ALTER TABLE */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "TABLE") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - "UNION SELECT 'ALL IN TABLESPACE'"); - } - - /* - * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're - * in ALTER TABLE sth ALTER - */ - else if (pg_strcasecmp(prev_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") != 0) - { - static const char *const list_ALTER[] = - {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", - "EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION", - "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR", - "POLICY", "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "SYSTEM", "TABLE", - "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", - "USER", "USER MAPPING FOR", "VIEW", NULL}; - - COMPLETE_WITH_LIST(list_ALTER); - } - /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */ - else if (pg_strcasecmp(prev4_wd, "ALL") == 0 && - pg_strcasecmp(prev3_wd, "IN") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_ALTERALLINTSPC[] = - {"SET TABLESPACE", "OWNED BY", NULL}; - - COMPLETE_WITH_LIST(list_ALTERALLINTSPC); - } - /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */ - else if (pg_strcasecmp(prev6_wd, "ALL") == 0 && - pg_strcasecmp(prev5_wd, "IN") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - } - /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */ - else if (pg_strcasecmp(prev6_wd, "IN") == 0 && - pg_strcasecmp(prev5_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev3_wd, "OWNED") == 0 && - pg_strcasecmp(prev2_wd, "BY") == 0) - { - COMPLETE_WITH_CONST("SET TABLESPACE"); - } - /* ALTER AGGREGATE,FUNCTION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) - COMPLETE_WITH_CONST("("); - /* ALTER AGGREGATE,FUNCTION (...) */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev3_wd, "FUNCTION") == 0)) - { - if (prev_wd[strlen(prev_wd) - 1] == ')') - { - static const char *const list_ALTERAGG[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERAGG); - } - else - COMPLETE_WITH_FUNCTION_ARG(prev2_wd); - } - - /* ALTER SCHEMA */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SCHEMA") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } - - /* ALTER COLLATION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "COLLATION") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } - - /* ALTER CONVERSION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "CONVERSION") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } - - /* ALTER DATABASE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DATABASE") == 0) - { - static const char *const list_ALTERDATABASE[] = - {"RESET", "SET", "OWNER TO", "RENAME TO", "IS_TEMPLATE", - "ALLOW_CONNECTIONS", "CONNECTION LIMIT", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDATABASE); - } - - /* ALTER EVENT TRIGGER */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } - - /* ALTER EVENT TRIGGER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { - static const char *const list_ALTER_EVENT_TRIGGER[] = - {"DISABLE", "ENABLE", "OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_EVENT_TRIGGER); - } - - /* ALTER EVENT TRIGGER ENABLE */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "EVENT") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ENABLE") == 0) - { - static const char *const list_ALTER_EVENT_TRIGGER_ENABLE[] = - {"REPLICA", "ALWAYS", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_EVENT_TRIGGER_ENABLE); - } - - /* ALTER EXTENSION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EXTENSION") == 0) - { - static const char *const list_ALTEREXTENSION[] = - {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTEREXTENSION); - } - - /* ALTER FOREIGN */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_ALTER_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_FOREIGN); - } - - /* ALTER FOREIGN DATA WRAPPER */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) - { - static const char *const list_ALTER_FDW[] = - {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_FDW); - } - - /* ALTER FOREIGN TABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev2_wd, "TABLE") == 0) - { - static const char *const list_ALTER_FOREIGN_TABLE[] = - {"ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT", - "NO INHERIT", "OPTIONS", "OWNER TO", "RENAME", "SET", - "VALIDATE CONSTRAINT", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE); - } - - /* ALTER INDEX */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "INDEX") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - "UNION SELECT 'ALL IN TABLESPACE'"); - } - /* ALTER INDEX */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "INDEX") == 0) - { - static const char *const list_ALTERINDEX[] = - {"OWNER TO", "RENAME TO", "SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERINDEX); - } - /* ALTER INDEX SET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "INDEX") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_ALTERINDEXSET[] = - {"(", "TABLESPACE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERINDEXSET); - } - /* ALTER INDEX RESET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "INDEX") == 0 && - pg_strcasecmp(prev_wd, "RESET") == 0) - COMPLETE_WITH_CONST("("); - /* ALTER INDEX SET|RESET ( */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "INDEX") == 0 && - (pg_strcasecmp(prev2_wd, "SET") == 0 || - pg_strcasecmp(prev2_wd, "RESET") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_INDEXOPTIONS[] = - {"fillfactor", "fastupdate", "gin_pending_list_limit", NULL}; - - COMPLETE_WITH_LIST(list_INDEXOPTIONS); - } - - /* ALTER LANGUAGE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "LANGUAGE") == 0) - { - static const char *const list_ALTERLANGUAGE[] = - {"OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERLANGUAGE); - } - - /* ALTER LARGE OBJECT */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "LARGE") == 0 && - pg_strcasecmp(prev2_wd, "OBJECT") == 0) - { - static const char *const list_ALTERLARGEOBJECT[] = - {"OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT); - } - - /* ALTER MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - "UNION SELECT 'ALL IN TABLESPACE'"); - } - - /* ALTER USER,ROLE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && - (pg_strcasecmp(prev2_wd, "USER") == 0 || - pg_strcasecmp(prev2_wd, "ROLE") == 0)) - { - static const char *const list_ALTERUSER[] = - {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", - "CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS", - "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", - "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", - "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED", - "VALID UNTIL", "WITH", NULL}; - - COMPLETE_WITH_LIST(list_ALTERUSER); - } - - /* ALTER USER,ROLE WITH */ - else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "USER") == 0 || - pg_strcasecmp(prev3_wd, "ROLE") == 0) && - pg_strcasecmp(prev_wd, "WITH") == 0)) - { - /* Similar to the above, but don't complete "WITH" again. */ - static const char *const list_ALTERUSER_WITH[] = - {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", - "CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS", - "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", - "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", - "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED", - "VALID UNTIL", NULL}; - - COMPLETE_WITH_LIST(list_ALTERUSER_WITH); - } - - /* complete ALTER USER,ROLE ENCRYPTED,UNENCRYPTED with PASSWORD */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) - { - COMPLETE_WITH_CONST("PASSWORD"); - } - /* ALTER DEFAULT PRIVILEGES */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev_wd, "PRIVILEGES") == 0) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES[] = - {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES); - } - /* ALTER DEFAULT PRIVILEGES FOR */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev2_wd, "PRIVILEGES") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES_FOR[] = - {"ROLE", "USER", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_FOR); - } - /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */ - else if (pg_strcasecmp(prev5_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev4_wd, "PRIVILEGES") == 0 && - (pg_strcasecmp(prev3_wd, "FOR") == 0 || - pg_strcasecmp(prev3_wd, "IN") == 0)) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES_REST[] = - {"GRANT", "REVOKE", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST); - } - /* ALTER DOMAIN */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DOMAIN") == 0) - { - static const char *const list_ALTERDOMAIN[] = - {"ADD", "DROP", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN); - } - /* ALTER DOMAIN DROP */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_ALTERDOMAIN2[] = - {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN2); - } - /* ALTER DOMAIN DROP|RENAME|VALIDATE CONSTRAINT */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "DOMAIN") == 0 && - (pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0 || - pg_strcasecmp(prev2_wd, "VALIDATE") == 0) && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_constraint_of_type); - } - /* ALTER DOMAIN RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) - { - static const char *const list_ALTERDOMAIN[] = - {"CONSTRAINT", "TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN); - } - /* ALTER DOMAIN RENAME CONSTRAINT */ - else if (pg_strcasecmp(prev5_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) - COMPLETE_WITH_CONST("TO"); - - /* ALTER DOMAIN SET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_ALTERDOMAIN3[] = - {"DEFAULT", "NOT NULL", "SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN3); - } - /* ALTER SEQUENCE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0) - { - static const char *const list_ALTERSEQUENCE[] = - {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", - "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERSEQUENCE); - } - /* ALTER SEQUENCE NO */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 && - pg_strcasecmp(prev_wd, "NO") == 0) - { - static const char *const list_ALTERSEQUENCE2[] = - {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERSEQUENCE2); - } - /* ALTER SERVER */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SERVER") == 0) - { - static const char *const list_ALTER_SERVER[] = - {"VERSION", "OPTIONS", "OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_SERVER); - } - /* ALTER SYSTEM SET, RESET, RESET ALL */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "SYSTEM") == 0) - { - static const char *const list_ALTERSYSTEM[] = - {"SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERSYSTEM); - } - /* ALTER SYSTEM SET|RESET */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SYSTEM") == 0 && - (pg_strcasecmp(prev_wd, "SET") == 0 || - pg_strcasecmp(prev_wd, "RESET") == 0)) - COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars); - /* ALTER VIEW */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - { - static const char *const list_ALTERVIEW[] = - {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERVIEW); - } - /* ALTER MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - { - static const char *const list_ALTERMATVIEW[] = - {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERMATVIEW); - } - - /* ALTER POLICY */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "POLICY") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_policies); - /* ALTER POLICY ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) - COMPLETE_WITH_CONST("ON"); - /* ALTER POLICY ON
*/ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); - } - /* ALTER POLICY ON
- show options */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "POLICY") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_ALTERPOLICY[] = - {"RENAME TO", "TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_ALTERPOLICY); - } - /* ALTER POLICY ON
TO */ - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - /* ALTER POLICY ON
USING ( */ - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - COMPLETE_WITH_CONST("("); - /* ALTER POLICY ON
WITH CHECK ( */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "WITH") == 0 && - pg_strcasecmp(prev_wd, "CHECK") == 0) - COMPLETE_WITH_CONST("("); - - /* ALTER RULE , add ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) - COMPLETE_WITH_CONST("ON"); - - /* If we have ALTER RULE ON, then add the correct tablename */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); - } - - /* ALTER RULE ON */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "RULE") == 0) - COMPLETE_WITH_CONST("RENAME TO"); - - /* ALTER TRIGGER , add ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - COMPLETE_WITH_CONST("ON"); - - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); - } - - /* - * If we have ALTER TRIGGER ON, then add the correct tablename - */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - - /* ALTER TRIGGER ON */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - COMPLETE_WITH_CONST("RENAME TO"); - - /* - * If we detect ALTER TABLE , suggest sub commands - */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TABLE") == 0) - { - static const char *const list_ALTER2[] = - {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT", - "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", - "VALIDATE CONSTRAINT", "REPLICA IDENTITY", NULL}; - - COMPLETE_WITH_LIST(list_ALTER2); - } - /* ALTER TABLE xxx ENABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "ENABLE") == 0) - { - static const char *const list_ALTERENABLE[] = - {"ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERENABLE); - } - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - (pg_strcasecmp(prev_wd, "REPLICA") == 0 || - pg_strcasecmp(prev_wd, "ALWAYS") == 0)) - { - static const char *const list_ALTERENABLE2[] = - {"RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERENABLE2); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_rule_of_table); - } - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) - { - completion_info_charp = prev4_wd; - COMPLETE_WITH_QUERY(Query_for_rule_of_table); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_trigger_of_table); - } - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - completion_info_charp = prev4_wd; - COMPLETE_WITH_QUERY(Query_for_trigger_of_table); - } - /* ALTER TABLE xxx INHERIT */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "INHERIT") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); - } - /* ALTER TABLE xxx NO INHERIT */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "NO") == 0 && - pg_strcasecmp(prev_wd, "INHERIT") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); - } - /* ALTER TABLE xxx DISABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "DISABLE") == 0) - { - static const char *const list_ALTERDISABLE[] = - {"ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDISABLE); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DISABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_rule_of_table); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DISABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_trigger_of_table); - } - else if (pg_strcasecmp(prev4_wd, "DISABLE") == 0 && - pg_strcasecmp(prev3_wd, "ROW") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "SECURITY") == 0) - { - static const char *const list_DISABLERLS[] = - {"CASCADE", NULL}; - - COMPLETE_WITH_LIST(list_DISABLERLS); - } - - /* ALTER TABLE xxx ALTER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "ALTER") == 0) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); - - /* ALTER TABLE xxx RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); - - /* - * If we have TABLE ALTER COLUMN|RENAME COLUMN, provide list of - * columns - */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0) && - pg_strcasecmp(prev_wd, "COLUMN") == 0) - COMPLETE_WITH_ATTR(prev3_wd, ""); - - /* ALTER TABLE xxx RENAME yyy */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "RENAME") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINT") != 0 && - pg_strcasecmp(prev_wd, "TO") != 0) - COMPLETE_WITH_CONST("TO"); - - /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */ - else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - (pg_strcasecmp(prev2_wd, "COLUMN") == 0 || - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) && - pg_strcasecmp(prev_wd, "TO") != 0) - COMPLETE_WITH_CONST("TO"); - - /* If we have TABLE DROP, provide COLUMN or CONSTRAINT */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_TABLEDROP[] = - {"COLUMN", "CONSTRAINT", NULL}; - - COMPLETE_WITH_LIST(list_TABLEDROP); - } - /* If we have ALTER TABLE DROP COLUMN, provide list of columns */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "COLUMN") == 0) - COMPLETE_WITH_ATTR(prev3_wd, ""); - - /* - * If we have ALTER TABLE ALTER|DROP|RENAME|VALIDATE CONSTRAINT, - * provide list of constraints - */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0 || - pg_strcasecmp(prev2_wd, "VALIDATE") == 0) && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_constraint_of_table); - } - /* ALTER TABLE ALTER [COLUMN] */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ALTER") == 0)) - { - static const char *const list_COLUMNALTER[] = - {"TYPE", "SET", "RESET", "DROP", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNALTER); - } - /* ALTER TABLE ALTER [COLUMN] SET */ - else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ALTER") == 0)) && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_COLUMNSET[] = - {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNSET); - } - /* ALTER TABLE ALTER [COLUMN] SET ( */ - else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "COLUMN") == 0) || - pg_strcasecmp(prev4_wd, "ALTER") == 0) && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_COLUMNOPTIONS[] = - {"n_distinct", "n_distinct_inherited", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNOPTIONS); - } - /* ALTER TABLE ALTER [COLUMN] SET STORAGE */ - else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "COLUMN") == 0) || - pg_strcasecmp(prev4_wd, "ALTER") == 0) && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "STORAGE") == 0) - { - static const char *const list_COLUMNSTORAGE[] = - {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNSTORAGE); - } - /* ALTER TABLE ALTER [COLUMN] DROP */ - else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ALTER") == 0)) && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_COLUMNDROP[] = - {"DEFAULT", "NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNDROP); - } - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "CLUSTER") == 0) - COMPLETE_WITH_CONST("ON"); - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev3_wd; - COMPLETE_WITH_QUERY(Query_for_index_of_table); - } - /* If we have TABLE SET, provide list of attributes and '(' */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_TABLESET[] = - {"(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL}; - - COMPLETE_WITH_LIST(list_TABLESET); - } - /* If we have TABLE SET TABLESPACE provide a list of tablespaces */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "TABLESPACE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - /* If we have TABLE SET WITHOUT provide CLUSTER or OIDS */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "WITHOUT") == 0) - { - static const char *const list_TABLESET2[] = - {"CLUSTER", "OIDS", NULL}; - - COMPLETE_WITH_LIST(list_TABLESET2); - } - /* ALTER TABLE RESET */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "RESET") == 0) - COMPLETE_WITH_CONST("("); - /* ALTER TABLE SET|RESET ( */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "SET") == 0 || - pg_strcasecmp(prev2_wd, "RESET") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_TABLEOPTIONS[] = - { - "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_scale_factor", - "autovacuum_vacuum_threshold", - "fillfactor", - "log_autovacuum_min_duration", - "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_scale_factor", - "toast.autovacuum_vacuum_threshold", - "toast.log_autovacuum_min_duration", - "user_catalog_table", - NULL - }; - - COMPLETE_WITH_LIST(list_TABLEOPTIONS); - } - else if (pg_strcasecmp(prev4_wd, "REPLICA") == 0 && - pg_strcasecmp(prev3_wd, "IDENTITY") == 0 && - pg_strcasecmp(prev2_wd, "USING") == 0 && - pg_strcasecmp(prev_wd, "INDEX") == 0) - { - completion_info_charp = prev5_wd; - COMPLETE_WITH_QUERY(Query_for_index_of_table); - } - else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "REPLICA") == 0 && - pg_strcasecmp(prev2_wd, "IDENTITY") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - { - COMPLETE_WITH_CONST("INDEX"); - } - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "REPLICA") == 0 && - pg_strcasecmp(prev_wd, "IDENTITY") == 0) - { - static const char *const list_REPLICAID[] = - {"FULL", "NOTHING", "DEFAULT", "USING", NULL}; - - COMPLETE_WITH_LIST(list_REPLICAID); - } - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "REPLICA") == 0) - { - COMPLETE_WITH_CONST("IDENTITY"); - } - - /* ALTER TABLESPACE with RENAME TO, OWNER TO, SET, RESET */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_ALTERTSPC[] = - {"RENAME TO", "OWNER TO", "SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTSPC); - } - /* ALTER TABLESPACE SET|RESET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 && - (pg_strcasecmp(prev_wd, "SET") == 0 || - pg_strcasecmp(prev_wd, "RESET") == 0)) - COMPLETE_WITH_CONST("("); - /* ALTER TABLESPACE SET|RESET ( */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - (pg_strcasecmp(prev2_wd, "SET") == 0 || - pg_strcasecmp(prev2_wd, "RESET") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_TABLESPACEOPTIONS[] = - {"seq_page_cost", "random_page_cost", NULL}; - - COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS); - } - - /* ALTER TEXT SEARCH */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_ALTERTEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 || - pg_strcasecmp(prev2_wd, "PARSER") == 0)) - { - static const char *const list_ALTERTEXTSEARCH2[] = - {"RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2); - } - - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "DICTIONARY") == 0) - { - static const char *const list_ALTERTEXTSEARCH3[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3); - } - - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) - { - static const char *const list_ALTERTEXTSEARCH4[] = - {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH4); - } - - /* complete ALTER TYPE with actions */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TYPE") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE", - "OWNER TO", "RENAME", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } - /* complete ALTER TYPE ADD with actions */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TYPE") == 0 && - pg_strcasecmp(prev_wd, "ADD") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ATTRIBUTE", "VALUE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } - /* ALTER TYPE RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TYPE") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ATTRIBUTE", "TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } - /* ALTER TYPE xxx RENAME ATTRIBUTE yyy */ - else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0) - COMPLETE_WITH_CONST("TO"); - - /* - * If we have TYPE ALTER/DROP/RENAME ATTRIBUTE, provide list of - * attributes - */ - else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0) && - pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0) - COMPLETE_WITH_ATTR(prev3_wd, ""); - /* ALTER TYPE ALTER ATTRIBUTE */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)) - { - COMPLETE_WITH_CONST("TYPE"); - } - /* complete ALTER GROUP */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "GROUP") == 0) - { - static const char *const list_ALTERGROUP[] = - {"ADD USER", "DROP USER", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGROUP); - } - /* complete ALTER GROUP ADD|DROP with USER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "GROUP") == 0 && - (pg_strcasecmp(prev_wd, "ADD") == 0 || - pg_strcasecmp(prev_wd, "DROP") == 0)) - COMPLETE_WITH_CONST("USER"); - /* complete {ALTER} GROUP ADD|DROP USER with a user name */ - else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 && - (pg_strcasecmp(prev2_wd, "ADD") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0) && - pg_strcasecmp(prev_wd, "USER") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - -/* BEGIN, END, ABORT */ - else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 || - pg_strcasecmp(prev_wd, "END") == 0 || - pg_strcasecmp(prev_wd, "ABORT") == 0) - { - static const char *const list_TRANS[] = - {"WORK", "TRANSACTION", NULL}; - - COMPLETE_WITH_LIST(list_TRANS); - } -/* COMMIT */ - else if (pg_strcasecmp(prev_wd, "COMMIT") == 0) - { - static const char *const list_COMMIT[] = - {"WORK", "TRANSACTION", "PREPARED", NULL}; - - COMPLETE_WITH_LIST(list_COMMIT); - } -/* RELEASE SAVEPOINT */ - else if (pg_strcasecmp(prev_wd, "RELEASE") == 0) - COMPLETE_WITH_CONST("SAVEPOINT"); -/* ROLLBACK*/ - else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0) - { - static const char *const list_TRANS[] = - {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL}; - - COMPLETE_WITH_LIST(list_TRANS); - } -/* CLUSTER */ - - /* - * If the previous word is CLUSTER and not WITHOUT produce list of tables - */ - else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev2_wd, "WITHOUT") != 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); - - /* - * If the previous words are CLUSTER VERBOSE produce list of tables - */ - else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev2_wd, "CLUSTER") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); - - /* If we have CLUSTER , then add "USING" */ - else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "ON") != 0 && - pg_strcasecmp(prev_wd, "VERBOSE") != 0) - { - COMPLETE_WITH_CONST("USING"); - } - /* If we have CLUSTER VERBOSE , then add "USING" */ - else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev2_wd, "VERBOSE") == 0) - { - COMPLETE_WITH_CONST("USING"); - } - - /* - * If we have CLUSTER USING, then add the index as well. - */ - else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_index_of_table); - } - - /* - * If we have CLUSTER VERBOSE USING, then add the index as well. - */ - else if (pg_strcasecmp(prev4_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev3_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_index_of_table); - } - -/* COMMENT */ - else if (pg_strcasecmp(prev_wd, "COMMENT") == 0) - COMPLETE_WITH_CONST("ON"); - else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - static const char *const list_COMMENT[] = - {"CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION", - "FOREIGN DATA WRAPPER", "FOREIGN TABLE", - "SERVER", "INDEX", "LANGUAGE", "POLICY", "RULE", "SCHEMA", "SEQUENCE", - "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", - "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", - "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; - - COMPLETE_WITH_LIST(list_COMMENT); - } - else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_TRANS2[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_TRANS2); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_TRANS2[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_TRANS2); - } - else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) - { - COMPLETE_WITH_QUERY(Query_for_all_table_constraints); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) - { - COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev5_wd, "COMMENT") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev3_wd, "CONSTRAINT") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } - else if (((pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) || - (pg_strcasecmp(prev5_wd, "COMMENT") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0) || - (pg_strcasecmp(prev6_wd, "COMMENT") == 0 && - pg_strcasecmp(prev5_wd, "ON") == 0)) && - pg_strcasecmp(prev_wd, "IS") != 0) - COMPLETE_WITH_CONST("IS"); - -/* COPY */ - - /* - * If we have COPY [BINARY] (which you'd have to type yourself), offer - * list of tables (Also cover the analogous backslash command) - */ - else if (pg_strcasecmp(prev_wd, "COPY") == 0 || - pg_strcasecmp(prev_wd, "\\copy") == 0 || - (pg_strcasecmp(prev2_wd, "COPY") == 0 && - pg_strcasecmp(prev_wd, "BINARY") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* If we have COPY|BINARY , complete it with "TO" or "FROM" */ - else if (pg_strcasecmp(prev2_wd, "COPY") == 0 || - pg_strcasecmp(prev2_wd, "\\copy") == 0 || - pg_strcasecmp(prev2_wd, "BINARY") == 0) - { - static const char *const list_FROMTO[] = - {"FROM", "TO", NULL}; - - COMPLETE_WITH_LIST(list_FROMTO); - } - /* If we have COPY|BINARY FROM|TO, complete with filename */ - else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 || - pg_strcasecmp(prev3_wd, "\\copy") == 0 || - pg_strcasecmp(prev3_wd, "BINARY") == 0) && - (pg_strcasecmp(prev_wd, "FROM") == 0 || - pg_strcasecmp(prev_wd, "TO") == 0)) - { - completion_charp = ""; - matches = completion_matches(text, complete_from_files); - } - - /* Handle COPY|BINARY FROM|TO filename */ - else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 || - pg_strcasecmp(prev4_wd, "\\copy") == 0 || - pg_strcasecmp(prev4_wd, "BINARY") == 0) && - (pg_strcasecmp(prev2_wd, "FROM") == 0 || - pg_strcasecmp(prev2_wd, "TO") == 0)) - { - static const char *const list_COPY[] = - {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL}; - - COMPLETE_WITH_LIST(list_COPY); - } - - /* Handle COPY|BINARY FROM|TO filename CSV */ - else if (pg_strcasecmp(prev_wd, "CSV") == 0 && - (pg_strcasecmp(prev3_wd, "FROM") == 0 || - pg_strcasecmp(prev3_wd, "TO") == 0)) - { - static const char *const list_CSV[] = - {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_CSV); - } - - /* CREATE DATABASE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "DATABASE") == 0) - { - static const char *const list_DATABASE[] = - {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "IS_TEMPLATE", - "ALLOW_CONNECTIONS", "CONNECTION LIMIT", "LC_COLLATE", "LC_CTYPE", - NULL}; - - COMPLETE_WITH_LIST(list_DATABASE); - } - - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "DATABASE") == 0 && - pg_strcasecmp(prev_wd, "TEMPLATE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); - - /* CREATE EXTENSION */ - /* Complete with available extensions rather than installed ones. */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "EXTENSION") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions); - /* CREATE EXTENSION */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "EXTENSION") == 0) - COMPLETE_WITH_CONST("WITH SCHEMA"); - - /* CREATE FOREIGN */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_CREATE_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_FOREIGN); - } - - /* CREATE FOREIGN DATA WRAPPER */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) - { - static const char *const list_CREATE_FOREIGN_DATA_WRAPPER[] = - {"HANDLER", "VALIDATOR", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_FOREIGN_DATA_WRAPPER); - } - - /* CREATE INDEX */ - /* First off we complete CREATE UNIQUE with "INDEX" */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "UNIQUE") == 0) - COMPLETE_WITH_CONST("INDEX"); - /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */ - else if (pg_strcasecmp(prev_wd, "INDEX") == 0 && - (pg_strcasecmp(prev2_wd, "CREATE") == 0 || - pg_strcasecmp(prev2_wd, "UNIQUE") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'" - " UNION SELECT 'CONCURRENTLY'"); - /* Complete ... INDEX [] ON with a list of tables */ - else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); - /* If we have CREATE|UNIQUE INDEX CONCURRENTLY, then add "ON" */ - else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0) && - pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0) - COMPLETE_WITH_CONST("ON"); - /* If we have CREATE|UNIQUE INDEX , then add "ON" or "CONCURRENTLY" */ - else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 || - pg_strcasecmp(prev3_wd, "UNIQUE") == 0) && - pg_strcasecmp(prev2_wd, "INDEX") == 0) - { - static const char *const list_CREATE_INDEX[] = - {"CONCURRENTLY", "ON", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_INDEX); - } - - /* - * Complete INDEX ON
with a list of table columns (which - * should really be in parens) - */ - else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 || - pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_CREATE_INDEX2[] = - {"(", "USING", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_INDEX2); - } - else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - COMPLETE_WITH_ATTR(prev2_wd, ""); - /* same if you put in USING */ - else if (pg_strcasecmp(prev5_wd, "ON") == 0 && - pg_strcasecmp(prev3_wd, "USING") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - COMPLETE_WITH_ATTR(prev4_wd, ""); - /* Complete USING with an index method */ - else if ((pg_strcasecmp(prev6_wd, "INDEX") == 0 || - pg_strcasecmp(prev5_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "INDEX") == 0) && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); - else if (pg_strcasecmp(prev4_wd, "ON") == 0 && - (!(pg_strcasecmp(prev6_wd, "POLICY") == 0) && - !(pg_strcasecmp(prev4_wd, "FOR") == 0)) && - pg_strcasecmp(prev2_wd, "USING") == 0) - COMPLETE_WITH_CONST("("); - - /* CREATE POLICY */ - /* Complete "CREATE POLICY ON" */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) - COMPLETE_WITH_CONST("ON"); - /* Complete "CREATE POLICY ON
" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* Complete "CREATE POLICY ON
FOR|TO|USING|WITH CHECK" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "POLICY") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_POLICYOPTIONS[] = - {"FOR", "TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR - * ALL|SELECT|INSERT|UPDATE|DELETE" - */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - { - static const char *const list_POLICYCMDS[] = - {"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", NULL}; - - COMPLETE_WITH_LIST(list_POLICYCMDS); - } - /* Complete "CREATE POLICY ON
FOR INSERT TO|WITH CHECK" */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - pg_strcasecmp(prev_wd, "INSERT") == 0) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR SELECT TO|USING" Complete - * "CREATE POLICY ON
FOR DELETE TO|USING" - */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - (pg_strcasecmp(prev_wd, "SELECT") == 0 || - pg_strcasecmp(prev_wd, "DELETE") == 0)) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "USING", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR ALL TO|USING|WITH CHECK" - * Complete "CREATE POLICY ON
FOR UPDATE TO|USING|WITH - * CHECK" - */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - (pg_strcasecmp(prev_wd, "ALL") == 0 || - pg_strcasecmp(prev_wd, "UPDATE") == 0)) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - /* Complete "CREATE POLICY ON
TO " */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - /* Complete "CREATE POLICY ON
USING (" */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - COMPLETE_WITH_CONST("("); - -/* CREATE RULE */ - /* Complete "CREATE RULE " with "AS" */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) - COMPLETE_WITH_CONST("AS"); - /* Complete "CREATE RULE AS with "ON" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) - COMPLETE_WITH_CONST("ON"); - /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */ - else if (pg_strcasecmp(prev4_wd, "RULE") == 0 && - pg_strcasecmp(prev2_wd, "AS") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - static const char *const rule_events[] = - {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; - - COMPLETE_WITH_LIST(rule_events); - } - /* Complete "AS ON " with a "TO" */ - else if (pg_strcasecmp(prev3_wd, "AS") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - (pg_toupper((unsigned char) prev_wd[4]) == 'T' || - pg_toupper((unsigned char) prev_wd[5]) == 'T')) - COMPLETE_WITH_CONST("TO"); - /* Complete "AS ON TO" with a table name */ - else if (pg_strcasecmp(prev4_wd, "AS") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - -/* CREATE SERVER */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "SERVER") == 0) - { - static const char *const list_CREATE_SERVER[] = - {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_SERVER); - } - -/* CREATE TABLE */ - /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - (pg_strcasecmp(prev_wd, "TEMP") == 0 || - pg_strcasecmp(prev_wd, "TEMPORARY") == 0)) - { - static const char *const list_TEMP[] = - {"SEQUENCE", "TABLE", "VIEW", NULL}; - - COMPLETE_WITH_LIST(list_TEMP); - } - /* Complete "CREATE UNLOGGED" with TABLE */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "UNLOGGED") == 0) - { - static const char *const list_UNLOGGED[] = - {"TABLE", "MATERIALIZED VIEW", NULL}; - - COMPLETE_WITH_LIST(list_UNLOGGED); - } - -/* CREATE TABLESPACE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_CREATETABLESPACE[] = - {"OWNER", "LOCATION", NULL}; - - COMPLETE_WITH_LIST(list_CREATETABLESPACE); - } - /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev2_wd, "OWNER") == 0) - { - COMPLETE_WITH_CONST("LOCATION"); - } - -/* CREATE TEXT SEARCH */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_CREATETEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETEXTSEARCH); - } - else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) - COMPLETE_WITH_CONST("("); - -/* CREATE TRIGGER */ - /* complete CREATE TRIGGER with BEFORE,AFTER */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { - static const char *const list_CREATETRIGGER[] = - {"BEFORE", "AFTER", "INSTEAD OF", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER); - } - /* complete CREATE TRIGGER BEFORE,AFTER with an event */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev_wd, "BEFORE") == 0 || - pg_strcasecmp(prev_wd, "AFTER") == 0)) - { - static const char *const list_CREATETRIGGER_EVENTS[] = - {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS); - } - /* complete CREATE TRIGGER INSTEAD OF with an event */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev_wd, "OF") == 0) - { - static const char *const list_CREATETRIGGER_EVENTS[] = - {"INSERT", "DELETE", "UPDATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS); - } - /* complete CREATE TRIGGER BEFORE,AFTER sth with OR,ON */ - else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) || - (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev3_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev2_wd, "OF") == 0)) - { - static const char *const list_CREATETRIGGER2[] = - {"ON", "OR", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER2); - } - - /* - * complete CREATE TRIGGER BEFORE,AFTER event ON with a list of - * tables - */ - else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev3_wd, "BEFORE") == 0 || - pg_strcasecmp(prev3_wd, "AFTER") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ - else if (pg_strcasecmp(prev4_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev3_wd, "OF") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ - else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 && - !(pg_strcasecmp(prev2_wd, "GRANT") == 0 && prev3_wd[0] == '\0') && - prev2_wd[0] != '\0') - COMPLETE_WITH_CONST("PROCEDURE"); - -/* CREATE ROLE,USER,GROUP */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && - (pg_strcasecmp(prev2_wd, "ROLE") == 0 || - pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0)) - { - static const char *const list_CREATEROLE[] = - {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", - "CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS", - "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", - "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", - "REPLICATION", "ROLE", "SUPERUSER", "SYSID", "UNENCRYPTED", - "VALID UNTIL", "WITH", NULL}; - - COMPLETE_WITH_LIST(list_CREATEROLE); - } - -/* CREATE ROLE,USER,GROUP WITH */ - else if ((pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || - pg_strcasecmp(prev3_wd, "USER") == 0) && - pg_strcasecmp(prev_wd, "WITH") == 0)) - { - /* Similar to the above, but don't complete "WITH" again. */ - static const char *const list_CREATEROLE_WITH[] = - {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", - "CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS", - "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", - "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", - "REPLICATION", "ROLE", "SUPERUSER", "SYSID", "UNENCRYPTED", - "VALID UNTIL", NULL}; - - COMPLETE_WITH_LIST(list_CREATEROLE_WITH); - } - - /* - * complete CREATE ROLE,USER,GROUP ENCRYPTED,UNENCRYPTED with - * PASSWORD - */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) - { - COMPLETE_WITH_CONST("PASSWORD"); - } - /* complete CREATE ROLE,USER,GROUP IN with ROLE,GROUP */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - pg_strcasecmp(prev_wd, "IN") == 0) - { - static const char *const list_CREATEROLE3[] = - {"GROUP", "ROLE", NULL}; - - COMPLETE_WITH_LIST(list_CREATEROLE3); - } - -/* CREATE VIEW */ - /* Complete CREATE VIEW with AS */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - COMPLETE_WITH_CONST("AS"); - /* Complete "CREATE VIEW AS with "SELECT" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) - COMPLETE_WITH_CONST("SELECT"); - -/* CREATE MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) - COMPLETE_WITH_CONST("VIEW"); - /* Complete CREATE MATERIALIZED VIEW with AS */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - COMPLETE_WITH_CONST("AS"); - /* Complete "CREATE MATERIALIZED VIEW AS with "SELECT" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) - COMPLETE_WITH_CONST("SELECT"); - -/* CREATE EVENT TRIGGER */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "EVENT") == 0) - COMPLETE_WITH_CONST("TRIGGER"); - /* Complete CREATE EVENT TRIGGER with ON */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - COMPLETE_WITH_CONST("ON"); - /* Complete CREATE EVENT TRIGGER ON with event_type */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "EVENT") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - static const char *const list_CREATE_EVENT_TRIGGER_ON[] = - {"ddl_command_start", "ddl_command_end", "sql_drop", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_EVENT_TRIGGER_ON); - } - -/* DECLARE */ - else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0) - { - static const char *const list_DECLARE[] = - {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; - - COMPLETE_WITH_LIST(list_DECLARE); - } - -/* CURSOR */ - else if (pg_strcasecmp(prev_wd, "CURSOR") == 0) - { - static const char *const list_DECLARECURSOR[] = - {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; - - COMPLETE_WITH_LIST(list_DECLARECURSOR); - } - - -/* DELETE */ - - /* - * Complete DELETE with FROM (only if the word before that is not "ON" - * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) - */ - else if (pg_strcasecmp(prev_wd, "DELETE") == 0 && - !(pg_strcasecmp(prev2_wd, "ON") == 0 || - pg_strcasecmp(prev2_wd, "GRANT") == 0 || - pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) - COMPLETE_WITH_CONST("FROM"); - /* Complete DELETE FROM with a list of tables */ - else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 && - pg_strcasecmp(prev_wd, "FROM") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); - /* Complete DELETE FROM
*/ - else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 && - pg_strcasecmp(prev2_wd, "FROM") == 0) - { - static const char *const list_DELETE[] = - {"USING", "WHERE", NULL}; - - COMPLETE_WITH_LIST(list_DELETE); - } - /* XXX: implement tab completion for DELETE ... USING */ - -/* DISCARD */ - else if (pg_strcasecmp(prev_wd, "DISCARD") == 0) - { - static const char *const list_DISCARD[] = - {"ALL", "PLANS", "SEQUENCES", "TEMP", NULL}; - - COMPLETE_WITH_LIST(list_DISCARD); - } - -/* DO */ - - /* - * Complete DO with LANGUAGE. - */ - else if (pg_strcasecmp(prev_wd, "DO") == 0) - { - static const char *const list_DO[] = - {"LANGUAGE", NULL}; - - COMPLETE_WITH_LIST(list_DO); - } - -/* DROP (when not the previous word) */ - /* DROP AGGREGATE */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "AGGREGATE") == 0) - COMPLETE_WITH_CONST("("); - - /* DROP object with CASCADE / RESTRICT */ - else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 && - (pg_strcasecmp(prev2_wd, "COLLATION") == 0 || - pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || - pg_strcasecmp(prev2_wd, "DOMAIN") == 0 || - pg_strcasecmp(prev2_wd, "EXTENSION") == 0 || - pg_strcasecmp(prev2_wd, "FUNCTION") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 || - pg_strcasecmp(prev2_wd, "SCHEMA") == 0 || - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 || - pg_strcasecmp(prev2_wd, "SERVER") == 0 || - pg_strcasecmp(prev2_wd, "TABLE") == 0 || - pg_strcasecmp(prev2_wd, "TYPE") == 0 || - pg_strcasecmp(prev2_wd, "VIEW") == 0)) || - (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && - prev_wd[strlen(prev_wd) - 1] == ')') || - (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) || - (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) || - (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 || - pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 || - pg_strcasecmp(prev2_wd, "PARSER") == 0 || - pg_strcasecmp(prev2_wd, "TEMPLATE") == 0)) - ) - { - if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "FUNCTION") == 0) - { - COMPLETE_WITH_CONST("("); - } - else - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; - - COMPLETE_WITH_LIST(list_DROPCR); - } - } - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const drop_CREATE_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(drop_CREATE_FOREIGN); - } - - /* DROP MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) - { - COMPLETE_WITH_CONST("VIEW"); - } - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - } - - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev3_wd, "FUNCTION") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - COMPLETE_WITH_FUNCTION_ARG(prev2_wd); - /* DROP OWNED BY */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "OWNED") == 0) - COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - - static const char *const list_ALTERTEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); - } - - /* DROP TRIGGER */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { - COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); - } - else if (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; - - COMPLETE_WITH_LIST(list_DROPCR); - } - - /* DROP EVENT TRIGGER */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "EVENT") == 0) - { - COMPLETE_WITH_CONST("TRIGGER"); - } - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } - - /* DROP POLICY */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "POLICY") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_policies); - } - /* DROP POLICY ON */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) - { - COMPLETE_WITH_CONST("ON"); - } - /* DROP POLICY ON
*/ - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); - } - - /* DROP RULE */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) - { - COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - completion_info_charp = prev2_wd; - COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); - } - else if (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "RULE") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; - - COMPLETE_WITH_LIST(list_DROPCR); - } - -/* EXECUTE, but not EXECUTE embedded in other commands */ - else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 && - prev2_wd[0] == '\0') - COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); - -/* EXPLAIN */ - - /* - * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands - */ - else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } - else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } - else if ((pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0) || - (pg_strcasecmp(prev3_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev2_wd, "ANALYZE") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0)) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } - -/* FETCH && MOVE */ - /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ - else if (pg_strcasecmp(prev_wd, "FETCH") == 0 || - pg_strcasecmp(prev_wd, "MOVE") == 0) - { - static const char *const list_FETCH1[] = - {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL}; - - COMPLETE_WITH_LIST(list_FETCH1); - } - /* Complete FETCH with one of ALL, NEXT, PRIOR */ - else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 || - pg_strcasecmp(prev2_wd, "MOVE") == 0) - { - static const char *const list_FETCH2[] = - {"ALL", "NEXT", "PRIOR", NULL}; - - COMPLETE_WITH_LIST(list_FETCH2); - } - - /* - * Complete FETCH 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 (pg_strcasecmp(prev3_wd, "FETCH") == 0 || - pg_strcasecmp(prev3_wd, "MOVE") == 0) - { - static const char *const list_FROMIN[] = - {"FROM", "IN", NULL}; - - COMPLETE_WITH_LIST(list_FROMIN); - } - -/* FOREIGN DATA WRAPPER */ - /* applies in ALTER/DROP FDW and in CREATE SERVER */ - else if (pg_strcasecmp(prev4_wd, "CREATE") != 0 && - pg_strcasecmp(prev3_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev2_wd, "DATA") == 0 && - pg_strcasecmp(prev_wd, "WRAPPER") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_fdws); - -/* FOREIGN TABLE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 && - pg_strcasecmp(prev2_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev_wd, "TABLE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); - -/* GRANT && REVOKE */ - /* Complete GRANT/REVOKE with a list of roles and privileges */ - else if (pg_strcasecmp(prev_wd, "GRANT") == 0 || - pg_strcasecmp(prev_wd, "REVOKE") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'SELECT'" - " UNION SELECT 'INSERT'" - " UNION SELECT 'UPDATE'" - " UNION SELECT 'DELETE'" - " UNION SELECT 'TRUNCATE'" - " UNION SELECT 'REFERENCES'" - " UNION SELECT 'TRIGGER'" - " UNION SELECT 'CREATE'" - " UNION SELECT 'CONNECT'" - " UNION SELECT 'TEMPORARY'" - " UNION SELECT 'EXECUTE'" - " UNION SELECT 'USAGE'" - " UNION SELECT 'ALL'"); - } - - /* - * Complete GRANT/REVOKE with "ON", GRANT/REVOKE with - * TO/FROM - */ - else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 || - pg_strcasecmp(prev2_wd, "REVOKE") == 0) - { - if (pg_strcasecmp(prev_wd, "SELECT") == 0 - || pg_strcasecmp(prev_wd, "INSERT") == 0 - || pg_strcasecmp(prev_wd, "UPDATE") == 0 - || pg_strcasecmp(prev_wd, "DELETE") == 0 - || pg_strcasecmp(prev_wd, "TRUNCATE") == 0 - || pg_strcasecmp(prev_wd, "REFERENCES") == 0 - || pg_strcasecmp(prev_wd, "TRIGGER") == 0 - || pg_strcasecmp(prev_wd, "CREATE") == 0 - || pg_strcasecmp(prev_wd, "CONNECT") == 0 - || pg_strcasecmp(prev_wd, "TEMPORARY") == 0 - || pg_strcasecmp(prev_wd, "TEMP") == 0 - || pg_strcasecmp(prev_wd, "EXECUTE") == 0 - || pg_strcasecmp(prev_wd, "USAGE") == 0 - || pg_strcasecmp(prev_wd, "ALL") == 0) - COMPLETE_WITH_CONST("ON"); - else - { - if (pg_strcasecmp(prev2_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } - } - - /* - * Complete GRANT/REVOKE ON with a list of tables, views, sequences, - * and indexes - * - * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via - * UNION; seems to work intuitively - * - * 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 ((pg_strcasecmp(prev3_wd, "GRANT") == 0 || - pg_strcasecmp(prev3_wd, "REVOKE") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, - " UNION SELECT 'DATABASE'" - " UNION SELECT 'DOMAIN'" - " UNION SELECT 'FOREIGN DATA WRAPPER'" - " UNION SELECT 'FOREIGN SERVER'" - " UNION SELECT 'FUNCTION'" - " UNION SELECT 'LANGUAGE'" - " UNION SELECT 'LARGE OBJECT'" - " UNION SELECT 'SCHEMA'" - " UNION SELECT 'TABLESPACE'" - " UNION SELECT 'TYPE'"); - else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 || - pg_strcasecmp(prev4_wd, "REVOKE") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_privilege_foreign[] = - {"DATA WRAPPER", "SERVER", NULL}; - - COMPLETE_WITH_LIST(list_privilege_foreign); - } - - /* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */ - else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 || - pg_strcasecmp(prev4_wd, "REVOKE") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - if (pg_strcasecmp(prev_wd, "DATABASE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); - else if (pg_strcasecmp(prev_wd, "DOMAIN") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); - else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_languages); - else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - else if (pg_strcasecmp(prev_wd, "TYPE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); - else if (pg_strcasecmp(prev4_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } - - /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */ - else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) - { - if (pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - else - COMPLETE_WITH_CONST("TO"); - } - else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) - { - if (pg_strcasecmp(prev_wd, "FROM") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - else - COMPLETE_WITH_CONST("FROM"); - } - - /* Complete "GRANT/REVOKE * TO/FROM" with username, GROUP, or PUBLIC */ - else if (pg_strcasecmp(prev3_wd, "GRANT") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - } - else if (pg_strcasecmp(prev3_wd, "REVOKE") == 0 && - pg_strcasecmp(prev_wd, "FROM") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - } - -/* GROUP BY */ - else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && - pg_strcasecmp(prev_wd, "GROUP") == 0) - COMPLETE_WITH_CONST("BY"); - -/* IMPORT FOREIGN SCHEMA */ - else if (pg_strcasecmp(prev_wd, "IMPORT") == 0) - COMPLETE_WITH_CONST("FOREIGN SCHEMA"); - else if (pg_strcasecmp(prev2_wd, "IMPORT") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - COMPLETE_WITH_CONST("SCHEMA"); - -/* INSERT */ - /* Complete INSERT with "INTO" */ - else if (pg_strcasecmp(prev_wd, "INSERT") == 0) - COMPLETE_WITH_CONST("INTO"); - /* Complete INSERT INTO with table names */ - else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 && - pg_strcasecmp(prev_wd, "INTO") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); - /* Complete "INSERT INTO
(" with attribute names */ - else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 && - pg_strcasecmp(prev3_wd, "INTO") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - COMPLETE_WITH_ATTR(prev2_wd, ""); - - /* - * Complete INSERT INTO
with "(" or "VALUES" or "SELECT" or - * "TABLE" or "DEFAULT VALUES" - */ - else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 && - pg_strcasecmp(prev2_wd, "INTO") == 0) - { - static const char *const list_INSERT[] = - {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL}; - - COMPLETE_WITH_LIST(list_INSERT); - } - - /* - * Complete INSERT INTO
(attribs) with "VALUES" or "SELECT" or - * "TABLE" - */ - else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 && - pg_strcasecmp(prev3_wd, "INTO") == 0 && - prev_wd[strlen(prev_wd) - 1] == ')') - { - static const char *const list_INSERT[] = - {"SELECT", "TABLE", "VALUES", NULL}; - - COMPLETE_WITH_LIST(list_INSERT); - } - - /* Insert an open parenthesis after "VALUES" */ - else if (pg_strcasecmp(prev_wd, "VALUES") == 0 && - pg_strcasecmp(prev2_wd, "DEFAULT") != 0) - COMPLETE_WITH_CONST("("); - -/* LOCK */ - /* Complete LOCK [TABLE] with a list of tables */ - else if (pg_strcasecmp(prev_wd, "LOCK") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION SELECT 'TABLE'"); - else if (pg_strcasecmp(prev_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "LOCK") == 0) - 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]
with "IN" */ - else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 && - pg_strcasecmp(prev_wd, "TABLE") != 0) || - (pg_strcasecmp(prev2_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "LOCK") == 0)) - COMPLETE_WITH_CONST("IN"); - - /* Complete LOCK [TABLE]
IN with a lock mode */ - else if (pg_strcasecmp(prev_wd, "IN") == 0 && - (pg_strcasecmp(prev3_wd, "LOCK") == 0 || - (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev4_wd, "LOCK") == 0))) - { - static const char *const lock_modes[] = - {"ACCESS SHARE MODE", - "ROW SHARE MODE", "ROW EXCLUSIVE MODE", - "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE", - "SHARE ROW EXCLUSIVE MODE", - "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL}; - - COMPLETE_WITH_LIST(lock_modes); - } - -/* NOTIFY */ - else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0) - COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'"); - -/* OPTIONS */ - else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0) - COMPLETE_WITH_CONST("("); - -/* OWNER TO - complete with available roles */ - else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - -/* ORDER BY */ - else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && - pg_strcasecmp(prev_wd, "ORDER") == 0) - COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev4_wd, "FROM") == 0 && - pg_strcasecmp(prev2_wd, "ORDER") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) - COMPLETE_WITH_ATTR(prev3_wd, ""); - -/* PREPARE xx AS */ - else if (pg_strcasecmp(prev_wd, "AS") == 0 && - pg_strcasecmp(prev3_wd, "PREPARE") == 0) - { - static const char *const list_PREPARE[] = - {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; - - COMPLETE_WITH_LIST(list_PREPARE); - } - -/* - * 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 (pg_strcasecmp(prev_wd, "REASSIGN") == 0) - COMPLETE_WITH_CONST("OWNED"); - else if (pg_strcasecmp(prev_wd, "OWNED") == 0 && - pg_strcasecmp(prev2_wd, "REASSIGN") == 0) - COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev_wd, "BY") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev3_wd, "REASSIGN") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (pg_strcasecmp(prev2_wd, "BY") == 0 && - pg_strcasecmp(prev3_wd, "OWNED") == 0 && - pg_strcasecmp(prev4_wd, "REASSIGN") == 0) - COMPLETE_WITH_CONST("TO"); - else if (pg_strcasecmp(prev_wd, "TO") == 0 && - pg_strcasecmp(prev3_wd, "BY") == 0 && - pg_strcasecmp(prev4_wd, "OWNED") == 0 && - pg_strcasecmp(prev5_wd, "REASSIGN") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - -/* REFRESH MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev_wd, "REFRESH") == 0) - COMPLETE_WITH_CONST("MATERIALIZED VIEW"); - else if (pg_strcasecmp(prev2_wd, "REFRESH") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) - COMPLETE_WITH_CONST("VIEW"); - else if (pg_strcasecmp(prev3_wd, "REFRESH") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - " UNION SELECT 'CONCURRENTLY'"); - else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - COMPLETE_WITH_CONST("WITH"); - else if (pg_strcasecmp(prev5_wd, "REFRESH") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) - COMPLETE_WITH_CONST("WITH DATA"); - else if (pg_strcasecmp(prev5_wd, "REFRESH") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "WITH") == 0) - { - static const char *const list_WITH_DATA[] = - {"NO DATA", "DATA", NULL}; - - COMPLETE_WITH_LIST(list_WITH_DATA); - } - else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 && - pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev4_wd, "VIEW") == 0 && - pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0 && - pg_strcasecmp(prev_wd, "WITH") == 0) - COMPLETE_WITH_CONST("DATA"); - else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 && - pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev4_wd, "VIEW") == 0 && - pg_strcasecmp(prev2_wd, "WITH") == 0 && - pg_strcasecmp(prev_wd, "NO") == 0) - COMPLETE_WITH_CONST("DATA"); - -/* REINDEX */ - else if (pg_strcasecmp(prev_wd, "REINDEX") == 0) - { - static const char *const list_REINDEX[] = - {"TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE", NULL}; - - COMPLETE_WITH_LIST(list_REINDEX); - } - else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0) - { - if (pg_strcasecmp(prev_wd, "TABLE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); - else if (pg_strcasecmp(prev_wd, "INDEX") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); - else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 || - pg_strcasecmp(prev_wd, "DATABASE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); - } - -/* SECURITY LABEL */ - else if (pg_strcasecmp(prev_wd, "SECURITY") == 0) - COMPLETE_WITH_CONST("LABEL"); - else if (pg_strcasecmp(prev2_wd, "SECURITY") == 0 && - pg_strcasecmp(prev_wd, "LABEL") == 0) - { - static const char *const list_SECURITY_LABEL_preposition[] = - {"ON", "FOR"}; - - COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition); - } - else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 && - pg_strcasecmp(prev3_wd, "LABEL") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0) - COMPLETE_WITH_CONST("ON"); - else if ((pg_strcasecmp(prev3_wd, "SECURITY") == 0 && - pg_strcasecmp(prev2_wd, "LABEL") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) || - (pg_strcasecmp(prev5_wd, "SECURITY") == 0 && - pg_strcasecmp(prev4_wd, "LABEL") == 0 && - pg_strcasecmp(prev3_wd, "FOR") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0)) - { - static const char *const list_SECURITY_LABEL[] = - {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", - "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "DOMAIN", - "LARGE OBJECT", NULL}; - - COMPLETE_WITH_LIST(list_SECURITY_LABEL); - } - else if (pg_strcasecmp(prev5_wd, "SECURITY") == 0 && - pg_strcasecmp(prev4_wd, "LABEL") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) - COMPLETE_WITH_CONST("IS"); - -/* SELECT */ - /* naah . . . */ - -/* SET, RESET, SHOW */ - /* Complete with a variable name */ - else if ((pg_strcasecmp(prev_wd, "SET") == 0 && - pg_strcasecmp(prev3_wd, "UPDATE") != 0) || - pg_strcasecmp(prev_wd, "RESET") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_set_vars); - else if (pg_strcasecmp(prev_wd, "SHOW") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_show_vars); - /* Complete "SET TRANSACTION" */ - else if ((pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev2_wd, "START") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev2_wd, "BEGIN") == 0 - && pg_strcasecmp(prev_wd, "WORK") == 0) - || (pg_strcasecmp(prev2_wd, "BEGIN") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev4_wd, "SESSION") == 0 - && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0 - && pg_strcasecmp(prev2_wd, "AS") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)) - { - static const char *const my_list[] = - {"ISOLATION LEVEL", "READ", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev3_wd, "SET") == 0 - || pg_strcasecmp(prev3_wd, "BEGIN") == 0 - || pg_strcasecmp(prev3_wd, "START") == 0 - || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0 - && pg_strcasecmp(prev3_wd, "AS") == 0)) - && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 - || pg_strcasecmp(prev2_wd, "WORK") == 0) - && pg_strcasecmp(prev_wd, "ISOLATION") == 0) - COMPLETE_WITH_CONST("LEVEL"); - else if ((pg_strcasecmp(prev4_wd, "SET") == 0 - || pg_strcasecmp(prev4_wd, "BEGIN") == 0 - || pg_strcasecmp(prev4_wd, "START") == 0 - || pg_strcasecmp(prev4_wd, "AS") == 0) - && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0 - || pg_strcasecmp(prev3_wd, "WORK") == 0) - && pg_strcasecmp(prev2_wd, "ISOLATION") == 0 - && pg_strcasecmp(prev_wd, "LEVEL") == 0) - { - static const char *const my_list[] = - {"READ", "REPEATABLE", "SERIALIZABLE", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev4_wd, "WORK") == 0) && - pg_strcasecmp(prev3_wd, "ISOLATION") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "READ") == 0) - { - static const char *const my_list[] = - {"UNCOMMITTED", "COMMITTED", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev4_wd, "WORK") == 0) && - pg_strcasecmp(prev3_wd, "ISOLATION") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "REPEATABLE") == 0) - COMPLETE_WITH_CONST("READ"); - else if ((pg_strcasecmp(prev3_wd, "SET") == 0 || - pg_strcasecmp(prev3_wd, "BEGIN") == 0 || - pg_strcasecmp(prev3_wd, "START") == 0 || - pg_strcasecmp(prev3_wd, "AS") == 0) && - (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev2_wd, "WORK") == 0) && - pg_strcasecmp(prev_wd, "READ") == 0) - { - static const char *const my_list[] = - {"ONLY", "WRITE", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - /* SET CONSTRAINTS */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINTS") == 0) - { - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); - } - /* Complete SET CONSTRAINTS with DEFERRED|IMMEDIATE */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0) - { - static const char *const constraint_list[] = - {"DEFERRED", "IMMEDIATE", NULL}; - - COMPLETE_WITH_LIST(constraint_list); - } - /* Complete SET ROLE */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "ROLE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "SESSION") == 0) - { - static const char *const my_list[] = - {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - /* Complete SET SESSION AUTHORIZATION with username */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 - && pg_strcasecmp(prev2_wd, "SESSION") == 0 - && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); - /* Complete RESET SESSION with AUTHORIZATION */ - else if (pg_strcasecmp(prev2_wd, "RESET") == 0 && - pg_strcasecmp(prev_wd, "SESSION") == 0) - COMPLETE_WITH_CONST("AUTHORIZATION"); - /* Complete SET with "TO" */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev4_wd, "UPDATE") != 0 && - pg_strcasecmp(prev_wd, "TABLESPACE") != 0 && - pg_strcasecmp(prev_wd, "SCHEMA") != 0 && - prev_wd[strlen(prev_wd) - 1] != ')' && - prev_wd[strlen(prev_wd) - 1] != '=' && - pg_strcasecmp(prev4_wd, "DOMAIN") != 0) - COMPLETE_WITH_CONST("TO"); - /* Suggest possible variable values */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 && - (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0)) - { - if (pg_strcasecmp(prev2_wd, "DateStyle") == 0) - { - static const char *const my_list[] = - {"ISO", "SQL", "Postgres", "German", - "YMD", "DMY", "MDY", - "US", "European", "NonEuropean", - "DEFAULT", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0) - { - static const char *const my_list[] = - {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if (pg_strcasecmp(prev2_wd, "GEQO") == 0) - { - static const char *const my_list[] = - {"ON", "OFF", "DEFAULT", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if (pg_strcasecmp(prev2_wd, "search_path") == 0) - { - COMPLETE_WITH_QUERY(Query_for_list_of_schemas - " AND nspname not like 'pg\\_toast%%' " - " AND nspname not like 'pg\\_temp%%' " - " UNION SELECT 'DEFAULT' "); - } - else - { - static const char *const my_list[] = - {"DEFAULT", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - } - -/* START TRANSACTION */ - else if (pg_strcasecmp(prev_wd, "START") == 0) - COMPLETE_WITH_CONST("TRANSACTION"); - -/* TABLE, but not TABLE embedded in other commands */ - else if (pg_strcasecmp(prev_wd, "TABLE") == 0 && - prev2_wd[0] == '\0') - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); - -/* TABLESAMPLE */ - else if (pg_strcasecmp(prev_wd, "TABLESAMPLE") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods); - - else if (pg_strcasecmp(prev2_wd, "TABLESAMPLE") == 0) - COMPLETE_WITH_CONST("("); - -/* TRUNCATE */ - else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - -/* UNLISTEN */ - else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0) - COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s' UNION SELECT '*'"); - -/* UPDATE */ - /* If prev. word is UPDATE suggest a list of tables */ - else if (pg_strcasecmp(prev_wd, "UPDATE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); - /* Complete UPDATE
with "SET" */ - else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0) - COMPLETE_WITH_CONST("SET"); - - /* - * If the previous word is SET (and it wasn't caught above as the _first_ - * word) the word before it was (hopefully) a table name and we'll now - * make a list of attributes. - */ - else if (pg_strcasecmp(prev_wd, "SET") == 0) - COMPLETE_WITH_ATTR(prev2_wd, ""); - -/* UPDATE xx SET yy = */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev4_wd, "UPDATE") == 0) - COMPLETE_WITH_CONST("="); - -/* USER MAPPING */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 || - pg_strcasecmp(prev3_wd, "CREATE") == 0 || - pg_strcasecmp(prev3_wd, "DROP") == 0) && - pg_strcasecmp(prev2_wd, "USER") == 0 && - pg_strcasecmp(prev_wd, "MAPPING") == 0) - COMPLETE_WITH_CONST("FOR"); - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "USER") == 0 && - pg_strcasecmp(prev2_wd, "MAPPING") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'CURRENT_USER'" - " UNION SELECT 'PUBLIC'" - " UNION SELECT 'USER'"); - else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 || - pg_strcasecmp(prev4_wd, "DROP") == 0) && - pg_strcasecmp(prev3_wd, "USER") == 0 && - pg_strcasecmp(prev2_wd, "MAPPING") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); - else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 || - pg_strcasecmp(prev5_wd, "ALTER") == 0 || - pg_strcasecmp(prev5_wd, "DROP") == 0) && - pg_strcasecmp(prev4_wd, "USER") == 0 && - pg_strcasecmp(prev3_wd, "MAPPING") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0) - COMPLETE_WITH_CONST("SERVER"); - -/* - * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] - * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] - */ - else if (pg_strcasecmp(prev_wd, "VACUUM") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'FULL'" - " UNION SELECT 'FREEZE'" - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - (pg_strcasecmp(prev_wd, "FULL") == 0 || - pg_strcasecmp(prev_wd, "FREEZE") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0 && - (pg_strcasecmp(prev2_wd, "FULL") == 0 || - pg_strcasecmp(prev2_wd, "FREEZE") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - (pg_strcasecmp(prev2_wd, "FULL") == 0 || - pg_strcasecmp(prev2_wd, "FREEZE") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); - else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 && - pg_strcasecmp(prev2_wd, "VERBOSE") == 0) || - (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev2_wd, "ANALYZE") == 0)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); - -/* WITH [RECURSIVE] */ - - /* - * Only match when WITH is the first word, as WITH may appear in many - * other contexts. - */ - else if (pg_strcasecmp(prev_wd, "WITH") == 0 && - prev2_wd[0] == '\0') - COMPLETE_WITH_CONST("RECURSIVE"); - -/* ANALYZE */ - /* If the previous word is ANALYZE, produce list of tables */ - else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); - -/* WHERE */ - /* Simple case of the word before the where being the table name */ - else if (pg_strcasecmp(prev_wd, "WHERE") == 0) - COMPLETE_WITH_ATTR(prev2_wd, ""); - -/* ... FROM ... */ -/* TODO: also include SRF ? */ - else if (pg_strcasecmp(prev_wd, "FROM") == 0 && - pg_strcasecmp(prev3_wd, "COPY") != 0 && - pg_strcasecmp(prev3_wd, "\\copy") != 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); - -/* ... JOIN ... */ - else if (pg_strcasecmp(prev_wd, "JOIN") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); - -/* Backslash commands */ -/* TODO: \dc \dd \dl */ - else if (strcmp(prev_wd, "\\?") == 0) - { - static const char *const my_list[] = - {"commands", "options", "variables", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - { - if (!recognized_connection_string(text)) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); - } - else if (strcmp(prev2_wd, "\\connect") == 0 || strcmp(prev2_wd, "\\c") == 0) - { - if (!recognized_connection_string(prev_wd)) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - } - else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); - else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); - else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_servers); - else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); - else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_fdws); - - else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); - else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers); - else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates); - /* must be at end of \dF */ - else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); - - else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); - else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_languages); - else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0 - || strncmp(prev_wd, "\\z", strlen("\\z")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); - else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); - else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); - else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0 - || (strncmp(prev_wd, "\\dg", strlen("\\dg")) == 0)) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (strncmp(prev_wd, "\\dx", strlen("\\dx")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_extensions); - else if (strncmp(prev_wd, "\\dm", strlen("\\dm")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - else if (strncmp(prev_wd, "\\dE", strlen("\\dE")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); - else if (strncmp(prev_wd, "\\dy", strlen("\\dy")) == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - - /* must be at end of \d list */ - else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); - - else if (strcmp(prev_wd, "\\ef") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - - else if (strcmp(prev_wd, "\\encoding") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_encodings); - else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0) - COMPLETE_WITH_LIST(sql_commands); - else if (strcmp(prev_wd, "\\password") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (strcmp(prev_wd, "\\pset") == 0) - { - static const char *const my_list[] = - {"border", "columns", "expanded", "fieldsep", "fieldsep_zero", - "footer", "format", "linestyle", "null", "numericlocale", - "pager", "recordsep", "recordsep_zero", "tableattr", "title", - "tuples_only", "unicode_border_linestyle", - "unicode_column_linestyle", "unicode_header_linestyle", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev2_wd, "\\pset") == 0) - { - if (strcmp(prev_wd, "format") == 0) - { - static const char *const my_list[] = - {"unaligned", "aligned", "wrapped", "html", "asciidoc", - "latex", "latex-longtable", "troff-ms", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "linestyle") == 0) - { - static const char *const my_list[] = - {"ascii", "old-ascii", "unicode", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "unicode_border_linestyle") == 0 || - strcmp(prev_wd, "unicode_column_linestyle") == 0 || - strcmp(prev_wd, "unicode_header_linestyle") == 0) - { - static const char *const my_list[] = - {"single", "double", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - - } - } - else if (strcmp(prev_wd, "\\unset") == 0) - { - matches = complete_from_variables(text, "", "", true); - } - else if (strcmp(prev_wd, "\\set") == 0) - { - matches = complete_from_variables(text, "", "", false); - } - else if (strcmp(prev2_wd, "\\set") == 0) - { - static const char *const boolean_value_list[] = - {"on", "off", NULL}; - - if (strcmp(prev_wd, "AUTOCOMMIT") == 0) - COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "COMP_KEYWORD_CASE") == 0) - { - static const char *const my_list[] = - {"lower", "upper", "preserve-lower", "preserve-upper", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "ECHO") == 0) - { - static const char *const my_list[] = - {"errors", "queries", "all", "none", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "ECHO_HIDDEN") == 0) - { - static const char *const my_list[] = - {"noexec", "off", "on", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "HISTCONTROL") == 0) - { - static const char *const my_list[] = - {"ignorespace", "ignoredups", "ignoreboth", "none", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "ON_ERROR_ROLLBACK") == 0) - { - static const char *const my_list[] = - {"on", "off", "interactive", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - else if (strcmp(prev_wd, "ON_ERROR_STOP") == 0) - COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "QUIET") == 0) - COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "SINGLELINE") == 0) - COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "SINGLESTEP") == 0) - COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "VERBOSITY") == 0) - { - static const char *const my_list[] = - {"default", "verbose", "terse", NULL}; - - COMPLETE_WITH_LIST_CS(my_list); - } - } - else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (strcmp(prev_wd, "\\cd") == 0 || - strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 || - strcmp(prev_wd, "\\g") == 0 || - strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 || - strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 || - strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 || - strcmp(prev_wd, "\\s") == 0 || - strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 || - strcmp(prev_wd, "\\lo_import") == 0 - ) - { - completion_charp = "\\"; - matches = completion_matches(text, complete_from_files); - } - - /* - * 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. - */ - else - { - int i; - - for (i = 0; words_after_create[i].name; i++) - { - if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0) - { - if (words_after_create[i].query) - COMPLETE_WITH_QUERY(words_after_create[i].query); - else if (words_after_create[i].squery) - COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery, - NULL); - break; - } - } - } - - /* - * 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(""); -#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER - rl_completion_append_character = '\0'; -#endif - } - - /* free storage */ - { - int i; - - for (i = 0; i < lengthof(previous_words); i++) - free(previous_words[i]); - } - - /* Return our Grand List O' Matches */ - 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); -} - -/* The following two functions are wrappers for _complete_from_query */ - -static char * -complete_from_query(const char *text, int state) -{ - return _complete_from_query(0, text, state); -} - -static char * -complete_from_schema_query(const char *text, int state) -{ - return _complete_from_query(1, text, state); -} - - -/* - * This creates a list of matching things, according to a query pointed to - * by completion_charp. - * The query can be one of two kinds: - * - * 1. A simple query which must contain a %d and a %s, which will be replaced - * by the string length of the text and the text itself. The query may also - * have up to four more %s in it; the first two such will be replaced by the - * value of completion_info_charp, the next two by the value of - * completion_info_charp2. - * - * 2. A schema query used for completion of both schema and relation names. - * These are more complex and must contain in the following order: - * %d %s %d %s %d %s %s %d %s - * where %d is the string length of the text and %s the text itself. - * - * It is assumed that strings should be escaped to become SQL literals - * (that is, what is in the query is actually ... '%s' ...) - * - * See top of file for examples of both kinds of query. - */ -static char * -_complete_from_query(int is_schema_query, const char *text, int state) -{ - static int list_index, - string_length; - static PGresult *result = NULL; - - /* - * 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 *e_text; - char *e_info_charp; - char *e_info_charp2; - - list_index = 0; - string_length = strlen(text); - - /* Free any prior result */ - PQclear(result); - result = NULL; - - /* Set up suitably-escaped copies of textual inputs */ - e_text = pg_malloc(string_length * 2 + 1); - PQescapeString(e_text, text, string_length); - - if (completion_info_charp) - { - size_t charp_len; - - charp_len = strlen(completion_info_charp); - e_info_charp = pg_malloc(charp_len * 2 + 1); - PQescapeString(e_info_charp, completion_info_charp, - charp_len); - } - else - e_info_charp = NULL; - - if (completion_info_charp2) - { - size_t charp_len; - - charp_len = strlen(completion_info_charp2); - e_info_charp2 = pg_malloc(charp_len * 2 + 1); - PQescapeString(e_info_charp2, completion_info_charp2, - charp_len); - } - else - e_info_charp2 = NULL; - - initPQExpBuffer(&query_buffer); - - if (is_schema_query) - { - /* completion_squery gives us the pieces to assemble */ - const char *qualresult = completion_squery->qualresult; - - if (qualresult == NULL) - qualresult = completion_squery->result; - - /* Get unqualified names matching the input-so-far */ - appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ", - completion_squery->result, - completion_squery->catname); - if (completion_squery->selcondition) - appendPQExpBuffer(&query_buffer, "%s AND ", - completion_squery->selcondition); - appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'", - completion_squery->result, - string_length, e_text); - appendPQExpBuffer(&query_buffer, " AND %s", - completion_squery->viscondition); - - /* - * 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(completion_squery->catname, - "pg_catalog.pg_class c") == 0 && - strncmp(text, "pg_", 3) !=0) - { - appendPQExpBufferStr(&query_buffer, - " AND c.relnamespace <> (SELECT oid FROM" - " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); - } - - /* - * Add in matching schema names, but only if there is more than - * one potential match among schema names. - */ - appendPQExpBuffer(&query_buffer, "\nUNION\n" - "SELECT pg_catalog.quote_ident(n.nspname) || '.' " - "FROM pg_catalog.pg_namespace n " - "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", - string_length, e_text); - appendPQExpBuffer(&query_buffer, - " AND (SELECT pg_catalog.count(*)" - " FROM pg_catalog.pg_namespace" - " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", - string_length, e_text); - - /* - * Add in matching qualified names, but only if there is exactly - * one schema matching the input-so-far. - */ - appendPQExpBuffer(&query_buffer, "\nUNION\n" - "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " - "FROM %s, pg_catalog.pg_namespace n " - "WHERE %s = n.oid AND ", - qualresult, - completion_squery->catname, - completion_squery->namespace); - if (completion_squery->selcondition) - appendPQExpBuffer(&query_buffer, "%s AND ", - completion_squery->selcondition); - appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", - qualresult, - string_length, e_text); - - /* - * This condition exploits the single-matching-schema rule to - * speed up the query - */ - appendPQExpBuffer(&query_buffer, - " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", - string_length, e_text); - appendPQExpBuffer(&query_buffer, - " AND (SELECT pg_catalog.count(*)" - " FROM pg_catalog.pg_namespace" - " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" - " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", - string_length, e_text); - - /* If an addon query was provided, use it */ - if (completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); - } - else - { - /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, completion_charp, - string_length, e_text, - e_info_charp, e_info_charp, - e_info_charp2, e_info_charp2); - } - - /* Limit the number of records in the result */ - appendPQExpBuffer(&query_buffer, "\nLIMIT %d", - completion_max_records); - - result = exec_query(query_buffer.data); - - termPQExpBuffer(&query_buffer); - free(e_text); - if (e_info_charp) - free(e_info_charp); - if (e_info_charp2) - free(e_info_charp2); - } - - /* Find something that matches */ - if (result && PQresultStatus(result) == PGRES_TUPLES_OK) - { - const char *item; - - while (list_index < PQntuples(result) && - (item = PQgetvalue(result, list_index++, 0))) - if (pg_strncasecmp(text, item, string_length) == 0) - return pg_strdup(item); - } - - /* If nothing matches, free the db structure and return null */ - PQclear(result); - result = NULL; - return NULL; -} - - -/* - * 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. This should be used if - * there is only one possibility that can appear at a certain spot, so - * misspellings will be overwritten. The string to be passed must be in - * completion_charp. - */ -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 the variables - * that have the set values are picked up. - */ -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; - - static const char *const known_varnames[] = { - "AUTOCOMMIT", "COMP_KEYWORD_CASE", "DBNAME", "ECHO", "ECHO_HIDDEN", - "ENCODING", "FETCH_COUNT", "HISTCONTROL", "HISTFILE", "HISTSIZE", - "HOST", "IGNOREEOF", "LASTOID", "ON_ERROR_ROLLBACK", "ON_ERROR_STOP", - "PORT", "PROMPT1", "PROMPT2", "PROMPT3", "QUIET", "SINGLELINE", - "SINGLESTEP", "USER", "VERBOSITY", NULL - }; - - varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *)); - - if (!need_value) - { - for (i = 0; known_varnames[i] && nvars < maxvars; i++) - append_variable_names(&varnames, &nvars, &maxvars, - known_varnames[i], prefix, suffix); - } - - for (ptr = pset.vars->next; ptr; ptr = ptr->next) - { - if (need_value && !(ptr->value)) - continue; - for (i = 0; known_varnames[i]; i++) /* remove duplicate entry */ - { - if (strcmp(ptr->name, known_varnames[i]) == 0) - 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. - */ -static char * -complete_from_files(const char *text, int state) -{ - static const char *unquoted_text; - char *unquoted_match; - char *ret = NULL; - - 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 = filename_completion_function(unquoted_text, state); - if (unquoted_match) - { - /* - * Caller sets 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. Since we have only two callers, don't - * bother providing a macro to simplify this. - */ - ret = quote_if_needed(unquoted_match, " \t\r\n\"`", - '\'', *completion_charp, pset.encoding); - if (ret) - free(unquoted_match); - else - ret = unquoted_match; - } - - return ret; -} - - -/* 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; -} - - -/* - * Execute a query and report any errors. 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) - { -#ifdef NOT_USED - psql_error("tab completion query failed: %s\nQuery was:\n%s\n", - PQerrorMessage(pset.db), query); -#endif - PQclear(result); - result = NULL; - } - - return result; -} - - -/* - * Return the nwords word(s) before point. Words are returned right to left, - * that is, previous_words[0] gets the last word before point. - * If we run out of words, remaining array elements are set to empty strings. - * Each array element is filled with a malloc'd string. - */ -static void -get_previous_words(int point, char **previous_words, int nwords) -{ - const char *buf = rl_line_buffer; /* alias */ - int i; - - /* first we look for a non-word char before the current point */ - for (i = point - 1; i >= 0; i--) - if (strchr(WORD_BREAKS, buf[i])) - break; - point = i; - - while (nwords-- > 0) - { - int start, - end; - char *s; - - /* 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 return an empty string, because there is no word - * before the point - */ - if (end < 0) - { - point = end; - s = pg_strdup(""); - } - else - { - /* - * 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. - */ - bool inquotes = false; - int parentheses = 0; - - 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; - } - } - - point = start - 1; - - /* make a copy of chars from start to end inclusive */ - s = pg_malloc(end - start + 2); - strlcpy(s, &buf[start], end - start + 2); - } - - *previous_words++ = s; - } -} - -#ifdef NOT_USED - -/* - * Surround a string with single quotes. This works for both SQL and - * psql internal. Currently disabled because it is reported not to - * cooperate with certain versions of readline. - */ -static char * -quote_file_name(char *text, int match_type, char *quote_pointer) -{ - char *s; - size_t length; - - (void) quote_pointer; /* not used */ - - length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2); - s = pg_malloc(length); - s[0] = '\''; - strcpy(s + 1, text); - if (match_type == SINGLE_MATCH) - s[length - 2] = '\''; - s[length - 1] = '\0'; - return s; -} - -static char * -dequote_file_name(char *text, char quote_char) -{ - char *s; - size_t length; - - if (!quote_char) - return pg_strdup(text); - - length = strlen(text); - s = pg_malloc(length - 2 + 1); - strlcpy(s, text +1, length - 2 + 1); - - return s; -} -#endif /* NOT_USED */ - -#endif /* USE_READLINE */ diff --git a/src/bin/csql/tab-complete.h b/src/bin/csql/tab-complete.h deleted file mode 100644 index 9dcd7e742..000000000 --- a/src/bin/csql/tab-complete.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/tab-complete.h - */ -#ifndef TAB_COMPLETE_H -#define TAB_COMPLETE_H - -#include "postgres_fe.h" - -void initialize_readline(void); - -#endif diff --git a/src/bin/csql/variables.c b/src/bin/csql/variables.c deleted file mode 100644 index b9a3dfad3..000000000 --- a/src/bin/csql/variables.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/variables.c - */ -#include "postgres_fe.h" - -#include "common.h" -#include "variables.h" - - -/* - * Check whether a variable's name is allowed. - * - * We allow any non-ASCII character, as well as ASCII letters, digits, and - * underscore. Keep this in sync with the definition of variable_char in - * psqlscan.l. - */ -static bool -valid_variable_name(const char *name) -{ - const unsigned char *ptr = (const unsigned char *) name; - - /* Mustn't be zero-length */ - if (*ptr == '\0') - return false; - - while (*ptr) - { - if (IS_HIGHBIT_SET(*ptr) || - strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" - "_0123456789", *ptr) != NULL) - ptr++; - else - return false; - } - - return true; -} - -/* - * A "variable space" is represented by an otherwise-unused struct _variable - * that serves as list header. - */ -VariableSpace -CreateVariableSpace(void) -{ - struct _variable *ptr; - - ptr = pg_malloc(sizeof *ptr); - ptr->name = NULL; - ptr->value = NULL; - ptr->assign_hook = NULL; - ptr->next = NULL; - - return ptr; -} - -const char * -GetVariable(VariableSpace space, const char *name) -{ - struct _variable *current; - - if (!space) - return NULL; - - for (current = space->next; current; current = current->next) - { - if (strcmp(current->name, name) == 0) - { - /* this is correct answer when value is NULL, too */ - return current->value; - } - } - - return NULL; -} - -/* - * Try to interpret "value" as boolean value. - * - * Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique - * prefixes thereof. - * - * "name" is the name of the variable we're assigning to, to use in error - * report if any. Pass name == NULL to suppress the error report. - */ -bool -ParseVariableBool(const char *value, const char *name) -{ - size_t len; - - if (value == NULL) - return false; /* not set -> assume "off" */ - - len = strlen(value); - - if (pg_strncasecmp(value, "true", len) == 0) - return true; - else if (pg_strncasecmp(value, "false", len) == 0) - return false; - else if (pg_strncasecmp(value, "yes", len) == 0) - return true; - else if (pg_strncasecmp(value, "no", len) == 0) - return false; - /* 'o' is not unique enough */ - else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) - return true; - else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) - return false; - else if (pg_strcasecmp(value, "1") == 0) - return true; - else if (pg_strcasecmp(value, "0") == 0) - return false; - else - { - /* NULL is treated as false, so a non-matching value is 'true' */ - if (name) - psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", - value, name, "on"); - return true; - } -} - - -/* - * Read numeric variable, or defaultval if it is not set, or faultval if its - * value is not a valid numeric string. If allowtrail is false, this will - * include the case where there are trailing characters after the number. - */ -int -ParseVariableNum(const char *val, - int defaultval, - int faultval, - bool allowtrail) -{ - int result; - - if (!val) - result = defaultval; - else if (!val[0]) - result = faultval; - else - { - char *end; - - result = strtol(val, &end, 0); - if (!allowtrail && *end) - result = faultval; - } - - return result; -} - -int -GetVariableNum(VariableSpace space, - const char *name, - int defaultval, - int faultval, - bool allowtrail) -{ - const char *val; - - val = GetVariable(space, name); - return ParseVariableNum(val, defaultval, faultval, allowtrail); -} - -void -PrintVariables(VariableSpace space) -{ - struct _variable *ptr; - - if (!space) - return; - - for (ptr = space->next; ptr; ptr = ptr->next) - { - if (ptr->value) - printf("%s = '%s'\n", ptr->name, ptr->value); - if (cancel_pressed) - break; - } -} - -bool -SetVariable(VariableSpace space, const char *name, const char *value) -{ - struct _variable *current, - *previous; - - if (!space) - return false; - - if (!valid_variable_name(name)) - return false; - - if (!value) - return DeleteVariable(space, name); - - for (previous = space, current = space->next; - current; - previous = current, current = current->next) - { - if (strcmp(current->name, name) == 0) - { - /* found entry, so update */ - if (current->value) - free(current->value); - current->value = pg_strdup(value); - if (current->assign_hook) - (*current->assign_hook) (current->value); - return true; - } - } - - /* not present, make new entry */ - current = pg_malloc(sizeof *current); - current->name = pg_strdup(name); - current->value = pg_strdup(value); - current->assign_hook = NULL; - current->next = NULL; - previous->next = current; - return true; -} - -/* - * This both sets a hook function, and calls it on the current value (if any) - */ -bool -SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook) -{ - struct _variable *current, - *previous; - - if (!space) - return false; - - if (!valid_variable_name(name)) - return false; - - for (previous = space, current = space->next; - current; - previous = current, current = current->next) - { - if (strcmp(current->name, name) == 0) - { - /* found entry, so update */ - current->assign_hook = hook; - (*hook) (current->value); - return true; - } - } - - /* not present, make new entry */ - current = pg_malloc(sizeof *current); - current->name = pg_strdup(name); - current->value = NULL; - current->assign_hook = hook; - current->next = NULL; - previous->next = current; - (*hook) (NULL); - return true; -} - -bool -SetVariableBool(VariableSpace space, const char *name) -{ - return SetVariable(space, name, "on"); -} - -bool -DeleteVariable(VariableSpace space, const char *name) -{ - struct _variable *current, - *previous; - - if (!space) - return false; - - for (previous = space, current = space->next; - current; - previous = current, current = current->next) - { - if (strcmp(current->name, name) == 0) - { - if (current->value) - free(current->value); - current->value = NULL; - /* Physically delete only if no hook function to remember */ - if (current->assign_hook) - (*current->assign_hook) (NULL); - else - { - previous->next = current->next; - free(current->name); - free(current); - } - return true; - } - } - - return true; -} diff --git a/src/bin/csql/variables.h b/src/bin/csql/variables.h deleted file mode 100644 index c071846d9..000000000 --- a/src/bin/csql/variables.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * psql - the PostgreSQL interactive terminal - * - * Copyright (c) 2000-2015, PostgreSQL Global Development Group - * - * src/bin/psql/variables.h - */ -#ifndef VARIABLES_H -#define VARIABLES_H - -/* - * This implements a sort of variable repository. One could also think of it - * as a cheap version of an associative array. In each one of these - * datastructures you can store name/value pairs. There can also be an - * "assign hook" function that is called whenever the variable's value is - * changed. - * - * An "unset" operation causes the hook to be called with newval == NULL. - * - * Note: if value == NULL then the variable is logically unset, but we are - * keeping the struct around so as not to forget about its hook function. - */ -typedef void (*VariableAssignHook) (const char *newval); - -struct _variable -{ - char *name; - char *value; - VariableAssignHook assign_hook; - struct _variable *next; -}; - -typedef struct _variable *VariableSpace; - -VariableSpace CreateVariableSpace(void); -const char *GetVariable(VariableSpace space, const char *name); - -bool ParseVariableBool(const char *value, const char *name); -int ParseVariableNum(const char *val, - int defaultval, - int faultval, - bool allowtrail); -int GetVariableNum(VariableSpace space, - const char *name, - int defaultval, - int faultval, - bool allowtrail); - -void PrintVariables(VariableSpace space); - -bool SetVariable(VariableSpace space, const char *name, const char *value); -bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook); -bool SetVariableBool(VariableSpace space, const char *name); -bool DeleteVariable(VariableSpace space, const char *name); - -#endif /* VARIABLES_H */