void * pg_calloc(size_t nmemb, size_t size) { void *tmp; tmp = calloc(nmemb, size); if (!tmp) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } return tmp; }
/* * 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", false))) 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; }
/* * This function is for saving the readline history when user * runs \s command or when psql finishes. * * We have an argument named encodeFlag to handle the cases differently. * In case of call via \s we don't really need to encode \n as \x01, * but when we save history for Readline we must do that conversion. */ bool saveHistory(char *fname, bool encodeFlag) { #ifdef USE_READLINE /* * 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 (useHistory && fname && strcmp(fname, DEVNULL) != 0) { if (encodeFlag) encode_history(); /* * return value of write_history is not standardized across GNU * readline and libedit. Therefore, check for errno becoming set to * see if the write failed. */ errno = 0; (void) write_history(fname); if (errno == 0) return true; psql_error("could not save history to file \"%s\": %s\n", fname, strerror(errno)); } #else /* only get here in \s case, so complain */ psql_error("history is not supported by this installation\n"); #endif return false; }
bool saveHistory(char *fname) { #ifdef USE_READLINE if (useHistory && fname) { if (write_history(fname) == 0) return true; psql_error("could not save history to file \"%s\": %s\n", fname, strerror(errno)); } #endif return false; }
void * pg_malloc(size_t size) { void *tmp; /* Avoid unportable behavior of malloc(0) */ if (size == 0) size = 1; tmp = malloc(size); if (!tmp) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } return tmp; }
/* * setQFout * -- handler for -o command line option and \o command * * Tries to open file fname (or pipe if fname starts with '|') * and stores the file handle in pset) * Upon failure, sets stdout and returns false. */ bool setQFout(const char *fname) { bool status = true; /* Close old file/pipe */ if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr) { if (pset.queryFoutPipe) pclose(pset.queryFout); else fclose(pset.queryFout); } /* If no filename, set stdout */ if (!fname || fname[0] == '\0') { pset.queryFout = stdout; pset.queryFoutPipe = false; } else if (*fname == '|') { pset.queryFout = popen(fname + 1, "w"); pset.queryFoutPipe = true; } else { pset.queryFout = fopen(fname, "w"); pset.queryFoutPipe = false; } if (!(pset.queryFout)) { psql_error("%s: %s\n", fname, strerror(errno)); pset.queryFout = stdout; pset.queryFoutPipe = false; status = false; } /* Direct signals */ #ifndef WIN32 pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL); #endif return status; }
/* * 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; }
/* * "Safe" wrapper around strdup() */ char * pg_strdup(const char *string) { char *tmp; if (!string) { fprintf(stderr, _("%s: pg_strdup: cannot duplicate null pointer (internal error)\n"), pset.progname); exit(EXIT_FAILURE); } tmp = strdup(string); if (!tmp) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } return tmp; }
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 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 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 show_context_hook(const char *newval) { if (newval == NULL) pset.show_context = PQSHOW_CONTEXT_ERRORS; else if (pg_strcasecmp(newval, "never") == 0) pset.show_context = PQSHOW_CONTEXT_NEVER; else if (pg_strcasecmp(newval, "errors") == 0) pset.show_context = PQSHOW_CONTEXT_ERRORS; else if (pg_strcasecmp(newval, "always") == 0) pset.show_context = PQSHOW_CONTEXT_ALWAYS; else { psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", newval, "SHOW_CONTEXT", "errors"); pset.show_context = PQSHOW_CONTEXT_ERRORS; } if (pset.db) PQsetErrorContextVisibility(pset.db, pset.show_context); }
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; sys = pg_malloc(strlen(shellName) + 16); #ifndef WIN32 sprintf(sys, /* See EDITOR handling comment for an explaination */ "exec %s", shellName); #else /* See EDITOR handling comment for an explaination */ sprintf(sys, SYSTEMQUOTE "\"%s\"" SYSTEMQUOTE, shellName); #endif result = system(sys); free(sys); } else result = system(command); if (result == 127 || result == -1) { psql_error("\\!: failed\n"); return false; } return true; }
/* * Try to interpret "value" as a boolean value, and if successful, * store it in *result. Otherwise don't clobber *result. * * 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. * * Return true when "value" is syntactically valid, false otherwise. */ bool ParseVariableBool(const char *value, const char *name, bool *result) { size_t len; bool valid = true; /* Treat "unset" as an empty string, which will lead to error below */ if (value == NULL) value = ""; len = strlen(value); if (len > 0 && pg_strncasecmp(value, "true", len) == 0) *result = true; else if (len > 0 && pg_strncasecmp(value, "false", len) == 0) *result = false; else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0) *result = true; else if (len > 0 && pg_strncasecmp(value, "no", len) == 0) *result = false; /* 'o' is not unique enough */ else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) *result = true; else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) *result = false; else if (pg_strcasecmp(value, "1") == 0) *result = true; else if (pg_strcasecmp(value, "0") == 0) *result = false; else { /* string is not recognized; don't clobber *result */ if (name) psql_error("unrecognized value \"%s\" for \"%s\": boolean expected\n", value, name); valid = false; } return valid; }
/* * AcceptResult * * Checks whether a result is valid, giving an error message if necessary; * resets cancelConn as needed, 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 = true; ResetCancelConn(); if (!result) OK = false; else switch (PQresultStatus(result)) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: case PGRES_EMPTY_QUERY: case PGRES_COPY_IN: /* Fine, do nothing */ break; case PGRES_COPY_OUT: /* keep cancel connection for copy out state */ SetCancelConn(); break; default: OK = false; break; } if (!OK) { psql_error("%s", PQerrorMessage(pset.db)); CheckConnection(); } return OK; }
/* * Try to interpret value as boolean value. Valid values are: true, * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof. */ bool ParseVariableBool(const char *value) { 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' */ psql_error("unrecognized boolean value; assuming \"on\".\n"); return true; } /* suppress compiler warning */ return true; }
/* * 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); }
/* call this one */ static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, 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("cannot 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", tmpdir, "/", (int) getpid()); #else snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d", 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)); fclose(stream); remove(fname); error = true; } else if (fclose(stream) != 0) { psql_error("%s: %s\n", fname, strerror(errno)); remove(fname); 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); 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 * * Read commands from filename and then them to the main processing loop * Handler for \i, but can be used for other things as well. Returns * MainLoop() error code. */ int process_file(char *filename, bool single_txn) { FILE *fd; int result; char *oldfilename; PGresult *res; if (!filename) return EXIT_FAILURE; if (strcmp(filename, "-") != 0) { canonicalize_path(filename); fd = fopen(filename, PG_BINARY_R); } else fd = stdin; if (!fd) { psql_error("%s: %s\n", filename, strerror(errno)); return EXIT_FAILURE; } oldfilename = pset.inputfile; pset.inputfile = filename; if (single_txn) { if ((res = PSQLexec("BEGIN", false)) == NULL) { if (pset.on_error_stop) { result = EXIT_USER; goto error; } } else PQclear(res); } result = MainLoop(fd); if (single_txn) { if ((res = PSQLexec("COMMIT", false)) == 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; }
/* * 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; pw = getpwuid(geteuid()); if (!pw) { psql_error("could not get home directory: %s\n", strerror(errno)); 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 (pset.dirname) free(pset.dirname); pset.dirname = pg_strdup(dir); canonicalize_path(pset.dirname); if (opt) free(opt); } /* \conninfo -- display information about the current connection */ else if (strcmp(cmd, "conninfo") == 0) { char *db = PQdb(pset.db); char *host = PQhost(pset.db); if (db == NULL) printf(_("You are not connected.\n")); else { 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 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)); } } /* \copy */ else if (pg_strcasecmp(cmd, "copy") == 0) { /* Default fetch-it-all-and-print mode */ instr_time before, after; char *opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, false); if (pset.timing) INSTR_TIME_SET_CURRENT(before); success = do_copy(opt); if (pset.timing && success) { INSTR_TIME_SET_CURRENT(after); INSTR_TIME_SUBTRACT(after, before); printf(_("Time: %.3f ms\n"), INSTR_TIME_GET_MILLISEC(after)); } 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': /* GPDB: This is a change from old behavior: We used to show just system tables */ if (pattern) success = describeTableDetails(pattern, show_verbose, show_system); else /* standard listing of interesting things */ success = listTables("tvsxr", 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_system); break; case 'C': success = listCasts(pattern); 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_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 'n': success = listSchemas(pattern, show_verbose); break; case 'o': success = describeOperators(pattern, show_system); break; case 'p': success = permissionsList(pattern); break; case 'T': success = describeTypes(pattern, show_verbose, show_system); break; case 't': case 'v': case 'i': case 's': case 'E': /* PostgreSQL use dx for extension, change to dE for foreign table */ /* case 'S': // GPDB: We used to show just system tables for this */ case 'P': /* GPDB: Parent-only tables, no children */ 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; /* GPDB uses \dr for foreign tables ? */ success = listTables(&cmd[1], pattern, show_verbose, show_system); 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 'x': /* Extensions */ if (show_verbose) success = listExtensionContents(pattern); else success = listExtensions(pattern); 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; fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); expand_tilde(&fname); if (fname) canonicalize_path(fname); if (do_edit(fname, query_buf, NULL)) status = PSQL_CMD_NEWEDIT; else status = PSQL_CMD_ERROR; free(fname); } } /* * \ef -- edit the named function, or present a blank CREATE FUNCTION * template if no argument is given */ else if (strcmp(cmd, "ef") == 0) { 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); 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(pset.db, func, &foid)) { /* error already reported */ status = PSQL_CMD_ERROR; } else if (!get_create_function_cmd(pset.db, foid, query_buf)) { /* error already reported */ status = PSQL_CMD_ERROR; } if (func) free(func); } if (status != PSQL_CMD_ERROR) { bool edited = false; if (!do_edit(0, query_buf, &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 means send query */ 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; } /* 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 is include file */ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 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 { expand_tilde(&fname); success = (process_file(fname, false) == EXIT_SUCCESS); free(fname); } } /* \l is list databases */ else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) success = listAllDbs(false); else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) success = listAllDbs(true); /* * 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: "******"Enter it again: ", 100, false); if (strcmp(pw1, pw2) != 0) { fprintf(stderr, _("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) { fprintf(stderr, _("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, false); termPQExpBuffer(&buf); if (!res) success = false; else PQclear(res); PQfreemem(encrypted_password); } } 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\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) { psql_error("\\%s: missing required argument\n", cmd); success = false; } 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); #if defined(WIN32) && !defined(__CYGWIN__) /* * XXX This does not work for all terminal environments or for output * containing non-ASCII characters; see comments in simple_prompt(). */ #define DEVTTY "con" #else #define DEVTTY "/dev/tty" #endif expand_tilde(&fname); /* This scrolls off the screen when using /dev/tty */ success = saveHistory(fname ? fname : DEVTTY, -1, false, false); if (success && !pset.quiet && fname) printf(gettext("Wrote history to file \"%s/%s\".\n"), pset.dirname ? pset.dirname : ".", 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 = realloc(newval, strlen(newval) + strlen(opt) + 1); if (!newval) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } strcat(newval, opt); free(opt); } if (!SetVariable(pset.vars, opt0, newval)) { psql_error("\\%s: error\n", cmd); success = false; } free(newval); } free(opt0); } /* \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 <table ...> 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); 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\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; 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; } } free(fname); } /* \x -- 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) 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))) { fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value); free(value); } } #endif else status = PSQL_CMD_UNKNOWN; if (!success) status = PSQL_CMD_ERROR; return status; }
/* * This function saves the readline history when user * runs \s command or 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. * * appendFlag: if true, try to append just our new lines to the file. * If false, write the whole available history. * * encodeFlag: whether to encode \n as \x01. For \s calls we don't wish * to do that, but must do so when saving the final history file. */ bool saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag) { #ifdef USE_READLINE /* * 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 (useHistory && fname && strcmp(fname, DEVNULL) != 0) { if (encodeFlag) 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(). * * Note: return value of write_history is not standardized across GNU * readline and libedit. Therefore, check for errno becoming set to * see if the write failed. Similarly for append_history. */ #if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY) if (appendFlag) { 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; errno = 0; (void) append_history(nlines, fname); if (errno == 0) return true; } else #endif { /* truncate what we have ... */ if (max_lines >= 0) stifle_history(max_lines); /* ... and overwrite file. Tough luck for concurrent sessions. */ errno = 0; (void) write_history(fname); if (errno == 0) return true; } psql_error("could not save history to file \"%s\": %s\n", fname, strerror(errno)); } #else /* only get here in \s case, so complain */ psql_error("history is not supported by this installation\n"); #endif return false; }
static struct 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_calloc(1, 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; token = strtokx(NULL, whitespace, NULL, "'", 0, false, true, pset.encoding); if (!token) goto error; if (pg_strcasecmp(token, "stdin") == 0 || pg_strcasecmp(token, "stdout") == 0) { result->psql_inout = false; result->file = NULL; } else if (pg_strcasecmp(token, "pstdin") == 0 || pg_strcasecmp(token, "pstdout") == 0) { result->psql_inout = true; result->file = NULL; } else { result->psql_inout = false; 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); 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; }
bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary) { bool OK; const char* prompt; char buf[COPYBUFSIZ]; PGresult* res; /* * 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, _("canceled by user")); /* Check command status and return to normal libpq state */ res = PQgetResult(conn); if (PQresultStatus(res) != PGRES_COMMAND_OK) psql_error("%s", PQerrorMessage(conn)); PQclear(res); return false; } /* 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; } } } 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; 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) { 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; } } if (copystream == pset.cur_cmd_source) pset.lineno++; } } /* Check for read error */ if (ferror(copystream)) OK = false; /* Terminate data transfer */ if (PQputCopyEnd(conn, OK ? NULL : _("aborted because of read failure")) <= 0) OK = false; /* Check command status and return to normal libpq state */ res = PQgetResult(conn); if (PQresultStatus(res) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(conn)); OK = false; } PQclear(res); return OK; }
/* * Execute a \copy command (frontend copy). We have to open a file, 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) { struct pqbuf query; FILE* copystream; struct copy_options *options; PGresult* result; bool success; struct stat st; /* parse options */ options = parse_slash_copy(args); if (!options) return false; /* prepare to read or write the target file */ if (options->file) canonicalize_path(options->file); if (options->from) { if (options->file) copystream = fopen(options->file, PG_BINARY_R); else if (!options->psql_inout) copystream = pset.cur_cmd_source; else copystream = stdin; } else { if (options->file) copystream = fopen(options->file, PG_BINARY_W); else if (!options->psql_inout) copystream = pset.queryFout; else copystream = stdout; } if (!copystream) { psql_error("%s: %s\n", options->file, strerror(errno)); free_copy_options(options); return false; } /* make sure the specified file is not a directory */ fstat(fileno(copystream), &st); if (S_ISDIR(st.st_mode)) { fclose(copystream); psql_error("%s: cannot copy from/to a directory\n", options->file); free_copy_options(options); return false; } /* build the command we will send to the backend */ init_pqbuf(&query); print_pqbuf(&query, "COPY "); append_pqbuf_str(&query, options->before_tofrom); if (options->from) append_pqbuf(&query, " FROM STDIN "); else append_pqbuf(&query, " TO STDOUT "); if (options->after_tofrom) append_pqbuf_str(&query, options->after_tofrom); result = PSQLexec(query.data, true); term_pqbuf(&query); switch (PQresultStatus(result)) { case PGRES_COPY_OUT: SetCancelConn(); success = handleCopyOut(pset.db, copystream); ResetCancelConn(); break; case PGRES_COPY_IN: SetCancelConn(); success = handleCopyIn(pset.db, copystream, PQbinaryTuples(result)); ResetCancelConn(); break; case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: success = false; psql_error("\\copy: %s", PQerrorMessage(pset.db)); break; default: success = false; psql_error("\\copy: unexpected response (%d)\n", PQresultStatus(result)); break; } PQclear(result); /* * Make sure we have pumped libpq dry of results; else it may still be in * ASYNC_BUSY state, leading to false readings in, eg, get_prompt(). */ while ((result = PQgetResult(pset.db)) != NULL) { success = false; psql_error("\\copy: unexpected response (%d)\n", PQresultStatus(result)); /* if still in COPY IN state, try to get out of it */ if (PQresultStatus(result) == PGRES_COPY_IN) PQputCopyEnd(pset.db, _("trying to exit copy mode")); PQclear(result); } if (options->file != NULL) { if (fclose(copystream) != 0) { psql_error("%s: %s\n", options->file, strerror(errno)); success = false; } } free_copy_options(options); return success; }
/* * 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; if (!dbname) dbname = PQdb(o_conn); if (!user) user = PQuser(o_conn); if (!host) host = PQhost(o_conn); if (!port) port = PQport(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 * has 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 && user && strcmp(PQuser(o_conn), user) == 0) { password = strdup(PQpass(o_conn)); } while (true) { #define PARAMS_ARRAY_SIZE 7 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] = host; keywords[1] = "port"; values[1] = port; keywords[2] = "user"; values[2] = user; keywords[3] = "password"; values[3] = password; keywords[4] = "dbname"; values[4] = dbname; keywords[5] = "fallback_application_name"; values[5] = pset.progname; keywords[6] = NULL; values[6] = NULL; n_conn = PQconnectdbParams(keywords, values, true); free(keywords); free(values); /* We can immediately discard the password -- no longer needed */ if (password) 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) fputs(_("Previous connection kept\n"), stderr); } 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 (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; }
/* * for backend Notice messages (INFO, WARNING, etc) */ void NoticeProcessor(void *arg, const char *message) { (void) arg; /* not used */ psql_error("%s", message); }
bool do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) { size_t vallen = 0; psql_assert(param); 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("latex", value, vallen) == 0) popt->topt.format = PRINT_LATEX; 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, latex, troff-ms\n"); return false; } if (!quiet) printf(_("Output format is %s.\n"), _align2string(popt->topt.format)); } /* 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; } if (!quiet) printf(_("Line style is %s.\n"), get_line_style(&popt->topt)->name); } /* set border style/width */ else if (strcmp(param, "border") == 0) { if (value) popt->topt.border = atoi(value); if (!quiet) printf(_("Border style is %d.\n"), popt->topt.border); } /* set expanded/vertical mode */ else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0) { if (value) popt->topt.expanded = ParseVariableBool(value); else popt->topt.expanded = !popt->topt.expanded; if (!quiet) printf(popt->topt.expanded ? _("Expanded display is on.\n") : _("Expanded display is off.\n")); } /* locale-aware numeric output */ else if (strcmp(param, "numericlocale") == 0) { if (value) popt->topt.numericLocale = ParseVariableBool(value); else popt->topt.numericLocale = !popt->topt.numericLocale; if (!quiet) { if (popt->topt.numericLocale) puts(_("Showing locale-adjusted numeric output.")); else puts(_("Locale-adjusted numeric output is off.")); } } /* null display */ else if (strcmp(param, "null") == 0) { if (value) { free(popt->nullPrint); popt->nullPrint = pg_strdup(value); } if (!quiet) printf(_("Null display is \"%s\".\n"), popt->nullPrint ? popt->nullPrint : ""); } /* field separator for unaligned text */ else if (strcmp(param, "fieldsep") == 0) { if (value) { free(popt->topt.fieldSep); popt->topt.fieldSep = pg_strdup(value); } if (!quiet) printf(_("Field separator is \"%s\".\n"), popt->topt.fieldSep); } /* record separator for unaligned text */ else if (strcmp(param, "recordsep") == 0) { if (value) { free(popt->topt.recordSep); popt->topt.recordSep = pg_strdup(value); } if (!quiet) { if (strcmp(popt->topt.recordSep, "\n") == 0) printf(_("Record separator is <newline>.")); else printf(_("Record separator is \"%s\".\n"), popt->topt.recordSep); } } /* 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); else popt->topt.tuples_only = !popt->topt.tuples_only; if (!quiet) { if (popt->topt.tuples_only) puts(_("Showing only tuples.")); else puts(_("Tuples only is off.")); } } /* set title override */ else if (strcmp(param, "title") == 0) { free(popt->title); if (!value) popt->title = NULL; else popt->title = pg_strdup(value); if (!quiet) { if (popt->title) printf(_("Title is \"%s\".\n"), popt->title); else printf(_("Title is unset.\n")); } } /* 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); if (!quiet) { if (popt->topt.tableAttr) printf(_("Table attribute is \"%s\".\n"), popt->topt.tableAttr); else printf(_("Table attributes unset.\n")); } } /* 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)) popt->topt.pager = 1; else popt->topt.pager = 0; else if (popt->topt.pager == 1) popt->topt.pager = 0; else popt->topt.pager = 1; if (!quiet) { if (popt->topt.pager == 1) puts(_("Pager is used for long output.")); else if (popt->topt.pager == 2) puts(_("Pager is always used.")); else puts(_("Pager usage is off.")); } } /* disable "(x rows)" footer */ else if (strcmp(param, "footer") == 0) { if (value) popt->default_footer = ParseVariableBool(value); else popt->default_footer = !popt->default_footer; if (!quiet) { if (popt->default_footer) puts(_("Default footer is on.")); else puts(_("Default footer is off.")); } } /* set border style/width */ else if (strcmp(param, "columns") == 0) { if (value) popt->topt.columns = atoi(value); if (!quiet) printf(_("Target width for \"wrapped\" format is %d.\n"), popt->topt.columns); } else { psql_error("\\pset: unknown option: %s\n", param); return false; } return true; }
void usage(void) { const char *env; const char *user; #ifndef WIN32 struct passwd *pw = NULL; #endif /* Find default user, in case we need it. */ user = getenv("PGUSER"); if (!user) { #if !defined(WIN32) && !defined(__OS2__) pw = getpwuid(geteuid()); if (pw) user = pw->pw_name; else { psql_error("could not get current user name: %s\n", strerror(errno)); exit(EXIT_FAILURE); } #else /* WIN32 */ char buf[128]; DWORD bufsize = sizeof(buf) - 1; if (GetUserName(buf, &bufsize)) user = buf; #endif /* WIN32 */ } #if 0 /* psql 9.0 does it this way: */ printf(_("psql is the PostgreSQL interactive terminal.\n\n")); #else /* GPDB : for compatibility with 4.0 and 3.3, say this */ /* >>> If this " is the start of the string then it ought to end there to fit in 80 columns >> " */ printf(_("This is psql %s, the PostgreSQL interactive terminal (Greenplum version).\n\n"), PG_VERSION); #endif printf(_("Usage:\n")); printf(_(" psql [OPTION]... [DBNAME [USERNAME]]\n\n")); printf(_("General options:\n")); /* Display default database */ env = getenv("PGDATABASE"); if (!env) env = user; printf(_(" -c, --command=COMMAND run only single command (SQL or internal) and exit\n")); printf(_(" -d, --dbname=DBNAME database name to connect to (default: \"%s\")\n"), env); printf(_(" -f, --file=FILENAME execute commands from file, then exit\n")); printf(_(" -l, --list list available databases, then exit\n")); printf(_(" -v, --set=, --variable=NAME=VALUE\n" " set psql variable NAME to VALUE\n")); printf(_(" -X, --no-psqlrc do not read startup file (~/.psqlrc)\n")); printf(_(" -1 (\"one\"), --single-transaction\n" " execute command file as a single transaction\n")); printf(_(" --help show this help, then exit\n")); printf(_(" --version output version information, then exit\n")); printf(_("\nInput and output options:\n")); printf(_(" -a, --echo-all echo all input from script\n")); printf(_(" -e, --echo-queries echo commands sent to server\n")); printf(_(" -E, --echo-hidden display queries that internal commands generate\n")); printf(_(" -L, --log-file=FILENAME send session log to file\n")); printf(_(" -n, --no-readline disable enhanced command line editing (readline)\n")); printf(_(" -o, --output=FILENAME send query results to file (or |pipe)\n")); printf(_(" -q, --quiet run quietly (no messages, only query output)\n")); printf(_(" -s, --single-step single-step mode (confirm each query)\n")); printf(_(" -S, --single-line single-line mode (end of line terminates SQL command)\n")); printf(_("\nOutput format options:\n")); printf(_(" -A, --no-align unaligned table output mode\n")); printf(_(" -F, --field-separator=STRING\n" " set field separator (default: \"%s\")\n"), DEFAULT_FIELD_SEP); printf(_(" -H, --html HTML table output mode\n")); printf(_(" -P, --pset=VAR[=ARG] set printing option VAR to ARG (see \\pset command)\n")); printf(_(" -R, --record-separator=STRING\n" " set record separator (default: newline)\n")); printf(_(" -t, --tuples-only print rows only\n")); printf(_(" -T, --table-attr=TEXT set HTML table tag attributes (e.g., width, border)\n")); printf(_(" -x, --expanded turn on expanded table output\n")); printf(_("\nConnection options:\n")); /* Display default host */ env = getenv("PGHOST"); printf(_(" -h, --host=HOSTNAME database server host or socket directory (default: \"%s\")\n"), env ? env : _("local socket")); /* Display default port */ env = getenv("PGPORT"); printf(_(" -p, --port=PORT database server port (default: \"%s\")\n"), env ? env : DEF_PGPORT_STR); /* Display default user */ env = getenv("PGUSER"); if (!env) env = user; printf(_(" -U, --username=USERNAME database user name (default: \"%s\")\n"), env); printf(_(" -w, --no-password never prompt for password\n")); printf(_(" -W, --password force password prompt (should happen automatically)\n")); printf(_("\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")); printf(_("Report bugs to <*****@*****.**>.\n")); }
/* * 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, 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"); return false; } 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') return false; } 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(); return false; } 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) { fprintf(stderr, _("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(); return false; } 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 = (AcceptResult(results) && ProcessCopyResult(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) OK = PrintQueryResults(results); } else { /* Fetch-in-segments mode */ OK = ExecQueryUsingCursor(query, &elapsed_msec); ResetCancelConn(); results = NULL; /* PQclear(NULL) does nothing */ } /* If we made a temporary savepoint, possibly release/rollback */ if (on_error_rollback_savepoint) { const char *svptcmd; transaction_status = PQtransactionStatus(pset.db); if (transaction_status == PQTRANS_INERROR) { /* We always rollback on an error */ svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint"; } else if (transaction_status != PQTRANS_INTRANS) { /* If they are no longer in a transaction, then do nothing */ svptcmd = NULL; } else { /* * 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"; } if (svptcmd) { PGresult *svptres; svptres = PQexec(pset.db, svptcmd); if (PQresultStatus(svptres) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); PQclear(svptres); PQclear(results); ResetCancelConn(); return false; } PQclear(svptres); } } PQclear(results); /* Possible microtiming output */ if (OK && 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(); return OK; }