📄 common.c
字号:
/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.76.2.1 2003/11/12 22:55:42 tgl Exp $ */#include "postgres_fe.h"#include "common.h"#include <ctype.h>#ifndef HAVE_STRDUP#include <strdup.h>#endif#include <signal.h>#ifndef WIN32#include <sys/time.h>#include <unistd.h> /* for write() */#include <setjmp.h>#else#include <io.h> /* for _write() */#include <win32.h>#include <sys/timeb.h> /* for _ftime() */#endif#include "libpq-fe.h"#include "pqsignal.h"#include "settings.h"#include "variables.h"#include "command.h"#include "copy.h"#include "prompt.h"#include "print.h"#include "mainloop.h"#include "mb/pg_wchar.h"/* Workarounds for Windows *//* Probably to be moved up the source tree in the future, perhaps to be replaced by * more specific checks like configure-style HAVE_GETTIMEOFDAY macros. */#ifndef WIN32typedef struct timeval TimevalStruct;#define GETTIMEOFDAY(T) gettimeofday(T, NULL)#define DIFF_MSEC(T, U) \ ((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \ ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)#elsetypedef struct _timeb TimevalStruct;#define GETTIMEOFDAY(T) _ftime(T)#define DIFF_MSEC(T, U) \ (((T)->time - (U)->time) * 1000.0 + \ ((T)->millitm - (U)->millitm))#endifextern bool prompt_state;static bool is_transact_command(const char *query);/* * "Safe" wrapper around strdup() */char *xstrdup(const char *string){ char *tmp; if (!string) { fprintf(stderr, gettext("%s: xstrdup: 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;}/* * 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. */boolsetQFout(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;}/* * Error reporting for scripts. Errors should look like * psql:filename:lineno: message * */voidpsql_error(const char *fmt,...){ va_list ap; fflush(stdout); if (pset.queryFout != stdout) fflush(pset.queryFout); if (pset.inputfile) fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno); va_start(ap, fmt); vfprintf(stderr, gettext(fmt), ap); va_end(ap);}/* * for backend Notice messages (INFO, WARNING, etc) */voidNoticeProcessor(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 a SIGINT signal catcher that sends a * cancel request to the backend. Note that sending the cancel directly from * the signal handler is safe because PQrequestCancel() is written to make it * so. We use write() to print to stdout because it's better to use simple * facilities in a signal handler. */static PGconn *volatile cancelConn = NULL;volatile bool cancel_pressed = false;#ifndef WIN32#define write_stderr(String) write(fileno(stderr), String, strlen(String))voidhandle_sigint(SIGNAL_ARGS){ int save_errno = errno; /* Don't muck around if prompting for a password. */ if (prompt_state) return; if (cancelConn == NULL) siglongjmp(main_loop_jmp, 1); cancel_pressed = true; if (PQrequestCancel(cancelConn)) write_stderr("Cancel request sent\n"); else { write_stderr("Could not send cancel request: "); write_stderr(PQerrorMessage(cancelConn)); } errno = save_errno; /* just in case the write changed it */}#endif /* not WIN32 *//* ConnectionUp * * Returns whether our backend connection is still there. */static boolConnectionUp(){ 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 boolCheckConnection(void){ bool OK; OK = ConnectionUp(); if (!OK) { if (!pset.cur_cmd_interactive) { psql_error("connection to server was lost\n"); exit(EXIT_BADCONN); } fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr); PQreset(pset.db); OK = ConnectionUp(); if (!OK) { fputs(gettext("Failed.\n"), stderr); PQfinish(pset.db); pset.db = NULL; ResetCancelConn(); UnsyncVariables(); } else fputs(gettext("Succeeded.\n"), stderr); } return OK;}/* * SetCancelConn * * Set cancelConn to point to the current database connection. */static voidSetCancelConn(void){ cancelConn = pset.db;}/* * ResetCancelConn * * Set cancelConn to NULL. I don't know what this means exactly, but it saves * having to export the variable. */voidResetCancelConn(void){ cancelConn = NULL;}/* * 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 boolAcceptResult(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;}/* * PSQLexec * * This is the way to send "backdoor" queries (those not directly entered * by the user). It is subject to -E but not -e. * * In autocommit-off mode, a new transaction block is started if start_xact * is true; nothing special is done when start_xact is false. Typically, * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands. * * 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, bool start_xact){ PGresult *res; int echo_hidden; if (!pset.db) { psql_error("You are currently not connected to a database.\n"); return NULL; } echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL); if (echo_hidden != VAR_NOTSET) { printf("********* QUERY **********\n" "%s\n" "**************************\n\n", query); fflush(stdout); if (echo_hidden == 1) /* noexec? */ return NULL; } SetCancelConn(); if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE && !GetVariableBool(pset.vars, "AUTOCOMMIT")) { res = PQexec(pset.db, "BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); PQclear(res); ResetCancelConn(); return NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -