postgresql/src/backend/tcop/postgres.c

1529 lines
39 KiB
C

/*-------------------------------------------------------------------------
*
* 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.4 1996/07/22 23:00:26 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;
#ifdef OPENLINK_PATCHES
int fsyncOff = 0;
#endif
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) */
int InWarn;
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)
{
#ifdef OPENLINK_PATCHES
fprintf(stderr,"\t[-P portno] [-t tracetype] [-x opttype] [-bCEiLFNopQSs] [dbname]\n");
#else
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");
#endif
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");
#ifdef OPENLINK_PATCHES
fprintf(stderr, " F: turn off fsync\n");
#endif
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;
}
#ifdef OPENLINK_PATCHES
while ((flag = getopt(argc, argv, "B:bCd:Ef:iLm:MNo:P:pQSst:x:F")) != EOF)
#else
while ((flag = getopt(argc, argv, "B:bCd:Ef:iLm:MNo:P:pQSst:x:")) != EOF)
#endif
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;
#ifdef OPENLINK_PATCHES
case 'F':
/* --------------------
* turn off fsync
* --------------------
*/
fsyncOff = 1;
break;
#endif
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 */
InWarn = 1;
time(&tim);
if (! Quiet)
printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));
memset(parser_input, 0, MAX_PARSE_BUFFER);
AbortCurrentTransaction();
}
InWarn = 0;
/* ----------------
* POSTGRES main processing loop begins here
* ----------------
*/
if (IsUnderPostmaster == false) {
puts("\nPOSTGRES backend interactive interface");
puts("$Revision: 1.4 $ $Date: 1996/07/22 23:00:26 $");
}
/* ----------------
* 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 */