postgres/src/backend/tcop/postgres.c

3572 lines
92 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* postgres.c
* POSTGRES C Backend Interface
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.493 2006/07/29 03:02:56 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
* hence the main module of the "traffic cop".
*
*-------------------------------------------------------------------------
*/
1996-11-08 06:02:30 +00:00
#include "postgres.h"
1996-11-08 06:02:30 +00:00
#include <unistd.h>
#include <signal.h>
1996-11-08 06:02:30 +00:00
#include <fcntl.h>
#include <sys/socket.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_GETOPT_H
1999-07-16 05:23:30 +00:00
#include <getopt.h>
1999-01-17 06:20:06 +00:00
#endif
#include "access/printtup.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/async.h"
#include "commands/prepare.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
1999-07-16 05:00:38 +00:00
#include "miscadmin.h"
#include "nodes/print.h"
#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parser.h"
1999-07-16 03:14:30 +00:00
#include "rewrite/rewriteHandler.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/sinval.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
1999-07-16 03:14:30 +00:00
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/flatfiles.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
1999-07-16 05:00:38 +00:00
#include "mb/pg_wchar.h"
#include "pgstat.h"
extern int optind;
extern char *optarg;
/* ----------------
* global variables
* ----------------
*/
2005-10-15 02:49:52 +00:00
const char *debug_query_string; /* for pgmonitor and log_min_error_statement */
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
CommandDest whereToSendOutput = DestDebug;
/* flag for logging end of session */
2004-08-29 05:07:03 +00:00
bool Log_disconnections = false;
> >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line.
2004-04-07 05:05:50 +00:00
LogStmtLevel log_statement = LOGSTMT_NONE;
/* GUC variable for maximum stack depth (measured in kilobytes) */
int max_stack_depth = 2048;
/* wait N seconds to allow attach from a debugger */
int PostAuthDelay = 0;
/* ----------------
* private variables
* ----------------
*/
/* max_stack_depth converted to bytes for speed of checking */
2004-08-29 05:07:03 +00:00
static int max_stack_depth_bytes = 2048 * 1024;
2006-02-17 03:29:02 +00:00
/*
* Stack base pointer -- initialized by PostgresMain. This is not static
* so that PL/Java can modify it.
*/
2005-10-15 02:49:52 +00:00
char *stack_base_ptr = NULL;
/*
* 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 sig_atomic_t got_SIGHUP = false;
/*
* Flag to keep track of whether we have started a transaction.
* For extended query protocol this has to be remembered across messages.
*/
static bool xact_started = false;
/*
* Flag to indicate that we are doing the outer loop's read-from-client,
* as opposed to any random read from client that might happen within
* commands like COPY FROM STDIN.
*/
static bool DoingCommandRead = false;
/*
* Flags to implement skip-till-Sync-after-error behavior for messages of
* the extended query protocol.
*/
static bool doing_extended_query_message = false;
static bool ignore_till_sync = false;
/*
* If an unnamed prepared statement exists, it's stored here.
* We keep it separate from the hashtable kept by commands/prepare.c
* in order to reduce overhead for short-lived queries.
*/
static MemoryContext unnamed_stmt_context = NULL;
static PreparedStatement *unnamed_stmt_pstmt = NULL;
static bool EchoQuery = false; /* default don't echo */
/*
* people who want to use EOF should #define DONTUSENEWLINE in
* tcop/tcopdebug.h
*/
#ifndef TCOP_DONTUSENEWLINE
2005-10-15 02:49:52 +00:00
static int UseNewLine = 1; /* Use newlines query delimiters (the default) */
#else
static int UseNewLine = 0; /* Use EOF as query delimiters */
#endif /* TCOP_DONTUSENEWLINE */
/* ----------------------------------------------------------------
* decls for routines only used in this file
* ----------------------------------------------------------------
*/
static int InteractiveBackend(StringInfo inBuf);
static int SocketBackend(StringInfo inBuf);
static int ReadCommand(StringInfo inBuf);
static bool log_after_parse(List *raw_parsetree_list,
const char *query_string, char **prepare_string);
static List *pg_rewrite_queries(List *querytree_list);
static void start_xact_command(void);
static void finish_xact_command(void);
static bool IsTransactionExitStmt(Node *parsetree);
static bool IsTransactionExitStmtList(List *parseTrees);
static bool IsTransactionStmtList(List *parseTrees);
static void SigHupHandler(SIGNAL_ARGS);
static void log_disconnections(int code, Datum arg);
/* ----------------------------------------------------------------
* routines to obtain user input
* ----------------------------------------------------------------
*/
/* ----------------
* InteractiveBackend() is called for user interactive connections
*
* the string entered by the user is placed in its parameter inBuf,
* and we act like a Q message was received.
*
* EOF is returned if end-of-file input is seen; time to shut down.
* ----------------
*/
static int
InteractiveBackend(StringInfo inBuf)
{
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("backend> ");
fflush(stdout);
/* Reset inBuf to empty */
inBuf->len = 0;
inBuf->data[0] = '\0';
inBuf->cursor = 0;
for (;;)
{
if (UseNewLine)
{
/*
2005-10-15 02:49:52 +00:00
* if we are using \n as a delimiter, then read characters until
* the \n.
*/
while ((c = getc(stdin)) != EOF)
{
if (c == '\n')
{
if (backslashSeen)
{
/* discard backslash from inBuf */
inBuf->data[--inBuf->len] = '\0';
backslashSeen = false;
continue;
}
else
{
/* keep the newline character */
appendStringInfoChar(inBuf, '\n');
break;
}
}
else if (c == '\\')
backslashSeen = true;
else
backslashSeen = false;
appendStringInfoChar(inBuf, (char) c);
}
if (c == EOF)
end = true;
}
else
{
/*
* otherwise read characters until EOF.
*/
while ((c = getc(stdin)) != EOF)
appendStringInfoChar(inBuf, (char) c);
if (inBuf->len == 0)
end = true;
}
if (end)
return EOF;
/*
* otherwise we have a user query so process it.
*/
break;
}
/* Add '\0' to make it look the same as message case. */
appendStringInfoChar(inBuf, (char) '\0');
/*
* if the query echo flag was given, print the query..
*/
if (EchoQuery)
printf("statement: %s\n", inBuf->data);
fflush(stdout);
1998-09-01 03:29:17 +00:00
return 'Q';
}
/* ----------------
* SocketBackend() Is called for frontend-backend connections
*
* Returns the message type code, and loads message body data into inBuf.
*
* EOF is returned if the connection is lost.
* ----------------
*/
static int
SocketBackend(StringInfo inBuf)
{
int qtype;
/*
* Get message type code from the frontend.
*/
qtype = pq_getbyte();
if (qtype == EOF) /* frontend disconnected */
{
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("unexpected EOF on client connection")));
return qtype;
}
/*
2005-10-15 02:49:52 +00:00
* Validate message type code before trying to read body; if we have lost
* sync, better to say "command unknown" than to run out of memory because
* we used garbage as a length word.
*
* This also gives us a place to set the doing_extended_query_message flag
* as soon as possible.
*/
switch (qtype)
{
case 'Q': /* simple query */
doing_extended_query_message = false;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
/* old style without length word; convert */
if (pq_getstring(inBuf))
{
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("unexpected EOF on client connection")));
return EOF;
}
}
break;
case 'F': /* fastpath function call */
/* we let fastpath.c cope with old-style input of this */
doing_extended_query_message = false;
break;
case 'X': /* terminate */
doing_extended_query_message = false;
ignore_till_sync = false;
break;
case 'B': /* bind */
case 'C': /* close */
case 'D': /* describe */
case 'E': /* execute */
case 'H': /* flush */
case 'P': /* parse */
doing_extended_query_message = true;
/* these are only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid frontend message type %d", qtype)));
break;
case 'S': /* sync */
/* stop any active skip-till-Sync */
ignore_till_sync = false;
/* mark not-extended, so that a new error doesn't begin skip */
doing_extended_query_message = false;
/* only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid frontend message type %d", qtype)));
break;
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
doing_extended_query_message = false;
/* these are only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid frontend message type %d", qtype)));
break;
default:
2003-08-04 00:43:34 +00:00
/*
2005-10-15 02:49:52 +00:00
* Otherwise we got garbage from the frontend. We treat this as
* fatal because we have probably lost message boundary sync, and
* there's no good way to recover.
*/
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("invalid frontend message type %d", qtype)));
break;
}
/*
2005-10-15 02:49:52 +00:00
* In protocol version 3, all frontend messages have a length word next
* after the type code; we can read the message contents independently of
* the type.
*/
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
if (pq_getmessage(inBuf, 0))
return EOF; /* suitable message already logged */
}
return qtype;
}
/* ----------------
* ReadCommand reads a command from either the frontend or
* standard input, places it in inBuf, and returns the
* message type code (first byte of the message).
* EOF is returned if end of file.
* ----------------
*/
static int
ReadCommand(StringInfo inBuf)
{
int result;
if (whereToSendOutput == DestRemote)
result = SocketBackend(inBuf);
else
result = InteractiveBackend(inBuf);
return result;
}
/*
* prepare_for_client_read -- set up to possibly block on client input
*
* This must be called immediately before any low-level read from the
* client connection. It is necessary to do it at a sufficiently low level
* that there won't be any other operations except the read kernel call
* itself between this call and the subsequent client_read_ended() call.
* In particular there mustn't be use of malloc() or other potentially
* non-reentrant libc functions. This restriction makes it safe for us
* to allow interrupt service routines to execute nontrivial code while
* we are waiting for input.
*/
void
prepare_for_client_read(void)
{
if (DoingCommandRead)
{
/* Enable immediate processing of asynchronous signals */
EnableNotifyInterrupt();
EnableCatchupInterrupt();
/* Allow "die" interrupt to be processed while waiting */
ImmediateInterruptOK = true;
/* And don't forget to detect one that already arrived */
QueryCancelPending = false;
CHECK_FOR_INTERRUPTS();
}
}
/*
* client_read_ended -- get out of the client-input state
*/
void
client_read_ended(void)
{
if (DoingCommandRead)
{
ImmediateInterruptOK = false;
QueryCancelPending = false; /* forget any CANCEL signal */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
}
}
/*
* 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.
*
* NOTE: this routine is no longer used for processing interactive queries,
* but it is still needed for parsing of SQL function bodies.
*/
List *
2003-08-04 00:43:34 +00:00
pg_parse_and_rewrite(const char *query_string, /* string to execute */
Oid *paramTypes, /* parameter types */
2003-08-04 00:43:34 +00:00
int numParams) /* number of parameters */
{
List *raw_parsetree_list;
List *querytree_list;
ListCell *list_item;
/*
* (1) parse the request string into a list of raw parse trees.
*/
raw_parsetree_list = pg_parse_query(query_string);
/*
* (2) Do parse analysis and rule rewrite.
*/
querytree_list = NIL;
foreach(list_item, raw_parsetree_list)
{
2001-03-22 04:01:46 +00:00
Node *parsetree = (Node *) lfirst(list_item);
querytree_list = list_concat(querytree_list,
2004-08-29 05:07:03 +00:00
pg_analyze_and_rewrite(parsetree,
query_string,
2004-08-29 05:07:03 +00:00
paramTypes,
numParams));
}
return querytree_list;
}
/*
* Do raw parsing (only).
*
* A list of parsetrees is returned, since there might be multiple
* commands in the given string.
*
* NOTE: for interactive queries, it is important to keep this routine
* separate from the analysis & rewrite stages. Analysis and rewriting
* cannot be done in an aborted transaction, since they require access to
* database tables. So, we rely on the raw parser to determine whether
* we've seen a COMMIT or ABORT command; when we are in abort state, other
* commands are not processed any further than the raw parse stage.
*/
List *
pg_parse_query(const char *query_string)
{
List *raw_parsetree_list;
if (log_parser_stats)
ResetUsage();
raw_parsetree_list = raw_parser(query_string);
if (log_parser_stats)
ShowUsage("PARSER STATISTICS");
return raw_parsetree_list;
}
static bool
log_after_parse(List *raw_parsetree_list, const char *query_string,
2005-10-15 02:49:52 +00:00
char **prepare_string)
{
ListCell *parsetree_item;
bool log_this_statement = (log_statement == LOGSTMT_ALL);
*prepare_string = NULL;
2005-10-15 02:49:52 +00:00
/* Check if we need to log the statement, and get prepare_string. */
foreach(parsetree_item, raw_parsetree_list)
> >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line.
2004-04-07 05:05:50 +00:00
{
Node *parsetree = (Node *) lfirst(parsetree_item);
const char *commandTag;
if (IsA(parsetree, ExplainStmt) &&
((ExplainStmt *) parsetree)->analyze)
parsetree = (Node *) (((ExplainStmt *) parsetree)->query);
if (IsA(parsetree, PrepareStmt))
parsetree = (Node *) (((PrepareStmt *) parsetree)->query);
if (IsA(parsetree, SelectStmt) &&
((SelectStmt *) parsetree)->into == NULL)
2005-10-15 02:49:52 +00:00
continue; /* optimization for frequent command */
if (log_statement == LOGSTMT_MOD &&
(IsA(parsetree, InsertStmt) ||
IsA(parsetree, UpdateStmt) ||
IsA(parsetree, DeleteStmt) ||
IsA(parsetree, TruncateStmt) ||
(IsA(parsetree, CopyStmt) &&
2005-10-15 02:49:52 +00:00
((CopyStmt *) parsetree)->is_from))) /* COPY FROM */
log_this_statement = true;
commandTag = CreateCommandTag(parsetree);
if ((log_statement == LOGSTMT_MOD ||
log_statement == LOGSTMT_DDL) &&
(strncmp(commandTag, "CREATE ", strlen("CREATE ")) == 0 ||
2005-10-15 02:49:52 +00:00
IsA(parsetree, SelectStmt) || /* SELECT INTO, CREATE AS */
strncmp(commandTag, "ALTER ", strlen("ALTER ")) == 0 ||
strncmp(commandTag, "DROP ", strlen("DROP ")) == 0 ||
2005-10-15 02:49:52 +00:00
IsA(parsetree, GrantStmt) || /* GRANT or REVOKE */
IsA(parsetree, CommentStmt)))
log_this_statement = true;
/*
2005-10-15 02:49:52 +00:00
* For the first EXECUTE we find, record the client statement used by
* the PREPARE. PREPARE doesn't save the parse tree so we have no
* way to conditionally output based on the type of query prepared.
*/
if (IsA(parsetree, ExecuteStmt))
> >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line.
2004-04-07 05:05:50 +00:00
{
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
PreparedStatement *entry;
if (*prepare_string == NULL &&
(entry = FetchPreparedStatement(stmt->name, false)) != NULL &&
entry->query_string)
> >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line.
2004-04-07 05:05:50 +00:00
{
*prepare_string = palloc(strlen(entry->query_string) +
strlen(" [PREPARE: %s]") - 2 + 1);
sprintf(*prepare_string, " [PREPARE: %s]",
2005-10-15 02:49:52 +00:00
entry->query_string);
> >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line.
2004-04-07 05:05:50 +00:00
}
}
}
2005-10-15 02:49:52 +00:00
if (log_this_statement)
{
ereport(LOG,
(errmsg("statement: %s%s", query_string,
*prepare_string ? *prepare_string : "")));
return true;
}
else
return false;
}
/*
* Given a raw parsetree (gram.y output), and optionally information about
* types of parameter symbols ($n), perform parse analysis and rule rewriting.
*
* A list of Query nodes is returned, since either the analyzer or the
* rewriter might expand one query to several.
*
* NOTE: for reasons mentioned above, this must be separate from raw parsing.
*/
List *
pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
Oid *paramTypes, int numParams)
{
List *querytree_list;
/*
* (1) Perform parse analysis.
*/
if (log_parser_stats)
ResetUsage();
querytree_list = parse_analyze(parsetree, query_string,
paramTypes, numParams);
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
/*
* (2) Rewrite the queries, as necessary
*/
querytree_list = pg_rewrite_queries(querytree_list);
return querytree_list;
}
/*
* Perform rewriting of a list of queries produced by parse analysis.
*
* Note: queries must just have come from the parser, because we do not do
* AcquireRewriteLocks() on them.
*/
static List *
pg_rewrite_queries(List *querytree_list)
{
List *new_list = NIL;
ListCell *list_item;
if (log_parser_stats)
ResetUsage();
/*
2005-10-15 02:49:52 +00:00
* rewritten queries are collected in new_list. Note there may be more or
* fewer than in the original list.
*/
foreach(list_item, querytree_list)
{
Query *querytree = (Query *) lfirst(list_item);
if (Debug_print_parse)
elog_node_display(DEBUG1, "parse tree", querytree,
Debug_pretty_print);
if (querytree->commandType == CMD_UTILITY)
{
/* don't rewrite utilities, just dump 'em into new_list */
new_list = lappend(new_list, querytree);
}
else
{
/* rewrite regular queries */
List *rewritten = QueryRewrite(querytree);
new_list = list_concat(new_list, rewritten);
}
}
querytree_list = new_list;
if (log_parser_stats)
ShowUsage("REWRITER STATISTICS");
#ifdef COPY_PARSE_PLAN_TREES
2001-03-22 04:01:46 +00:00
/*
2005-10-15 02:49:52 +00:00
* Optional debugging check: pass querytree output through copyObject()
2001-03-22 04:01:46 +00:00
*/
new_list = (List *) copyObject(querytree_list);
/* This checks both copyObject() and the equal() routines... */
2001-03-22 04:01:46 +00:00
if (!equal(new_list, querytree_list))
elog(WARNING, "copyObject() failed to produce an equal parse tree");
else
querytree_list = new_list;
#endif
if (Debug_print_rewritten)
elog_node_display(DEBUG1, "rewritten parse tree", querytree_list,
Debug_pretty_print);
return querytree_list;
}
/* Generate a plan for a single already-rewritten query. */
Plan *
pg_plan_query(Query *querytree, ParamListInfo boundParams)
{
Plan *plan;
/* Utility commands have no plans. */
if (querytree->commandType == CMD_UTILITY)
return NULL;
if (log_planner_stats)
ResetUsage();
/* call the optimizer */
plan = planner(querytree, false, 0, boundParams);
if (log_planner_stats)
ShowUsage("PLANNER STATISTICS");
#ifdef COPY_PARSE_PLAN_TREES
/* Optional debugging check: pass plan output through copyObject() */
{
2001-03-22 04:01:46 +00:00
Plan *new_plan = (Plan *) copyObject(plan);
2001-03-22 04:01:46 +00:00
/*
2005-10-15 02:49:52 +00:00
* 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... */
2001-03-22 04:01:46 +00:00
if (!equal(new_plan, plan))
elog(WARNING, "copyObject() failed to produce an equal plan tree");
else
#endif
plan = new_plan;
}
#endif
/*
* Print plan if debugging.
*/
if (Debug_print_plan)
elog_node_display(DEBUG1, "plan", plan, Debug_pretty_print);
This is the final state of the rule system for 6.4 after the patch is applied: Rewrite rules on relation level work fine now. Event qualifications on insert/update/delete rules work fine now. I added the new keyword OLD to reference the CURRENT tuple. CURRENT will be removed in 6.5. Update rules can reference NEW and OLD in the rule qualification and the actions. Insert/update/delete rules on views can be established to let them behave like real tables. For insert/update/delete rules multiple actions are supported now. The actions can also be surrounded by parantheses to make psql happy. Multiple actions are required if update to a view requires updates to multiple tables. Regular users are permitted to create/drop rules on tables they have RULE permissions for (DefineQueryRewrite() is now able to get around the access restrictions on pg_rewrite). This enables view creation for regular users too. This required an extra boolean parameter to pg_parse_and_plan() that tells to set skipAcl on all rangetable entries of the resulting queries. There is a new function pg_exec_query_acl_override() that could be used by backend utilities to use this facility. All rule actions (not only views) inherit the permissions of the event relations owner. Sample: User A creates tables T1 and T2, creates rules that log INSERT/UPDATE/DELETE on T1 in T2 (like in the regression tests for rules I created) and grants ALL but RULE on T1 to user B. User B can now fully access T1 and the logging happens in T2. But user B cannot access T2 at all, only the rule actions can. And due to missing RULE permissions on T1, user B cannot disable logging. Rules on the attribute level are disabled (they don't work properly and since regular users are now permitted to create rules I decided to disable them). Rules on select must have exactly one action that is a select (so select rules must be a view definition). UPDATE NEW/OLD rules are disabled (still broken, but triggers can do it). There are two new system views (pg_rule and pg_view) that show the definition of the rules or views so the db admin can see what the users do. They use two new functions pg_get_ruledef() and pg_get_viewdef() that are builtins. The functions pg_get_ruledef() and pg_get_viewdef() could be used to implement rule and view support in pg_dump. PostgreSQL is now the only database system I know, that has rewrite rules on the query level. All others (where I found a rule statement at all) use stored database procedures or the like (triggers as we call them) for active rules (as some call them). Future of the rule system: The now disabled parts of the rule system (attribute level, multiple actions on select and update new stuff) require a complete new rewrite handler from scratch. The old one is too badly wired up. After 6.4 I'll start to work on a new rewrite handler, that fully supports the attribute level rules, multiple actions on select and update new. This will be available for 6.5 so we get full rewrite rule capabilities. Jan
1998-08-24 01:38:11 +00:00
return plan;
}
/*
* Generate plans for a list of already-rewritten queries.
*
* If needSnapshot is TRUE, we haven't yet set a snapshot for the current
* query. A snapshot must be set before invoking the planner, since it
* might try to evaluate user-defined functions. But we must not set a
* snapshot if the list contains only utility statements, because some
* utility statements depend on not having frozen the snapshot yet.
* (We assume that such statements cannot appear together with plannable
* statements in the rewriter's output.)
*/
List *
pg_plan_queries(List *querytrees, ParamListInfo boundParams,
bool needSnapshot)
{
List *plan_list = NIL;
ListCell *query_list;
foreach(query_list, querytrees)
{
Query *query = (Query *) lfirst(query_list);
Plan *plan;
if (query->commandType == CMD_UTILITY)
{
/* Utility commands have no plans. */
plan = NULL;
}
else
{
if (needSnapshot)
{
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
needSnapshot = false;
}
plan = pg_plan_query(query, boundParams);
}
plan_list = lappend(plan_list, plan);
}
return plan_list;
}
/*
* exec_simple_query
*
* Execute a "simple Query" protocol message.
*/
static void
exec_simple_query(const char *query_string)
{
2003-08-04 00:43:34 +00:00
CommandDest dest = whereToSendOutput;
MemoryContext oldcontext;
List *parsetree_list;
ListCell *parsetree_item;
bool save_log_statement_stats = log_statement_stats;
2005-10-15 02:49:52 +00:00
char *prepare_string = NULL;
bool was_logged = false;
2005-10-15 02:49:52 +00:00
/*
* Report query to various monitoring facilities.
*/
debug_query_string = query_string;
pgstat_report_activity(query_string);
/*
* We use save_log_statement_stats so ShowUsage doesn't report incorrect
* results because ResetUsage wasn't called.
*/
if (save_log_statement_stats)
ResetUsage();
/*
2001-03-22 04:01:46 +00:00
* Start up a transaction command. All queries generated by the
* query_string will be in this same command block, *unless* we find a
2005-10-15 02:49:52 +00:00
* BEGIN/COMMIT/ABORT statement; we have to force a new xact command after
* one of those, else bad things will happen in xact.c. (Note that this
* will normally change current memory context.)
*/
start_xact_command();
/*
2005-10-15 02:49:52 +00:00
* Zap any pre-existing unnamed statement. (While not strictly necessary,
* it seems best to define simple-Query mode as if it used the unnamed
* statement and portal; this ensures we recover any storage used by prior
* unnamed operations.)
*/
unnamed_stmt_pstmt = NULL;
if (unnamed_stmt_context)
{
DropDependentPortals(unnamed_stmt_context);
MemoryContextDelete(unnamed_stmt_context);
}
unnamed_stmt_context = NULL;
/*
* Switch to appropriate context for constructing parsetrees.
*/
oldcontext = MemoryContextSwitchTo(MessageContext);
QueryContext = CurrentMemoryContext;
1999-05-25 16:15:34 +00:00
/*
2005-10-15 02:49:52 +00:00
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
*/
parsetree_list = pg_parse_query(query_string);
/* Log immediately if dictated by log_statement */
if (log_statement != LOGSTMT_NONE)
was_logged = log_after_parse(parsetree_list, query_string,
2005-10-15 02:49:52 +00:00
&prepare_string);
/*
* Switch back to transaction context to enter the loop.
*/
MemoryContextSwitchTo(oldcontext);
/*
* Run through the raw parsetree(s) and process each one.
*/
foreach(parsetree_item, parsetree_list)
{
2001-03-22 04:01:46 +00:00
Node *parsetree = (Node *) lfirst(parsetree_item);
const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
2001-03-22 04:01:46 +00:00
List *querytree_list,
*plantree_list;
Portal portal;
DestReceiver *receiver;
int16 format;
/*
2005-10-15 02:49:52 +00:00
* Get the command name for use in status display (it also becomes the
* default completion tag, down inside PortalRun). Set ps_status and
* do any special start-of-SQL-command processing needed by the
* destination.
*/
commandTag = CreateCommandTag(parsetree);
set_ps_display(commandTag, false);
BeginCommand(commandTag, dest);
/*
* If we are in an aborted transaction, reject all commands except
2005-10-15 02:49:52 +00:00
* COMMIT/ABORT. It is important that this test occur before we try
* to do parse analysis, rewrite, or planning, since all those phases
* try to do database accesses, which may fail in abort state. (It
* might be safe to allow some additional utility commands in this
* state, but not many...)
*/
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(parsetree))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
2005-10-15 02:49:52 +00:00
"commands ignored until end of transaction block")));
/* Make sure we are in a transaction command */
start_xact_command();
/* If we got a cancel signal in parsing or prior command, quit */
CHECK_FOR_INTERRUPTS();
/*
* OK to analyze, rewrite, and plan this query.
*
2001-03-22 04:01:46 +00:00
* Switch to appropriate context for constructing querytrees (again,
* these must outlive the execution context).
*/
oldcontext = MemoryContextSwitchTo(MessageContext);
querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
NULL, 0);
1998-12-16 11:53:55 +00:00
plantree_list = pg_plan_queries(querytree_list, NULL, true);
/* If we got a cancel signal in analysis or planning, quit */
CHECK_FOR_INTERRUPTS();
/*
2003-08-04 00:43:34 +00:00
* Create unnamed portal to run the query or queries in. If there
* already is one, silently drop it.
*/
portal = CreatePortal("", true, true);
/* Don't display the portal in pg_cursors */
portal->visible = false;
PortalDefineQuery(portal,
query_string,
commandTag,
querytree_list,
plantree_list,
MessageContext);
/*
* Start the portal. No parameters here.
*/
PortalStart(portal, NULL, InvalidSnapshot);
/*
2005-10-15 02:49:52 +00:00
* Select the appropriate output format: text unless we are doing a
* FETCH from a binary cursor. (Pretty grotty to have to do this here
* --- but it avoids grottiness in other places. Ah, the joys of
* backward compatibility...)
*/
format = 0; /* TEXT is default */
if (IsA(parsetree, FetchStmt))
{
FetchStmt *stmt = (FetchStmt *) parsetree;
if (!stmt->ismove)
{
Portal fportal = GetPortalByName(stmt->portalname);
if (PortalIsValid(fportal) &&
(fportal->cursorOptions & CURSOR_OPT_BINARY))
2003-08-04 00:43:34 +00:00
format = 1; /* BINARY */
}
}
PortalSetResultFormat(portal, 1, &format);
/*
* Now we can create the destination receiver object.
*/
receiver = CreateDestReceiver(dest, portal);
/*
* Switch back to transaction context for execution.
*/
MemoryContextSwitchTo(oldcontext);
/*
2005-10-15 02:49:52 +00:00
* Run the portal to completion, and then drop it (and the receiver).
*/
(void) PortalRun(portal,
FETCH_ALL,
receiver,
receiver,
completionTag);
(*receiver->rDestroy) (receiver);
PortalDrop(portal, false);
if (IsA(parsetree, TransactionStmt))
{
/*
2005-10-15 02:49:52 +00:00
* If this was a transaction control statement, commit it. We will
* start a new xact command for the next command (if any).
*/
finish_xact_command();
}
else if (lnext(parsetree_item) == NULL)
{
/*
2005-10-15 02:49:52 +00:00
* If this is the last parsetree of the query string, close down
* transaction statement before reporting command-complete. This
* is so that any end-of-transaction errors are reported before
* the command-complete message is issued, to avoid confusing
* clients who will expect either a command-complete message or an
* error, not one and then the other. But for compatibility with
* historical Postgres behavior, we do not force a transaction
* boundary between queries appearing in a single query string.
*/
finish_xact_command();
}
else
{
/*
2003-08-04 00:43:34 +00:00
* We need a CommandCounterIncrement after every query, except
* those that start or end a transaction block.
*/
CommandCounterIncrement();
}
/*
2005-10-15 02:49:52 +00:00
* Tell client that we're done with this query. Note we emit exactly
* one EndCommand report for each raw parsetree, thus one for each SQL
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
*/
EndCommand(completionTag, dest);
2001-03-22 04:01:46 +00:00
} /* end loop over parsetrees */
/*
* Close down transaction statement, if one is open.
*/
finish_xact_command();
/*
* If there were no parsetrees, return EmptyQueryResponse message.
*/
if (!parsetree_list)
NullCommand(dest);
QueryContext = NULL;
/*
* Emit duration logging if appropriate.
*/
if (log_duration || log_min_duration_statement >= 0)
{
long secs;
int usecs;
int msecs;
2003-08-04 00:43:34 +00:00
TimestampDifference(GetCurrentStatementStartTimestamp(),
GetCurrentTimestamp(),
&secs, &usecs);
msecs = usecs / 1000;
2003-08-04 00:43:34 +00:00
/*
* The odd-looking test for log_min_duration_statement being
* exceeded is designed to avoid integer overflow with very
* long durations: don't compute secs * 1000 until we've
* verified it will fit in int.
*/
if (log_duration ||
log_min_duration_statement == 0 ||
(log_min_duration_statement > 0 &&
(secs > log_min_duration_statement / 1000 ||
secs * 1000 + msecs >= log_min_duration_statement)))
{
if (was_logged)
ereport(LOG,
(errmsg("duration: %ld.%03d ms",
secs, msecs)));
else
ereport(LOG,
(errmsg("duration: %ld.%03d ms statement: %s%s",
secs, msecs,
query_string,
prepare_string ? prepare_string : "")));
}
}
if (save_log_statement_stats)
ShowUsage("QUERY STATISTICS");
if (prepare_string != NULL)
pfree(prepare_string);
debug_query_string = NULL;
}
/*
* exec_parse_message
*
* Execute a "Parse" protocol message.
*/
static void
exec_parse_message(const char *query_string, /* string to execute */
const char *stmt_name, /* name for prepared stmt */
2003-08-04 00:43:34 +00:00
Oid *paramTypes, /* parameter types */
int numParams) /* number of parameters */
{
MemoryContext oldcontext;
List *parsetree_list;
const char *commandTag;
List *querytree_list,
*plantree_list,
*param_list;
bool is_named;
bool save_log_statement_stats = log_statement_stats;
/*
* Report query to various monitoring facilities.
*/
debug_query_string = query_string;
pgstat_report_activity(query_string);
set_ps_display("PARSE", false);
if (save_log_statement_stats)
ResetUsage();
if (log_statement == LOGSTMT_ALL)
ereport(LOG,
(errmsg("statement: [protocol] PREPARE %s AS %s",
(*stmt_name != '\0') ? stmt_name : "<unnamed>",
query_string)));
/*
2005-10-15 02:49:52 +00:00
* Start up a transaction command so we can run parse analysis etc. (Note
* that this will normally change current memory context.) Nothing happens
* if we are already in one.
*/
start_xact_command();
/*
* Switch to appropriate context for constructing parsetrees.
*
2003-08-04 00:43:34 +00:00
* We have two strategies depending on whether the prepared statement is
* named or not. For a named prepared statement, we do parsing in
* MessageContext and copy the finished trees into the prepared
2005-10-15 02:49:52 +00:00
* statement's private context; then the reset of MessageContext releases
* temporary space used by parsing and planning. For an unnamed prepared
* statement, we assume the statement isn't going to hang around long, so
* getting rid of temp space quickly is probably not worth the costs of
* copying parse/plan trees. So in this case, we set up a special context
* for the unnamed statement, and do all the parsing/planning therein.
*/
is_named = (stmt_name[0] != '\0');
if (is_named)
{
/* Named prepared statement --- parse in MessageContext */
oldcontext = MemoryContextSwitchTo(MessageContext);
}
else
{
/* Unnamed prepared statement --- release any prior unnamed stmt */
unnamed_stmt_pstmt = NULL;
if (unnamed_stmt_context)
{
DropDependentPortals(unnamed_stmt_context);
MemoryContextDelete(unnamed_stmt_context);
}
unnamed_stmt_context = NULL;
/* create context for parsing/planning */
unnamed_stmt_context =
AllocSetContextCreate(TopMemoryContext,
"unnamed prepared statement",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
}
QueryContext = CurrentMemoryContext;
/*
2005-10-15 02:49:52 +00:00
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
*/
parsetree_list = pg_parse_query(query_string);
/*
2005-10-15 02:49:52 +00:00
* We only allow a single user statement in a prepared statement. This is
* mainly to keep the protocol simple --- otherwise we'd need to worry
* about multiple result tupdescs and things like that.
*/
if (list_length(parsetree_list) > 1)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
2005-10-15 02:49:52 +00:00
errmsg("cannot insert multiple commands into a prepared statement")));
if (parsetree_list != NIL)
{
Node *parsetree = (Node *) linitial(parsetree_list);
2003-08-04 00:43:34 +00:00
int i;
/*
* Get the command name for possible use in status display.
*/
commandTag = CreateCommandTag(parsetree);
/*
* If we are in an aborted transaction, reject all commands except
2005-10-15 02:49:52 +00:00
* COMMIT/ROLLBACK. It is important that this test occur before we
* try to do parse analysis, rewrite, or planning, since all those
* phases try to do database accesses, which may fail in abort state.
* (It might be safe to allow some additional utility commands in this
* state, but not many...)
*/
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(parsetree))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
2005-10-15 02:49:52 +00:00
"commands ignored until end of transaction block")));
/*
* OK to analyze, rewrite, and plan this query. Note that the
2005-10-15 02:49:52 +00:00
* originally specified parameter set is not required to be complete,
* so we have to use parse_analyze_varparams().
*/
if (log_parser_stats)
ResetUsage();
querytree_list = parse_analyze_varparams(parsetree,
query_string,
&paramTypes,
&numParams);
/*
* Check all parameter types got determined, and convert array
* representation to a list for storage.
*/
param_list = NIL;
for (i = 0; i < numParams; i++)
{
2003-08-04 00:43:34 +00:00
Oid ptype = paramTypes[i];
if (ptype == InvalidOid || ptype == UNKNOWNOID)
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
2005-10-15 02:49:52 +00:00
errmsg("could not determine data type of parameter $%d",
i + 1)));
param_list = lappend_oid(param_list, ptype);
}
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
querytree_list = pg_rewrite_queries(querytree_list);
/*
2005-10-15 02:49:52 +00:00
* If this is the unnamed statement and it has parameters, defer query
* planning until Bind. Otherwise do it now.
*/
if (!is_named && numParams > 0)
plantree_list = NIL;
else
plantree_list = pg_plan_queries(querytree_list, NULL, true);
}
else
{
2003-08-04 00:43:34 +00:00
/* Empty input string. This is legal. */
commandTag = NULL;
querytree_list = NIL;
plantree_list = NIL;
param_list = NIL;
}
/* If we got a cancel signal in analysis or planning, quit */
CHECK_FOR_INTERRUPTS();
/*
* Store the query as a prepared statement. See above comments.
*/
if (is_named)
{
StorePreparedStatement(stmt_name,
query_string,
commandTag,
querytree_list,
plantree_list,
param_list,
false);
}
else
{
PreparedStatement *pstmt;
pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement));
/* query_string needs to be copied into unnamed_stmt_context */
pstmt->query_string = pstrdup(query_string);
/* the rest is there already */
pstmt->commandTag = commandTag;
pstmt->query_list = querytree_list;
pstmt->plan_list = plantree_list;
pstmt->argtype_list = param_list;
pstmt->from_sql = false;
pstmt->context = unnamed_stmt_context;
/* Now the unnamed statement is complete and valid */
unnamed_stmt_pstmt = pstmt;
}
MemoryContextSwitchTo(oldcontext);
QueryContext = NULL;
/*
2005-10-15 02:49:52 +00:00
* We do NOT close the open transaction command here; that only happens
* when the client sends Sync. Instead, do CommandCounterIncrement just
* in case something happened during parse/plan.
*/
CommandCounterIncrement();
/*
* Send ParseComplete.
*/
if (whereToSendOutput == DestRemote)
pq_putemptymessage('1');
if (save_log_statement_stats)
ShowUsage("PARSE MESSAGE STATISTICS");
debug_query_string = NULL;
}
/*
* exec_bind_message
*
* Process a "Bind" message to create a portal from a prepared statement
*/
static void
exec_bind_message(StringInfo input_message)
{
const char *portal_name;
const char *stmt_name;
int numPFormats;
int16 *pformats = NULL;
int numParams;
int numRFormats;
int16 *rformats = NULL;
int i;
PreparedStatement *pstmt;
Portal portal;
ParamListInfo params;
pgstat_report_activity("<BIND>");
set_ps_display("BIND", false);
/*
2005-10-15 02:49:52 +00:00
* Start up a transaction command so we can call functions etc. (Note that
* this will normally change current memory context.) Nothing happens if
* we are already in one.
*/
start_xact_command();
/* Switch back to message context */
MemoryContextSwitchTo(MessageContext);
/* Get the fixed part of the message */
portal_name = pq_getmsgstring(input_message);
stmt_name = pq_getmsgstring(input_message);
/* Get the parameter format codes */
numPFormats = pq_getmsgint(input_message, 2);
if (numPFormats > 0)
{
pformats = (int16 *) palloc(numPFormats * sizeof(int16));
for (i = 0; i < numPFormats; i++)
pformats[i] = pq_getmsgint(input_message, 2);
}
/* Get the parameter value count */
numParams = pq_getmsgint(input_message, 2);
if (numPFormats > 1 && numPFormats != numParams)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("bind message has %d parameter formats but %d parameters",
numPFormats, numParams)));
/* Find prepared statement */
if (stmt_name[0] != '\0')
pstmt = FetchPreparedStatement(stmt_name, true);
else
{
/* special-case the unnamed statement */
pstmt = unnamed_stmt_pstmt;
if (!pstmt)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
2005-10-15 02:49:52 +00:00
errmsg("unnamed prepared statement does not exist")));
}
if (numParams != list_length(pstmt->argtype_list))
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
2005-10-15 02:49:52 +00:00
numParams, stmt_name, list_length(pstmt->argtype_list))));
/*
* If we are in aborted transaction state, the only portals we can
* actually run are those containing COMMIT or ROLLBACK commands. We
* disallow binding anything else to avoid problems with infrastructure
* that expects to run inside a valid transaction. We also disallow
* binding any parameters, since we can't risk calling user-defined I/O
* functions.
*/
if (IsAbortedTransactionBlockState() &&
(!IsTransactionExitStmtList(pstmt->query_list) ||
numParams != 0))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block")));
/*
2005-10-15 02:49:52 +00:00
* Create the portal. Allow silent replacement of an existing portal only
* if the unnamed portal is specified.
*/
if (portal_name[0] == '\0')
portal = CreatePortal(portal_name, true, true);
else
portal = CreatePortal(portal_name, false, false);
/* We need to output the parameter values someday */
if (log_statement == LOGSTMT_ALL)
ereport(LOG,
(errmsg("statement: [protocol] <BIND> %s", portal_name)));
/*
* Fetch parameters, if any, and store in the portal's memory context.
*/
if (numParams > 0)
{
ListCell *l;
MemoryContext oldContext;
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
/* sizeof(ParamListInfoData) includes the first array element */
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(numParams - 1) * sizeof(ParamExternData));
params->numParams = numParams;
i = 0;
foreach(l, pstmt->argtype_list)
{
Oid ptype = lfirst_oid(l);
int32 plength;
bool isNull;
StringInfoData pbuf;
char csave;
int16 pformat;
plength = pq_getmsgint(input_message, 4);
isNull = (plength == -1);
if (!isNull)
{
const char *pvalue = pq_getmsgbytes(input_message, plength);
/*
* Rather than copying data around, we just set up a phony
* StringInfo pointing to the correct portion of the message
* buffer. We assume we can scribble on the message buffer so
* as to maintain the convention that StringInfos have a
* trailing null. This is grotty but is a big win when
* dealing with very large parameter strings.
*/
pbuf.data = (char *) pvalue;
pbuf.maxlen = plength + 1;
pbuf.len = plength;
pbuf.cursor = 0;
csave = pbuf.data[plength];
pbuf.data[plength] = '\0';
}
else
{
pbuf.data = NULL; /* keep compiler quiet */
csave = 0;
}
if (numPFormats > 1)
pformat = pformats[i];
else if (numPFormats > 0)
pformat = pformats[0];
else
pformat = 0; /* default = text */
if (pformat == 0)
{
Oid typinput;
Oid typioparam;
char *pstring;
getTypeInputInfo(ptype, &typinput, &typioparam);
/*
* We have to do encoding conversion before calling the
* typinput routine.
*/
if (isNull)
pstring = NULL;
else
pstring = pg_client_to_server(pbuf.data, plength);
params->params[i].value = OidInputFunctionCall(typinput,
pstring,
typioparam,
-1);
/* Free result of encoding conversion, if any */
if (pstring && pstring != pbuf.data)
pfree(pstring);
}
else if (pformat == 1)
{
Oid typreceive;
Oid typioparam;
StringInfo bufptr;
/*
* Call the parameter type's binary input converter
*/
getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
if (isNull)
bufptr = NULL;
else
bufptr = &pbuf;
params->params[i].value = OidReceiveFunctionCall(typreceive,
bufptr,
typioparam,
-1);
/* Trouble if it didn't eat the whole buffer */
if (!isNull && pbuf.cursor != pbuf.len)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("incorrect binary data format in bind parameter %d",
i + 1)));
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unsupported format code: %d",
pformat)));
}
/* Restore message buffer contents */
if (!isNull)
pbuf.data[plength] = csave;
params->params[i].isnull = isNull;
params->params[i].ptype = ptype;
i++;
}
MemoryContextSwitchTo(oldContext);
}
else
params = NULL;
/* Get the result format codes */
numRFormats = pq_getmsgint(input_message, 2);
if (numRFormats > 0)
{
rformats = (int16 *) palloc(numRFormats * sizeof(int16));
for (i = 0; i < numRFormats; i++)
rformats[i] = pq_getmsgint(input_message, 2);
}
pq_getmsgend(input_message);
/*
2005-10-15 02:49:52 +00:00
* If we didn't plan the query before, do it now. This allows the planner
* to make use of the concrete parameter values we now have.
*
2004-08-29 05:07:03 +00:00
* This happens only for unnamed statements, and so switching into the
* statement context for planning is correct (see notes in
* exec_parse_message).
*/
if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
{
MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true);
MemoryContextSwitchTo(oldContext);
}
/*
* Define portal and start execution.
*/
PortalDefineQuery(portal,
pstmt->query_string,
pstmt->commandTag,
pstmt->query_list,
pstmt->plan_list,
pstmt->context);
PortalStart(portal, params, InvalidSnapshot);
/*
* Apply the result format requests to the portal.
*/
PortalSetResultFormat(portal, numRFormats, rformats);
/*
* Send BindComplete.
*/
if (whereToSendOutput == DestRemote)
pq_putemptymessage('2');
}
/*
* exec_execute_message
*
* Process an "Execute" message for a portal
*/
static void
exec_execute_message(const char *portal_name, long max_rows)
{
2003-08-04 00:43:34 +00:00
CommandDest dest;
DestReceiver *receiver;
Portal portal;
bool completed;
char completionTag[COMPLETION_TAG_BUFSIZE];
bool save_log_statement_stats = log_statement_stats;
bool execute_is_fetch = false;
/* Adjust destination to tell printtup.c what to do */
dest = whereToSendOutput;
if (dest == DestRemote)
dest = DestRemoteExecute;
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("portal \"%s\" does not exist", portal_name)));
/*
2005-10-15 02:49:52 +00:00
* If we re-issue an Execute protocol request against an existing portal,
* then we are only fetching more rows rather than completely re-executing
* the query from the start. atStart is never reset for a v3 portal, so we
* are safe to use this check.
*/
if (!portal->atStart)
execute_is_fetch = true;
/*
2003-08-04 00:43:34 +00:00
* If the original query was a null string, just return
* EmptyQueryResponse.
*/
if (portal->commandTag == NULL)
{
Assert(portal->parseTrees == NIL);
NullCommand(dest);
return;
}
/* Should we display the portal names here? */
if (execute_is_fetch)
{
debug_query_string = "fetch message";
pgstat_report_activity("<FETCH>");
}
else if (portal->sourceText)
{
debug_query_string = portal->sourceText;
pgstat_report_activity(portal->sourceText);
}
else
{
debug_query_string = "execute message";
pgstat_report_activity("<EXECUTE>");
}
set_ps_display(portal->commandTag, false);
/*
* We use save_log_statement_stats so ShowUsage doesn't report incorrect
* results because ResetUsage wasn't called.
*/
if (save_log_statement_stats)
ResetUsage();
if (log_statement == LOGSTMT_ALL)
/* We have the portal, so output the source query. */
ereport(LOG,
(errmsg("statement: [protocol] %sEXECUTE %s [PREPARE: %s]",
execute_is_fetch ? "FETCH from " : "",
(*portal_name) ? portal_name : "<unnamed>",
portal->sourceText ? portal->sourceText : "")));
BeginCommand(portal->commandTag, dest);
/*
2005-10-15 02:49:52 +00:00
* Create dest receiver in MessageContext (we don't want it in transaction
* context, because that may get deleted if portal contains VACUUM).
*/
receiver = CreateDestReceiver(dest, portal);
/*
2003-08-04 00:43:34 +00:00
* Ensure we are in a transaction command (this should normally be the
* case already due to prior BIND).
*/
start_xact_command();
/*
* If we are in aborted transaction state, the only portals we can
* actually run are those containing COMMIT or ROLLBACK commands.
*/
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmtList(portal->parseTrees))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
2005-10-15 02:49:52 +00:00
"commands ignored until end of transaction block")));
/* Check for cancel signal before we start execution */
CHECK_FOR_INTERRUPTS();
/*
* Okay to run the portal.
*/
if (max_rows <= 0)
max_rows = FETCH_ALL;
completed = PortalRun(portal,
max_rows,
receiver,
receiver,
completionTag);
(*receiver->rDestroy) (receiver);
if (completed)
{
if (IsTransactionStmtList(portal->parseTrees))
{
/*
2003-08-04 00:43:34 +00:00
* If this was a transaction control statement, commit it. We
2005-10-15 02:49:52 +00:00
* will start a new xact command for the next command (if any).
*/
finish_xact_command();
}
else
{
/*
2003-08-04 00:43:34 +00:00
* We need a CommandCounterIncrement after every query, except
* those that start or end a transaction block.
*/
CommandCounterIncrement();
}
/* Send appropriate CommandComplete to client */
EndCommand(completionTag, dest);
}
else
{
/* Portal run not complete, so send PortalSuspended */
if (whereToSendOutput == DestRemote)
pq_putemptymessage('s');
}
/*
* Emit duration logging if appropriate.
*/
if (log_duration || log_min_duration_statement >= 0)
{
long secs;
int usecs;
int msecs;
TimestampDifference(GetCurrentStatementStartTimestamp(),
GetCurrentTimestamp(),
&secs, &usecs);
msecs = usecs / 1000;
/*
* The odd-looking test for log_min_duration_statement being
* exceeded is designed to avoid integer overflow with very
* long durations: don't compute secs * 1000 until we've
* verified it will fit in int.
*/
if (log_duration ||
log_min_duration_statement == 0 ||
(log_min_duration_statement > 0 &&
(secs > log_min_duration_statement / 1000 ||
secs * 1000 + msecs >= log_min_duration_statement)))
{
if (log_statement == LOGSTMT_ALL) /* already logged? */
ereport(LOG,
(errmsg("duration: %ld.%03d ms",
secs, msecs)));
else
ereport(LOG,
(errmsg("duration: %ld.%03d ms statement: [protocol] %sEXECUTE %s [PREPARE: %s]",
secs, msecs,
execute_is_fetch ? "FETCH from " : "",
(*portal_name) ? portal_name : "<unnamed>",
portal->sourceText ? portal->sourceText : "")));
}
}
if (save_log_statement_stats)
ShowUsage("QUERY STATISTICS");
debug_query_string = NULL;
}
/*
* exec_describe_statement_message
*
* Process a "Describe" message for a prepared statement
*/
static void
exec_describe_statement_message(const char *stmt_name)
{
PreparedStatement *pstmt;
TupleDesc tupdesc;
ListCell *l;
StringInfoData buf;
/*
* Start up a transaction command. (Note that this will normally change
* current memory context.) Nothing happens if we are already in one.
*/
start_xact_command();
/* Switch back to message context */
MemoryContextSwitchTo(MessageContext);
/* Find prepared statement */
if (stmt_name[0] != '\0')
pstmt = FetchPreparedStatement(stmt_name, true);
else
{
/* special-case the unnamed statement */
pstmt = unnamed_stmt_pstmt;
if (!pstmt)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
2005-10-15 02:49:52 +00:00
errmsg("unnamed prepared statement does not exist")));
}
/*
* If we are in aborted transaction state, we can't safely create a result
* tupledesc, because that needs catalog accesses. Hence, refuse to
* Describe statements that return data. (We shouldn't just refuse all
* Describes, since that might break the ability of some clients to issue
* COMMIT or ROLLBACK commands, if they use code that blindly Describes
* whatever it does.) We can Describe parameters without doing anything
* dangerous, so we don't restrict that.
*/
if (IsAbortedTransactionBlockState() &&
PreparedStatementReturnsTuples(pstmt))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block")));
if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */
/*
* First describe the parameters...
*/
2003-08-04 00:43:34 +00:00
pq_beginmessage(&buf, 't'); /* parameter description message type */
pq_sendint(&buf, list_length(pstmt->argtype_list), 2);
foreach(l, pstmt->argtype_list)
{
Oid ptype = lfirst_oid(l);
pq_sendint(&buf, (int) ptype, 4);
}
pq_endmessage(&buf);
/*
* Next send RowDescription or NoData to describe the result...
*/
tupdesc = FetchPreparedStatementResultDesc(pstmt);
if (tupdesc)
SendRowDescriptionMessage(tupdesc,
FetchPreparedStatementTargetList(pstmt),
NULL);
else
pq_putemptymessage('n'); /* NoData */
}
/*
* exec_describe_portal_message
*
* Process a "Describe" message for a portal
*/
static void
exec_describe_portal_message(const char *portal_name)
{
Portal portal;
/*
* Start up a transaction command. (Note that this will normally change
* current memory context.) Nothing happens if we are already in one.
*/
start_xact_command();
/* Switch back to message context */
MemoryContextSwitchTo(MessageContext);
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("portal \"%s\" does not exist", portal_name)));
/*
* If we are in aborted transaction state, we can't run
* SendRowDescriptionMessage(), because that needs catalog accesses.
* Hence, refuse to Describe portals that return data. (We shouldn't just
* refuse all Describes, since that might break the ability of some
* clients to issue COMMIT or ROLLBACK commands, if they use code that
* blindly Describes whatever it does.)
*/
if (IsAbortedTransactionBlockState() &&
portal->tupDesc)
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block")));
if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */
if (portal->tupDesc)
SendRowDescriptionMessage(portal->tupDesc,
FetchPortalTargetList(portal),
portal->formats);
else
pq_putemptymessage('n'); /* NoData */
}
/*
* Convenience routines for starting/committing a single command.
*/
static void
start_xact_command(void)
{
if (!xact_started)
{
ereport(DEBUG3,
(errmsg_internal("StartTransactionCommand")));
StartTransactionCommand();
/* Set statement timeout running, if any */
/* NB: this mustn't be enabled until we are within an xact */
if (StatementTimeout > 0)
enable_sig_alarm(StatementTimeout, true);
else
cancel_from_timeout = false;
2005-10-15 02:49:52 +00:00
xact_started = true;
}
}
static void
finish_xact_command(void)
{
if (xact_started)
{
/* Cancel any active statement timeout before committing */
disable_sig_alarm(true);
/* Now commit the command */
ereport(DEBUG3,
(errmsg_internal("CommitTransactionCommand")));
CommitTransactionCommand();
#ifdef MEMORY_CONTEXT_CHECKING
/* Check all memory contexts that weren't freed during commit */
/* (those that were, were checked before being deleted) */
MemoryContextCheck(TopMemoryContext);
#endif
#ifdef SHOW_MEMORY_STATS
/* Print mem stats after each commit for leak tracking */
if (ShowStats)
MemoryContextStats(TopMemoryContext);
#endif
xact_started = false;
}
}
/*
* Convenience routines for checking whether a statement is one of the
* ones that we allow in transaction-aborted state.
*/
static bool
IsTransactionExitStmt(Node *parsetree)
{
if (parsetree && IsA(parsetree, TransactionStmt))
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
return true;
}
return false;
}
static bool
IsTransactionExitStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
IsTransactionExitStmt(query->utilityStmt))
return true;
}
return false;
}
static bool
IsTransactionStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
query->utilityStmt && IsA(query->utilityStmt, TransactionStmt))
return true;
}
return false;
}
/* --------------------------------
* signal handler routines used in PostgresMain()
* --------------------------------
*/
/*
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
* quickdie() occurs when signalled SIGQUIT by the postmaster.
*
* Some backend has bought the farm,
* so we need to stop what we're doing and exit.
*/
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
void
quickdie(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
2003-08-04 00:43:34 +00:00
/*
2005-10-15 02:49:52 +00:00
* Ideally this should be ereport(FATAL), but then we'd not get control
* back...
*/
ereport(WARNING,
(errcode(ERRCODE_CRASH_SHUTDOWN),
2004-08-29 05:07:03 +00:00
errmsg("terminating connection because of crash of another server process"),
2005-10-15 02:49:52 +00:00
errdetail("The postmaster has commanded this server process to roll back"
" the current transaction and exit, because another"
" server process exited abnormally and possibly corrupted"
" shared memory."),
errhint("In a moment you should be able to reconnect to the"
" database and repeat your command.")));
2003-08-04 00:43:34 +00:00
/*
* DO NOT proc_exit() -- we're here because shared memory may be
2005-10-15 02:49:52 +00:00
* corrupted, so we don't want to try to clean up our transaction. Just
* nail the windows shut and get out of town.
*
2005-10-15 02:49:52 +00:00
* Note we do exit(1) not exit(0). This is to force the postmaster into a
* system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
* backend. This is necessary precisely because we don't clean up our
* shared memory state.
*/
exit(1);
}
/*
* Shutdown signal from postmaster: abort transaction and exit
* at soonest convenient time
*/
void
die(SIGNAL_ARGS)
{
int save_errno = errno;
/* Don't joggle the elbow of proc_exit */
2001-03-22 04:01:46 +00:00
if (!proc_exit_inprogress)
{
InterruptPending = true;
ProcDiePending = true;
2001-03-22 04:01:46 +00:00
/*
2005-10-15 02:49:52 +00:00
* If it's safe to interrupt, and we're waiting for input or a lock,
* service the interrupt immediately
*/
if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
CritSectionCount == 0)
{
/* bump holdoff count to make ProcessInterrupts() a no-op */
/* until we are done getting ready for it */
InterruptHoldoffCount++;
DisableNotifyInterrupt();
DisableCatchupInterrupt();
/* Make sure CheckDeadLock won't run while shutting down... */
LockWaitCancel();
InterruptHoldoffCount--;
ProcessInterrupts();
}
}
errno = save_errno;
}
/*
* Timeout or shutdown signal from postmaster during client authentication.
* Simply exit(0).
*
* XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while
* doing so, nor mess up the authentication message exchange.
*/
void
authdie(SIGNAL_ARGS)
{
exit(0);
}
/*
* Query-cancel signal from postmaster: abort current transaction
* at soonest convenient time
*/
void
StatementCancelHandler(SIGNAL_ARGS)
{
int save_errno = errno;
2001-03-22 04:01:46 +00:00
/*
* Don't joggle the elbow of proc_exit
2001-03-22 04:01:46 +00:00
*/
if (!proc_exit_inprogress)
{
InterruptPending = true;
QueryCancelPending = true;
2001-03-22 04:01:46 +00:00
/*
2005-10-15 02:49:52 +00:00
* If it's safe to interrupt, and we're waiting for a lock, service
* the interrupt immediately. No point in interrupting if we're
* waiting for input, however.
*/
if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
CritSectionCount == 0)
{
/* bump holdoff count to make ProcessInterrupts() a no-op */
/* until we are done getting ready for it */
InterruptHoldoffCount++;
if (LockWaitCancel())
{
DisableNotifyInterrupt();
DisableCatchupInterrupt();
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
InterruptHoldoffCount--;
ProcessInterrupts();
}
else
InterruptHoldoffCount--;
}
}
errno = save_errno;
}
/* signal handler for floating point exception */
void
FloatExceptionHandler(SIGNAL_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FLOATING_POINT_EXCEPTION),
errmsg("floating-point exception"),
2005-10-15 02:49:52 +00:00
errdetail("An invalid floating-point operation was signaled. "
"This probably means an out-of-range result or an "
"invalid operation, such as division by zero.")));
}
/* SIGHUP: set flag to re-read config file at next convenient time */
static void
SigHupHandler(SIGNAL_ARGS)
{
got_SIGHUP = true;
}
/*
* ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro
*
* If an interrupt condition is pending, and it's safe to service it,
* then clear the flag and accept the interrupt. Called only when
* InterruptPending is true.
*/
void
ProcessInterrupts(void)
{
/* OK to accept interrupt now? */
if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
return;
InterruptPending = false;
if (ProcDiePending)
{
ProcDiePending = false;
2001-03-22 04:01:46 +00:00
QueryCancelPending = false; /* ProcDie trumps QueryCancel */
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
2005-10-15 02:49:52 +00:00
errmsg("terminating connection due to administrator command")));
}
if (QueryCancelPending)
{
QueryCancelPending = false;
2001-03-22 04:01:46 +00:00
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
if (cancel_from_timeout)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
else
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to user request")));
}
/* If we get here, do nothing (probably, QueryCancelPending was reset) */
}
/*
* check_stack_depth: check for excessively deep recursion
*
* This should be called someplace in any recursive routine that might possibly
* recurse deep enough to overflow the stack. Most Unixen treat stack
* overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
* before hitting the hardware limit. Unfortunately we have no direct way
* to detect the hardware limit, so we have to rely on the admin to set a
* GUC variable for it ...
*/
void
check_stack_depth(void)
{
2004-08-29 05:07:03 +00:00
char stack_top_loc;
int stack_depth;
/*
* Compute distance from PostgresMain's local variables to my own
*
2005-10-15 02:49:52 +00:00
* Note: in theory stack_depth should be ptrdiff_t or some such, but since
* the whole point of this code is to bound the value to something much
* less than integer-sized, int should work fine.
*/
stack_depth = (int) (stack_base_ptr - &stack_top_loc);
2004-08-29 05:07:03 +00:00
/*
2005-10-15 02:49:52 +00:00
* Take abs value, since stacks grow up on some machines, down on others
*/
if (stack_depth < 0)
stack_depth = -stack_depth;
2004-08-29 05:07:03 +00:00
/*
* Trouble?
*
* The test on stack_base_ptr prevents us from erroring out if called
* during process setup or in a non-backend process. Logically it should
* be done first, but putting it here avoids wasting cycles during normal
* cases.
*/
if (stack_depth > max_stack_depth_bytes &&
stack_base_ptr != NULL)
{
ereport(ERROR,
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
errmsg("stack depth limit exceeded"),
errhint("Increase the configuration parameter \"max_stack_depth\".")));
}
}
/* GUC assign hook to update max_stack_depth_bytes from max_stack_depth */
bool
assign_max_stack_depth(int newval, bool doit, GucSource source)
{
/* Range check was already handled by guc.c */
if (doit)
max_stack_depth_bytes = newval * 1024;
return true;
}
/*
* set_debug_options --- apply "-d N" command line option
*
* -d is not quite the same as setting log_min_messages because it enables
* other output options.
*/
void
set_debug_options(int debug_flag, GucContext context, GucSource source)
{
if (debug_flag > 0)
{
char debugstr[64];
sprintf(debugstr, "debug%d", debug_flag);
SetConfigOption("log_min_messages", debugstr, context, source);
}
else
SetConfigOption("log_min_messages", "notice", context, source);
if (debug_flag >= 1 && context == PGC_POSTMASTER)
{
SetConfigOption("log_connections", "true", context, source);
SetConfigOption("log_disconnections", "true", context, source);
}
if (debug_flag >= 2)
SetConfigOption("log_statement", "all", context, source);
if (debug_flag >= 3)
SetConfigOption("debug_print_parse", "true", context, source);
if (debug_flag >= 4)
SetConfigOption("debug_print_plan", "true", context, source);
if (debug_flag >= 5)
SetConfigOption("debug_print_rewritten", "true", context, source);
}
bool
set_plan_disabling_options(const char *arg, GucContext context, GucSource source)
{
char *tmp = NULL;
switch (arg[0])
{
case 's': /* seqscan */
tmp = "enable_seqscan";
break;
case 'i': /* indexscan */
tmp = "enable_indexscan";
break;
case 'b': /* bitmapscan */
tmp = "enable_bitmapscan";
break;
case 't': /* tidscan */
tmp = "enable_tidscan";
break;
case 'n': /* nestloop */
tmp = "enable_nestloop";
break;
case 'm': /* mergejoin */
tmp = "enable_mergejoin";
break;
case 'h': /* hashjoin */
tmp = "enable_hashjoin";
break;
}
if (tmp)
{
SetConfigOption(tmp, "false", context, source);
return true;
}
else
return false;
}
const char *
get_stats_option_name(const char *arg)
{
switch (arg[0])
{
case 'p':
if (optarg[1] == 'a') /* "parser" */
return "log_parser_stats";
else if (optarg[1] == 'l') /* "planner" */
return "log_planner_stats";
break;
case 'e': /* "executor" */
return "log_executor_stats";
break;
}
return NULL;
}
/* ----------------------------------------------------------------
* PostgresMain
2001-03-22 04:01:46 +00:00
* postgres main loop -- all backends, interactive or otherwise start here
*
* 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.)
* username is the (possibly authenticated) PostgreSQL user name to be used
* for the session.
* ----------------------------------------------------------------
*/
int
PostgresMain(int argc, char *argv[], const char *username)
{
int flag;
const char *dbname = NULL;
char *userDoption = NULL;
bool secure;
int errs = 0;
2005-10-15 02:49:52 +00:00
int debug_flag = -1; /* -1 means not given */
List *guc_names = NIL; /* for SUSET options */
List *guc_values = NIL;
GucContext ctx;
GucSource gucsource;
bool am_superuser;
int firstchar;
char stack_base;
2004-08-29 05:07:03 +00:00
StringInfoData input_message;
sigjmp_buf local_sigjmp_buf;
volatile bool send_ready_for_query = true;
2004-08-29 05:07:03 +00:00
#define PendingConfigOption(name,val) \
(guc_names = lappend(guc_names, pstrdup(name)), \
guc_values = lappend(guc_values, pstrdup(val)))
/*
* initialize globals (already done if under postmaster, but not if
* standalone; cheap enough to do over)
*/
MyProcPid = getpid();
/*
* Fire up essential subsystems: error and memory management
*
* If we are running under the postmaster, this is done already.
*/
if (!IsUnderPostmaster)
MemoryContextInit();
set_ps_display("startup", false);
SetProcessingMode(InitProcessing);
/* Set up reference point for stack depth checking */
stack_base_ptr = &stack_base;
/* Compute paths, if we didn't inherit them from postmaster */
if (my_exec_path[0] == '\0')
{
if (find_my_exec(argv[0], my_exec_path) < 0)
elog(FATAL, "%s: could not locate my own executable path",
argv[0]);
}
2004-08-29 05:07:03 +00:00
2004-05-19 21:17:33 +00:00
if (pkglib_path[0] == '\0')
get_pkglib_path(my_exec_path, pkglib_path);
/*
* Set default values for command-line options.
*/
EchoQuery = false;
if (!IsUnderPostmaster)
InitializeGUCOptions();
/* ----------------
* 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.
* ----------------
*/
/* Ignore the initial --single argument, if present */
if (argc > 1 && strcmp(argv[1], "--single") == 0)
{
argv++;
argc--;
}
/* all options are allowed until '-p' */
secure = true;
ctx = PGC_POSTMASTER;
gucsource = PGC_S_ARGV; /* initial switches came from command line */
while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:y:-:")) != -1)
{
switch (flag)
{
case 'A':
SetConfigOption("debug_assertions", optarg, ctx, gucsource);
break;
case 'B':
SetConfigOption("shared_buffers", optarg, ctx, gucsource);
break;
case 'D':
if (secure)
userDoption = optarg;
break;
case 'd':
debug_flag = atoi(optarg);
break;
case 'E':
EchoQuery = true;
break;
case 'e':
SetConfigOption("datestyle", "euro", ctx, gucsource);
break;
case 'F':
SetConfigOption("fsync", "false", ctx, gucsource);
break;
case 'f':
if (!set_plan_disabling_options(optarg, ctx, gucsource))
errs++;
break;
case 'h':
SetConfigOption("listen_addresses", optarg, ctx, gucsource);
break;
case 'i':
SetConfigOption("listen_addresses", "*", ctx, gucsource);
break;
case 'j':
UseNewLine = 0;
break;
case 'k':
SetConfigOption("unix_socket_directory", optarg, ctx, gucsource);
break;
case 'l':
SetConfigOption("ssl", "true", ctx, gucsource);
break;
case 'N':
SetConfigOption("max_connections", optarg, ctx, gucsource);
break;
case 'n':
/* ignored for consistency with postmaster */
break;
case 'O':
SetConfigOption("allow_system_table_mods", "true", ctx, gucsource);
2000-02-18 09:30:20 +00:00
break;
1999-05-01 17:16:25 +00:00
case 'o':
errs++;
break;
case 'P':
SetConfigOption("ignore_system_indexes", "true", ctx, gucsource);
1999-05-01 17:16:25 +00:00
break;
case 'p':
SetConfigOption("port", optarg, ctx, gucsource);
break;
2004-08-29 05:07:03 +00:00
case 'r':
/* send output (stdout and stderr) to the given file */
if (secure)
StrNCpy(OutputFileName, optarg, MAXPGPATH);
break;
case 'S':
SetConfigOption("work_mem", optarg, ctx, gucsource);
break;
case 's':
/*
* Since log options are SUSET, we need to postpone unless
* still in secure context
*/
if (ctx == PGC_BACKEND)
PendingConfigOption("log_statement_stats", "true");
else
SetConfigOption("log_statement_stats", "true",
ctx, gucsource);
break;
case 'T':
/* ignored for consistency with postmaster */
break;
case 't':
{
const char *tmp = get_stats_option_name(optarg);
if (tmp)
{
if (ctx == PGC_BACKEND)
PendingConfigOption(tmp, "true");
else
SetConfigOption(tmp, "true", ctx, gucsource);
}
else
errs++;
break;
}
case 'v':
if (secure)
FrontendProtocol = (ProtocolVersion) atoi(optarg);
break;
case 'W':
SetConfigOption("post_auth_delay", optarg, ctx, gucsource);
break;
case 'y':
/*
* y - special flag passed if backend was forked by a
* postmaster.
*/
if (secure)
{
dbname = strdup(optarg);
secure = false; /* subsequent switches are NOT secure */
ctx = PGC_BACKEND;
gucsource = PGC_S_CLIENT;
}
break;
case 'c':
case '-':
{
2001-03-22 04:01:46 +00:00
char *name,
*value;
2001-03-22 04:01:46 +00:00
ParseLongOption(optarg, &name, &value);
if (!value)
{
if (flag == '-')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("--%s requires a value",
optarg)));
2001-03-22 04:01:46 +00:00
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("-c %s requires a value",
optarg)));
2001-03-22 04:01:46 +00:00
}
/*
2005-10-15 02:49:52 +00:00
* If a SUSET option, must postpone evaluation, unless we
* are still reading secure switches.
*/
if (ctx == PGC_BACKEND && IsSuperuserConfigOption(name))
PendingConfigOption(name, value);
else
SetConfigOption(name, value, ctx, gucsource);
2001-03-22 04:01:46 +00:00
free(name);
if (value)
free(value);
break;
}
default:
errs++;
1999-05-01 17:16:25 +00:00
break;
}
}
/*
2005-10-15 02:49:52 +00:00
* Process any additional GUC variable settings passed in startup packet.
* These are handled exactly like command-line variables.
*/
if (MyProcPort != NULL)
{
ListCell *gucopts = list_head(MyProcPort->guc_options);
while (gucopts)
{
char *name;
char *value;
name = lfirst(gucopts);
gucopts = lnext(gucopts);
value = lfirst(gucopts);
gucopts = lnext(gucopts);
if (IsSuperuserConfigOption(name))
PendingConfigOption(name, value);
else
SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
}
}
/* Acquire configuration parameters, unless inherited from postmaster */
if (!IsUnderPostmaster)
{
if (!SelectConfigFiles(userDoption, argv[0]))
proc_exit(1);
/* If timezone is not set, determine what the OS uses */
pg_timezone_initialize();
/* If timezone_abbreviations is not set, select default */
pg_timezone_abbrev_initialize();
}
if (PostAuthDelay)
pg_usleep(PostAuthDelay * 1000000L);
/*
* Set up signal handlers and masks.
*
* Note that postmaster blocked all signals before forking child process,
* so there is no race condition whereby we might receive a signal before
* we have set up the handler.
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
*
2005-10-15 02:49:52 +00:00
* Also note: it's best not to use any signals that are SIG_IGNored in the
* postmaster. If such a signal arrives before we are able to change the
* handler to non-SIG_IGN, it'll get dropped. Instead, make a dummy
* handler in the postmaster to reserve the signal. (Of course, this isn't
* an issue for signals that are locally generated, such as SIGALRM and
* SIGPIPE.)
*/
pqsignal(SIGHUP, SigHupHandler); /* set flag to read config file */
2002-09-04 20:31:48 +00:00
pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */
pqsignal(SIGTERM, die); /* cancel current query and exit */
pqsignal(SIGQUIT, quickdie); /* hard crash time */
2003-08-04 00:43:34 +00:00
pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
/*
* Ignore failure to write to frontend. Note: if frontend closes
* connection, we will notice it and exit cleanly when control next
2005-10-15 02:49:52 +00:00
* returns to outer loop. This seems safer than forcing exit in the midst
* of output during who-knows-what operation...
*/
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, CatchupInterruptHandler);
pqsignal(SIGUSR2, NotifyInterruptHandler);
pqsignal(SIGFPE, FloatExceptionHandler);
/*
2005-10-15 02:49:52 +00:00
* Reset some signals that are accepted by postmaster but not by backend
*/
2005-10-15 02:49:52 +00:00
pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some platforms */
pqinitmask();
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
/* We allow SIGQUIT (quickdie) at all times */
#ifdef HAVE_SIGPROCMASK
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
sigdelset(&BlockSig, SIGQUIT);
#else
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
BlockSig &= ~(sigmask(SIGQUIT));
#endif
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 01:17:06 +00:00
PG_SETMASK(&BlockSig); /* block everything except SIGQUIT */
if (IsUnderPostmaster)
{
/* noninteractive case: nothing should be left after switches */
if (errs || argc != optind || dbname == NULL)
{
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
2005-10-15 02:49:52 +00:00
errmsg("invalid command-line arguments for server process"),
errhint("Try \"%s --help\" for more information.", argv[0])));
}
BaseInit();
}
else
{
/* interactive case: database name can be last arg on command line */
if (errs || argc - optind > 1)
{
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s: invalid command-line arguments",
argv[0]),
2005-10-15 02:49:52 +00:00
errhint("Try \"%s --help\" for more information.", argv[0])));
}
else if (argc - optind == 1)
dbname = argv[optind];
else if ((dbname = username) == NULL)
{
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("%s: no database nor user name specified",
argv[0])));
}
2000-01-09 12:17:33 +00:00
/*
2005-10-15 02:49:52 +00:00
* Validate we have been given a reasonable-looking DataDir (if under
* postmaster, assume postmaster did this already).
*/
Assert(DataDir);
ValidatePgVersion(DataDir);
/* Change into DataDir (if under postmaster, was done already) */
ChangeToDataDir();
2000-01-09 12:17:33 +00:00
/*
* Create lockfile for data directory.
2000-01-09 12:17:33 +00:00
*/
CreateDataDirLockFile(false);
2000-01-09 12:17:33 +00:00
BaseInit();
/*
* Start up xlog for standalone backend, and register to have it
* closed down at exit.
*/
StartupXLOG();
on_shmem_exit(ShutdownXLOG, 0);
/*
2005-10-15 02:49:52 +00:00
* Read any existing FSM cache file, and register to write one out at
* exit.
*/
LoadFreeSpaceMap();
on_shmem_exit(DumpFreeSpaceMap, 0);
/*
2005-10-15 02:49:52 +00:00
* We have to build the flat file for pg_database, but not for the
* user and group tables, since we won't try to do authentication.
*/
BuildFlatFiles(true);
}
/*
* Create a per-backend PGPROC struct in shared memory, except in
* the EXEC_BACKEND case where this was done in SubPostmasterMain.
* We must do this before we can use LWLocks (and in the EXEC_BACKEND
* case we already had to do some stuff with LWLocks).
*/
#ifdef EXEC_BACKEND
if (!IsUnderPostmaster)
InitProcess();
#else
InitProcess();
#endif
/*
* General initialization.
*
* NOTE: if you are tempted to add code in this vicinity, consider putting
* it inside InitPostgres() instead. In particular, anything that
* involves database access should be there, not here.
*/
ereport(DEBUG3,
(errmsg_internal("InitPostgres")));
am_superuser = InitPostgres(dbname, username);
SetProcessingMode(NormalProcessing);
/*
* Now that we know if client is a superuser, we can try to apply SUSET
* GUC options that came from the client.
*/
ctx = am_superuser ? PGC_SUSET : PGC_USERSET;
if (debug_flag >= 0)
set_debug_options(debug_flag, ctx, PGC_S_CLIENT);
if (guc_names != NIL)
{
ListCell *namcell,
*valcell;
forboth(namcell, guc_names, valcell, guc_values)
{
char *name = (char *) lfirst(namcell);
char *value = (char *) lfirst(valcell);
SetConfigOption(name, value, ctx, PGC_S_CLIENT);
pfree(name);
pfree(value);
}
}
/*
* Now all GUC states are fully set up. Report them to client if
* appropriate.
*/
BeginReportingGUCOptions();
/*
2005-10-15 02:49:52 +00:00
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
*/
if (IsUnderPostmaster && Log_disconnections)
on_proc_exit(log_disconnections, 0);
/*
* Send this backend's cancellation info to the frontend.
*/
if (whereToSendOutput == DestRemote &&
PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
{
StringInfoData buf;
1999-05-25 16:15:34 +00:00
pq_beginmessage(&buf, 'K');
pq_sendint(&buf, (int32) MyProcPid, sizeof(int32));
pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32));
pq_endmessage(&buf);
/* Need not flush since ReadyForQuery will do it. */
}
/* Welcome banner for standalone case */
if (whereToSendOutput == DestDebug)
printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION);
/*
* Create the memory context we will use in the main loop.
*
* MessageContext is reset once per iteration of the main loop, ie, upon
* completion of processing of each command message from the client.
*/
MessageContext = AllocSetContextCreate(TopMemoryContext,
"MessageContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/*
* Remember stand-alone backend startup time
*/
if (!IsUnderPostmaster)
PgStartTime = GetCurrentTimestamp();
/*
* POSTGRES main processing loop begins here
*
2005-10-15 02:49:52 +00:00
* If an exception is encountered, processing resumes here so we abort the
* current transaction and start a new one.
*
* You might wonder why this isn't coded as an infinite loop around a
* PG_TRY construct. The reason is that this is the bottom of the
* exception stack, and so with PG_TRY there would be no exception handler
* in force at all during the CATCH part. By leaving the outermost setjmp
* always active, we have at least some chance of recovering from an error
* during error recovery. (If we get into an infinite loop thereby, it
* will soon be stopped by overflow of elog.c's internal state stack.)
*/
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/*
* NOTE: if you are tempted to add more code in this if-block,
* consider the high probability that it should be in
2004-08-29 05:07:03 +00:00
* AbortTransaction() instead. The only stuff done directly here
2005-10-15 02:49:52 +00:00
* should be stuff that is guaranteed to apply *only* for outer-level
* error recovery, such as adjusting the FE/BE protocol status.
*/
/* Since not using PG_TRY, must reset error stack by hand */
error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/*
2005-10-15 02:49:52 +00:00
* Forget any pending QueryCancel request, since we're returning to
* the idle loop anyway, and cancel the statement timer if running.
*/
QueryCancelPending = false;
disable_sig_alarm(true);
2003-08-04 00:43:34 +00:00
QueryCancelPending = false; /* again in case timeout occurred */
/*
2005-10-15 02:49:52 +00:00
* Turn off these interrupts too. This is only needed here and not in
* other exception-catching places since these interrupts are only
* enabled while we wait for client input.
*/
DoingCommandRead = false;
DisableNotifyInterrupt();
DisableCatchupInterrupt();
/* Make sure libpq is in a good state */
pq_comm_reset();
/* Report the error to the client and/or server log */
EmitErrorReport();
/*
2005-10-15 02:49:52 +00:00
* Make sure debug_query_string gets reset before we possibly clobber
* the storage it points at.
*/
debug_query_string = NULL;
/*
* Abort the current transaction in order to recover.
*/
AbortCurrentTransaction();
/*
2005-10-15 02:49:52 +00:00
* Now return to normal top-level context and clear ErrorContext for
* next time.
*/
MemoryContextSwitchTo(TopMemoryContext);
FlushErrorState();
QueryContext = NULL;
/*
2005-10-15 02:49:52 +00:00
* If we were handling an extended-query-protocol message, initiate
* skip till next Sync. This also causes us not to issue
2003-08-04 00:43:34 +00:00
* ReadyForQuery (until we get Sync).
*/
if (doing_extended_query_message)
ignore_till_sync = true;
/* We don't have a transaction command open anymore */
xact_started = false;
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
}
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
PG_SETMASK(&UnBlockSig);
if (!ignore_till_sync)
send_ready_for_query = true; /* initially, or after error */
/*
* Non-error queries loop here.
*/
for (;;)
{
/*
2003-08-04 00:43:34 +00:00
* At top of loop, reset extended-query-message flag, so that any
* errors encountered in "idle" state don't provoke skip.
*/
doing_extended_query_message = false;
/*
2005-10-15 02:49:52 +00:00
* Release storage left over from prior query cycle, and create a new
* query input buffer in the cleared MessageContext.
*/
MemoryContextSwitchTo(MessageContext);
MemoryContextResetAndDeleteChildren(MessageContext);
initStringInfo(&input_message);
/*
2005-10-15 02:49:52 +00:00
* (1) If we've reached idle state, tell the frontend we're ready for
* a new query.
*
* Note: this includes fflush()'ing the last of the prior output.
*
* This is also a good time to send collected statistics to the
* collector, and to update the PS stats display. We avoid doing
2005-10-15 02:49:52 +00:00
* those every time through the message loop because it'd slow down
* processing of batched messages, and because we don't want to report
* uncommitted updates (that confuses autovacuum).
*/
if (send_ready_for_query)
{
if (IsTransactionOrTransactionBlock())
{
set_ps_display("idle in transaction", false);
pgstat_report_activity("<IDLE> in transaction");
}
else
{
pgstat_report_tabstat();
set_ps_display("idle", false);
pgstat_report_activity("<IDLE>");
}
ReadyForQuery(whereToSendOutput);
send_ready_for_query = false;
}
/*
2005-10-15 02:49:52 +00:00
* (2) Allow asynchronous signals to be executed immediately if they
* come in while we are waiting for client input. (This must be
* conditional since we don't want, say, reads on behalf of COPY FROM
* STDIN doing the same thing.)
*/
QueryCancelPending = false; /* forget any earlier CANCEL signal */
DoingCommandRead = true;
/*
* (3) read a command (loop blocks here)
*/
firstchar = ReadCommand(&input_message);
/*
* (4) disable async signal conditions again.
*/
DoingCommandRead = false;
/*
2005-10-15 02:49:52 +00:00
* (5) check for any other interesting events that happened while we
* slept.
*/
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
/*
2003-08-04 00:43:34 +00:00
* (6) process the command. But ignore it if we're skipping till
* Sync.
*/
if (ignore_till_sync && firstchar != EOF)
continue;
switch (firstchar)
{
case 'Q': /* simple query */
{
const char *query_string;
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
query_string = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
exec_simple_query(query_string);
send_ready_for_query = true;
}
break;
case 'P': /* parse */
{
const char *stmt_name;
const char *query_string;
int numParams;
Oid *paramTypes = NULL;
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
stmt_name = pq_getmsgstring(&input_message);
query_string = pq_getmsgstring(&input_message);
numParams = pq_getmsgint(&input_message, 2);
if (numParams > 0)
{
2003-08-04 00:43:34 +00:00
int i;
paramTypes = (Oid *) palloc(numParams * sizeof(Oid));
for (i = 0; i < numParams; i++)
paramTypes[i] = pq_getmsgint(&input_message, 4);
}
pq_getmsgend(&input_message);
exec_parse_message(query_string, stmt_name,
paramTypes, numParams);
}
break;
case 'B': /* bind */
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
2003-08-04 00:43:34 +00:00
/*
2005-10-15 02:49:52 +00:00
* this message is complex enough that it seems best to put
* the field extraction out-of-line
*/
exec_bind_message(&input_message);
break;
case 'E': /* execute */
{
const char *portal_name;
2003-08-04 00:43:34 +00:00
int max_rows;
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
portal_name = pq_getmsgstring(&input_message);
max_rows = pq_getmsgint(&input_message, 4);
pq_getmsgend(&input_message);
exec_execute_message(portal_name, max_rows);
}
break;
case 'F': /* fastpath function call */
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
/* Tell the collector what we're doing */
pgstat_report_activity("<FASTPATH> function call");
/* start an xact for this function invocation */
start_xact_command();
/*
* Note: we may at this point be inside an aborted
* transaction. We can't throw error for that until
* we've finished reading the function-call message, so
* HandleFunctionRequest() must check for it after doing so.
* Be careful not to do anything that assumes we're inside a
* valid transaction here.
*/
/* switch back to message context */
MemoryContextSwitchTo(MessageContext);
if (HandleFunctionRequest(&input_message) == EOF)
{
/* lost frontend connection during F message input */
2002-09-04 20:31:48 +00:00
/*
* Reset whereToSendOutput to prevent ereport from
2002-09-04 20:31:48 +00:00
* attempting to send any more messages to client.
*/
if (whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
proc_exit(0);
}
/* commit the function-invocation transaction */
finish_xact_command();
send_ready_for_query = true;
break;
2003-08-04 00:43:34 +00:00
case 'C': /* close */
{
2003-08-04 00:43:34 +00:00
int close_type;
const char *close_target;
close_type = pq_getmsgbyte(&input_message);
close_target = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
switch (close_type)
{
case 'S':
if (close_target[0] != '\0')
DropPreparedStatement(close_target, false);
else
{
/* special-case the unnamed statement */
unnamed_stmt_pstmt = NULL;
if (unnamed_stmt_context)
{
DropDependentPortals(unnamed_stmt_context);
MemoryContextDelete(unnamed_stmt_context);
}
unnamed_stmt_context = NULL;
}
break;
case 'P':
{
Portal portal;
portal = GetPortalByName(close_target);
if (PortalIsValid(portal))
PortalDrop(portal, false);
}
break;
default:
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid CLOSE message subtype %d",
close_type)));
break;
}
if (whereToSendOutput == DestRemote)
2003-08-04 00:43:34 +00:00
pq_putemptymessage('3'); /* CloseComplete */
}
break;
case 'D': /* describe */
{
2003-08-04 00:43:34 +00:00
int describe_type;
const char *describe_target;
/* Set statement_timestamp() (needed for xact) */
SetCurrentStatementStartTimestamp();
describe_type = pq_getmsgbyte(&input_message);
describe_target = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
switch (describe_type)
{
case 'S':
exec_describe_statement_message(describe_target);
break;
case 'P':
exec_describe_portal_message(describe_target);
break;
default:
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid DESCRIBE message subtype %d",
describe_type)));
break;
}
}
break;
2003-08-04 00:43:34 +00:00
case 'H': /* flush */
pq_getmsgend(&input_message);
if (whereToSendOutput == DestRemote)
pq_flush();
break;
2003-08-04 00:43:34 +00:00
case 'S': /* sync */
pq_getmsgend(&input_message);
finish_xact_command();
send_ready_for_query = true;
break;
/*
2005-10-15 02:49:52 +00:00
* 'X' means that the frontend is closing down the socket. EOF
* means unexpected loss of frontend connection. Either way,
* perform normal shutdown.
*/
case 'X':
case EOF:
2002-09-04 20:31:48 +00:00
/*
2005-10-15 02:49:52 +00:00
* Reset whereToSendOutput to prevent ereport from attempting
* to send any more messages to client.
*/
if (whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
2001-03-22 04:01:46 +00:00
/*
* NOTE: if you are tempted to add more code here, DON'T!
2001-03-22 04:01:46 +00:00
* Whatever you had in mind to do should be set up as an
2005-10-15 02:49:52 +00:00
* on_proc_exit or on_shmem_exit callback, instead. Otherwise
* it will fail to be called during other backend-shutdown
* scenarios.
*/
proc_exit(0);
2003-08-04 00:43:34 +00:00
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
/*
2003-08-04 00:43:34 +00:00
* Accept but ignore these messages, per protocol spec; we
2005-10-15 02:49:52 +00:00
* probably got here because a COPY failed, and the frontend
* is still sending data.
*/
break;
default:
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("invalid frontend message type %d",
firstchar)));
}
} /* end of input-reading loop */
/* can't get here because the above loop never exits */
Assert(false);
return 1; /* keep compiler quiet */
}
#ifndef HAVE_GETRUSAGE
1999-07-16 05:23:30 +00:00
#include "rusagestub.h"
#else
#include <sys/resource.h>
#endif /* HAVE_GETRUSAGE */
static struct rusage Save_r;
static struct timeval Save_t;
void
ResetUsage(void)
{
getrusage(RUSAGE_SELF, &Save_r);
gettimeofday(&Save_t, NULL);
ResetBufferUsage();
/* ResetTupleCount(); */
}
void
ShowUsage(const char *title)
{
StringInfoData str;
struct timeval user,
sys;
struct timeval elapse_t;
struct rusage r;
2002-09-04 20:31:48 +00:00
char *bufusage;
getrusage(RUSAGE_SELF, &r);
gettimeofday(&elapse_t, NULL);
memcpy((char *) &user, (char *) &r.ru_utime, sizeof(user));
memcpy((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
2005-10-15 02:49:52 +00:00
* 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.
*/
initStringInfo(&str);
appendStringInfo(&str, "! system usage stats:\n");
appendStringInfo(&str,
2005-10-15 02:49:52 +00:00
"!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
(long) (elapse_t.tv_sec - Save_t.tv_sec),
(long) (elapse_t.tv_usec - Save_t.tv_usec),
(long) (r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec),
2005-10-15 02:49:52 +00:00
(long) (r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec),
(long) (r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec),
2005-10-15 02:49:52 +00:00
(long) (r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec));
appendStringInfo(&str,
2002-09-04 20:31:48 +00:00
"!\t[%ld.%06ld user %ld.%06ld sys total]\n",
(long) user.tv_sec,
(long) user.tv_usec,
(long) sys.tv_sec,
(long) sys.tv_usec);
#if defined(HAVE_GETRUSAGE)
appendStringInfo(&str,
2002-09-04 20:31:48 +00:00
"!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
r.ru_inblock - Save_r.ru_inblock,
/* they only drink coffee at dec */
2002-09-04 20:31:48 +00:00
r.ru_oublock - Save_r.ru_oublock,
r.ru_inblock, r.ru_oublock);
appendStringInfo(&str,
2005-10-15 02:49:52 +00:00
"!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
2002-09-04 20:31:48 +00:00
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);
appendStringInfo(&str,
2005-10-15 02:49:52 +00:00
"!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
2002-09-04 20:31:48 +00:00
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);
appendStringInfo(&str,
2005-10-15 02:49:52 +00:00
"!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
2002-09-04 20:31:48 +00:00
r.ru_nvcsw - Save_r.ru_nvcsw,
r.ru_nivcsw - Save_r.ru_nivcsw,
r.ru_nvcsw, r.ru_nivcsw);
#endif /* HAVE_GETRUSAGE */
bufusage = ShowBufferUsage();
appendStringInfo(&str, "! buffer usage stats:\n%s", bufusage);
pfree(bufusage);
/* remove trailing newline */
2002-09-04 20:31:48 +00:00
if (str.data[str.len - 1] == '\n')
str.data[--str.len] = '\0';
ereport(LOG,
(errmsg_internal("%s", title),
errdetail("%s", str.data)));
pfree(str.data);
}
/*
* on_proc_exit handler to log end of session
*/
2004-08-29 05:07:03 +00:00
static void
log_disconnections(int code, Datum arg)
{
2005-10-15 02:49:52 +00:00
Port *port = MyProcPort;
long secs;
int usecs;
int msecs;
2005-10-15 02:49:52 +00:00
int hours,
minutes,
seconds;
TimestampDifference(port->SessionStartTime,
GetCurrentTimestamp(),
&secs, &usecs);
msecs = usecs / 1000;
hours = secs / SECS_PER_HOUR;
secs %= SECS_PER_HOUR;
minutes = secs / SECS_PER_MINUTE;
seconds = secs % SECS_PER_MINUTE;
ereport(LOG,
(errmsg("disconnection: session time: %d:%02d:%02d.%03d "
"user=%s database=%s host=%s%s%s",
hours, minutes, seconds, msecs,
port->user_name, port->database_name, port->remote_host,
port->remote_port[0] ? " port=" : "", port->remote_port)));
}