📄 command.c
字号:
/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.154.2.3 2006/03/04 04:30:51 momjian Exp $ */#include "postgres_fe.h"#include "command.h"#ifdef WIN32_CLIENT_ONLY /* needed for BCC */#undef mkdir#endif#include <errno.h>#include <ctype.h>#ifdef HAVE_PWD_H#include <pwd.h>#endif#ifndef WIN32#include <sys/types.h> /* for umask() */#include <sys/stat.h> /* for stat() */#include <fcntl.h> /* open() flags */#include <unistd.h> /* for geteuid(), getpid(), stat() */#else#include <win32.h>#include <io.h>#include <fcntl.h>#include <direct.h>#ifndef WIN32_CLIENT_ONLY#include <sys/types.h> /* for umask() */#include <sys/stat.h> /* for stat() */#endif#endif#include "libpq-fe.h"#include "pqexpbuffer.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 "variables.h"#include "mb/pg_wchar.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);static bool do_connect(const char *new_dbname, const char *new_user);static bool do_shell(const char *command);/*---------- * 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. *---------- */backslashResultHandleSlashCmds(PsqlScanState scan_state, PQExpBuffer query_buf){ backslashResult status = CMD_SKIP_LINE; char *cmd; char *arg; psql_assert(scan_state); /* 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 == CMD_UNKNOWN && strlen(cmd) > 1) { /* * If the command was not recognized, try to parse it as a one-letter * command with immediately following argument (a still-supported, but * no longer encouraged, syntax). */ char new_cmd[2]; /* don't change cmd until we know it's okay */ new_cmd[0] = cmd[0]; new_cmd[1] = '\0'; psql_scan_slash_pushback(scan_state, cmd + 1); status = exec_command(new_cmd, scan_state, query_buf); if (status != CMD_UNKNOWN) { /* adjust cmd for possible messages below */ cmd[1] = '\0'; } } if (status == CMD_UNKNOWN) { if (pset.cur_cmd_interactive) fprintf(stderr, _("Invalid command \\%s. Try \\? for help.\n"), cmd); else psql_error("invalid command \\%s\n", cmd); status = CMD_ERROR; } if (status != 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_VERBATIM, 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); return status;}/* * Subroutine to actually try to execute a backslash command. */static backslashResultexec_command(const char *cmd, PsqlScanState scan_state, PQExpBuffer query_buf){ bool success = true; /* indicate here if the command ran ok or * failed */ bool quiet = QUIET(); backslashResult status = 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, quiet); else success = do_pset("format", "unaligned", &pset.popt, 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, quiet); free(opt); } /*---------- * \c or \connect -- connect to new database or as different user * * \c foo bar connect to db "foo" as user "bar" * \c foo [-] connect to db "foo" as current user * \c - bar connect to current db as user "bar" * \c connect to default db as default user *---------- */ else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) { char *opt1, *opt2; char opt1q, opt2q; /* * 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. */ opt1 = psql_scan_slash_option(scan_state, OT_SQLIDHACK, &opt1q, true); opt2 = psql_scan_slash_option(scan_state, OT_SQLIDHACK, &opt2q, true); if (opt2) /* gave username */ success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1, !opt2q && (strcmp(opt2, "-") == 0 || strcmp(opt2, "") == 0) ? "" : opt2); else if (opt1) /* gave database name */ success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1, ""); else /* connect to default db as default user */ success = do_connect(NULL, NULL); free(opt1); free(opt2); } /* \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) pfree(pset.dirname); pset.dirname = pg_strdup(dir); canonicalize_path(pset.dirname); if (opt) free(opt); } /* \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; /* 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; switch (cmd[1]) { case '\0': case '+': if (pattern) success = describeTableDetails(pattern, show_verbose); else /* standard listing of interesting things */ success = listTables("tvs", NULL, show_verbose); break; case 'a': success = describeAggregates(pattern, show_verbose); break; case 'b': success = describeTablespaces(pattern, show_verbose); break; case 'c': success = listConversions(pattern); break; case 'C': success = listCasts(pattern); break; case 'd': success = objectDescription(pattern); break; case 'D': success = listDomains(pattern); break; case 'f': success = describeFunctions(pattern, show_verbose); break; case 'g': /* no longer distinct from \du */ success = describeRoles(pattern); break; case 'l': success = do_lo_list(); break; case 'n': success = listSchemas(pattern, show_verbose); break; case 'o': success = describeOperators(pattern); break; case 'p': success = permissionsList(pattern); break; case 'T': success = describeTypes(pattern, show_verbose); break; case 't': case 'v': case 'i': case 's': case 'S': success = listTables(&cmd[1], pattern, show_verbose); break; case 'u': success = describeRoles(pattern); break; default: status = CMD_UNKNOWN; } if (pattern) free(pattern); } /* * \e or \edit -- edit the current query buffer (or a file and make it the * query buffer */ else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0) { char *fname; if (!query_buf) { psql_error("no query buffer\n"); status = CMD_ERROR; } else { fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); expand_tilde(&fname); if (fname) canonicalize_path(fname); status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR; free(fname); } } /* \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, 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 = 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); 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, quiet); else success = do_pset("format", "aligned", &pset.popt, 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) == 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -