📄 command.c
字号:
/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.105 2003/10/11 18:04:26 momjian Exp $ */#include "postgres_fe.h"#include "command.h"#include <errno.h>#include <assert.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>#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 "settings.h"#include "variables.h"#include "mb/pg_wchar.h"/* functions for use in this file */static backslashResult exec_command(const char *cmd, const char *options_string, const char **continue_parse, PQExpBuffer query_buf, volatile int *paren_level);/* different ways for scan_option to handle parameter words */enum 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 */};static char *scan_option(char **string, enum option_type type, char *quote, bool semicolon);static char *unescape(const unsigned char *source, size_t len);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(). * * 'line' is the current input line, which should not start with a '\' * but with the actual command name * (that is taken care of by MainLoop) * * '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(const char *line, PQExpBuffer query_buf, const char **end_of_cmd, volatile int *paren_level){ backslashResult status = CMD_SKIP_LINE; char *my_line; char *options_string = NULL; size_t blank_loc; const char *continue_parse = NULL; /* tell the mainloop where the * backslash command ended */#ifdef USE_ASSERT_CHECKING assert(line);#endif my_line = xstrdup(line); /* * Find the first whitespace. line[blank_loc] will now be the * whitespace character or the \0 at the end * * Also look for a backslash, so stuff like \p\g works. */ blank_loc = strcspn(my_line, " \t\n\r\\"); if (my_line[blank_loc] == '\\') { continue_parse = &my_line[blank_loc]; my_line[blank_loc] = '\0'; /* If it's a double backslash, we skip it. */ if (my_line[blank_loc + 1] == '\\') continue_parse += 2; } /* do we have an option string? */ else if (my_line[blank_loc] != '\0') { options_string = &my_line[blank_loc + 1]; my_line[blank_loc] = '\0'; } status = exec_command(my_line, options_string, &continue_parse, query_buf, paren_level); if (status == CMD_UNKNOWN) { /* * 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]; new_cmd[0] = my_line[0]; new_cmd[1] = '\0'; /* use line for options, because my_line was clobbered above */ status = exec_command(new_cmd, line + 1, &continue_parse, query_buf, paren_level); /* * continue_parse must be relative to my_line for calculation * below */ continue_parse += my_line - line;#if 0 /* turned out to be too annoying */ if (status != CMD_UNKNOWN && isalpha((unsigned char) new_cmd[0])) psql_error("Warning: This syntax is deprecated.\n");#endif } if (status == CMD_UNKNOWN) { if (pset.cur_cmd_interactive) fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), my_line); else psql_error("invalid command \\%s\n", my_line); status = CMD_ERROR; } if (continue_parse && *continue_parse && *(continue_parse + 1) == '\\') continue_parse += 2; if (end_of_cmd) { if (continue_parse) *end_of_cmd = line + (continue_parse - my_line); else *end_of_cmd = line + strlen(line); } free(my_line); return status;}static backslashResultexec_command(const char *cmd, const char *options_string, const char **continue_parse, PQExpBuffer query_buf, volatile int *paren_level){ bool success = true; /* indicate here if the command ran ok or * failed */ bool quiet = QUIET(); backslashResult status = CMD_SKIP_LINE; char *string, *string_cpy, *val; /* * The 'string' variable will be overwritten to point to the next * token, hence we need an extra pointer so we can free this at the * end. */ if (options_string) string = string_cpy = xstrdup(options_string); else string = string_cpy = NULL; /* * \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 = scan_option(&string, 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 = scan_option(&string, OT_SQLIDHACK, &opt1q, true); opt2 = scan_option(&string, 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 = scan_option(&string, 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 (opt) free(opt); } /* \copy */ else if (strcasecmp(cmd, "copy") == 0) { success = do_copy(options_string); if (options_string) string += strlen(string); } /* \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 = scan_option(&string, 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 '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 'l': success = do_lo_list(); break; case 'n': success = listSchemas(pattern); 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 = describeUsers(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 = scan_option(&string, OT_NORMAL, NULL, true); 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 = scan_option(&string, 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 = scan_option(&string, 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 = scan_option(&string, 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 = scan_option(&string, OT_FILEPIPE, NULL, false); if (!fname) pset.gfname = NULL; else pset.gfname = xstrdup(fname); free(fname); status = CMD_SEND; } /* help */ else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { helpSQL(options_string ? &options_string[strspn(options_string, " \t\n\r")] : NULL, pset.popt.topt.pager); /* set pointer to end of line */ if (string) string += strlen(string); } /* 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 = scan_option(&string, OT_NORMAL, NULL, true); if (!fname) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else { 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; opt1 = scan_option(&string, OT_NORMAL, NULL, true); opt2 = scan_option(&string, OT_NORMAL, NULL, true); if (strcmp(cmd + 3, "export") == 0) { if (!opt2) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else 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 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 = CMD_UNKNOWN; free(opt1); free(opt2); } /* \o -- set query output */ else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0) { char *fname = scan_option(&string, OT_FILEPIPE, NULL, true); 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 (!quiet) puts(gettext("Query buffer is empty.")); fflush(stdout); } /* \pset -- set printing parameters */ else if (strcmp(cmd, "pset") == 0) { char *opt0 = scan_option(&string, OT_NORMAL, NULL, false); char *opt1 = scan_option(&string, OT_NORMAL, NULL, false); if (!opt0) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else success = do_pset(opt0, opt1, &pset.popt, quiet); free(opt0); free(opt1); } /* \q or \quit */ else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0) status = CMD_TERMINATE; /* reset(clear) the buffer */ else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) { resetPQExpBuffer(query_buf); if (paren_level) *paren_level = 0; if (!quiet) puts(gettext("Query buffer reset (cleared).")); } /* \s save history in a file or show it on the screen */ else if (strcmp(cmd, "s") == 0) { char *fname = scan_option(&string, OT_NORMAL, NULL, true); success = saveHistory(fname ? fname : "/dev/tty"); if (success && !quiet && fname) printf(gettext("Wrote history to file \"%s\".\n"), fname); free(fname); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -