diff options
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r-- | src/backend/tcop/postgres.c | 1500 |
1 files changed, 1500 insertions, 0 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c new file mode 100644 index 00000000000..d6447eaffc2 --- /dev/null +++ b/src/backend/tcop/postgres.c @@ -0,0 +1,1500 @@ +/*------------------------------------------------------------------------- + * + * postgres.c-- + * POSTGRES C Backend Interface + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + * NOTES + * this is the "main" module of the postgres backend and + * hence the main module of the "traffic cop". + * + *------------------------------------------------------------------------- + */ +#include "libpq/pqsignal.h" /* substitute for <signal.h> */ +#if defined(PORTNAME_linux) +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#endif /* defined(PORTNAME_linux) */ +#include <setjmp.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifndef WIN32 +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif /* WIN32 */ +#include <errno.h> +#ifdef PORTNAME_aix +#include <sys/select.h> +#endif /* PORTNAME_aix */ + + +#include "postgres.h" +#include "miscadmin.h" +#include "catalog/catname.h" +#include "access/xact.h" + +#include "lib/dllist.h" + +#include "parser/catalog_utils.h" +#include "parser/parse_query.h" /* for MakeTimeRange() */ +#include "commands/async.h" +#include "tcop/tcopprot.h" /* where declarations for this file go */ +#include "optimizer/planner.h" + +#include "tcop/tcopdebug.h" + +#include "executor/execdebug.h" +#include "executor/executor.h" +#include "nodes/relation.h" + +#include "optimizer/cost.h" +#include "optimizer/planner.h" +#if 0 +#include "optimizer/xfunc.h" +#endif +#include "optimizer/prep.h" +#include "nodes/plannodes.h" + +#include "storage/bufmgr.h" +#include "fmgr.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/rel.h" + +#include "nodes/pg_list.h" +#include "tcop/dest.h" +#include "nodes/memnodes.h" +#include "utils/mcxt.h" +#include "tcop/pquery.h" +#include "tcop/utility.h" +#include "tcop/fastpath.h" + +#include "libpq/libpq.h" +#include "rewrite/rewriteHandler.h" /* for QueryRewrite() */ + +/* ---------------- + * global variables + * ---------------- + */ +static bool DebugPrintPlan = false; +static bool DebugPrintParse = false; +static bool DebugPrintRewrittenParsetree = false; +/*static bool EnableRewrite = true; , never changes why have it*/ +CommandDest whereToSendOutput; + +extern int lockingOff; +extern int NBuffers; + +int dontExecute = 0; +static int ShowStats; +static bool IsEmptyQuery = false; + +Relation reldesc; /* current relation descritor */ +char relname[80]; /* current relation name */ + +#if defined(WIN32) || defined(PORTNAME_next) +jmp_buf Warn_restart; +#define sigsetjmp(x,y) setjmp(x) +#define siglongjmp longjmp +#else +sigjmp_buf Warn_restart; +#endif /*defined(WIN32) || defined(PORTNAME_next) */ + +extern int NBuffers; + +static int EchoQuery = 0; /* default don't echo */ +time_t tim; +char pg_pathname[256]; +static int ShowParserStats; +static int ShowPlannerStats; +int ShowExecutorStats; +FILE *StatFp; + +typedef struct frontend { + bool fn_connected; + Port fn_port; + FILE *fn_Pfin; /* the input fd */ + FILE *fn_Pfout; /* the output fd */ + bool fn_done; /* set after the frontend closes its connection */ +} FrontEnd; + +static Dllist* frontendList; + +/* ---------------- + * people who want to use EOF should #define DONTUSENEWLINE in + * tcop/tcopdebug.h + * ---------------- + */ +#ifndef TCOP_DONTUSENEWLINE +int UseNewLine = 1; /* Use newlines query delimiters (the default) */ +#else +int UseNewLine = 0; /* Use EOF as query delimiters */ +#endif /* TCOP_DONTUSENEWLINE */ + +/* ---------------- + * bushy tree plan flag: if true planner will generate bushy-tree + * plans + * ---------------- + */ +int BushyPlanFlag = 0; /* default to false -- consider only left-deep trees */ + +/* +** Flags for expensive function optimization -- JMH 3/9/92 +*/ +int XfuncMode = 0; + +/* + * ---------------- + * Note: _exec_repeat_ defaults to 1 but may be changed + * by a DEBUG command. If you set this to a large + * number N, run a single query, and then set it + * back to 1 and run N queries, you can get an idea + * of how much time is being spent in the parser and + * planner b/c in the first case this overhead only + * happens once. -cim 6/9/91 + * ---------------- +*/ +int _exec_repeat_ = 1; + +/* ---------------------------------------------------------------- + * decls for routines only used in this file + * ---------------------------------------------------------------- + */ +static char InteractiveBackend(char *inBuf); +static char SocketBackend(char *inBuf, int multiplexedBackend); +static char ReadCommand(char *inBuf, int multiplexedBackend); + + +/* ---------------------------------------------------------------- + * routines to obtain user input + * ---------------------------------------------------------------- + */ + +/* ---------------- + * InteractiveBackend() is called for user interactive connections + * the string entered by the user is placed in its parameter inBuf. + * ---------------- + */ + +static char +InteractiveBackend(char *inBuf) +{ + char *stuff = inBuf; /* current place in input buffer */ + int c; /* character read from getc() */ + bool end = false; /* end-of-input flag */ + bool backslashSeen = false; /* have we seen a \ ? */ + + /* ---------------- + * display a prompt and obtain input from the user + * ---------------- + */ + printf("> "); + + for (;;) { + if (UseNewLine) { + /* ---------------- + * if we are using \n as a delimiter, then read + * characters until the \n. + * ---------------- + */ + while ( (c = getc(stdin)) != EOF) { + if (c == '\n') { + if (backslashSeen) { + stuff--; + continue; + } else { + /* keep the newline character */ + *stuff++ = '\n'; + *stuff++ = '\0'; + break; + } + } else if (c == '\\') + backslashSeen = true; + else + backslashSeen = false; + + *stuff++ = (char)c; + } + + if (c == EOF) + end = true; + } else { + /* ---------------- + * otherwise read characters until EOF. + * ---------------- + */ + while ( (c = getc(stdin)) != EOF ) + *stuff++ = (char)c; + + if ( stuff == inBuf ) + end = true; + } + + if (end) { + if (!Quiet) puts("EOF"); + IsEmptyQuery = true; + exitpg(0); + } + + /* ---------------- + * otherwise we have a user query so process it. + * ---------------- + */ + break; + } + + /* ---------------- + * if the query echo flag was given, print the query.. + * ---------------- + */ + if (EchoQuery) + printf("query is: %s\n", inBuf); + + return('Q'); +} + +/* ---------------- + * SocketBackend() Is called for frontend-backend connections + * + * If the input is a query (case 'Q') then the string entered by + * the user is placed in its parameter inBuf. + * + * If the input is a fastpath function call (case 'F') then + * the function call is processed in HandleFunctionRequest(). + * (now called from PostgresMain()) + * ---------------- + */ + +static char +SocketBackend(char *inBuf, int multiplexedBackend) +{ + char qtype[2]; + char result; + + /* ---------------- + * get input from the frontend + * ---------------- + */ + (void) strcpy(qtype, "?"); + if (pq_getnchar(qtype,0,1) == EOF) { + /* ------------ + * when front-end applications quits/dies + * ------------ + */ + if (multiplexedBackend) { + return 'X'; + } + else + exitpg(0); + } + + switch(*qtype) { + /* ---------------- + * 'Q': user entered a query + * ---------------- + */ + case 'Q': + pq_getstr(inBuf, MAX_PARSE_BUFFER); + result = 'Q'; + break; + + /* ---------------- + * 'F': calling user/system functions + * ---------------- + */ + case 'F': + pq_getstr(inBuf, MAX_PARSE_BUFFER);/* ignore the rest of the line */ + result = 'F'; + break; + + /* ---------------- + * 'X': frontend is exiting + * ---------------- + */ + case 'X': + result = 'X'; + break; + + /* ---------------- + * otherwise we got garbage from the frontend. + * + * XXX are we certain that we want to do an elog(FATAL) here? + * -cim 1/24/90 + * ---------------- + */ + default: + elog(FATAL, "Socket command type %c unknown\n", *qtype); + break; + } + return result; +} + +/* ---------------- + * ReadCommand reads a command from either the frontend or + * standard input, places it in inBuf, and returns a char + * representing whether the string is a 'Q'uery or a 'F'astpath + * call. + * ---------------- + */ +static char +ReadCommand(char *inBuf, int multiplexedBackend) +{ + if (IsUnderPostmaster || multiplexedBackend) + return SocketBackend(inBuf, multiplexedBackend); + else + return InteractiveBackend(inBuf); +} + +List * +pg_plan(char *query_string, /* string to execute */ + Oid *typev, /* argument types */ + int nargs, /* number of arguments */ + QueryTreeList **queryListP, /* pointer to the parse trees */ + CommandDest dest) /* where results should go */ +{ + QueryTreeList *querytree_list; + int i; + List *plan_list = NIL; + Plan *plan; + int j; + QueryTreeList *new_list; + List *rewritten = NIL; + Query* querytree; + + /* ---------------- + * (1) parse the request string into a list of parse trees + * ---------------- + */ + if (ShowParserStats) + ResetUsage(); + + querytree_list = parser(query_string, typev, nargs); + + if (ShowParserStats) { + fprintf(stderr, "! Parser Stats:\n"); + ShowUsage(); + } + + /* new_list holds the rewritten queries */ + new_list = (QueryTreeList*)malloc(sizeof(QueryTreeList)); + new_list->len = querytree_list->len; + new_list->qtrees = (Query**)malloc(new_list->len * sizeof(Query*)); + + /* ---------------- + * (2) rewrite the queries, as necessary + * ---------------- + */ + j = 0; /* counter for the new_list, new_list can be longer than + old list as a result of rewrites */ + for (i=0;i<querytree_list->len;i++) { + querytree = querytree_list->qtrees[i]; + + + /* don't rewrite utilites */ + if (querytree->commandType == CMD_UTILITY) { + new_list->qtrees[j++] = querytree; + continue; + } + + if ( DebugPrintParse == true ) { + printf("\ninput string is \"%s\"\n",query_string); + printf("\n---- \tparser outputs :\n"); + nodeDisplay(querytree); + printf("\n"); + } + + /* rewrite queries (retrieve, append, delete, replace) */ + rewritten = QueryRewrite(querytree); + if (rewritten != NULL) { + int len, k; + len = length(rewritten); + if (len == 1) + new_list->qtrees[j++] = (Query*)lfirst(rewritten); + else { + /* rewritten queries are longer than original query */ + /* grow the new_list to accommodate */ + new_list->len += len - 1; /* - 1 because originally we + allocated one space for the query */ + new_list->qtrees = realloc(new_list->qtrees, + new_list->len * sizeof(Query*)); + for (k=0;k<len;k++) + new_list->qtrees[j++] = (Query*)nth(k, rewritten); + } + } + } + + /* we're done with the original lists, free it */ + free(querytree_list->qtrees); + free(querytree_list); + + querytree_list = new_list; + + /* ---------------- + * Fix time range quals + * this _must_ go here, because it must take place after rewrites + * ( if they take place ) so that time quals are usable by the executor + * + * Also, need to frob the range table entries here to plan union + * queries for archived relations. + * ---------------- + */ + for (i=0;i<querytree_list->len;i++) { + List *l; + List *rt = NULL; + + querytree = querytree_list->qtrees[i]; + + /* ---------------- + * utilities don't have time ranges + * ---------------- + */ + if (querytree->commandType == CMD_UTILITY) + continue; + + rt = querytree->rtable; + + foreach (l, rt) { + RangeTblEntry *rte = lfirst(l); + TimeRange *timequal = rte->timeRange; + + if (timequal) { + int timecode = (rte->timeRange->endDate == NULL)? 0 : 1; + + rte->timeQual = makeTimeRange(rte->timeRange->startDate, + rte->timeRange->endDate, + timecode); + }else { + rte->timeQual = NULL; + } + } + + /* check for archived relations */ + plan_archive(rt); + } + + if (DebugPrintRewrittenParsetree == true) { + printf("\n=================\n"); + printf(" After Rewriting\n"); + printf("=================\n"); + + for (i=0; i<querytree_list->len; i++) { + print(querytree_list->qtrees[i]); + printf("\n"); + } + } + + for (i=0; i<querytree_list->len;i++) { + querytree = querytree_list->qtrees[i]; + + /* + * For each query that isn't a utility invocation, + * generate a plan. + */ + + if (querytree->commandType != CMD_UTILITY) { + + if (IsAbortedTransactionBlockState()) { + /* ---------------- + * the EndCommand() stuff is to tell the frontend + * that the command ended. -cim 6/1/90 + * ---------------- + */ + char *tag = "*ABORT STATE*"; + EndCommand(tag, dest); + + elog(NOTICE, "(transaction aborted): %s", + "queries ignored until END"); + + *queryListP = (QueryTreeList*)NULL; + return (List*)NULL; + } + + if (ShowPlannerStats) ResetUsage(); + plan = planner(querytree); + if (ShowPlannerStats) { + fprintf(stderr, "! Planner Stats:\n"); + ShowUsage(); + } + plan_list = lappend(plan_list, plan); + } + } + + if (queryListP) + *queryListP = querytree_list; + + return (plan_list); +} + +/* ---------------------------------------------------------------- + * pg_eval() + * + * Takes a querystring, runs the parser/utilities or + * parser/planner/executor over it as necessary + * Begin Transaction Should have been called before this + * and CommitTransaction After this is called + * This is strictly because we do not allow for nested xactions. + * + * NON-OBVIOUS-RESTRICTIONS + * this function _MUST_ allocate a new "parsetree" each time, + * since it may be stored in a named portal and should not + * change its value. + * + * ---------------------------------------------------------------- + */ + +void +pg_eval(char *query_string, char *argv[], Oid *typev, int nargs) +{ + pg_eval_dest(query_string, argv, typev, nargs, whereToSendOutput); +} + +void +pg_eval_dest(char *query_string, /* string to execute */ + char *argv[], /* arguments */ + Oid *typev, /* argument types */ + int nargs, /* number of arguments */ + CommandDest dest) /* where results should go */ +{ + List *plan_list; + Plan *plan; + Query *querytree; + int i,j; + QueryTreeList *querytree_list; + + /* plan the queries */ + plan_list = pg_plan(query_string, typev, nargs, &querytree_list, dest); + + /* pg_plan could have failed */ + if (querytree_list == NULL) + return; + + for (i=0;i<querytree_list->len;i++) { + querytree = querytree_list->qtrees[i]; + + if (querytree->commandType == CMD_UTILITY) { + /* ---------------- + * process utility functions (create, destroy, etc..) + * + * Note: we do not check for the transaction aborted state + * because that is done in ProcessUtility. + * ---------------- + */ + if (! Quiet) { + time(&tim); + printf("\tProcessUtility() at %s\n", ctime(&tim)); + } + + ProcessUtility(querytree->utilityStmt, dest); + + } else { + plan = (Plan *) lfirst(plan_list); + plan_list = lnext(plan_list); + + /* ---------------- + * print plan if debugging + * ---------------- + */ + if ( DebugPrintPlan == true ) { + printf("\nPlan is :\n"); + nodeDisplay(plan); + printf("\n"); + } + + /* ---------------- + * execute the plan + * + */ + if (ShowExecutorStats) + ResetUsage(); + + for (j = 0; j < _exec_repeat_; j++) { + if (! Quiet) { + time(&tim); + printf("\tProcessQuery() at %s\n", ctime(&tim)); + } + ProcessQuery(querytree, plan, argv, typev, nargs, dest); + } + + if (ShowExecutorStats) { + fprintf(stderr, "! Executor Stats:\n"); + ShowUsage(); + } + } + /* + * In a query block, we want to increment the command counter + * between queries so that the effects of early queries are + * visible to subsequent ones. + */ + + if (querytree_list) + CommandCounterIncrement(); + } + + free(querytree_list->qtrees); + free(querytree_list); +} + +/* -------------------------------- + * signal handler routines used in PostgresMain() + * + * handle_warn() is used to catch kill(getpid(),1) which + * occurs when elog(WARN) is called. + * + * quickdie() occurs when signalled by the postmaster, some backend + * has bought the farm we need to stop what we're doing and exit. + * + * die() preforms an orderly cleanup via ExitPostgres() + * -------------------------------- + */ + +void +handle_warn() +{ + siglongjmp(Warn_restart, 1); +} + +void +quickdie() +{ + elog(NOTICE, "I have been signalled by the postmaster."); + elog(NOTICE, "Some backend process has died unexpectedly and possibly"); + elog(NOTICE, "corrupted shared memory. The current transaction was"); + elog(NOTICE, "aborted, and I am going to exit. Please resend the"); + elog(NOTICE, "last query. -- The postgres backend"); + + /* + * DO NOT ExitPostgres(0) -- we're here because shared memory may be + * corrupted, so we don't want to flush any shared state to stable + * storage. Just nail the windows shut and get out of town. + */ + + exit (0); +} + +void +die() +{ + ExitPostgres(0); +} + +/* signal handler for floating point exception */ +void +FloatExceptionHandler() +{ + elog(WARN, "floating point exception! the last floating point operation eit\ +her exceeded legal ranges or was a divide by zero"); +} + + +static void usage(char* progname) +{ + fprintf(stderr, + "Usage: %s [-B nbufs] [-d lvl] ] [-f plantype] \t[-m portno] [\t -o filename]\n", + progname); + fprintf(stderr,"\t[-P portno] [-t tracetype] [-x opttype] [-bCEiLNopQSs] [dbname]\n"); + fprintf(stderr, " b: consider bushy plan trees during optimization\n"); + fprintf(stderr, " B: set number of buffers in buffer pool\n"); + fprintf(stderr, " C: supress version info\n"); + fprintf(stderr, " d: set debug level\n"); + fprintf(stderr, " E: echo query before execution\n"); + fprintf(stderr, " f: forbid plantype generation\n"); + fprintf(stderr, " i: don't execute the query, just show the plan tree\n"); + fprintf(stderr, " L: turn off locking\n"); + fprintf(stderr, " m: set up a listening backend at portno to support multiple front-ends\n"); + fprintf(stderr, " M: start as postmaster\n"); + fprintf(stderr, " N: don't use newline as query delimiter\n"); + fprintf(stderr, " o: send stdout and stderr to given filename \n"); + fprintf(stderr, " p: backend started by postmaster\n"); + fprintf(stderr, " P: set port file descriptor\n"); + fprintf(stderr, " Q: suppress informational messages\n"); + fprintf(stderr, " S: assume stable main memory\n"); + fprintf(stderr, " s: show stats after each query\n"); + fprintf(stderr, " t: trace component execution times\n"); + fprintf(stderr, " T: execute all possible plans for each query\n"); + fprintf(stderr, " x: control expensive function optimization\n"); +} + +/* ---------------------------------------------------------------- + * PostgresMain + * postgres main loop + * all backends, interactive or otherwise start here + * ---------------------------------------------------------------- + */ +int +PostgresMain(int argc, char *argv[]) +{ + int flagC; + int flagQ; + int flagS; + int flagE; + int flag; + + char *DBName; + int errs = 0; + + char firstchar; + char parser_input[MAX_PARSE_BUFFER]; + char *userName; + + int multiplexedBackend = 0; + char* hostName; /* the host name of the backend server */ + char hostbuf[MAXHOSTNAMELEN]; + int serverSock; + int serverPortnum; + int nSelected; /* number of descriptors ready from select(); */ + int maxFd; /* max file descriptor + 1 */ + fd_set rmask, basemask; + FrontEnd *newFE, *currentFE; + int numFE = 0; /* keep track of number of active frontends */ + Port *newPort; + int newFd; + Dlelem *curr; + int status; + +#ifdef WIN32 + WSADATA WSAData; +#endif /* WIN32 */ + + extern int optind; + extern char *optarg; + extern short DebugLvl; + + /* ---------------- + * register signal handlers. + * ---------------- + */ + signal(SIGINT, die); + +#ifndef WIN32 + signal(SIGHUP, die); + signal(SIGTERM, die); + signal(SIGPIPE, die); + signal(SIGUSR1, quickdie); + signal(SIGUSR2, Async_NotifyHandler); + signal(SIGFPE, FloatExceptionHandler); +#endif /* WIN32 */ + + /* -------------------- + * initialize globals + * ------------------- + */ + + InitGlobals(); + + /* ---------------- + * parse command line arguments + * ---------------- + */ + flagC = flagQ = flagS = flagE = ShowStats = 0; + ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0; + + /* get hostname is either the environment variable PGHOST + or 'localhost' */ + if (!(hostName = getenv("PGHOST"))) { + if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0) + (void) strcpy(hostbuf, "localhost"); + hostName = hostbuf; + } + + while ((flag = getopt(argc, argv, "B:bCd:Ef:iLm:MNo:P:pQSst:x:")) != EOF) + switch (flag) { + + case 'b': + /* ---------------- + * set BushyPlanFlag to true. + * ---------------- + */ + BushyPlanFlag = 1; + break; + case 'B': + /* ---------------- + * specify the size of buffer pool + * ---------------- + */ + NBuffers = atoi(optarg); + break; + + case 'C': + /* ---------------- + * don't print version string (don't know why this is 'C' --mao) + * ---------------- + */ + flagC = 1; + break; + + /* ---------------- + * -debug mode + * ---------------- + */ + case 'd': + /* DebugMode = true; */ + flagQ = 0; + DebugPrintPlan = true; + DebugPrintParse = true; + DebugPrintRewrittenParsetree = true; + DebugLvl = (short)atoi(optarg); + break; + + case 'E': + /* ---------------- + * E - echo the query the user entered + * ---------------- + */ + flagE = 1; + break; + + case 'f': + /* ----------------- + * f - forbid generation of certain plans + * ----------------- + */ + switch (optarg[0]) { + case 's': /* seqscan */ + _enable_seqscan_ = false; + break; + case 'i': /* indexscan */ + _enable_indexscan_ = false; + break; + case 'n': /* nestloop */ + _enable_nestloop_ = false; + break; + case 'm': /* mergejoin */ + _enable_mergesort_ = false; + break; + case 'h': /* hashjoin */ + _enable_hashjoin_ = false; + break; + default: + errs++; + } + break; + + case 'i': + dontExecute = 1; + break; + + case 'L': + /* -------------------- + * turn off locking + * -------------------- + */ + lockingOff = 1; + break; + + case 'm': + /* start up a listening backend that can respond to + multiple front-ends. (Note: all the front-end connections + are still connected to a single-threaded backend. Requests + are FCFS. Everything is in one transaction + */ + multiplexedBackend = 1; + serverPortnum = atoi(optarg); +#ifdef WIN32 + /* There was no postmaster started so the shared memory + ** for the shared memory table hasn't been allocated so + ** do it now. + */ + _nt_init(); +#endif /* WIN32 */ + break; + case 'M': + exit(PostmasterMain(argc, argv)); + break; + case 'N': + /* ---------------- + * N - Don't use newline as a query delimiter + * ---------------- + */ + UseNewLine = 0; + break; + + case 'o': + /* ---------------- + * o - send output (stdout and stderr) to the given file + * ---------------- + */ + (void) strncpy(OutputFileName, optarg, MAXPGPATH); + break; + + case 'p': /* started by postmaster */ + /* ---------------- + * p - special flag passed if backend was forked + * by a postmaster. + * ---------------- + */ + IsUnderPostmaster = true; + break; + + case 'P': + /* ---------------- + * P - Use the passed file descriptor number as the port + * on which to communicate with the user. This is ONLY + * useful for debugging when fired up by the postmaster. + * ---------------- + */ + Portfd = atoi(optarg); + break; + + case 'Q': + /* ---------------- + * Q - set Quiet mode (reduce debugging output) + * ---------------- + */ + flagQ = 1; + break; + + case 'S': + /* ---------------- + * S - assume stable main memory + * (don't flush all pages at end transaction) + * ---------------- + */ + flagS = 1; + SetTransactionFlushEnabled(false); + break; + + case 's': + /* ---------------- + * s - report usage statistics (timings) after each query + * ---------------- + */ + ShowStats = 1; + StatFp = stderr; + break; + + case 't': + /* ---------------- + * tell postgres to report usage statistics (timings) for + * each query + * + * -tpa[rser] = print stats for parser time of each query + * -tpl[anner] = print stats for planner time of each query + * -te[xecutor] = print stats for executor time of each query + * caution: -s can not be used together with -t. + * ---------------- + */ + StatFp = stderr; + switch (optarg[0]) { + case 'p': if (optarg[1] == 'a') + ShowParserStats = 1; + else if (optarg[1] == 'l') + ShowPlannerStats = 1; + else + errs++; + break; + case 'e': ShowExecutorStats = 1; break; + default: errs++; break; + } + break; + + case 'x': +#if 0 /* planner/xfunc.h */ + /* control joey hellerstein's expensive function optimization */ + if (XfuncMode != 0) + { + fprintf(stderr, "only one -x flag is allowed\n"); + errs++; + break; + } + if (strcmp(optarg, "off") == 0) + XfuncMode = XFUNC_OFF; + else if (strcmp(optarg, "nor") == 0) + XfuncMode = XFUNC_NOR; + else if (strcmp(optarg, "nopull") == 0) + XfuncMode = XFUNC_NOPULL; + else if (strcmp(optarg, "nopm") == 0) + XfuncMode = XFUNC_NOPM; + else if (strcmp(optarg, "pullall") == 0) + XfuncMode = XFUNC_PULLALL; + else if (strcmp(optarg, "wait") == 0) + XfuncMode = XFUNC_WAIT; + else { + fprintf(stderr, "use -x {off,nor,nopull,nopm,pullall,wait}\n"); + errs++; + } +#endif + break; + + default: + /* ---------------- + * default: bad command line option + * ---------------- + */ + errs++; + } + + /* ---------------- + * get user name and pathname and check command line validity + * ---------------- + */ + SetPgUserName(); + userName = GetPgUserName(); + + if (FindBackend(pg_pathname, argv[0]) < 0) + elog(FATAL, "%s: could not locate executable, bailing out...", + argv[0]); + + if (errs || argc - optind > 1) { + usage (argv[0]); + exitpg(1); + } else if (argc - optind == 1) { + DBName = argv[optind]; + } else if ((DBName = userName) == NULL) { + fprintf(stderr, "%s: USER undefined and no database specified\n", + argv[0]); + exitpg(1); + } + + if (ShowStats && + (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) { + fprintf(stderr, "-s can not be used together with -t.\n"); + exitpg(1); + } + + Noversion = flagC; + Quiet = flagQ; + EchoQuery = flagE; + + /* ---------------- + * print flags + * ---------------- + */ + if (! Quiet) { + puts("\t---debug info---"); + printf("\tQuiet = %c\n", Quiet ? 't' : 'f'); + printf("\tNoversion = %c\n", Noversion ? 't' : 'f'); + printf("\tstable = %c\n", flagS ? 't' : 'f'); + printf("\ttimings = %c\n", ShowStats ? 't' : 'f'); + printf("\tbufsize = %d\n", NBuffers); + + printf("\tquery echo = %c\n", EchoQuery ? 't' : 'f'); + printf("\tmultiplexed backend? = %c\n", multiplexedBackend ? 't' : 'f'); + printf("\tDatabaseName = [%s]\n", DBName); + puts("\t----------------\n"); + } + + /* ---------------- + * initialize portal file descriptors + * ---------------- + */ + if (IsUnderPostmaster == true) { + if (Portfd < 0) { + fprintf(stderr, + "Postmaster flag set: no port number specified, use /dev/null\n"); + Portfd = open(NULL_DEV, O_RDWR, 0666); + } + pq_init(Portfd); + } + +#ifdef WIN32 + if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0) + (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus); + else { + fprintf(stderr, "Error initializing WinSock: %d is the err", status); + exit(1); + } +#endif /* WIN32 */ + + if (multiplexedBackend) { + if (StreamServerPort(hostName, serverPortnum, &serverSock) != STATUS_OK) + { + fprintf(stderr, "Postgres: cannot create stream port %d\n", serverPortnum); + exit(1); + } +/* +{ + char buf[100]; + sprintf(buf, "stream port %d created, socket = %d\n", serverPortnum, serverSock); + puts(buf); +} +*/ + FD_ZERO(&rmask); + FD_ZERO(&basemask); + FD_SET(serverSock, &basemask); + + frontendList = DLNewList(); + /* add the original FrontEnd to the list */ + if (IsUnderPostmaster == true) { + FrontEnd *fe = malloc(sizeof(FrontEnd)); + + FD_SET(Portfd, &basemask); + maxFd = Max(serverSock,Portfd) + 1; + + fe->fn_connected = true; + fe->fn_Pfin = Pfin; + fe->fn_Pfout = Pfout; + fe->fn_done = false; + (fe->fn_port).sock = Portfd; + DLAddHead(frontendList, DLNewElem(fe)); + numFE++; + } else { + numFE = 1; + maxFd = serverSock + 1; + } + } + + if (IsUnderPostmaster || multiplexedBackend) + whereToSendOutput = Remote; + else + whereToSendOutput = Debug; + + SetProcessingMode(InitProcessing); + + /* initialize */ + if (! Quiet) { + puts("\tInitPostgres().."); + } + +#if WIN32 + _nt_attach(); +#endif /* WIN32 */ + + InitPostgres(DBName); + + /* ---------------- + * if an exception is encountered, processing resumes here + * so we abort the current transaction and start a new one. + * This must be done after we initialize the slave backends + * so that the slaves signal the master to abort the transaction + * rather than calling AbortCurrentTransaction() themselves. + * + * Note: elog(WARN) causes a kill(getpid(),1) to occur sending + * us back here. + * ---------------- + */ + +#ifndef WIN32 + signal(SIGHUP, handle_warn); + + if (sigsetjmp(Warn_restart, 1) != 0) { +#else + if (setjmp(Warn_restart) != 0) { +#endif /* WIN32 */ + + time(&tim); + + if (! Quiet) + printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim)); + + memset(parser_input, 0, MAX_PARSE_BUFFER); + + AbortCurrentTransaction(); + } + + /* ---------------- + * POSTGRES main processing loop begins here + * ---------------- + */ + if (IsUnderPostmaster == false) { + puts("\nPOSTGRES backend interactive interface"); + puts("$Revision: 1.1.1.1 $ $Date: 1996/07/09 06:22:00 $"); + } + + /* ---------------- + * if stable main memory is assumed (-S flag is set), it is necessary + * to flush all dirty shared buffers before exit + * plai 8/7/90 + * ---------------- + */ + if (!TransactionFlushEnabled()) + on_exitpg(FlushBufferPool, (caddr_t) 0); + + for (;;) { + + if (multiplexedBackend) { + if (numFE == 0) + break; + + memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set)); + nSelected = select(maxFd, &rmask,0,0,0); + + if (nSelected < 0) { + + if (errno == EINTR) continue; + fprintf(stderr,"postgres: multiplexed backend select failed\n"); + exitpg(1); + } + if (FD_ISSET(serverSock, &rmask)) { + /* new connection pending on our well-known port's socket */ + newFE = (FrontEnd*) malloc (sizeof(FrontEnd)); + memset(newFE, sizeof(FrontEnd),0); + newFE->fn_connected = false; + newFE->fn_done = false; + newPort = &(newFE->fn_port); + if (StreamConnection(serverSock,newPort) != STATUS_OK) { + StreamClose(newPort->sock); + newFd = -1; + } + else { + DLAddHead(frontendList, DLNewElem(newFE)); + numFE++; + newFd = newPort->sock; + if (newFd >= maxFd) maxFd = newFd + 1; + FD_SET(newFd, &rmask); + FD_SET(newFd, &basemask); + --nSelected; + FD_CLR(serverSock, &rmask); + } + continue; + } /* if FD_ISSET(serverSock) */ + + /* if we get here, it means that the serverSocket was not the one + selected. Instead, one of the front ends was selected. + find which one */ + curr = DLGetHead(frontendList); + while (curr) { + FrontEnd *fe = (FrontEnd*)DLE_VAL(curr); + Port *port = &(fe->fn_port); + + /* this is lifted from postmaster.c */ + if (FD_ISSET(port->sock, &rmask)) { + if (fe->fn_connected == false) { + /* we have a message from a new frontEnd */ + status = PacketReceive(port, &port->buf, NON_BLOCKING); + if (status == STATUS_OK) { + fe->fn_connected = true; + pq_init(port->sock); + fe->fn_Pfin = Pfin; + fe->fn_Pfout = Pfout; + } + else + fprintf(stderr,"Multiplexed backend: error in reading packets from %d\n", port->sock); + } + else /* we have a query from an existing, active FrontEnd */ + { + Pfin = fe->fn_Pfin; + Pfout = fe->fn_Pfout; + currentFE = fe; + } + if (fe->fn_done) + { + Dlelem *c = curr; + curr = DLGetSucc(curr); + DLRemove(c); + } + break; + } + else + curr = DLGetSucc(curr); + } + } + /* ---------------- + * (1) read a command. + * ---------------- + */ + memset(parser_input, 0, MAX_PARSE_BUFFER); + + firstchar = ReadCommand(parser_input, multiplexedBackend); + /* process the command */ + switch (firstchar) { + /* ---------------- + * 'F' indicates a fastpath call. + * XXX HandleFunctionRequest + * ---------------- + */ + case 'F': + IsEmptyQuery = false; + + /* start an xact for this function invocation */ + if (! Quiet) { + time(&tim); + printf("\tStartTransactionCommand() at %s\n", ctime(&tim)); + } + + StartTransactionCommand(); + HandleFunctionRequest(); + break; + + /* ---------------- + * 'Q' indicates a user query + * ---------------- + */ + case 'Q': + fflush(stdout); + + if ( parser_input[0] == ' ' && parser_input[1] == '\0' ) { + /* ---------------- + * if there is nothing in the input buffer, don't bother + * trying to parse and execute anything.. + * ---------------- + */ + IsEmptyQuery = true; + } else { + /* ---------------- + * otherwise, process the input string. + * ---------------- + */ + IsEmptyQuery = false; + if (ShowStats) + ResetUsage(); + + /* start an xact for this query */ + if (! Quiet) { + time(&tim); + printf("\tStartTransactionCommand() at %s\n", ctime(&tim)); + } + StartTransactionCommand(); + + pg_eval(parser_input, (char **) NULL, (Oid *) NULL, 0); + + if (ShowStats) + ShowUsage(); + } + break; + + /* ---------------- + * 'X' means that the frontend is closing down the socket + * ---------------- + */ + case 'X': + IsEmptyQuery = true; + if (multiplexedBackend) { + FD_CLR(currentFE->fn_port.sock, &basemask); + currentFE->fn_done = true; + numFE--; + } + pq_close(); + break; + + default: + elog(WARN,"unknown frontend message was recieved"); + } + + /* ---------------- + * (3) commit the current transaction + * + * Note: if we had an empty input buffer, then we didn't + * call pg_eval, so we don't bother to commit this transaction. + * ---------------- + */ + if (! IsEmptyQuery) { + if (! Quiet) { + time(&tim); + printf("\tCommitTransactionCommand() at %s\n", ctime(&tim)); + } + CommitTransactionCommand(); + + } else { + if (IsUnderPostmaster || multiplexedBackend) + NullCommand(Remote); + } + +} /* infinite for-loop */ + exitpg(0); + return 1; +} + +#ifndef WIN32 +#ifdef NEED_RUSAGE +#include "rusagestub.h" +#else /* NEED_RUSAGE */ +#include <sys/resource.h> +#endif /* NEED_RUSAGE */ + +struct rusage Save_r; +struct timeval Save_t; + +void +ResetUsage() +{ + struct timezone tz; + getrusage(RUSAGE_SELF, &Save_r); + gettimeofday(&Save_t, &tz); + ResetBufferUsage(); +/* ResetTupleCount(); */ +} + +void +ShowUsage() +{ + struct timeval user, sys; + struct timeval elapse_t; + struct timezone tz; + struct rusage r; + + getrusage(RUSAGE_SELF, &r); + gettimeofday(&elapse_t, &tz); + memmove((char *)&user, (char *)&r.ru_utime, sizeof(user)); + memmove((char *)&sys, (char *)&r.ru_stime,sizeof(sys)); + if (elapse_t.tv_usec < Save_t.tv_usec) { + elapse_t.tv_sec--; + elapse_t.tv_usec += 1000000; + } + if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) { + r.ru_utime.tv_sec--; + r.ru_utime.tv_usec += 1000000; + } + if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) { + r.ru_stime.tv_sec--; + r.ru_stime.tv_usec += 1000000; + } + + /* + * the only stats we don't show here are for memory usage -- i can't + * figure out how to interpret the relevant fields in the rusage + * struct, and they change names across o/s platforms, anyway. + * if you can figure out what the entries mean, you can somehow + * extract resident set size, shared text size, and unshared data + * and stack sizes. + */ + + fprintf(StatFp, "! system usage stats:\n"); + fprintf(StatFp, + "!\t%d.%06d elapsed %d.%06d user %d.%06d system sec\n", + elapse_t.tv_sec - Save_t.tv_sec, + elapse_t.tv_usec - Save_t.tv_usec, + r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec, + r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec, + r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec, + r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec); + fprintf(StatFp, + "!\t[%d.%06d user %d.%06d sys total]\n", + user.tv_sec, user.tv_usec, sys.tv_sec, sys.tv_usec); +#ifndef NEED_RUSAGE + fprintf(StatFp, + "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n", + r.ru_inblock - Save_r.ru_inblock, + /* they only drink coffee at dec */ + r.ru_oublock - Save_r.ru_oublock, + r.ru_inblock, r.ru_oublock); + fprintf(StatFp, + "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n", + r.ru_majflt - Save_r.ru_majflt, + r.ru_minflt - Save_r.ru_minflt, + r.ru_majflt, r.ru_minflt, + r.ru_nswap - Save_r.ru_nswap, + r.ru_nswap); + fprintf(StatFp, + "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n", + r.ru_nsignals - Save_r.ru_nsignals, + r.ru_nsignals, + r.ru_msgrcv - Save_r.ru_msgrcv, + r.ru_msgsnd - Save_r.ru_msgsnd, + r.ru_msgrcv, r.ru_msgsnd); + fprintf(StatFp, + "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n", + r.ru_nvcsw - Save_r.ru_nvcsw, + r.ru_nivcsw - Save_r.ru_nivcsw, + r.ru_nvcsw, r.ru_nivcsw); +#endif /* NEED_RUSAGE */ + fprintf(StatFp, "! postgres usage stats:\n"); + PrintBufferUsage(StatFp); +/* DisplayTupleCount(StatFp); */ +} +#else +void +ShowUsage() +{} + +void +ResetUsage() +{} +#endif /* WIN32 */ |