postgresql/src/backend/tcop/postgres.c

1569 lines
35 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* 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.78 1998/06/27 04:53:43 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
* hence the main module of the "traffic cop".
*
*-------------------------------------------------------------------------
*/
1996-11-08 07:02:30 +01:00
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
1996-11-08 07:02:30 +01:00
#include <setjmp.h>
#include <sys/time.h>
1996-11-08 07:02:30 +01:00
#include <sys/types.h>
#include <fcntl.h>
#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
#ifndef MAXHOSTNAMELEN
#include <netdb.h> /* for MAXHOSTNAMELEN on some */
#endif
#ifndef MAXHOSTNAMELEN /* for MAXHOSTNAMELEN under sco3.2v5.0.2 */
1997-07-28 02:57:08 +02:00
#include <sys/socket.h>
#endif
#include <errno.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* aix */
#include "postgres.h"
#include "miscadmin.h"
#include "fmgr.h"
#include "access/xact.h"
#include "catalog/catname.h"
#include "commands/async.h"
#include "executor/execdebug.h"
#include "executor/executor.h"
#include "libpq/libpq.h"
#include "libpq/libpq-be.h"
#include "libpq/pqsignal.h"
#include "nodes/pg_list.h"
#include "nodes/print.h"
#include "optimizer/cost.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "parser/parser.h"
#include "rewrite/rewriteHandler.h" /* for QueryRewrite() */
#include "storage/bufmgr.h"
#include "tcop/dest.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
#include "tcop/tcopdebug.h"
#include "tcop/tcopprot.h" /* where declarations for this file go */
#include "tcop/utility.h"
#include "utils/mcxt.h"
#include "utils/rel.h"
#if FALSE
#include "nodes/relation.h"
#endif
#if 0
#include "optimizer/xfunc.h"
#endif
#if FALSE
#include "nodes/plannodes.h"
#endif
#if FALSE
#include "nodes/memnodes.h"
#endif
#ifdef MB
#include "commands/variable.h"
#endif
/* ----------------
* global variables
* ----------------
*/
static bool DebugPrintQuery = false;
static bool DebugPrintPlan = false;
static bool DebugPrintParse = false;
static bool DebugPrintRewrittenParsetree = false;
/*static bool EnableRewrite = true; , never changes why have it*/
CommandDest whereToSendOutput;
const char **ps_status; /* this is our 'ps' status, argv[3] */
#ifdef LOCK_MGR_DEBUG
extern int lockDebug;
#endif
extern int lockingOff;
extern int NBuffers;
int dontExecute = 0;
static int ShowStats;
static bool IsEmptyQuery = false;
char relname[80]; /* current relation name */
#if defined(nextstep)
jmp_buf Warn_restart;
#define sigsetjmp(x,y) setjmp(x)
#define siglongjmp longjmp
#else
sigjmp_buf Warn_restart;
#endif /* defined(nextstep) */
bool InError;
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;
/* ----------------
* 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);
static char ReadCommand(char *inBuf);
/* ----------------------------------------------------------------
* 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;
proc_exit(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)
{
char qtype[2];
char result = '\0';
/* ----------------
* get input from the frontend
* ----------------
*/
strcpy(qtype, "?");
if (pq_getnchar(qtype, 0, 1) == EOF)
{
/* ------------
* when front-end applications quits/dies
* ------------
*/
proc_exit(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)
{
if (IsUnderPostmaster)
return SocketBackend(inBuf);
else
return InteractiveBackend(inBuf);
}
List *
pg_parse_and_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++)
{
List *union_result,
*union_list,
*rewritten_list;
querytree = querytree_list->qtrees[i];
/* don't rewrite utilites */
if (querytree->commandType == CMD_UTILITY)
{
new_list->qtrees[j++] = querytree;
continue;
}
if (DebugPrintQuery == true)
{
printf("\n---- \tquery is:\n%s\n", query_string);
printf("\n");
fflush(stdout);
}
if (DebugPrintParse == true)
{
printf("\n---- \tparser outputs :\n");
nodeDisplay(querytree);
printf("\n");
}
/* rewrite queries (retrieve, append, delete, replace) */
rewritten = QueryRewrite(querytree);
1998-01-09 06:48:22 +01:00
/*
* Rewrite the UNIONS.
1998-01-09 06:48:22 +01:00
*/
foreach(rewritten_list, rewritten)
{
Query *qry = (Query *) lfirst(rewritten_list);
1998-01-09 06:48:22 +01:00
union_result = NIL;
foreach(union_list, qry->unionClause)
union_result = nconc(union_result, QueryRewrite((Query *) lfirst(union_list)));
1998-01-09 06:48:22 +01:00
qry->unionClause = union_result;
}
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;
if (DebugPrintRewrittenParsetree == true)
{
printf("\n---- \tafter rewriting:\n");
for (i = 0; i < querytree_list->len; i++)
{
nodeDisplay(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();
/* call that optimizer */
plan = planner(querytree);
if (ShowPlannerStats)
{
fprintf(stderr, "! Planner Stats:\n");
ShowUsage();
}
plan_list = lappend(plan_list, plan);
#ifdef INDEXSCAN_PATCH
/* ----------------
* Print plan if debugging.
* This has been moved here to get debugging output
* also for queries in functions. DZ - 27-8-1996
* ----------------
*/
if (DebugPrintPlan == true)
{
printf("\n---- \tplan is :\n");
nodeDisplay(plan);
printf("\n");
}
#endif
}
#ifdef FUNC_UTIL_PATCH
/*
* If the command is an utility append a null plan. This is needed
* to keep the plan_list aligned with the querytree_list or the
* function executor will crash. DZ - 30-8-1996
*/
else
plan_list = lappend(plan_list, NULL);
#endif
}
if (queryListP)
*queryListP = querytree_list;
return (plan_list);
}
/* ----------------------------------------------------------------
* pg_exec_query()
*
* 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_exec_query(char *query_string)
{
pg_exec_query_dest(query_string, whereToSendOutput);
}
void
pg_exec_query_dest(char *query_string, /* string to execute */
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_parse_and_plan(query_string, NULL, 0, &querytree_list, dest);
if (QueryCancel)
CancelQuery();
/* pg_parse_and_plan could have failed */
if (querytree_list == NULL)
return;
for (i = 0; i < querytree_list->len; i++)
{
querytree = querytree_list->qtrees[i];
#ifdef FUNC_UTIL_PATCH
/*
* Advance on the plan_list in every case. Now the plan_list has
* the same length of the querytree_list. DZ - 30-8-1996
*/
plan = (Plan *) lfirst(plan_list);
plan_list = lnext(plan_list);
#endif
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
{
#ifndef FUNC_UTIL_PATCH
/*
* Moved before the if. DZ - 30-8-1996
*/
plan = (Plan *) lfirst(plan_list);
plan_list = lnext(plan_list);
#endif
#ifdef INDEXSCAN_PATCH
/*
* Print moved in pg_parse_and_plan. DZ - 27-8-1996
*/
#else
/* ----------------
* print plan if debugging
* ----------------
*/
if (DebugPrintPlan == true)
{
printf("\n---- plan is :\n");
nodeDisplay(plan);
printf("\n");
}
#endif
/* ----------------
* 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, 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(ERROR) is called.
*
* quickdie() occurs when signalled by the postmaster.
* Some backend has bought the farm,
* so we need to stop what we're doing and exit.
*
* die() preforms an orderly cleanup via ExitPostgres()
* --------------------------------
*/
void
handle_warn(SIGNAL_ARGS)
{
siglongjmp(Warn_restart, 1);
}
void
quickdie(SIGNAL_ARGS)
{
elog(NOTICE, "Message from PostgreSQL backend:"
"\n\tThe Postmaster has informed me that some other backend"
" died abnormally and possibly corrupted shared memory."
"\n\tI have rolled back the current transaction and am"
" going to terminate your database system connection and exit."
"\n\tPlease reconnect to the database system and repeat your query.");
/*
* 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(SIGNAL_ARGS)
{
ExitPostgres(0);
}
/* signal handler for floating point exception */
void
FloatExceptionHandler(SIGNAL_ARGS)
{
elog(ERROR, "floating point exception!"
" The last floating point operation either exceeded legal ranges"
" or was a divide by zero");
}
/* signal handler for query cancel */
static void
QueryCancelHandler(SIGNAL_ARGS)
{
QueryCancel = true;
}
void
CancelQuery(void)
{
char dummy;
/* throw it away */
while (pq_recvoob(&dummy, 1) > 0)
;
/* QueryCancel reset in longjump after elog() call */
elog(ERROR, "Query was cancelled.");
}
static void
usage(char *progname)
{
fprintf(stderr,
1998-05-27 20:32:05 +02:00
"Usage: %s [options] [dbname]\n", progname);
fprintf(stderr, "\t-B buffers\tset number of buffers in buffer pool\n");
fprintf(stderr, "\t-C \t\tsupress version info\n");
fprintf(stderr, "\t-D dir\t\tdata directory\n");
fprintf(stderr, "\t-E \t\techo query before execution\n");
fprintf(stderr, "\t-F \t\tturn off fsync\n");
#ifdef LOCK_MGR_DEBUG
1998-05-27 20:32:05 +02:00
fprintf(stderr, "\t-K \t\tset locking debug level [0|1|2]\n");
#endif
1998-05-27 20:32:05 +02:00
fprintf(stderr, "\t-P port\t\tset port file descriptor\n");
fprintf(stderr, "\t-Q \t\tsuppress informational messages\n");
fprintf(stderr, "\t-S buffers\tset amount of sort memory available\n");
fprintf(stderr, "\t-d [1|2|3]\tset debug level\n");
fprintf(stderr, "\t-e \t\tturn on European date format\n");
fprintf(stderr, "\t-o file\t\tsend stdout and stderr to given filename \n");
fprintf(stderr, "\t-s \t\tshow stats after each query\n");
fprintf(stderr, "\t-v version\tset protocol version being used by frontend\n");
}
/* ----------------------------------------------------------------
* PostgresMain
* postgres main loop
* all backends, interactive or otherwise start here
* ----------------------------------------------------------------
*/
int
PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
{
bool flagC = false,
flagQ = false,
flagE = false,
flagEu = false;
int flag;
char *DBName = NULL;
int errs = 0;
char firstchar;
char parser_input[MAX_PARSE_BUFFER];
char *userName;
char *DBDate = NULL;
extern int optind;
extern char *optarg;
extern short DebugLvl;
/* ----------------
* parse command line arguments
* ----------------
*/
/*
* Set default values.
*/
ShowStats = 0;
ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
#ifdef LOCK_MGR_DEBUG
lockDebug = 0;
#endif
/*
* get hostname is either the environment variable PGHOST or NULL NULL
* means Unix-socket only
*/
DataDir = getenv("PGDATA");
/*
* Try to get initial values for date styles and formats. Does not do
* a complete job, but should be good enough for backend. Cannot call
* parse_date() since palloc/pfree memory is not set up yet.
*/
DBDate = getenv("PGDATESTYLE");
if (DBDate != NULL)
{
if (strcasecmp(DBDate, "ISO") == 0)
DateStyle = USE_ISO_DATES;
else if (strcasecmp(DBDate, "SQL") == 0)
DateStyle = USE_SQL_DATES;
else if (strcasecmp(DBDate, "POSTGRES") == 0)
DateStyle = USE_POSTGRES_DATES;
else if (strcasecmp(DBDate, "GERMAN") == 0)
{
DateStyle = USE_GERMAN_DATES;
EuroDates = TRUE;
}
if (strcasecmp(DBDate, "NONEURO") == 0)
EuroDates = FALSE;
else if (strcasecmp(DBDate, "EURO") == 0)
EuroDates = TRUE;
}
optind = 1; /* reset after postmaster usage */
while ((flag = getopt(argc, argv, "B:bCD:d:Eef:iK:Lm:MNo:P:pQS:st:v:x:F"))
!= 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 = true;
break;
case 'D': /* PGDATA directory */
DataDir = optarg;
case 'd': /* debug level */
flagQ = false;
DebugLvl = (short) atoi(optarg);
if (DebugLvl > 1)
DebugPrintQuery = true;
if (DebugLvl > 2)
{
DebugPrintParse = true;
DebugPrintPlan = true;
DebugPrintRewrittenParsetree = true;
}
break;
case 'E':
/* ----------------
* E - echo the query the user entered
* ----------------
*/
flagE = true;
break;
case 'e':
/* --------------------------
* Use european date formats.
* --------------------------
*/
flagEu = true;
break;
case 'F':
/* --------------------
* turn off fsync
* --------------------
*/
fsyncOff = 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 'K':
#ifdef LOCK_MGR_DEBUG
lockDebug = atoi(optarg);
#else
fprintf(stderr, "Lock debug not compiled in\n");
#endif
break;
case 'L':
/* --------------------
* turn off locking
* --------------------
*/
lockingOff = 1;
break;
case 'm':
/* Multiplexed backends are no longer supported. */
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
* ----------------
*/
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 = true;
break;
case 'S':
/* ----------------
1997-09-18 16:33:46 +02:00
* S - amount of sort memory to use in 1k bytes
* ----------------
*/
{
int S;
1997-09-18 16:33:46 +02:00
S = atoi(optarg);
if (S >= 4 * BLCKSZ / 1024)
1997-09-18 16:33:46 +02:00
SortMem = S;
}
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 'v':
FrontendProtocol = (ProtocolVersion) atoi(optarg);
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();
#ifdef CYR_RECODE
SetCharSet();
#endif
if (FindExec(pg_pathname, argv[0], "postgres") < 0)
elog(FATAL, "%s: could not locate executable, bailing out...",
argv[0]);
if (errs || argc - optind > 1)
{
usage(argv[0]);
proc_exit(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]);
proc_exit(1);
}
if (ShowStats &&
(ShowParserStats || ShowPlannerStats || ShowExecutorStats))
{
fprintf(stderr, "-s can not be used together with -t.\n");
proc_exit(1);
}
if (!DataDir)
{
fprintf(stderr, "%s does not know where to find the database system "
"data. You must specify the directory that contains the "
"database system either by specifying the -D invocation "
"option or by setting the PGDATA environment variable.\n\n",
argv[0]);
proc_exit(1);
}
Noversion = flagC;
Quiet = flagQ;
EchoQuery = flagE;
EuroDates = flagEu;
/* ----------------
* print flags
* ----------------
*/
if (!Quiet)
{
puts("\t---debug info---");
printf("\tQuiet = %c\n", Quiet ? 't' : 'f');
printf("\tNoversion = %c\n", Noversion ? 't' : 'f');
printf("\ttimings = %c\n", ShowStats ? 't' : 'f');
printf("\tdates = %s\n", EuroDates ? "European" : "Normal");
printf("\tbufsize = %d\n", NBuffers);
printf("\tsortmem = %d\n", SortMem);
printf("\tquery echo = %c\n", EchoQuery ? 't' : 'f');
printf("\tDatabaseName = [%s]\n", DBName);
puts("\t----------------\n");
}
/* ----------------
* set process params for ps
* ----------------
*/
if (IsUnderPostmaster)
{
int i;
Assert(real_argc >= 4);
real_argv[1] = userName;
real_argv[2] = DBName;
ps_status = (const char **)&real_argv[3];
*ps_status = "idle";
for (i = 4; i < real_argc; i++)
real_argv[i] = ""; /* blank them */
}
/* we just put a dummy here so we don't have to test everywhere */
else ps_status = malloc(sizeof(char *));
/* ----------------
* initialize portal file descriptors
* ----------------
*/
if (IsUnderPostmaster)
{
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);
whereToSendOutput = Remote;
pq_regoob(QueryCancelHandler); /* we do it here so the backend it connected */
}
else
whereToSendOutput = Debug;
SetProcessingMode(InitProcessing);
/* initialize */
if (!Quiet)
puts("\tInitPostgres()..");
InitPostgres(DBName);
#ifdef MB
/* set default client encoding */
if (!Quiet)
{
puts("\treset_client_encoding()..");
}
reset_client_encoding();
if (!Quiet)
{
puts("\treset_client_encoding() done.");
}
#endif
/* ----------------
* 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(ERROR) causes a kill(getpid(),1) to occur sending
* us back here.
* ----------------
*/
pqsignal(SIGHUP, handle_warn);
if (sigsetjmp(Warn_restart, 1) != 0)
{
InError = true;
time(&tim);
if (!Quiet)
printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));
1997-09-18 22:22:58 +02:00
MemSet(parser_input, 0, MAX_PARSE_BUFFER);
AbortCurrentTransaction();
}
InError = false;
/* ----------------
* POSTGRES main processing loop begins here
* ----------------
*/
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface");
puts("$Revision: 1.78 $ $Date: 1998/06/27 04:53:43 $");
}
/* ----------------
* if stable main memory is assumed (-S(old) flag is set), it is necessary
* to flush all dirty shared buffers before exit
* plai 8/7/90
* ----------------
*/
if (!TransactionFlushEnabled())
on_shmem_exit(FlushBufferPool, NULL);
for (;;)
{
/* ----------------
* (0) tell the frontend we're ready for a new query.
* ----------------
*/
ReadyForQuery(whereToSendOutput);
/* ----------------
* (1) read a command.
* ----------------
*/
1997-09-18 22:22:58 +02:00
MemSet(parser_input, 0, MAX_PARSE_BUFFER);
firstchar = ReadCommand(parser_input);
QueryCancel = false;
/* 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();
*ps_status = "idle";
break;
/* ----------------
* 'Q' indicates a user query
* ----------------
*/
case 'Q':
fflush(stdout);
if (strspn(parser_input, " \t\n") == strlen(parser_input))
{
/* ----------------
* 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_exec_query(parser_input);
*ps_status = "idle";
if (ShowStats)
ShowUsage();
}
break;
/* ----------------
* 'X' means that the frontend is closing down the socket
* ----------------
*/
case 'X':
pq_close();
proc_exit(0);
break;
default:
elog(ERROR, "unknown frontend message was recieved");
}
/* ----------------
* (3) commit the current transaction
*
* Note: if we had an empty input buffer, then we didn't
* call pg_exec_query, 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)
NullCommand(Remote);
}
} /* infinite for-loop */
proc_exit(0);
return 1;
}
#ifndef HAVE_GETRUSAGE
#include "rusagestub.h"
#else /* HAVE_GETRUSAGE */
#include <sys/resource.h>
#endif /* HAVE_GETRUSAGE */
struct rusage Save_r;
struct timeval Save_t;
void
ResetUsage(void)
{
struct timezone tz;
getrusage(RUSAGE_SELF, &Save_r);
gettimeofday(&Save_t, &tz);
ResetBufferUsage();
/* ResetTupleCount(); */
}
void
ShowUsage(void)
{
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%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
(long int) elapse_t.tv_sec - Save_t.tv_sec,
(long int) elapse_t.tv_usec - Save_t.tv_usec,
(long int) r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
(long int) r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
(long int) r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
(long int) r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
fprintf(StatFp,
"!\t[%ld.%06ld user %ld.%06ld sys total]\n",
(long int) user.tv_sec,
(long int) user.tv_usec,
(long int) sys.tv_sec,
(long int) sys.tv_usec);
#ifdef HAVE_GETRUSAGE
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 /* HAVE_GETRUSAGE */
fprintf(StatFp, "! postgres usage stats:\n");
PrintBufferUsage(StatFp);
/* DisplayTupleCount(StatFp); */
}