mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
In particular, no more compiled-in default for PGDATA or LIBDIR. Commands that need them need either invocation options or environment variables. PGPORT default is hardcoded as 5432, but overrideable with options or environment variables.
1578 lines
47 KiB
C
1578 lines
47 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.19 1996/11/14 10:24:07 bryanh 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(linux)
|
|
#ifndef __USE_POSIX
|
|
#define __USE_POSIX
|
|
#endif
|
|
#endif /* defined(linux) */
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <setjmp.h>
|
|
#include <sys/time.h>
|
|
#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
|
|
#include <errno.h>
|
|
#ifdef aix
|
|
#include <sys/select.h>
|
|
#endif /* 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/tcopprot.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/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 fsyncOff = 0;
|
|
|
|
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(next)
|
|
jmp_buf Warn_restart;
|
|
#define sigsetjmp(x,y) setjmp(x)
|
|
#define siglongjmp longjmp
|
|
#else
|
|
sigjmp_buf Warn_restart;
|
|
#endif /*defined(WIN32) || defined(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, bool multiplexedBackend);
|
|
static char ReadCommand(char *inBuf, bool 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, bool multiplexedBackend)
|
|
{
|
|
char qtype[2];
|
|
char result = '\0';
|
|
|
|
/* ----------------
|
|
* 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, bool 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);
|
|
#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("\nPlan 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_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];
|
|
|
|
#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_plan. DZ - 27-8-1996
|
|
*/
|
|
#else
|
|
/* ----------------
|
|
* print plan if debugging
|
|
* ----------------
|
|
*/
|
|
if ( DebugPrintPlan == true ) {
|
|
printf("\nPlan 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, 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(SIGNAL_ARGS)
|
|
{
|
|
siglongjmp(Warn_restart, 1);
|
|
}
|
|
|
|
void
|
|
quickdie(SIGNAL_ARGS)
|
|
{
|
|
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(SIGNAL_ARGS)
|
|
{
|
|
ExitPostgres(0);
|
|
}
|
|
|
|
/* signal handler for floating point exception */
|
|
static void
|
|
FloatExceptionHandler(SIGNAL_ARGS)
|
|
{
|
|
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] [-bCEiLFNopQSs] [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: turn off fsync\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 = NULL;
|
|
int errs = 0;
|
|
|
|
char firstchar;
|
|
char parser_input[MAX_PARSE_BUFFER];
|
|
char *userName;
|
|
|
|
bool multiplexedBackend;
|
|
char* hostName; /* the host name of the backend server */
|
|
char hostbuf[MAXHOSTNAMELEN];
|
|
int serverSock;
|
|
int serverPortnum = 0;
|
|
int nSelected; /* number of descriptors ready from select(); */
|
|
int maxFd = 0; /* max file descriptor + 1 */
|
|
fd_set rmask, basemask;
|
|
FrontEnd *newFE, *currentFE = NULL;
|
|
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
|
|
* -------------------
|
|
*/
|
|
|
|
MasterPid = getpid();
|
|
DataDir = GetPGData();
|
|
|
|
/* ----------------
|
|
* 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;
|
|
}
|
|
|
|
DataDir = getenv("PGDATA"); /* default */
|
|
multiplexedBackend = false; /* default */
|
|
|
|
while ((flag = getopt(argc, argv, "B:bCD:d:Ef:iLm:MNo:P:pQSst: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 = 1;
|
|
break;
|
|
|
|
case 'D': /* PGDATA directory */
|
|
DataDir = optarg;
|
|
|
|
case 'd': /* debug level */
|
|
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':
|
|
/* --------------------
|
|
* 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 '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 = true;
|
|
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);
|
|
}
|
|
|
|
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]);
|
|
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 (serverPortnum == 0 ||
|
|
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.19 $ $Date: 1996/11/14 10:24:07 $");
|
|
}
|
|
|
|
/* ----------------
|
|
* 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, 0, sizeof(FrontEnd));
|
|
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(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);
|
|
#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 */
|