1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* postgres.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* POSTGRES C Backend Interface
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2000-08-30 22:30:06 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.174 2000/08/30 20:30:06 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 07:04:48 +02:00
|
|
|
* this is the "main" module of the postgres backend and
|
|
|
|
* hence the main module of the "traffic cop".
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-08 07:02:30 +01:00
|
|
|
|
2000-06-29 09:35:57 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1996-11-08 07:02:30 +01:00
|
|
|
#include <unistd.h>
|
1996-12-26 23:08:34 +01:00
|
|
|
#include <signal.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <time.h>
|
|
|
|
#include <sys/time.h>
|
1996-11-08 07:02:30 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
1999-10-23 05:13:33 +02:00
|
|
|
#include <sys/socket.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <errno.h>
|
1998-02-02 01:05:03 +01:00
|
|
|
#if HAVE_SYS_SELECT_H
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <sys/select.h>
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* aix */
|
1998-08-25 23:34:10 +02:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netdb.h>
|
1999-07-19 04:27:16 +02:00
|
|
|
#ifdef HAVE_GETOPT_H
|
1999-07-16 07:23:30 +02:00
|
|
|
#include <getopt.h>
|
1999-01-17 07:20:06 +01:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#include "commands/async.h"
|
1999-09-29 18:06:40 +02:00
|
|
|
#include "commands/trigger.h"
|
2000-02-19 23:10:47 +01:00
|
|
|
#include "commands/variable.h"
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "libpq/libpq.h"
|
1999-04-25 05:19:27 +02:00
|
|
|
#include "libpq/pqformat.h"
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "libpq/pqsignal.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "miscadmin.h"
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "nodes/print.h"
|
|
|
|
#include "optimizer/cost.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/planner.h"
|
1997-11-25 23:07:18 +01:00
|
|
|
#include "parser/parser.h"
|
1999-07-16 05:14:30 +02:00
|
|
|
#include "rewrite/rewriteHandler.h"
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "tcop/fastpath.h"
|
|
|
|
#include "tcop/pquery.h"
|
1999-07-16 05:14:30 +02:00
|
|
|
#include "tcop/tcopprot.h"
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "tcop/utility.h"
|
1999-10-06 23:58:18 +02:00
|
|
|
#include "storage/proc.h"
|
2000-07-08 05:04:41 +02:00
|
|
|
#include "utils/exc.h"
|
|
|
|
#include "utils/guc.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
#include "utils/memutils.h"
|
1998-08-25 23:34:10 +02:00
|
|
|
#include "utils/ps_status.h"
|
1999-02-02 04:45:56 +01:00
|
|
|
#include "utils/temprel.h"
|
1998-07-18 20:34:34 +02:00
|
|
|
#ifdef MULTIBYTE
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "mb/pg_wchar.h"
|
1998-06-16 09:29:54 +02:00
|
|
|
#endif
|
|
|
|
|
1998-08-25 23:34:10 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* global variables
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
/*
|
|
|
|
* XXX For ps display. That stuff needs to be cleaned up.
|
|
|
|
*/
|
|
|
|
bool HostnameLookup;
|
|
|
|
bool ShowPortNumber;
|
|
|
|
|
|
|
|
bool Log_connections = false;
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
CommandDest whereToSendOutput = Debug;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
extern void StartupXLOG(void);
|
|
|
|
extern void ShutdownXLOG(void);
|
1999-10-06 23:58:18 +02:00
|
|
|
|
2000-08-29 11:36:51 +02:00
|
|
|
extern void HandleDeadLock(SIGNAL_ARGS);
|
1999-10-06 23:58:18 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
extern char XLogDir[];
|
|
|
|
extern char ControlFilePath[];
|
1999-10-06 23:58:18 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
static bool dontExecute = false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static bool IsEmptyQuery = false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-04-20 04:19:59 +02:00
|
|
|
/* note: these declarations had better match tcopprot.h */
|
1999-05-25 18:15:34 +02:00
|
|
|
DLLIMPORT sigjmp_buf Warn_restart;
|
1999-09-24 02:25:33 +02:00
|
|
|
|
1999-11-16 07:13:36 +01:00
|
|
|
bool Warn_restart_ready = false;
|
1999-10-06 23:58:18 +02:00
|
|
|
bool InError = false;
|
|
|
|
bool ExitAfterAbort = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static bool EchoQuery = false; /* default don't echo */
|
1999-10-25 05:08:03 +02:00
|
|
|
char pg_pathname[MAXPGPATH];
|
2000-04-28 07:07:34 +02:00
|
|
|
FILE *StatFp = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* people who want to use EOF should #define DONTUSENEWLINE in
|
|
|
|
* tcop/tcopdebug.h
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
#ifndef TCOP_DONTUSENEWLINE
|
1997-09-08 04:41:22 +02:00
|
|
|
int UseNewLine = 1; /* Use newlines query delimiters (the
|
1997-09-07 07:04:48 +02:00
|
|
|
* default) */
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#else
|
1997-09-08 04:41:22 +02:00
|
|
|
int UseNewLine = 0; /* Use EOF as query delimiters */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* TCOP_DONTUSENEWLINE */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
** Flags for expensive function optimization -- JMH 3/9/92
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
int XfuncMode = 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* decls for routines only used in this file
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2000-04-12 19:17:23 +02:00
|
|
|
static int InteractiveBackend(StringInfo inBuf);
|
|
|
|
static int SocketBackend(StringInfo inBuf);
|
|
|
|
static int ReadCommand(StringInfo inBuf);
|
2000-08-29 11:36:51 +02:00
|
|
|
static void SigHupHandler(SIGNAL_ARGS);
|
|
|
|
static void FloatExceptionHandler(SIGNAL_ARGS);
|
|
|
|
static void quickdie(SIGNAL_ARGS);
|
2000-05-31 02:28:42 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Flag to mark SIGHUP. Whenever the main loop comes around it
|
|
|
|
* will reread the configuration file. (Better than doing the
|
|
|
|
* reading in the signal handler, ey?)
|
|
|
|
*/
|
|
|
|
static volatile bool got_SIGHUP = false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* routines to obtain user input
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* ----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* InteractiveBackend() is called for user interactive connections
|
|
|
|
* the string entered by the user is placed in its parameter inBuf.
|
1999-07-22 04:40:07 +02:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* EOF is returned if end-of-file input is seen; time to shut down.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
static int
|
1999-08-31 06:26:40 +02:00
|
|
|
InteractiveBackend(StringInfo inBuf)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int c; /* character read from getc() */
|
|
|
|
bool end = false; /* end-of-input flag */
|
|
|
|
bool backslashSeen = false; /* have we seen a \ ? */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* display a prompt and obtain input from the user
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 04:55:58 +02:00
|
|
|
printf("backend> ");
|
1998-10-02 03:14:14 +02:00
|
|
|
fflush(stdout);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-31 06:26:40 +02:00
|
|
|
/* Reset inBuf to empty */
|
|
|
|
inBuf->len = 0;
|
|
|
|
inBuf->data[0] = '\0';
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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)
|
|
|
|
{
|
1999-08-31 06:26:40 +02:00
|
|
|
/* discard backslash from inBuf */
|
|
|
|
inBuf->data[--inBuf->len] = '\0';
|
|
|
|
backslashSeen = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* keep the newline character */
|
1999-08-31 06:26:40 +02:00
|
|
|
appendStringInfoChar(inBuf, '\n');
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == '\\')
|
|
|
|
backslashSeen = true;
|
|
|
|
else
|
|
|
|
backslashSeen = false;
|
|
|
|
|
1999-08-31 06:26:40 +02:00
|
|
|
appendStringInfoChar(inBuf, (char) c);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (c == EOF)
|
|
|
|
end = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ----------------
|
|
|
|
* otherwise read characters until EOF.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
while ((c = getc(stdin)) != EOF)
|
1999-08-31 06:26:40 +02:00
|
|
|
appendStringInfoChar(inBuf, (char) c);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-31 06:26:40 +02:00
|
|
|
if (inBuf->len == 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
end = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end)
|
1999-07-22 04:40:07 +02:00
|
|
|
return EOF;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* otherwise we have a user query so process it.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* if the query echo flag was given, print the query..
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
if (EchoQuery)
|
1999-08-31 06:26:40 +02:00
|
|
|
printf("query: %s\n", inBuf->data);
|
1998-10-02 03:14:14 +02:00
|
|
|
fflush(stdout);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return 'Q';
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* SocketBackend() Is called for frontend-backend connections
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If the input is a query (case 'Q') then the string entered by
|
|
|
|
* the user is placed in its parameter inBuf.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If the input is a fastpath function call (case 'F') then
|
1999-08-31 06:26:40 +02:00
|
|
|
* the function call is processed in HandleFunctionRequest()
|
1999-07-22 04:40:07 +02:00
|
|
|
* (now called from PostgresMain()).
|
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* EOF is returned if the connection is lost.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
static int
|
1999-08-31 06:26:40 +02:00
|
|
|
SocketBackend(StringInfo inBuf)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-04-25 05:19:27 +02:00
|
|
|
char qtype;
|
1997-09-08 04:41:22 +02:00
|
|
|
char result = '\0';
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* get input from the frontend
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-04-25 05:19:27 +02:00
|
|
|
qtype = '?';
|
|
|
|
if (pq_getbytes(&qtype, 1) == EOF)
|
1999-07-22 04:40:07 +02:00
|
|
|
return EOF;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-04-25 05:19:27 +02:00
|
|
|
switch (qtype)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* 'Q': user entered a query
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
case 'Q':
|
1999-08-31 06:26:40 +02:00
|
|
|
if (pq_getstr(inBuf))
|
1999-07-22 04:40:07 +02:00
|
|
|
return EOF;
|
1997-09-08 04:41:22 +02:00
|
|
|
result = 'Q';
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* 'F': calling user/system functions
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
case 'F':
|
1999-08-31 06:26:40 +02:00
|
|
|
if (pq_getstr(inBuf))
|
1999-07-22 04:40:07 +02:00
|
|
|
return EOF; /* ignore "string" at start of F message */
|
1997-09-08 04:41:22 +02:00
|
|
|
result = 'F';
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* 'X': frontend is exiting
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
case 'X':
|
|
|
|
result = 'X';
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* 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:
|
1999-04-25 05:19:27 +02:00
|
|
|
elog(FATAL, "Socket command type %c unknown", qtype);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1999-07-22 04:40:07 +02:00
|
|
|
* call. EOF is returned if end of file.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1999-07-22 04:40:07 +02:00
|
|
|
static int
|
1999-08-31 06:26:40 +02:00
|
|
|
ReadCommand(StringInfo inBuf)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
int result;
|
1999-08-31 06:26:40 +02:00
|
|
|
|
1998-01-26 02:42:53 +01:00
|
|
|
if (IsUnderPostmaster)
|
1999-08-31 06:26:40 +02:00
|
|
|
result = SocketBackend(inBuf);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1999-08-31 06:26:40 +02:00
|
|
|
result = InteractiveBackend(inBuf);
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse a query string and pass it through the rewriter.
|
|
|
|
*
|
|
|
|
* A list of Query nodes is returned, since the string might contain
|
|
|
|
* multiple queries and/or the rewriter might expand one query to several.
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
2000-06-28 05:33:33 +02:00
|
|
|
pg_parse_and_rewrite(char *query_string, /* string to execute */
|
|
|
|
Oid *typev, /* parameter types */
|
|
|
|
int nargs) /* number of parameters */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-04-04 23:44:40 +02:00
|
|
|
List *querytree_list;
|
1999-05-13 09:29:22 +02:00
|
|
|
List *querytree_list_item;
|
1997-09-08 04:41:22 +02:00
|
|
|
Query *querytree;
|
1999-05-13 09:29:22 +02:00
|
|
|
List *new_list;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_print_query)
|
|
|
|
elog(DEBUG, "query: %s", query_string);
|
1999-05-10 01:31:47 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
|
|
|
* (1) parse the request string into a list of parse trees
|
|
|
|
* ----------------
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_parser_stats)
|
1997-09-07 07:04:48 +02:00
|
|
|
ResetUsage();
|
|
|
|
|
|
|
|
querytree_list = parser(query_string, typev, nargs);
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_parser_stats)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
fprintf(StatFp, "PARSER STATISTICS\n");
|
1997-09-07 07:04:48 +02:00
|
|
|
ShowUsage();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* (2) rewrite the queries, as necessary
|
1999-05-10 01:31:47 +02:00
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* rewritten queries are collected in new_list. Note there may be
|
|
|
|
* more or fewer than in the original list.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-13 09:29:22 +02:00
|
|
|
new_list = NIL;
|
1999-05-25 18:15:34 +02:00
|
|
|
foreach(querytree_list_item, querytree_list)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
querytree = (Query *) lfirst(querytree_list_item);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_print_parse)
|
1998-08-25 17:00:17 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_pretty_print)
|
1999-05-25 18:15:34 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "parse tree:");
|
1999-05-11 11:06:35 +02:00
|
|
|
nodeDisplay(querytree);
|
1999-05-25 18:15:34 +02:00
|
|
|
}
|
|
|
|
else
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "parse tree: %s", nodeToString(querytree));
|
1998-08-25 17:00:17 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (querytree->commandType == CMD_UTILITY)
|
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
/* don't rewrite utilities, just dump 'em into new_list */
|
|
|
|
new_list = lappend(new_list, querytree);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-05-13 09:29:22 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
/* rewrite regular queries */
|
2000-04-12 19:17:23 +02:00
|
|
|
List *rewritten = QueryRewrite(querytree);
|
|
|
|
|
1999-05-13 09:29:22 +02:00
|
|
|
new_list = nconc(new_list, rewritten);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
querytree_list = new_list;
|
|
|
|
|
2000-06-29 09:35:57 +02:00
|
|
|
#ifdef COPY_PARSE_PLAN_TREES
|
|
|
|
/* Optional debugging check: pass parsetree output through copyObject() */
|
|
|
|
/*
|
|
|
|
* Note: we run this test after rewrite, not before, because copyObject()
|
|
|
|
* does not handle most kinds of nodes that are used only in raw parse
|
|
|
|
* trees. The present (bizarre) implementation of UNION/INTERSECT/EXCEPT
|
|
|
|
* doesn't run analysis of the second and later subqueries until rewrite,
|
|
|
|
* so we'd get false failures on these queries if we did it beforehand.
|
|
|
|
*/
|
2000-08-12 01:45:35 +02:00
|
|
|
new_list = (List *) copyObject(querytree_list);
|
|
|
|
/* This checks both copyObject() and the equal() routines... */
|
|
|
|
if (! equal(new_list, querytree_list))
|
|
|
|
elog(NOTICE, "pg_parse_and_rewrite: copyObject failed on parse tree");
|
|
|
|
else
|
|
|
|
querytree_list = new_list;
|
2000-06-29 09:35:57 +02:00
|
|
|
#endif
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_print_rewritten)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_pretty_print)
|
1999-05-25 18:15:34 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "rewritten parse tree:");
|
1999-05-25 18:15:34 +02:00
|
|
|
foreach(querytree_list_item, querytree_list)
|
1999-05-11 11:06:35 +02:00
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
querytree = (Query *) lfirst(querytree_list_item);
|
|
|
|
nodeDisplay(querytree);
|
1999-05-11 11:06:35 +02:00
|
|
|
printf("\n");
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "rewritten parse tree:");
|
1999-05-11 11:06:35 +02:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
foreach(querytree_list_item, querytree_list)
|
1999-05-11 11:06:35 +02:00
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
querytree = (Query *) lfirst(querytree_list_item);
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "%s", nodeToString(querytree));
|
1999-05-11 11:06:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
return querytree_list;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
/* Generate a plan for a single query. */
|
|
|
|
Plan *
|
|
|
|
pg_plan_query(Query *querytree)
|
|
|
|
{
|
|
|
|
Plan *plan;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
/* Utility commands have no plans. */
|
|
|
|
if (querytree->commandType == CMD_UTILITY)
|
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_planner_stats)
|
2000-04-04 23:44:40 +02:00
|
|
|
ResetUsage();
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
/* call that optimizer */
|
|
|
|
plan = planner(querytree);
|
1997-12-11 18:36:58 +01:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_planner_stats)
|
2000-04-04 23:44:40 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
fprintf(stderr, "PLANNER STATISTICS\n");
|
2000-04-04 23:44:40 +02:00
|
|
|
ShowUsage();
|
|
|
|
}
|
1997-12-11 18:36:58 +01:00
|
|
|
|
2000-06-29 09:35:57 +02:00
|
|
|
#ifdef COPY_PARSE_PLAN_TREES
|
|
|
|
/* Optional debugging check: pass plan output through copyObject() */
|
|
|
|
{
|
|
|
|
Plan *new_plan = (Plan *) copyObject(plan);
|
|
|
|
|
|
|
|
/* equal() currently does not have routines to compare Plan nodes,
|
|
|
|
* so don't try to test equality here. Perhaps fix someday?
|
|
|
|
*/
|
|
|
|
#ifdef NOT_USED
|
|
|
|
/* This checks both copyObject() and the equal() routines... */
|
|
|
|
if (! equal(new_plan, plan))
|
|
|
|
elog(NOTICE, "pg_plan_query: copyObject failed on plan tree");
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
plan = new_plan;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
/* ----------------
|
|
|
|
* Print plan if debugging.
|
|
|
|
* ----------------
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_print_plan)
|
2000-04-04 23:44:40 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_pretty_print)
|
2000-04-04 23:44:40 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "plan:");
|
2000-04-04 23:44:40 +02:00
|
|
|
nodeDisplay(plan);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(DEBUG, "plan: %s", nodeToString(plan));
|
1998-08-24 03:38:11 +02:00
|
|
|
}
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
return plan;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------------------------------------------------------
|
2000-06-28 05:33:33 +02:00
|
|
|
* pg_exec_query_dest()
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
|
|
|
* Takes a querystring, runs the parser/utilities or
|
2000-06-28 05:33:33 +02:00
|
|
|
* parser/planner/executor over it as necessary.
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
*
|
|
|
|
* Caller is responsible for calling StartTransactionCommand() beforehand
|
|
|
|
* and CommitTransactionCommand() afterwards (if successful).
|
|
|
|
*
|
|
|
|
* The CurrentMemoryContext at entry references a context that is
|
|
|
|
* appropriate for execution of individual queries (typically this will be
|
|
|
|
* TransactionCommandContext). Note that this routine resets that context
|
|
|
|
* after each individual query, so don't store anything there that
|
|
|
|
* must outlive the call!
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-06-28 05:33:33 +02:00
|
|
|
* parse_context references a context suitable for holding the
|
|
|
|
* parse/rewrite trees (typically this will be QueryContext).
|
2000-07-04 08:11:54 +02:00
|
|
|
* This context *must* be longer-lived than the CurrentMemoryContext!
|
2000-06-28 05:33:33 +02:00
|
|
|
* In fact, if the query string might contain BEGIN/COMMIT commands,
|
|
|
|
* parse_context had better outlive TopTransactionContext!
|
|
|
|
*
|
|
|
|
* We could have hard-wired knowledge about QueryContext and
|
|
|
|
* TransactionCommandContext into this routine, but it seems better
|
|
|
|
* not to, in case callers from outside this module need to use some
|
|
|
|
* other contexts.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
1998-02-26 05:46:47 +01:00
|
|
|
pg_exec_query_dest(char *query_string, /* string to execute */
|
1998-08-24 03:38:11 +02:00
|
|
|
CommandDest dest, /* where results should go */
|
2000-06-28 05:33:33 +02:00
|
|
|
MemoryContext parse_context) /* context for parsetrees */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-06-28 05:33:33 +02:00
|
|
|
MemoryContext oldcontext;
|
|
|
|
List *querytree_list,
|
|
|
|
*querytree_item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-04 08:11:54 +02:00
|
|
|
/*
|
|
|
|
* If you called this routine with parse_context = CurrentMemoryContext,
|
|
|
|
* you blew it. They *must* be different, else the context reset
|
|
|
|
* at the bottom of the loop will destroy the querytree list.
|
|
|
|
* (We really ought to check that parse_context isn't a child of
|
|
|
|
* CurrentMemoryContext either, but that would take more cycles than
|
|
|
|
* it's likely to be worth.)
|
|
|
|
*/
|
|
|
|
Assert(parse_context != CurrentMemoryContext);
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Switch to appropriate context for constructing parsetrees.
|
|
|
|
*/
|
|
|
|
oldcontext = MemoryContextSwitchTo(parse_context);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
2000-06-28 05:33:33 +02:00
|
|
|
* Parse and rewrite the query or queries.
|
1999-05-13 09:29:22 +02:00
|
|
|
*/
|
2000-06-28 05:33:33 +02:00
|
|
|
querytree_list = pg_parse_and_rewrite(query_string, NULL, 0);
|
2000-04-04 23:44:40 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Switch back to execution context for planning and execution.
|
|
|
|
*/
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run through the query or queries and execute each one.
|
|
|
|
*/
|
|
|
|
foreach(querytree_item, querytree_list)
|
|
|
|
{
|
|
|
|
Query *querytree = (Query *) lfirst(querytree_item);
|
2000-04-04 23:44:40 +02:00
|
|
|
|
|
|
|
/* if we got a cancel signal in parsing or prior command, quit */
|
|
|
|
if (QueryCancel)
|
|
|
|
CancelQuery();
|
1999-05-13 09:29:22 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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.
|
|
|
|
* ----------------
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_print_query)
|
|
|
|
elog(DEBUG, "ProcessUtility: %s", query_string);
|
|
|
|
else if (DebugLvl > 1)
|
|
|
|
elog(DEBUG, "ProcessUtility");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
ProcessUtility(querytree->utilityStmt, dest);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-04-04 23:44:40 +02:00
|
|
|
Plan *plan;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-05 01:52:50 +02:00
|
|
|
/* If aborted transaction, skip planning and execution */
|
2000-04-04 23:44:40 +02:00
|
|
|
if (IsAbortedTransactionBlockState())
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-04-04 23:44:40 +02:00
|
|
|
/* ----------------
|
|
|
|
* the EndCommand() stuff is to tell the frontend
|
|
|
|
* that the command ended. -cim 6/1/90
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
char *tag = "*ABORT STATE*";
|
|
|
|
|
|
|
|
elog(NOTICE, "current transaction is aborted, "
|
|
|
|
"queries ignored until end of transaction block");
|
|
|
|
|
|
|
|
EndCommand(tag, dest);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* We continue in the loop, on the off chance that there
|
2000-04-05 01:52:50 +02:00
|
|
|
* is a COMMIT or ROLLBACK utility command later in the
|
|
|
|
* query string.
|
|
|
|
*/
|
|
|
|
continue;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
plan = pg_plan_query(querytree);
|
|
|
|
|
|
|
|
/* if we got a cancel signal whilst planning, quit */
|
|
|
|
if (QueryCancel)
|
|
|
|
CancelQuery();
|
|
|
|
|
|
|
|
/* Initialize snapshot state for query */
|
1998-12-16 12:53:55 +01:00
|
|
|
SetQuerySnapshot();
|
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* execute the plan
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_executor_stats)
|
1997-09-07 07:04:48 +02:00
|
|
|
ResetUsage();
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
if (dontExecute)
|
|
|
|
{
|
|
|
|
/* don't execute it, just show the query plan */
|
|
|
|
print_plan(plan, querytree);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (DebugLvl > 1)
|
|
|
|
elog(DEBUG, "ProcessQuery");
|
|
|
|
ProcessQuery(querytree, plan, dest);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_executor_stats)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
fprintf(stderr, "EXECUTOR STATISTICS\n");
|
1997-09-07 07:04:48 +02:00
|
|
|
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.
|
|
|
|
*/
|
1999-05-13 09:29:22 +02:00
|
|
|
CommandCounterIncrement();
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Also, clear the execution context to recover temporary
|
|
|
|
* memory used by the query. NOTE: if query string contains
|
|
|
|
* BEGIN/COMMIT transaction commands, execution context may
|
|
|
|
* now be different from what we were originally passed;
|
|
|
|
* so be careful to clear current context not "oldcontext".
|
|
|
|
*/
|
|
|
|
MemoryContextResetAndDeleteChildren(CurrentMemoryContext);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* signal handler routines used in PostgresMain()
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* handle_warn() catches SIGQUIT. It forces control back to the main
|
1999-04-20 04:19:59 +02:00
|
|
|
* loop, just as if an internal error (elog(ERROR,...)) had occurred.
|
|
|
|
* elog() used to actually use kill(2) to induce a SIGQUIT to get here!
|
|
|
|
* But that's not 100% reliable on some systems, so now it does its own
|
1999-05-25 18:15:34 +02:00
|
|
|
* siglongjmp() instead.
|
1999-04-20 04:19:59 +02:00
|
|
|
* We still provide the signal catcher so that an error quit can be
|
1999-05-25 18:15:34 +02:00
|
|
|
* forced externally. This should be done only with great caution,
|
1999-04-20 04:19:59 +02:00
|
|
|
* however, since an asynchronous signal could leave the system in
|
|
|
|
* who-knows-what inconsistent state.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-11-10 16:24:56 +01:00
|
|
|
* quickdie() occurs when signalled by the postmaster.
|
|
|
|
* Some backend has bought the farm,
|
|
|
|
* so we need to stop what we're doing and exit.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-11-16 07:13:36 +01:00
|
|
|
* die() performs an orderly cleanup via proc_exit()
|
1996-07-09 08:22:35 +02:00
|
|
|
* --------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2000-08-29 11:36:51 +02:00
|
|
|
handle_warn(SIGNAL_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
siglongjmp(Warn_restart, 1);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-06-09 00:38:00 +02:00
|
|
|
static void
|
2000-08-29 11:36:51 +02:00
|
|
|
quickdie(SIGNAL_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-06 23:58:18 +02:00
|
|
|
PG_SETMASK(&BlockSig);
|
1997-11-10 16:24:56 +01:00
|
|
|
elog(NOTICE, "Message from PostgreSQL backend:"
|
1998-02-26 05:46:47 +01:00
|
|
|
"\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.");
|
1997-11-09 05:47:09 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-11-16 07:13:36 +01:00
|
|
|
* DO NOT proc_exit(0) -- we're here because shared memory may be
|
1997-09-07 07:04:48 +02:00
|
|
|
* corrupted, so we don't want to flush any shared state to stable
|
|
|
|
* storage. Just nail the windows shut and get out of town.
|
|
|
|
*/
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
exit(1);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
|
|
|
* Abort transaction and exit
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
void
|
2000-08-29 11:36:51 +02:00
|
|
|
die(SIGNAL_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-06 23:58:18 +02:00
|
|
|
PG_SETMASK(&BlockSig);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
|
|
|
* If ERROR/FATAL is in progress...
|
|
|
|
*/
|
|
|
|
if (InError)
|
|
|
|
{
|
|
|
|
ExitAfterAbort = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
elog(FATAL, "The system is shutting down");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* signal handler for floating point exception */
|
2000-06-09 00:38:00 +02:00
|
|
|
static void
|
2000-08-29 11:36:51 +02:00
|
|
|
FloatExceptionHandler(SIGNAL_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-01-07 22:07:04 +01:00
|
|
|
elog(ERROR, "floating point exception!"
|
1998-02-26 05:46:47 +01:00
|
|
|
" The last floating point operation either exceeded legal ranges"
|
|
|
|
" or was a divide by zero");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-07-09 05:29:11 +02:00
|
|
|
/* signal handler for query cancel signal from postmaster */
|
1998-05-19 20:05:58 +02:00
|
|
|
static void
|
2000-08-29 11:36:51 +02:00
|
|
|
QueryCancelHandler(SIGNAL_ARGS)
|
1998-05-19 20:05:58 +02:00
|
|
|
{
|
|
|
|
QueryCancel = true;
|
2000-02-22 10:55:26 +01:00
|
|
|
LockWaitCancel();
|
1998-05-19 20:05:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CancelQuery(void)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
/*
|
|
|
|
* QueryCancel flag will be reset in main loop, which we reach by
|
1998-07-09 05:29:11 +02:00
|
|
|
* longjmp from elog().
|
|
|
|
*/
|
1998-05-19 20:05:58 +02:00
|
|
|
elog(ERROR, "Query was cancelled.");
|
|
|
|
}
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
static void
|
2000-08-29 11:36:51 +02:00
|
|
|
SigHupHandler(SIGNAL_ARGS)
|
2000-05-31 02:28:42 +02:00
|
|
|
{
|
2000-06-09 00:38:00 +02:00
|
|
|
got_SIGHUP = true;
|
2000-05-31 02:28:42 +02:00
|
|
|
}
|
|
|
|
|
1998-05-19 20:05:58 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
static void
|
|
|
|
usage(char *progname)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
fprintf(stderr,
|
1998-05-27 20:32:05 +02:00
|
|
|
"Usage: %s [options] [dbname]\n", progname);
|
1998-08-25 23:04:41 +02:00
|
|
|
#ifdef USE_ASSERT_CHECKING
|
1999-05-01 19:16:25 +02:00
|
|
|
fprintf(stderr, "\t-A on\t\tenable/disable assert checking\n");
|
1998-08-25 23:04:41 +02:00
|
|
|
#endif
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-B buffers\tset number of buffers in buffer pool\n");
|
1999-05-01 19:16:25 +02:00
|
|
|
fprintf(stderr, "\t-C \t\tsuppress version info\n");
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-D dir\t\tdata directory\n");
|
|
|
|
fprintf(stderr, "\t-E \t\techo query before execution\n");
|
2000-05-31 02:28:42 +02:00
|
|
|
fprintf(stderr, "\t-F \t\tturn fsync off\n");
|
1999-05-22 19:47:54 +02:00
|
|
|
fprintf(stderr, "\t-L \t\tturn off locking\n");
|
|
|
|
fprintf(stderr, "\t-N \t\tdon't use newline as interactive query delimiter\n");
|
1999-03-17 23:53:31 +01:00
|
|
|
fprintf(stderr, "\t-O \t\tallow system table structure changes\n");
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-Q \t\tsuppress informational messages\n");
|
1999-05-22 19:47:54 +02:00
|
|
|
fprintf(stderr, "\t-S kbytes\tset amount of memory for sorts (in kbytes)\n");
|
1999-05-01 19:16:25 +02:00
|
|
|
fprintf(stderr, "\t-T options\tspecify pg_options\n");
|
|
|
|
fprintf(stderr, "\t-W sec\t\twait N seconds to allow attach from a debugger\n");
|
1999-10-08 07:36:58 +02:00
|
|
|
fprintf(stderr, "\t-d [1-5]\tset debug level\n");
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-e \t\tturn on European date format\n");
|
1999-05-01 19:16:25 +02:00
|
|
|
fprintf(stderr, "\t-f [s|i|n|m|h]\tforbid use of some plan types\n");
|
1999-05-22 19:47:54 +02:00
|
|
|
fprintf(stderr, "\t-i \t\tdon't execute queries\n");
|
|
|
|
fprintf(stderr, "\t-o file\t\tsend stdout and stderr to given filename\n");
|
|
|
|
fprintf(stderr, "\t-p database\tbackend is started under a postmaster\n");
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-s \t\tshow stats after each query\n");
|
1999-05-22 19:47:54 +02:00
|
|
|
fprintf(stderr, "\t-t [pa|pl|ex]\tshow timings after each query\n");
|
1998-05-27 20:32:05 +02:00
|
|
|
fprintf(stderr, "\t-v version\tset protocol version being used by frontend\n");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1999-05-22 19:47:54 +02:00
|
|
|
* PostgresMain
|
|
|
|
* postgres main loop
|
1997-09-07 07:04:48 +02:00
|
|
|
* all backends, interactive or otherwise start here
|
1999-05-22 19:47:54 +02:00
|
|
|
*
|
|
|
|
* argc/argv are the command line arguments to be used. When being forked
|
|
|
|
* by the postmaster, these are not the original argv array of the process.
|
|
|
|
* real_argc/real_argv point to the original argv array, which is needed by
|
|
|
|
* PS_INIT_STATUS on some platforms.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
int
|
1998-06-04 19:26:49 +02:00
|
|
|
PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int flag;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
char *DBName = NULL;
|
1999-05-22 19:47:54 +02:00
|
|
|
bool secure = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
int errs = 0;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
int firstchar;
|
1999-08-31 06:26:40 +02:00
|
|
|
StringInfo parser_input;
|
1998-09-01 06:40:42 +02:00
|
|
|
char *userName;
|
1998-08-30 23:05:27 +02:00
|
|
|
|
2000-06-04 03:44:38 +02:00
|
|
|
char *remote_host;
|
|
|
|
unsigned short remote_port;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
extern int optind;
|
|
|
|
extern char *optarg;
|
2000-04-12 19:17:23 +02:00
|
|
|
extern int DebugLvl;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Fire up essential subsystems: error and memory management
|
|
|
|
*
|
|
|
|
* If we are running under the postmaster, this is done already.
|
|
|
|
*/
|
|
|
|
if (!IsUnderPostmaster)
|
|
|
|
{
|
|
|
|
EnableExceptionHandling(true);
|
|
|
|
MemoryContextInit();
|
|
|
|
}
|
|
|
|
|
1997-12-16 16:57:00 +01:00
|
|
|
/*
|
1999-05-22 19:47:54 +02:00
|
|
|
* Set default values for command-line options.
|
1997-12-16 16:57:00 +01:00
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
Noversion = false;
|
|
|
|
EchoQuery = false;
|
2000-06-23 00:31:24 +02:00
|
|
|
|
|
|
|
if (!IsUnderPostmaster)
|
|
|
|
{
|
|
|
|
ResetAllOptions();
|
|
|
|
if (getenv("PGDATA"))
|
|
|
|
DataDir = strdup(getenv("PGDATA"));
|
|
|
|
}
|
2000-05-31 02:28:42 +02:00
|
|
|
StatFp = stderr;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
SetProcessingMode(InitProcessing);
|
|
|
|
|
2000-02-19 23:10:47 +01:00
|
|
|
/* Check for PGDATESTYLE environment variable */
|
|
|
|
set_default_datestyle();
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1999-05-22 19:47:54 +02:00
|
|
|
/* ----------------
|
|
|
|
* parse command line arguments
|
|
|
|
*
|
|
|
|
* There are now two styles of command line layout for the backend:
|
|
|
|
*
|
|
|
|
* For interactive use (not started from postmaster) the format is
|
|
|
|
* postgres [switches] [databasename]
|
|
|
|
* If the databasename is omitted it is taken to be the user name.
|
|
|
|
*
|
|
|
|
* When started from the postmaster, the format is
|
|
|
|
* postgres [secure switches] -p databasename [insecure switches]
|
|
|
|
* Switches appearing after -p came from the client (via "options"
|
|
|
|
* field of connection request). For security reasons we restrict
|
|
|
|
* what these switches can do.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
optind = 1; /* reset after postmaster's usage */
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
while ((flag = getopt(argc, argv, "A:B:CD:d:Eef:FiLNOPo:p:S:st:v:W:x:-:")) != EOF)
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (flag)
|
|
|
|
{
|
1998-08-25 23:04:41 +02:00
|
|
|
case 'A':
|
|
|
|
#ifdef USE_ASSERT_CHECKING
|
|
|
|
assert_enabled = atoi(optarg);
|
|
|
|
#else
|
2000-07-12 19:38:53 +02:00
|
|
|
fprintf(stderr, "Assert checking is not compiled in\n");
|
1998-08-25 23:04:41 +02:00
|
|
|
#endif
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'B':
|
|
|
|
/* ----------------
|
|
|
|
* specify the size of buffer pool
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
|
|
|
NBuffers = atoi(optarg);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'C':
|
|
|
|
/* ----------------
|
1999-05-22 19:47:54 +02:00
|
|
|
* don't print version string
|
1997-09-08 04:41:22 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
Noversion = true;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'D': /* PGDATA directory */
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
2000-06-23 00:31:24 +02:00
|
|
|
{
|
|
|
|
if (DataDir)
|
|
|
|
free(DataDir);
|
|
|
|
DataDir = strdup(optarg);
|
|
|
|
}
|
1998-10-13 21:51:50 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'd': /* debug level */
|
1999-10-08 06:28:57 +02:00
|
|
|
DebugLvl = atoi(optarg);
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl >= 1);
|
|
|
|
Log_connections = true;
|
1998-08-25 23:34:10 +02:00
|
|
|
if (DebugLvl >= 2)
|
2000-05-31 02:28:42 +02:00
|
|
|
Debug_print_query = true;
|
1998-08-25 23:34:10 +02:00
|
|
|
if (DebugLvl >= 3)
|
2000-05-31 02:28:42 +02:00
|
|
|
Debug_print_parse = true;
|
1999-10-08 07:27:14 +02:00
|
|
|
if (DebugLvl >= 4)
|
2000-05-31 02:28:42 +02:00
|
|
|
Debug_print_plan = true;
|
1999-05-11 11:06:35 +02:00
|
|
|
if (DebugLvl >= 5)
|
2000-05-31 02:28:42 +02:00
|
|
|
Debug_print_rewritten = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
case 'E':
|
|
|
|
/* ----------------
|
|
|
|
* E - echo the query the user entered
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
EchoQuery = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
case 'e':
|
|
|
|
/* --------------------------
|
|
|
|
* Use european date formats.
|
|
|
|
* --------------------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
EuroDates = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
case 'F':
|
|
|
|
/* --------------------
|
|
|
|
* turn off fsync
|
2000-04-23 02:13:16 +02:00
|
|
|
*
|
|
|
|
* 7.0 buffer manager can support different backends running
|
|
|
|
* with different fsync settings, so this no longer needs
|
|
|
|
* to be "if (secure)".
|
1997-09-08 04:41:22 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
enableFsync = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
case 'f':
|
|
|
|
/* -----------------
|
|
|
|
* f - forbid generation of certain plans
|
|
|
|
* -----------------
|
|
|
|
*/
|
|
|
|
switch (optarg[0])
|
|
|
|
{
|
|
|
|
case 's': /* seqscan */
|
2000-01-23 00:50:30 +01:00
|
|
|
enable_seqscan = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case 'i': /* indexscan */
|
2000-01-23 00:50:30 +01:00
|
|
|
enable_indexscan = false;
|
|
|
|
break;
|
|
|
|
case 't': /* tidscan */
|
|
|
|
enable_tidscan = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case 'n': /* nestloop */
|
2000-01-23 00:50:30 +01:00
|
|
|
enable_nestloop = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case 'm': /* mergejoin */
|
2000-01-23 00:50:30 +01:00
|
|
|
enable_mergejoin = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case 'h': /* hashjoin */
|
2000-01-23 00:50:30 +01:00
|
|
|
enable_hashjoin = false;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errs++;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'i':
|
2000-06-28 05:33:33 +02:00
|
|
|
dontExecute = true;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'L':
|
|
|
|
/* --------------------
|
|
|
|
* turn off locking
|
|
|
|
* --------------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
|
|
|
lockingOff = 1;
|
1999-05-01 19:16:25 +02:00
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'N':
|
|
|
|
/* ----------------
|
|
|
|
* N - Don't use newline as a query delimiter
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
UseNewLine = 0;
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-03-17 23:53:31 +01:00
|
|
|
case 'O':
|
|
|
|
/* --------------------
|
|
|
|
* allow system table structure modifications
|
|
|
|
* --------------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure) /* XXX safe to allow from client??? */
|
|
|
|
allowSystemTableMods = true;
|
1999-03-17 23:53:31 +01:00
|
|
|
break;
|
|
|
|
|
2000-02-18 10:30:20 +01:00
|
|
|
case 'P':
|
|
|
|
/* --------------------
|
|
|
|
* ignore system indexes
|
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
if (secure) /* XXX safe to allow from client??? */
|
|
|
|
IgnoreSystemIndexes(true);
|
|
|
|
break;
|
|
|
|
|
1999-05-01 19:16:25 +02:00
|
|
|
case 'o':
|
|
|
|
/* ----------------
|
|
|
|
* o - send output (stdout and stderr) to the given file
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
|
|
|
StrNCpy(OutputFileName, optarg, MAXPGPATH);
|
1999-05-01 19:16:25 +02:00
|
|
|
break;
|
|
|
|
|
1999-05-22 19:47:54 +02:00
|
|
|
case 'p':
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* p - special flag passed if backend was forked
|
|
|
|
* by a postmaster.
|
|
|
|
* ----------------
|
|
|
|
*/
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
|
|
|
{
|
2000-06-23 00:31:24 +02:00
|
|
|
DBName = strdup(optarg);
|
1999-05-25 18:15:34 +02:00
|
|
|
secure = false; /* subsequent switches are NOT
|
|
|
|
* secure */
|
1999-05-22 19:47:54 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'S':
|
|
|
|
/* ----------------
|
1997-09-18 16:33:46 +02:00
|
|
|
* S - amount of sort memory to use in 1k bytes
|
1997-09-08 04:41:22 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1997-09-18 07:19:17 +02:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
int S;
|
|
|
|
|
1997-09-18 16:33:46 +02:00
|
|
|
S = atoi(optarg);
|
1998-02-26 05:46:47 +01:00
|
|
|
if (S >= 4 * BLCKSZ / 1024)
|
1997-09-18 16:33:46 +02:00
|
|
|
SortMem = S;
|
1997-09-18 07:19:17 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
case 's':
|
|
|
|
/* ----------------
|
|
|
|
* s - report usage statistics (timings) after each query
|
|
|
|
* ----------------
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
Show_query_stats = 1;
|
1998-08-25 23:34:10 +02:00
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
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.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
switch (optarg[0])
|
|
|
|
{
|
|
|
|
case 'p':
|
|
|
|
if (optarg[1] == 'a')
|
2000-05-31 02:28:42 +02:00
|
|
|
Show_parser_stats = 1;
|
1997-09-08 04:41:22 +02:00
|
|
|
else if (optarg[1] == 'l')
|
2000-05-31 02:28:42 +02:00
|
|
|
Show_planner_stats = 1;
|
1997-09-08 04:41:22 +02:00
|
|
|
else
|
|
|
|
errs++;
|
|
|
|
break;
|
|
|
|
case 'e':
|
2000-05-31 02:28:42 +02:00
|
|
|
Show_executor_stats = 1;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errs++;
|
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
|
1998-01-26 02:42:53 +01:00
|
|
|
case 'v':
|
1999-05-22 19:47:54 +02:00
|
|
|
if (secure)
|
|
|
|
FrontendProtocol = (ProtocolVersion) atoi(optarg);
|
1998-01-26 02:42:53 +01:00
|
|
|
break;
|
|
|
|
|
1998-08-25 23:04:41 +02:00
|
|
|
case 'W':
|
|
|
|
/* ----------------
|
1998-09-01 06:40:42 +02:00
|
|
|
* wait N seconds to allow attach from a debugger
|
1998-08-25 23:04:41 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
sleep(atoi(optarg));
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'x':
|
1999-05-25 18:15:34 +02:00
|
|
|
#ifdef NOT_USED /* planner/xfunc.h */
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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++;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
case '-':
|
|
|
|
{
|
2000-07-03 22:46:10 +02:00
|
|
|
char *name, *value;
|
2000-05-31 02:28:42 +02:00
|
|
|
|
2000-07-03 22:46:10 +02:00
|
|
|
ParseLongOption(optarg, &name, &value);
|
|
|
|
if (!value)
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(ERROR, "--%s requires argument", optarg);
|
2000-07-03 22:46:10 +02:00
|
|
|
|
|
|
|
SetConfigOption(name, value, PGC_BACKEND);
|
|
|
|
free(name);
|
|
|
|
if (value)
|
|
|
|
free(value);
|
2000-05-31 02:28:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
default:
|
|
|
|
/* ----------------
|
|
|
|
* default: bad command line option
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
errs++;
|
1999-05-01 19:16:25 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_query_stats &&
|
|
|
|
(Show_parser_stats || Show_planner_stats || Show_executor_stats))
|
1999-10-06 23:58:18 +02:00
|
|
|
{
|
2000-06-09 00:38:00 +02:00
|
|
|
elog(NOTICE, "Query statistics are disabled because parser, planner, or executor statistics are on.");
|
|
|
|
Show_query_stats = false;
|
1999-10-06 23:58:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 "
|
2000-04-12 19:17:23 +02:00
|
|
|
"option or by setting the PGDATA environment variable.\n\n",
|
1999-10-06 23:58:18 +02:00
|
|
|
argv[0]);
|
|
|
|
proc_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* 1. Set BlockSig and UnBlockSig masks. 2. Set up signal handlers. 3.
|
|
|
|
* Allow only SIGUSR1 signal (we never block it) during
|
|
|
|
* initialization.
|
1999-10-06 23:58:18 +02:00
|
|
|
*
|
|
|
|
* Note that postmaster already blocked ALL signals to make us happy.
|
|
|
|
*/
|
2000-06-28 05:33:33 +02:00
|
|
|
pqinitmask();
|
1999-10-06 23:58:18 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_SIGPROCMASK
|
|
|
|
sigdelset(&BlockSig, SIGUSR1);
|
|
|
|
#else
|
|
|
|
BlockSig &= ~(sigmask(SIGUSR1));
|
|
|
|
#endif
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
PG_SETMASK(&BlockSig); /* block everything except SIGUSR1 */
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
pqsignal(SIGHUP, SigHupHandler); /* set flag to read config file */
|
2000-04-12 19:17:23 +02:00
|
|
|
pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */
|
|
|
|
pqsignal(SIGQUIT, handle_warn); /* handle error */
|
1999-10-06 23:58:18 +02:00
|
|
|
pqsignal(SIGTERM, die);
|
|
|
|
pqsignal(SIGALRM, HandleDeadLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore failure to write to frontend. Note: if frontend closes
|
|
|
|
* connection, we will notice it and exit cleanly when control next
|
|
|
|
* returns to outer loop. This seems safer than forcing exit in the
|
1999-10-06 23:58:18 +02:00
|
|
|
* midst of output during who-knows-what operation...
|
|
|
|
*/
|
|
|
|
pqsignal(SIGPIPE, SIG_IGN);
|
|
|
|
pqsignal(SIGUSR1, quickdie);
|
2000-04-12 19:17:23 +02:00
|
|
|
pqsignal(SIGUSR2, Async_NotifyHandler); /* flush also sinval cache */
|
1999-10-06 23:58:18 +02:00
|
|
|
pqsignal(SIGFPE, FloatExceptionHandler);
|
2000-04-12 19:17:23 +02:00
|
|
|
pqsignal(SIGCHLD, SIG_IGN); /* ignored, sent by LockOwners */
|
1999-10-06 23:58:18 +02:00
|
|
|
pqsignal(SIGTTIN, SIG_DFL);
|
|
|
|
pqsignal(SIGTTOU, SIG_DFL);
|
|
|
|
pqsignal(SIGCONT, SIG_DFL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get user name (needed now in case it is the default database name)
|
|
|
|
* and check command line validity
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
SetPgUserName();
|
|
|
|
userName = GetPgUserName();
|
|
|
|
|
1999-05-22 19:47:54 +02:00
|
|
|
if (IsUnderPostmaster)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-22 19:47:54 +02:00
|
|
|
/* noninteractive case: nothing should be left after switches */
|
|
|
|
if (errs || argc != optind || DBName == NULL)
|
|
|
|
{
|
|
|
|
usage(argv[0]);
|
2000-02-20 05:26:35 +01:00
|
|
|
proc_exit(0);
|
1999-05-22 19:47:54 +02:00
|
|
|
}
|
1999-10-06 23:58:18 +02:00
|
|
|
pq_init(); /* initialize libpq at backend startup */
|
|
|
|
whereToSendOutput = Remote;
|
|
|
|
BaseInit();
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-05-22 19:47:54 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-22 19:47:54 +02:00
|
|
|
/* interactive case: database name can be last arg on command line */
|
1999-10-06 23:58:18 +02:00
|
|
|
whereToSendOutput = Debug;
|
1999-05-22 19:47:54 +02:00
|
|
|
if (errs || argc - optind > 1)
|
|
|
|
{
|
|
|
|
usage(argv[0]);
|
2000-02-20 05:26:35 +01:00
|
|
|
proc_exit(0);
|
1999-05-22 19:47:54 +02:00
|
|
|
}
|
|
|
|
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]);
|
2000-02-20 05:26:35 +01:00
|
|
|
proc_exit(0);
|
1999-05-22 19:47:54 +02:00
|
|
|
}
|
2000-01-09 13:17:33 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to create pid file.
|
|
|
|
*/
|
|
|
|
SetPidFname(DataDir);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (SetPidFile(-getpid()))
|
2000-01-09 13:17:33 +01:00
|
|
|
proc_exit(0);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-01-09 13:17:33 +01:00
|
|
|
/*
|
|
|
|
* Register clean up proc.
|
|
|
|
*/
|
|
|
|
on_proc_exit(UnlinkPidFile, NULL);
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
BaseInit();
|
2000-07-03 22:48:46 +02:00
|
|
|
snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
|
|
|
|
snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
|
1999-10-06 23:58:18 +02:00
|
|
|
StartupXLOG();
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1999-05-22 19:47:54 +02:00
|
|
|
/*
|
|
|
|
* Set up additional info.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CYR_RECODE
|
|
|
|
SetCharSet();
|
|
|
|
#endif
|
|
|
|
|
1999-10-25 05:08:03 +02:00
|
|
|
/* On some systems our dynloader code needs the executable's pathname */
|
2000-06-04 03:44:38 +02:00
|
|
|
if (FindExec(pg_pathname, real_argv[0], "postgres") < 0)
|
1999-05-22 19:47:54 +02:00
|
|
|
elog(FATAL, "%s: could not locate executable, bailing out...",
|
2000-06-04 03:44:38 +02:00
|
|
|
real_argv[0]);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:34:10 +02:00
|
|
|
/*
|
|
|
|
* Find remote host name or address.
|
|
|
|
*/
|
2000-06-04 03:44:38 +02:00
|
|
|
remote_host = NULL;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (IsUnderPostmaster)
|
|
|
|
{
|
2000-06-04 03:44:38 +02:00
|
|
|
if (MyProcPort->raddr.sa.sa_family == AF_INET)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-06-04 03:44:38 +02:00
|
|
|
struct hostent *host_ent;
|
|
|
|
char * host_addr;
|
1998-08-25 23:34:10 +02:00
|
|
|
|
2000-06-04 03:44:38 +02:00
|
|
|
remote_port = ntohs(MyProcPort->raddr.in.sin_port);
|
|
|
|
host_addr = inet_ntoa(MyProcPort->raddr.in.sin_addr);
|
|
|
|
|
|
|
|
if (HostnameLookup)
|
|
|
|
{
|
|
|
|
host_ent = gethostbyaddr((char *) &MyProcPort->raddr.in.sin_addr, sizeof(MyProcPort->raddr.in.sin_addr), AF_INET);
|
|
|
|
|
|
|
|
if (host_ent)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-06-04 03:44:38 +02:00
|
|
|
remote_host = palloc(strlen(host_addr) + strlen(host_ent->h_name) + 3);
|
|
|
|
sprintf(remote_host, "%s[%s]", host_ent->h_name, host_addr);
|
1998-08-25 23:34:10 +02:00
|
|
|
}
|
2000-06-04 03:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (remote_host == NULL)
|
|
|
|
remote_host = pstrdup(host_addr);
|
|
|
|
|
|
|
|
if (ShowPortNumber)
|
|
|
|
{
|
|
|
|
char * str = palloc(strlen(remote_host) + 7);
|
|
|
|
sprintf(str, "%s:%hu", remote_host, remote_port);
|
|
|
|
pfree(remote_host);
|
|
|
|
remote_host = str;
|
|
|
|
}
|
1998-08-25 23:34:10 +02:00
|
|
|
}
|
2000-06-04 03:44:38 +02:00
|
|
|
else /* not AF_INET */
|
|
|
|
remote_host = "[local]";
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
2000-06-23 00:31:24 +02:00
|
|
|
* Set process parameters for ps
|
|
|
|
*
|
|
|
|
* WARNING: On some platforms the environment will be moved
|
|
|
|
* around to make room for the ps display string. So any
|
|
|
|
* references to optarg or getenv() from above will be invalid
|
|
|
|
* after this call. Better use strdup or something similar.
|
1999-10-06 23:58:18 +02:00
|
|
|
*/
|
2000-06-04 03:44:38 +02:00
|
|
|
init_ps_display(real_argc, real_argv, userName, DBName, remote_host);
|
|
|
|
set_ps_display("startup");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Log_connections)
|
|
|
|
elog(DEBUG, "connection: host=%s user=%s database=%s",
|
|
|
|
remote_host, userName, DBName);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
|
|
|
* general initialization
|
1999-05-22 19:47:54 +02:00
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl > 1)
|
|
|
|
elog(DEBUG, "InitPostgres");
|
1997-09-07 07:04:48 +02:00
|
|
|
InitPostgres(DBName);
|
|
|
|
|
1998-07-18 20:34:34 +02:00
|
|
|
#ifdef MULTIBYTE
|
1998-06-16 09:29:54 +02:00
|
|
|
/* set default client encoding */
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl > 1)
|
|
|
|
elog(DEBUG, "reset_client_encoding");
|
1998-06-16 09:29:54 +02:00
|
|
|
reset_client_encoding();
|
|
|
|
#endif
|
|
|
|
|
1999-02-02 04:45:56 +01:00
|
|
|
on_shmem_exit(remove_all_temp_relations, NULL);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Send this backend's cancellation info to the frontend.
|
1998-10-02 03:14:14 +02:00
|
|
|
*/
|
1998-07-09 05:29:11 +02:00
|
|
|
if (whereToSendOutput == Remote &&
|
|
|
|
PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
|
|
|
{
|
1999-04-25 05:19:27 +02:00
|
|
|
StringInfoData buf;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-04-25 05:19:27 +02:00
|
|
|
pq_beginmessage(&buf);
|
|
|
|
pq_sendbyte(&buf, 'K');
|
|
|
|
pq_sendint(&buf, (int32) MyProcPid, sizeof(int32));
|
|
|
|
pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32));
|
|
|
|
pq_endmessage(&buf);
|
1998-07-09 05:29:11 +02:00
|
|
|
/* Need not flush since ReadyForQuery will do it. */
|
|
|
|
}
|
|
|
|
|
1998-10-02 03:14:14 +02:00
|
|
|
if (!IsUnderPostmaster)
|
|
|
|
{
|
|
|
|
puts("\nPOSTGRES backend interactive interface ");
|
2000-08-30 22:30:06 +02:00
|
|
|
puts("$Revision: 1.174 $ $Date: 2000/08/30 20:30:06 $\n");
|
1998-10-02 03:14:14 +02:00
|
|
|
}
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
1999-09-29 18:06:40 +02:00
|
|
|
* Initialize the deferred trigger manager
|
|
|
|
*/
|
|
|
|
if (DeferredTriggerInit() != 0)
|
2000-08-30 22:30:06 +02:00
|
|
|
goto normalexit;
|
1999-11-16 07:13:36 +01:00
|
|
|
|
|
|
|
SetProcessingMode(NormalProcessing);
|
1999-09-29 18:06:40 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Create the memory context we will use in the main loop.
|
|
|
|
*
|
|
|
|
* QueryContext is reset once per iteration of the main loop,
|
|
|
|
* ie, upon completion of processing of each supplied query string.
|
|
|
|
* It can therefore be used for any data that should live just as
|
|
|
|
* long as the query string --- parse trees, for example.
|
|
|
|
*/
|
|
|
|
QueryContext = AllocSetContextCreate(TopMemoryContext,
|
|
|
|
"QueryContext",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
/*
|
|
|
|
* POSTGRES main processing loop begins here
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* If an exception is encountered, processing resumes here so we abort
|
|
|
|
* the current transaction and start a new one.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
if (sigsetjmp(Warn_restart, 1) != 0)
|
|
|
|
{
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Make sure we are in a valid memory context during recovery.
|
|
|
|
*
|
|
|
|
* We use ErrorContext in hopes that it will have some free space
|
|
|
|
* even if we're otherwise up against it...
|
|
|
|
*/
|
|
|
|
MemoryContextSwitchTo(ErrorContext);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl >= 1)
|
|
|
|
elog(DEBUG, "AbortCurrentTransaction");
|
1997-09-07 07:04:48 +02:00
|
|
|
AbortCurrentTransaction();
|
2000-06-28 05:33:33 +02:00
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
if (ExitAfterAbort)
|
2000-08-30 22:30:06 +02:00
|
|
|
goto errorexit;
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* If we recovered successfully, return to normal top-level context
|
|
|
|
* and clear ErrorContext for next time.
|
|
|
|
*/
|
|
|
|
MemoryContextSwitchTo(TopMemoryContext);
|
|
|
|
MemoryContextResetAndDeleteChildren(ErrorContext);
|
|
|
|
InError = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-05-19 20:05:58 +02:00
|
|
|
|
1999-11-16 07:13:36 +01:00
|
|
|
Warn_restart_ready = true; /* we can now handle elog(ERROR) */
|
|
|
|
|
1999-10-06 23:58:18 +02:00
|
|
|
PG_SETMASK(&UnBlockSig);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-10-02 03:14:14 +02:00
|
|
|
/*
|
|
|
|
* Non-error queries loop here.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
2000-06-28 05:33:33 +02:00
|
|
|
/*
|
|
|
|
* Release storage left over from prior query cycle, and
|
|
|
|
* create a new query input buffer in the cleared QueryContext.
|
|
|
|
*/
|
|
|
|
MemoryContextSwitchTo(QueryContext);
|
|
|
|
MemoryContextResetAndDeleteChildren(QueryContext);
|
|
|
|
|
|
|
|
parser_input = makeStringInfo();
|
1998-10-02 03:14:14 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
/* XXX this could be moved after ReadCommand below to get more
|
|
|
|
* sensical behaviour */
|
|
|
|
if (got_SIGHUP)
|
|
|
|
{
|
|
|
|
got_SIGHUP = false;
|
|
|
|
ProcessConfigFile(PGC_SIGHUP);
|
|
|
|
}
|
|
|
|
|
1998-05-07 01:51:16 +02:00
|
|
|
/* ----------------
|
1998-10-02 03:14:14 +02:00
|
|
|
* (1) tell the frontend we're ready for a new query.
|
|
|
|
*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Note: this includes fflush()'ing the last of the prior output.
|
1998-05-07 01:51:16 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1998-05-26 05:20:01 +02:00
|
|
|
ReadyForQuery(whereToSendOutput);
|
1998-05-07 01:51:16 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
1998-10-06 04:40:09 +02:00
|
|
|
* (2) deal with pending asynchronous NOTIFY from other backends,
|
1999-05-25 18:15:34 +02:00
|
|
|
* and enable async.c's signal handler to execute NOTIFY directly.
|
1998-10-06 04:40:09 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
QueryCancel = false; /* forget any earlier CANCEL signal */
|
2000-02-22 10:55:26 +01:00
|
|
|
SetWaitingForLock(false);
|
1998-10-06 04:40:09 +02:00
|
|
|
|
|
|
|
EnableNotifyInterrupt();
|
|
|
|
|
|
|
|
/* ----------------
|
2000-05-31 02:28:42 +02:00
|
|
|
* (3) read a command (loop blocks here)
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
2000-06-28 05:33:33 +02:00
|
|
|
set_ps_display("idle");
|
|
|
|
|
1998-01-26 02:42:53 +01:00
|
|
|
firstchar = ReadCommand(parser_input);
|
1998-05-19 20:05:58 +02:00
|
|
|
|
1998-10-02 03:14:14 +02:00
|
|
|
QueryCancel = false; /* forget any earlier CANCEL signal */
|
1998-05-19 20:05:58 +02:00
|
|
|
|
1998-10-02 03:14:14 +02:00
|
|
|
/* ----------------
|
1998-10-06 04:40:09 +02:00
|
|
|
* (4) disable async.c's signal handler.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
DisableNotifyInterrupt();
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* (5) process the command.
|
1998-10-02 03:14:14 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (firstchar)
|
|
|
|
{
|
|
|
|
/* ----------------
|
1997-09-08 04:41:22 +02:00
|
|
|
* 'F' indicates a fastpath call.
|
|
|
|
* XXX HandleFunctionRequest
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'F':
|
1997-09-07 07:04:48 +02:00
|
|
|
IsEmptyQuery = false;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* start an xact for this function invocation */
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl >= 1)
|
|
|
|
elog(DEBUG, "StartTransactionCommand");
|
1997-09-07 07:04:48 +02:00
|
|
|
StartTransactionCommand();
|
1998-10-02 03:14:14 +02:00
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
if (HandleFunctionRequest() == EOF)
|
|
|
|
{
|
|
|
|
/* lost frontend connection during F message input */
|
2000-08-30 22:30:06 +02:00
|
|
|
goto normalexit;
|
1999-07-22 04:40:07 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* ----------------
|
|
|
|
* 'Q' indicates a user query
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
case 'Q':
|
1999-08-31 06:26:40 +02:00
|
|
|
if (strspn(parser_input->data, " \t\n") == parser_input->len)
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
|
|
|
/* ----------------
|
|
|
|
* 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;
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_query_stats)
|
1997-09-08 04:41:22 +02:00
|
|
|
ResetUsage();
|
|
|
|
|
|
|
|
/* start an xact for this query */
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl >= 1)
|
|
|
|
elog(DEBUG, "StartTransactionCommand");
|
1997-09-08 04:41:22 +02:00
|
|
|
StartTransactionCommand();
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
pg_exec_query_dest(parser_input->data,
|
|
|
|
whereToSendOutput,
|
|
|
|
QueryContext);
|
1998-06-04 19:26:49 +02:00
|
|
|
|
1999-09-29 18:06:40 +02:00
|
|
|
/*
|
|
|
|
* Invoke IMMEDIATE constraint triggers
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
DeferredTriggerEndQuery();
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Show_query_stats)
|
2000-06-09 00:38:00 +02:00
|
|
|
{
|
|
|
|
fprintf(StatFp, "QUERY STATISTICS\n");
|
1997-09-08 04:41:22 +02:00
|
|
|
ShowUsage();
|
2000-06-09 00:38:00 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ----------------
|
1999-07-22 04:40:07 +02:00
|
|
|
* 'X' means that the frontend is closing down the socket.
|
|
|
|
* EOF means unexpected loss of frontend connection.
|
|
|
|
* Either way, perform normal shutdown.
|
1997-09-08 04:41:22 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
case 'X':
|
1999-07-22 04:40:07 +02:00
|
|
|
case EOF:
|
2000-08-30 22:30:06 +02:00
|
|
|
goto normalexit;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
|
|
|
default:
|
1998-07-09 05:29:11 +02:00
|
|
|
elog(ERROR, "unknown frontend message was received");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------
|
1998-10-06 04:40:09 +02:00
|
|
|
* (6) commit the current transaction
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
|
|
|
* Note: if we had an empty input buffer, then we didn't
|
2000-06-28 05:33:33 +02:00
|
|
|
* call pg_exec_query_dest, so we don't bother to commit
|
|
|
|
* this transaction.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
if (!IsEmptyQuery)
|
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
if (DebugLvl >= 1)
|
|
|
|
elog(DEBUG, "CommitTransactionCommand");
|
2000-06-04 03:44:38 +02:00
|
|
|
set_ps_display("commit");
|
1997-09-07 07:04:48 +02:00
|
|
|
CommitTransactionCommand();
|
2000-05-21 04:23:30 +02:00
|
|
|
#ifdef SHOW_MEMORY_STATS
|
|
|
|
/* print global-context stats at each commit for leak tracking */
|
|
|
|
if (ShowStats)
|
2000-06-28 05:33:33 +02:00
|
|
|
MemoryContextStats(TopMemoryContext);
|
2000-05-21 04:23:30 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-01-26 02:42:53 +01:00
|
|
|
if (IsUnderPostmaster)
|
1997-09-07 07:04:48 +02:00
|
|
|
NullCommand(Remote);
|
|
|
|
}
|
Here is the patch with memory leak checker. This checker allow detect
in-chunk leaks, overwrite-next-chunk leaks and overwrite block-freeptr leaks.
A in-chunk leak --- if something overwrite space after wanted (via palloc()
size, but it is still inside chunk. For example
x = palloc(12); /* create 16b chunk */
memset(x, '#', 13);
this leak is in the current source total invisible, because chunk is 16b and
leak is in the "align space".
For this feature I add data_size to StandardChunk, and all memory which go
from AllocSetAlloc() is marked as 0x7F.
The MemoryContextCheck() is compiled '#ifdef USE_ASSERT_CHECKING'.
I add this checking to 'tcop/postgres.c' and is active after each backend
query, but it is probably not sufficient, because some MemoryContext exist
only during memory processing --- will good if someone who known where
it is needful (Tom:-) add it for others contexts;
A problem in the current source is that we have still some malloc()
allocation that is not needful and this allocation is total invisible for
all context routines. For example Dllist in backend (pretty dirty it is in
catcache where values in Dllist are palloc-ed, but list is malloc-ed).
--- and BTW. this Dllist design stand in the way for query cache :-)
Tom, if you agree I start replace some mallocs.
BTW. --- Tom, have you idea for across transaction presistent allocation for
SQL functions? (like regex - now it is via malloc)
I almost forget. I add one if() to AllocSetAlloc(), for 'size' that are
greater than ALLOC_BIGCHUNK_LIMIT is not needful check AllocSetFreeIndex(),
because 'fidx' is always 'ALLOCSET_NUM_FREELISTS - 1'. It a little brisk up
allocation for very large chunks. Right?
Karel
2000-07-11 16:30:37 +02:00
|
|
|
|
|
|
|
#ifdef MEMORY_CONTEXT_CHECKING
|
|
|
|
/*
|
|
|
|
* Check all memory after each backend loop
|
|
|
|
*/
|
|
|
|
MemoryContextCheck(TopMemoryContext);
|
|
|
|
#endif
|
2000-08-30 22:30:06 +02:00
|
|
|
} /* end of main loop */
|
|
|
|
|
|
|
|
normalexit:
|
|
|
|
ExitAfterAbort = true; /* ensure we will exit if elog during abort */
|
|
|
|
AbortOutOfAnyTransaction();
|
|
|
|
if (!IsUnderPostmaster)
|
|
|
|
ShutdownXLOG();
|
|
|
|
|
|
|
|
errorexit:
|
|
|
|
pq_close();
|
|
|
|
ProcReleaseLocks(); /* Just to be sure... */
|
|
|
|
proc_exit(0);
|
1998-10-02 03:14:14 +02:00
|
|
|
|
2000-08-30 22:30:06 +02:00
|
|
|
return 1; /* keep compiler quiet */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-04-24 05:39:09 +02:00
|
|
|
#ifndef HAVE_GETRUSAGE
|
1999-07-16 07:23:30 +02:00
|
|
|
#include "rusagestub.h"
|
|
|
|
#else
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <sys/resource.h>
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* HAVE_GETRUSAGE */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
struct rusage Save_r;
|
|
|
|
struct timeval Save_t;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
void
|
1996-11-10 04:06:38 +01:00
|
|
|
ResetUsage(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
struct timezone tz;
|
|
|
|
|
|
|
|
getrusage(RUSAGE_SELF, &Save_r);
|
|
|
|
gettimeofday(&Save_t, &tz);
|
|
|
|
ResetBufferUsage();
|
|
|
|
/* ResetTupleCount(); */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1996-11-10 04:06:38 +01:00
|
|
|
ShowUsage(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
struct timeval user,
|
|
|
|
sys;
|
|
|
|
struct timeval elapse_t;
|
1997-09-07 07:04:48 +02:00
|
|
|
struct timezone tz;
|
1997-09-08 04:41:22 +02:00
|
|
|
struct rusage r;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-04-28 07:07:34 +02:00
|
|
|
/*
|
|
|
|
* Set output destination if not otherwise set
|
|
|
|
*/
|
|
|
|
if (StatFp == NULL)
|
|
|
|
StatFp = stderr;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
1997-04-24 05:39:09 +02:00
|
|
|
#ifdef HAVE_GETRUSAGE
|
1997-09-07 07:04:48 +02:00
|
|
|
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);
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* HAVE_GETRUSAGE */
|
1997-09-07 07:04:48 +02:00
|
|
|
fprintf(StatFp, "! postgres usage stats:\n");
|
|
|
|
PrintBufferUsage(StatFp);
|
|
|
|
/* DisplayTupleCount(StatFp); */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:04:41 +02:00
|
|
|
|
2000-06-09 00:38:00 +02:00
|
|
|
#ifdef NOT_USED
|
|
|
|
static int
|
1998-08-25 23:04:41 +02:00
|
|
|
assertEnable(int val)
|
|
|
|
{
|
|
|
|
assert_enabled = val;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ASSERT_CHECKING_TEST
|
|
|
|
int
|
|
|
|
assertTest(int val)
|
|
|
|
{
|
|
|
|
Assert(val == 0);
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (assert_enabled)
|
|
|
|
{
|
1998-08-25 23:04:41 +02:00
|
|
|
/* val != 0 should be trapped by previous Assert */
|
|
|
|
elog(NOTICE, "Assert test successfull (val = %d)", val);
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
elog(NOTICE, "Assert checking is disabled (val = %d)", val);
|
1998-08-25 23:04:41 +02:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:04:41 +02:00
|
|
|
#endif
|
|
|
|
#endif
|