* postgres.c
* POSTGRES C Backend Interface
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.341 2003/05/09 15:57:24 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.400 2004/04/19 17:42:58 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
extern int optind;
extern char *optarg;
-
/* ----------------
* global variables
* ----------------
bool Warn_restart_ready = false;
bool InError = false;
-extern bool autocommit;
+/* flag for logging end of session */
+bool Log_disconnections = false;
+
+LogStmtLevel log_statement = LOGSTMT_NONE;
/*
* Flags for expensive function optimization -- JMH 3/9/92
*/
int XfuncMode = 0;
+/* GUC variable for maximum stack depth (measured in kilobytes) */
+int max_stack_depth = 2048;
+
+
/* ----------------
* private variables
* ----------------
*/
+/* max_stack_depth converted to bytes for speed of checking */
+static int max_stack_depth_bytes = 2048*1024;
+
+/* stack base pointer (initialized by PostgresMain) */
+static 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
#ifndef TCOP_DONTUSENEWLINE
static int UseNewLine = 1; /* Use newlines query delimiters (the
* default) */
+
#else
static int UseNewLine = 0; /* Use EOF as query delimiters */
#endif /* TCOP_DONTUSENEWLINE */
static int SocketBackend(StringInfo inBuf);
static int ReadCommand(StringInfo inBuf);
static void start_xact_command(void);
-static void finish_xact_command(bool forceCommit);
+static void finish_xact_command(void);
static void SigHupHandler(SIGNAL_ARGS);
static void FloatExceptionHandler(SIGNAL_ARGS);
+static void log_disconnections(int code, Datum arg);
/* ----------------------------------------------------------------
if (qtype == EOF) /* frontend disconnected */
{
- elog(COMMERROR, "unexpected EOF on client connection");
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unexpected EOF on client connection")));
return qtype;
}
/*
* 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.
+ * 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.
/* old style without length word; convert */
if (pq_getstring(inBuf))
{
- elog(COMMERROR, "unexpected EOF on client connection");
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unexpected EOF on client connection")));
return EOF;
}
}
case 'X': /* terminate */
doing_extended_query_message = false;
+ ignore_till_sync = false;
break;
case 'B': /* bind */
doing_extended_query_message = true;
/* these are only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
- elog(FATAL, "Socket command type %c unknown", qtype);
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
break;
case 'S': /* sync */
doing_extended_query_message = false;
/* only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
- elog(FATAL, "Socket command type %c unknown", qtype);
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
break;
case 'd': /* copy data */
doing_extended_query_message = false;
/* these are only legal in protocol 3 */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
- elog(FATAL, "Socket command type %c unknown", qtype);
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
break;
default:
+
/*
- * 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.
+ * 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.
*/
- elog(FATAL, "Socket command type %c unknown", qtype);
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
break;
}
{
int result;
- if (IsUnderPostmaster)
+ if (whereToSendOutput == Remote)
result = SocketBackend(inBuf);
else
result = InteractiveBackend(inBuf);
* but it is still needed for parsing of SQL function bodies.
*/
List *
-pg_parse_and_rewrite(const char *query_string, /* string to execute */
+pg_parse_and_rewrite(const char *query_string, /* string to execute */
Oid *paramTypes, /* parameter types */
- int numParams) /* number of parameters */
+ int numParams) /* number of parameters */
{
List *raw_parsetree_list;
List *querytree_list;
List *
pg_parse_query(const char *query_string)
{
- List *raw_parsetree_list;
+ List *raw_parsetree_list,
+ *parsetree_item;
- if (log_statement)
- elog(LOG, "query: %s", query_string);
+ if (log_statement == LOGSTMT_ALL)
+ ereport(LOG,
+ (errmsg("statement: %s", query_string)));
if (log_parser_stats)
ResetUsage();
raw_parsetree_list = raw_parser(query_string);
+ /* do log_statement tests for mod and ddl */
+ if (log_statement == LOGSTMT_MOD ||
+ log_statement == LOGSTMT_DDL)
+ {
+ foreach(parsetree_item, raw_parsetree_list)
+ {
+ 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))
+ 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) &&
+ ((CopyStmt *)parsetree)->is_from))) /* COPY FROM */
+ {
+ ereport(LOG,
+ (errmsg("statement: %s", query_string)));
+ break;
+ }
+ commandTag = CreateCommandTag(parsetree);
+ if (strncmp(commandTag, "CREATE ", strlen("CREATE ")) == 0 ||
+ strncmp(commandTag, "ALTER ", strlen("ALTER ")) == 0 ||
+ strncmp(commandTag, "DROP ", strlen("DROP ")) == 0 ||
+ IsA(parsetree, GrantStmt) || /* GRANT or REVOKE */
+ IsA(parsetree, CommentStmt))
+ {
+ ereport(LOG,
+ (errmsg("statement: %s", query_string)));
+ break;
+ }
+ }
+ }
+
if (log_parser_stats)
ShowUsage("PARSER STATISTICS");
ResetUsage();
/*
- * rewritten queries are collected in new_list. Note there may be more
- * or fewer than in the original list.
+ * 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(LOG, "parse tree", querytree,
+ elog_node_display(DEBUG1, "parse tree", querytree,
Debug_pretty_print);
if (querytree->commandType == CMD_UTILITY)
new_list = (List *) copyObject(querytree_list);
/* This checks both copyObject() and the equal() routines... */
if (!equal(new_list, querytree_list))
- elog(WARNING, "pg_rewrite_queries: copyObject failed on parse tree");
+ elog(WARNING, "copyObject() failed to produce an equal parse tree");
else
querytree_list = new_list;
#endif
if (Debug_print_rewritten)
- elog_node_display(LOG, "rewritten parse tree", querytree_list,
+ elog_node_display(DEBUG1, "rewritten parse tree", querytree_list,
Debug_pretty_print);
return querytree_list;
#ifdef NOT_USED
/* This checks both copyObject() and the equal() routines... */
if (!equal(new_plan, plan))
- elog(WARNING, "pg_plan_query: copyObject failed on plan tree");
+ elog(WARNING, "copyObject() failed to produce an equal plan tree");
else
#endif
plan = new_plan;
* Print plan if debugging.
*/
if (Debug_print_plan)
- elog_node_display(LOG, "plan", plan, Debug_pretty_print);
+ elog_node_display(DEBUG1, "plan", plan, Debug_pretty_print);
return plan;
}
static void
exec_simple_query(const char *query_string)
{
- CommandDest dest = whereToSendOutput;
+ CommandDest dest = whereToSendOutput;
MemoryContext oldcontext;
List *parsetree_list,
*parsetree_item;
struct timeval start_t,
stop_t;
bool save_log_duration = log_duration;
+ int save_log_min_duration_statement = log_min_duration_statement;
bool save_log_statement_stats = log_statement_stats;
/*
pgstat_report_activity(query_string);
/*
- * We use save_log_duration so "SET log_duration = true" doesn't
- * report incorrect time because gettimeofday() wasn't called.
- * Similarly, log_statement_stats has to be captured once.
+ * We use save_log_* so "SET log_duration = true" and "SET
+ * log_min_duration_statement = true" don't report incorrect time
+ * because gettimeofday() wasn't called. Similarly,
+ * log_statement_stats has to be captured once.
*/
- if (save_log_duration)
+ if (save_log_duration || save_log_min_duration_statement != -1)
gettimeofday(&start_t, NULL);
if (save_log_statement_stats)
start_xact_command();
/*
- * 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.)
+ * 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)
int16 format;
/*
- * 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.
+ * 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);
}
if (!allowit)
- elog(ERROR, "current transaction is aborted, "
- "queries ignored until end of transaction block");
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end of transaction block")));
}
/* Make sure we are in a transaction command */
CHECK_FOR_INTERRUPTS();
/*
- * Create unnamed portal to run the query or queries in.
- * If there already is one, silently drop it.
+ * Create unnamed portal to run the query or queries in. If there
+ * already is one, silently drop it.
*/
portal = CreatePortal("", true, true);
/*
* 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...)
+ * 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))
if (PortalIsValid(fportal) &&
(fportal->cursorOptions & CURSOR_OPT_BINARY))
- format = 1; /* BINARY */
+ format = 1; /* BINARY */
}
}
PortalSetResultFormat(portal, 1, &format);
MemoryContextSwitchTo(oldcontext);
/*
- * Run the portal to completion, and then drop it (and the receiver).
+ * Run the portal to completion, and then drop it (and the
+ * receiver).
*/
(void) PortalRun(portal,
FETCH_ALL,
receiver,
completionTag);
- (*receiver->destroy) (receiver);
+ (*receiver->rDestroy) (receiver);
PortalDrop(portal, false);
-
- if (IsA(parsetree, TransactionStmt) ||
- IsA(parsetree, VariableSetStmt) ||
- IsA(parsetree, VariableShowStmt) ||
- IsA(parsetree, VariableResetStmt))
+ if (IsA(parsetree, TransactionStmt))
{
/*
- * If this was a transaction control statement or a variable
- * set/show/reset statement, commit it. We will start a
- * new xact command for the next command (if any).
+ * 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(true);
+ finish_xact_command();
}
- else if (lnext(parsetree_item) == NIL || !autocommit)
+ else if (lnext(parsetree_item) == NIL)
{
/*
- * 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.
+ * 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(false);
+ finish_xact_command();
}
else
{
/*
- * We need a CommandCounterIncrement after every query,
- * except those that start or end a transaction block.
+ * We need a CommandCounterIncrement after every query, except
+ * those that start or end a transaction block.
*/
CommandCounterIncrement();
}
EndCommand(completionTag, dest);
} /* end loop over parsetrees */
+ /*
+ * Close down transaction statement, if one is open.
+ */
+ finish_xact_command();
+
/*
* If there were no parsetrees, return EmptyQueryResponse message.
*/
QueryContext = NULL;
/*
- * Close down transaction statement, if one is open.
- */
- finish_xact_command(false);
-
- /*
- * Finish up monitoring.
+ * Combine processing here as we need to calculate the query duration
+ * in both instances.
*/
- if (save_log_duration)
+ if (save_log_duration || save_log_min_duration_statement != -1)
{
+ long usecs;
+
gettimeofday(&stop_t, NULL);
if (stop_t.tv_usec < start_t.tv_usec)
{
stop_t.tv_sec--;
stop_t.tv_usec += 1000000;
}
- elog(LOG, "duration: %ld.%06ld sec",
- (long) (stop_t.tv_sec - start_t.tv_sec),
- (long) (stop_t.tv_usec - start_t.tv_usec));
+ usecs = (long) (stop_t.tv_sec - start_t.tv_sec) * 1000000 + (long) (stop_t.tv_usec - start_t.tv_usec);
+
+ if (save_log_duration)
+ ereport(LOG,
+ (errmsg("duration: %ld.%03ld ms",
+ (long) ((stop_t.tv_sec - start_t.tv_sec) * 1000 +
+ (stop_t.tv_usec - start_t.tv_usec) / 1000),
+ (long) (stop_t.tv_usec - start_t.tv_usec) % 1000)));
+
+ /*
+ * Output a duration_statement to the log if the query has exceeded
+ * the min duration, or if we are to print all durations.
+ */
+ if (save_log_min_duration_statement == 0 ||
+ (save_log_min_duration_statement > 0 &&
+ usecs >= save_log_min_duration_statement * 1000))
+ ereport(LOG,
+ (errmsg("duration: %ld.%03ld ms statement: %s",
+ (long) ((stop_t.tv_sec - start_t.tv_sec) * 1000 +
+ (stop_t.tv_usec - start_t.tv_usec) / 1000),
+ (long) (stop_t.tv_usec - start_t.tv_usec) % 1000,
+ query_string)));
}
if (save_log_statement_stats)
static void
exec_parse_message(const char *query_string, /* string to execute */
const char *stmt_name, /* name for prepared stmt */
- Oid *paramTypes, /* parameter types */
- int numParams) /* number of parameters */
+ Oid *paramTypes, /* parameter types */
+ int numParams) /* number of parameters */
{
MemoryContext oldcontext;
List *parsetree_list;
/*
* Switch to appropriate context for constructing parsetrees.
*
- * 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
- * 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.
+ * 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
+ * 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)
parsetree_list = pg_parse_query(query_string);
/*
- * 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.
+ * 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 (length(parsetree_list) > 1)
- elog(ERROR, "Cannot insert multiple commands into a prepared statement");
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cannot insert multiple commands into a prepared statement")));
if (parsetree_list != NIL)
{
- Node *parsetree = (Node *) lfirst(parsetree_list);
- int i;
+ Node *parsetree = (Node *) lfirst(parsetree_list);
+ int i;
/*
* Get the command name for possible use in status display.
/*
* If we are in an aborted transaction, reject all commands except
- * 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
+ * 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())
}
if (!allowit)
- elog(ERROR, "current transaction is aborted, "
- "queries ignored until end of transaction block");
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end of transaction block")));
}
/*
param_list = NIL;
for (i = 0; i < numParams; i++)
{
- Oid ptype = paramTypes[i];
+ Oid ptype = paramTypes[i];
if (ptype == InvalidOid || ptype == UNKNOWNOID)
- elog(ERROR, "Could not determine datatype of parameter $%d",
- i + 1);
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_DATATYPE),
+ errmsg("could not determine data type of parameter $%d",
+ i + 1)));
param_list = lappendo(param_list, ptype);
}
}
else
{
- /* Empty input string. This is legal. */
+ /* Empty input string. This is legal. */
commandTag = NULL;
querytree_list = NIL;
plantree_list = NIL;
QueryContext = NULL;
/*
- * 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.
+ * 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();
set_ps_display("BIND");
/*
- * 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 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();
numParams = pq_getmsgint(input_message, 2);
if (numPFormats > 1 && numPFormats != numParams)
- elog(ERROR, "BIND message has %d parameter formats but %d parameters",
- numPFormats, numParams);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("bind message has %d parameter formats but %d parameters",
+ numPFormats, numParams)));
/* Find prepared statement */
if (stmt_name[0] != '\0')
/* special-case the unnamed statement */
pstmt = unnamed_stmt_pstmt;
if (!pstmt)
- elog(ERROR, "Unnamed prepared statement does not exist");
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
+ errmsg("unnamed prepared statement does not exist")));
}
if (numParams != length(pstmt->argtype_list))
- elog(ERROR, "Bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
- numParams, stmt_name, length(pstmt->argtype_list));
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
+ numParams, stmt_name, length(pstmt->argtype_list))));
/*
* Create the portal. Allow silent replacement of an existing portal
/*
* Fetch parameters, if any, and store in the portal's memory context.
*
- * In an aborted transaction, we can't risk calling user-defined functions,
- * but we can't fail to Bind either, so bind all parameters to null values.
+ * In an aborted transaction, we can't risk calling user-defined
+ * functions, but we can't fail to Bind either, so bind all parameters
+ * to null values.
*/
if (numParams > 0)
{
- bool isaborted = IsAbortedTransactionBlockState();
- StringInfoData pbuf;
- List *l;
+ bool isaborted = IsAbortedTransactionBlockState();
+ List *l;
MemoryContext oldContext;
- /* Note that the string buffer lives in MessageContext */
- initStringInfo(&pbuf);
-
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
params = (ParamListInfo)
if (!isNull)
{
- /* Reset pbuf to empty, and insert raw data into it */
- pbuf.len = 0;
- pbuf.data[0] = '\0';
- pbuf.cursor = 0;
-
- appendBinaryStringInfo(&pbuf,
- pq_getmsgbytes(input_message, plength),
- plength);
+ const char *pvalue = pq_getmsgbytes(input_message, plength);
if (isaborted)
{
}
else
{
- int16 pformat;
+ int16 pformat;
+ StringInfoData pbuf;
+ char csave;
if (numPFormats > 1)
pformat = pformats[i];
else if (numPFormats > 0)
pformat = pformats[0];
else
- pformat = 0; /* default = text */
+ pformat = 0; /* default = text */
+
+ /*
+ * 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';
if (pformat == 0)
{
char *pstring;
getTypeInputInfo(ptype, &typInput, &typElem);
+
/*
- * Since stringinfo.c keeps a trailing null in
- * place even for binary data, the contents of
- * pbuf are a valid C string. We have to do
- * encoding conversion before calling the typinput
- * routine, though.
+ * We have to do encoding conversion before
+ * calling the typinput routine.
*/
pstring = (char *)
pg_client_to_server((unsigned char *) pbuf.data,
}
else if (pformat == 1)
{
- /* XXX something similar to above */
- elog(ERROR, "Binary BIND not implemented yet");
+ Oid typReceive;
+ Oid typElem;
+
+ /*
+ * Call the parameter type's binary input
+ * converter
+ */
+ getTypeBinaryInputInfo(ptype, &typReceive, &typElem);
+
+ params[i].value =
+ OidFunctionCall2(typReceive,
+ PointerGetDatum(&pbuf),
+ ObjectIdGetDatum(typElem));
+
+ /* Trouble if it didn't eat the whole buffer */
+ if (pbuf.cursor != pbuf.len)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("incorrect binary data format in bind parameter %d",
+ i + 1)));
}
else
{
- elog(ERROR, "Invalid format code %d", pformat);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unsupported format code: %d",
+ pformat)));
}
+
+ /* Restore message buffer contents */
+ pbuf.data[plength] = csave;
}
}
static void
exec_execute_message(const char *portal_name, long max_rows)
{
- CommandDest dest;
+ CommandDest dest;
DestReceiver *receiver;
Portal portal;
bool is_trans_stmt = false;
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
- elog(ERROR, "Portal \"%s\" not found", portal_name);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR),
+ errmsg("portal \"%s\" does not exist", portal_name)));
/*
- * If the original query was a null string, just return EmptyQueryResponse.
+ * If the original query was a null string, just return
+ * EmptyQueryResponse.
*/
if (portal->commandTag == NULL)
{
/* Check for transaction-control commands */
if (length(portal->parseTrees) == 1)
{
- Query *query = (Query *) lfirst(portal->parseTrees);
+ Query *query = (Query *) lfirst(portal->parseTrees);
if (query->commandType == CMD_UTILITY &&
query->utilityStmt != NULL &&
}
/*
- * Create dest receiver in MessageContext (we don't want it in transaction
- * context, because that may get deleted if portal contains VACUUM).
+ * 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);
/*
- * Ensure we are in a transaction command (this should normally be
- * the case already due to prior BIND).
+ * Ensure we are in a transaction command (this should normally be the
+ * case already due to prior BIND).
*/
start_xact_command();
if (IsAbortedTransactionBlockState())
{
if (!is_trans_exit)
- elog(ERROR, "current transaction is aborted, "
- "queries ignored until end of transaction block");
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end of transaction block")));
}
/* Check for cancel signal before we start execution */
receiver,
completionTag);
- (*receiver->destroy) (receiver);
+ (*receiver->rDestroy) (receiver);
if (completed)
{
if (is_trans_stmt)
{
/*
- * If this was a transaction control statement, commit it. We will
- * start a new xact command for the next command (if any).
+ * 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(true);
+ finish_xact_command();
}
else
{
/*
- * We need a CommandCounterIncrement after every query,
- * except those that start or end a transaction block.
+ * We need a CommandCounterIncrement after every query, except
+ * those that start or end a transaction block.
*/
CommandCounterIncrement();
}
/* special-case the unnamed statement */
pstmt = unnamed_stmt_pstmt;
if (!pstmt)
- elog(ERROR, "Unnamed prepared statement does not exist");
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
+ errmsg("unnamed prepared statement does not exist")));
}
if (whereToSendOutput != Remote)
/*
* First describe the parameters...
*/
- pq_beginmessage(&buf, 't'); /* parameter description message type */
+ pq_beginmessage(&buf, 't'); /* parameter description message type */
pq_sendint(&buf, length(pstmt->argtype_list), 2);
foreach(l, pstmt->argtype_list)
tupdesc = FetchPreparedStatementResultDesc(pstmt);
if (tupdesc)
{
- List *targetlist;
+ List *targetlist;
if (ChoosePortalStrategy(pstmt->query_list) == PORTAL_ONE_SELECT)
targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
- elog(ERROR, "Portal \"%s\" not found", portal_name);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR),
+ errmsg("portal \"%s\" does not exist", portal_name)));
if (whereToSendOutput != Remote)
return; /* can't actually do anything... */
if (portal->tupDesc)
{
- List *targetlist;
+ List *targetlist;
if (portal->strategy == PORTAL_ONE_SELECT)
targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
{
if (!xact_started)
{
- elog(DEBUG2, "StartTransactionCommand");
- StartTransactionCommand(false);
+ ereport(DEBUG3,
+ (errmsg_internal("StartTransactionCommand")));
+ StartTransactionCommand();
/* Set statement timeout running, if any */
if (StatementTimeout > 0)
}
static void
-finish_xact_command(bool forceCommit)
+finish_xact_command(void)
{
if (xact_started)
{
disable_sig_alarm(true);
/* Now commit the command */
- elog(DEBUG2, "CommitTransactionCommand");
+ ereport(DEBUG3,
+ (errmsg_internal("CommitTransactionCommand")));
+
+ CommitTransactionCommand();
- CommitTransactionCommand(forceCommit);
+#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 at each commit for leak tracking */
+ /* Print mem stats after each commit for leak tracking */
if (ShowStats)
MemoryContextStats(TopMemoryContext);
#endif
quickdie(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
- elog(WARNING, "Message from PostgreSQL backend:"
- "\n\tThe Postmaster has informed me that some other backend"
- "\n\tdied abnormally and possibly corrupted shared memory."
- "\n\tI have rolled back the current transaction and am"
- "\n\tgoing to terminate your database system connection and exit."
- "\n\tPlease reconnect to the database system and repeat your query.");
+
+ /*
+ * Ideally this should be ereport(FATAL), but then we'd not get
+ * control back (perhaps could fix by doing local sigsetjmp?)
+ */
+ ereport(WARNING,
+ (errcode(ERRCODE_CRASH_SHUTDOWN),
+ errmsg("terminating connection because of crash of another server process"),
+ 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.")));
/*
* DO NOT proc_exit() -- we're here because shared memory may be
* random backend. This is necessary precisely because we don't clean
* up our shared memory state.
*/
-
exit(1);
}
static void
FloatExceptionHandler(SIGNAL_ARGS)
{
- elog(ERROR, "floating point exception!"
- " The last floating point operation either exceeded legal ranges"
- " or was a divide by zero");
+ ereport(ERROR,
+ (errcode(ERRCODE_FLOATING_POINT_EXCEPTION),
+ errmsg("floating-point exception"),
+ 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 */
QueryCancelPending = false; /* ProcDie trumps QueryCancel */
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
- elog(FATAL, "This connection has been terminated by the administrator.");
+ ereport(FATAL,
+ (errcode(ERRCODE_ADMIN_SHUTDOWN),
+ errmsg("terminating connection due to administrator command")));
}
if (QueryCancelPending)
{
QueryCancelPending = false;
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
- elog(ERROR, "Query was canceled.");
+ ereport(ERROR,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling query 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)
+{
+ char stack_top_loc;
+ int stack_depth;
+
+ /*
+ * Compute distance from PostgresMain's local variables to my own
+ *
+ * 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);
+ /*
+ * Take abs value, since stacks grow up on some machines, down on others
+ */
+ if (stack_depth < 0)
+ stack_depth = -stack_depth;
+ /*
+ * 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;
+}
+
+
static void
usage(char *progname)
{
- printf("%s is the PostgreSQL stand-alone backend. It is not\nintended to be used by normal users.\n\n", progname);
+ printf(gettext("%s is the PostgreSQL stand-alone backend. It is not\nintended to be used by normal users.\n\n"), progname);
- printf("Usage:\n %s [OPTION]... [DBNAME]\n\n", progname);
- printf("Options:\n");
+ printf(gettext("Usage:\n %s [OPTION]... [DBNAME]\n\n"), progname);
+ printf(gettext("Options:\n"));
#ifdef USE_ASSERT_CHECKING
- printf(" -A 1|0 enable/disable run-time assert checking\n");
+ printf(gettext(" -A 1|0 enable/disable run-time assert checking\n"));
#endif
- printf(" -B NBUFFERS number of shared buffers (default %d)\n", DEF_NBUFFERS);
- printf(" -c NAME=VALUE set run-time parameter\n");
- printf(" -d 0-5 debugging level (0 is off)\n");
- printf(" -D DATADIR database directory\n");
- printf(" -e use European date format\n");
- printf(" -E echo query before execution\n");
- printf(" -F turn fsync off\n");
- printf(" -N do not use newline as interactive query delimiter\n");
- printf(" -o FILENAME send stdout and stderr to given file\n");
- printf(" -P disable system indexes\n");
- printf(" -s show statistics after each query\n");
- printf(" -S SORT-MEM set amount of memory for sorts (in kbytes)\n");
- printf(" --help show this help, then exit\n");
- printf(" --version output version information, then exit\n");
- printf("\nDeveloper options:\n");
- printf(" -f s|i|n|m|h forbid use of some plan types\n");
- printf(" -i do not execute queries\n");
- printf(" -O allow system table structure changes\n");
- printf(" -t pa|pl|ex show timings after each query\n");
- printf(" -W NUM wait NUM seconds to allow attach from a debugger\n");
- printf("\nReport bugs to <pgsql-bugs@postgresql.org>.\n");
+ printf(gettext(" -B NBUFFERS number of shared buffers\n"));
+ printf(gettext(" -c NAME=VALUE set run-time parameter\n"));
+ printf(gettext(" -d 0-5 debugging level (0 is off)\n"));
+ printf(gettext(" -D DATADIR database directory\n"));
+ printf(gettext(" -e use European date input format (DMY)\n"));
+ printf(gettext(" -E echo query before execution\n"));
+ printf(gettext(" -F turn fsync off\n"));
+ printf(gettext(" -N do not use newline as interactive query delimiter\n"));
+ printf(gettext(" -o FILENAME send stdout and stderr to given file\n"));
+ printf(gettext(" -P disable system indexes\n"));
+ printf(gettext(" -s show statistics after each query\n"));
+ printf(gettext(" -S WORK-MEM set amount of memory for sorts (in kbytes)\n"));
+ printf(gettext(" --describe-config describe configuration parameters, then exit\n"));
+ printf(gettext(" --help show this help, then exit\n"));
+ printf(gettext(" --version output version information, then exit\n"));
+ printf(gettext("\nDeveloper options:\n"));
+ printf(gettext(" -f s|i|n|m|h forbid use of some plan types\n"));
+ printf(gettext(" -i do not execute queries\n"));
+ printf(gettext(" -O allow system table structure changes\n"));
+ printf(gettext(" -t pa|pl|ex show timings after each query\n"));
+ printf(gettext(" -W NUM wait NUM seconds to allow attach from a debugger\n"));
+ printf(gettext("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
}
bool secure;
int errs = 0;
int debug_flag = 0;
- GucContext ctx;
+ GucContext ctx,
+ debug_context;
GucSource gucsource;
char *tmp;
int firstchar;
- StringInfo input_message;
- bool send_rfq;
+ char stack_base;
+ StringInfoData input_message;
+ volatile bool send_rfq = true;
/*
* Catch standard options before doing much else. This even works on
* initialize globals (already done if under postmaster, but not if
* standalone; cheap enough to do over)
*/
-
MyProcPid = getpid();
/*
SetProcessingMode(InitProcessing);
+ /* Set up reference point for stack depth checking */
+ stack_base_ptr = &stack_base;
+
/*
* Set default values for command-line options.
*/
- Noversion = false;
EchoQuery = false;
- if (!IsUnderPostmaster /* when exec || ExecBackend*/)
+ if (!IsUnderPostmaster)
{
InitializeGUCOptions();
potential_DataDir = getenv("PGDATA");
/* all options are allowed until '-p' */
secure = true;
- ctx = PGC_POSTMASTER;
+ ctx = debug_context = PGC_POSTMASTER;
gucsource = PGC_S_ARGV; /* initial switches came from command line */
- while ((flag = getopt(argc, argv, "A:B:c:CD:d:Eef:FiNOPo:p:S:st:v:W:x:-:")) != -1)
+ while ((flag = getopt(argc, argv, "A:B:c:D:d:Eef:FiNOPo:p:S:st:v:W:x:-:")) != -1)
switch (flag)
{
case 'A':
#ifdef USE_ASSERT_CHECKING
SetConfigOption("debug_assertions", optarg, ctx, gucsource);
#else
- elog(WARNING, "Assert checking is not compiled in");
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("assert checking is not compiled in")));
#endif
break;
SetConfigOption("shared_buffers", optarg, ctx, gucsource);
break;
- case 'C':
-
- /*
- * don't print version string
- */
- Noversion = true;
- break;
-
case 'D': /* PGDATA directory */
if (secure)
potential_DataDir = optarg;
case 'd': /* debug level */
{
- debug_flag = atoi(optarg);
- /* Set server debugging level. */
- if (atoi(optarg) != 0)
+ /*
+ * Client option can't decrease debug level. We have
+ * to do the test here because we group priv and
+ * client set GUC calls below, after we know the final
+ * debug value.
+ */
+ if (ctx != PGC_BACKEND || atoi(optarg) > debug_flag)
{
- char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
-
- sprintf(debugstr, "debug%s", optarg);
- SetConfigOption("log_min_messages", debugstr, ctx, gucsource);
- pfree(debugstr);
+ debug_flag = atoi(optarg);
+ debug_context = ctx; /* save context for use
+ * below */
+ /* Set server debugging level. */
+ if (debug_flag != 0)
+ {
+ char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
+
+ sprintf(debugstr, "debug%s", optarg);
+ SetConfigOption("log_min_messages", debugstr, ctx, gucsource);
+ pfree(debugstr);
+
+ }
+ else
+ /*
+ * -d0 allows user to prevent postmaster debug
+ * from propagating to backend. It would be
+ * nice to set it to the postgresql.conf value
+ * here.
+ */
+ SetConfigOption("log_min_messages", "notice",
+ ctx, gucsource);
}
- else
- /*
- * -d0 allows user to prevent postmaster debug
- * from propagating to backend. It would be nice
- * to set it to the postgresql.conf value here.
- */
- SetConfigOption("log_min_messages", "notice",
- ctx, gucsource);
}
break;
case 'e':
/*
- * Use european date formats.
+ * Use European date input format (DMY)
*/
SetConfigOption("datestyle", "euro", ctx, gucsource);
break;
/*
* ignore system indexes
+ *
+ * As of PG 7.4 this is safe to allow from the client,
+ * since it only disables reading the system indexes,
+ * not writing them. Worst case consequence is slowness.
*/
- if (secure) /* XXX safe to allow from client??? */
- IgnoreSystemIndexes(true);
+ IgnoreSystemIndexes(true);
break;
case 'o':
break;
case 'p':
-
/*
* p - special flag passed if backend was forked by a
* postmaster.
*/
if (secure)
{
-#ifdef EXEC_BACKEND
- char *p;
- int i;
- int PMcanAcceptConnections; /* will eventually be global or static, when fork */
-
- sscanf(optarg, "%d,%d,%d,%p,", &MyProcPort->sock, &PMcanAcceptConnections,
- &UsedShmemSegID, &UsedShmemSegAddr);
- /* Grab dbname as last param */
- for (i = 0, p = optarg-1; i < 4 && p; i++)
- p = strchr(p+1, ',');
- if (i == 4 && p)
- dbname = strdup(p+1);
-#else
dbname = strdup(optarg);
-#endif
+
secure = false; /* subsequent switches are NOT
* secure */
ctx = PGC_BACKEND;
/*
* S - amount of sort memory to use in 1k bytes
*/
- SetConfigOption("sort_mem", optarg, ctx, gucsource);
+ SetConfigOption("work_mem", optarg, ctx, gucsource);
break;
case 's':
/*
* s - report usage statistics (timings) after each query
*/
- SetConfigOption("show_statement_stats", "true", ctx, gucsource);
+ SetConfigOption("log_statement_stats", "true", ctx, gucsource);
break;
case 't':
errs++;
break;
case 'e':
- tmp = "show_executor_stats";
+ tmp = "log_executor_stats";
break;
default:
errs++;
/*
* wait N seconds to allow attach from a debugger
*/
- sleep(atoi(optarg));
+ pg_usleep(atoi(optarg)*1000000L);
break;
case 'x':
if (!value)
{
if (flag == '-')
- elog(ERROR, "--%s requires argument", optarg);
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("--%s requires a value",
+ optarg)));
else
- elog(ERROR, "-c %s requires argument", optarg);
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("-c %s requires a value",
+ optarg)));
}
SetConfigOption(name, value, ctx, gucsource);
/*
- * -d is not the same as setting
- * log_min_messages because it enables other
- * output options.
+ * -d is not the same as setting log_min_messages because it enables
+ * other output options.
*/
if (debug_flag >= 1)
- SetConfigOption("log_connections", "true", ctx, gucsource);
+ {
+ SetConfigOption("log_connections", "true", debug_context, gucsource);
+ SetConfigOption("log_disconnections", "true", debug_context, gucsource);
+ }
if (debug_flag >= 2)
- SetConfigOption("log_statement", "true", ctx, gucsource);
+ SetConfigOption("log_statement", "all", debug_context, gucsource);
if (debug_flag >= 3)
- SetConfigOption("debug_print_parse", "true", ctx, gucsource);
+ SetConfigOption("debug_print_parse", "true", debug_context, gucsource);
if (debug_flag >= 4)
- SetConfigOption("debug_print_plan", "true", ctx, gucsource);
+ SetConfigOption("debug_print_plan", "true", debug_context, gucsource);
if (debug_flag >= 5)
- SetConfigOption("debug_print_rewritten", "true", ctx, gucsource);
+ SetConfigOption("debug_print_rewritten", "true", debug_context, gucsource);
/*
- * Process any additional GUC variable settings passed in startup packet.
+ * Process any additional GUC variable settings passed in startup
+ * packet.
*/
if (MyProcPort != NULL)
{
- List *gucopts = MyProcPort->guc_options;
+ List *gucopts = MyProcPort->guc_options;
while (gucopts)
{
gucopts = lnext(gucopts);
SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
}
+
+ /*
+ * set up handler to log session end.
+ */
+ if (IsUnderPostmaster && Log_disconnections)
+ on_proc_exit(log_disconnections,0);
}
/*
if (log_statement_stats &&
(log_parser_stats || log_planner_stats || log_executor_stats))
{
- elog(WARNING, "Query statistics are disabled because parser, planner, or executor statistics are on.");
- SetConfigOption("show_statement_stats", "false", ctx, gucsource);
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("statement-level statistics are disabled because parser, planner, or executor statistics are on")));
+ SetConfigOption("log_statement_stats", "false", ctx, gucsource);
}
- if (!IsUnderPostmaster)
+ if (!IsUnderPostmaster || ExecBackend)
{
if (!potential_DataDir)
{
- fprintf(stderr, "%s does not know where to find the database system "
- "data. You must specify the directory that contains the "
- "database system either by specifying the -D invocation "
- "option or by setting the PGDATA environment variable.\n\n",
+ fprintf(stderr,
+ gettext("%s does not know where to find the database system data.\n"
+ "You must specify the directory that contains the database system\n"
+ "either by specifying the -D invocation option or by setting the\n"
+ "PGDATA environment variable.\n"),
argv[0]);
proc_exit(1);
}
}
Assert(DataDir);
+ /* Acquire configuration parameters */
+ if (IsUnderPostmaster)
+ {
#ifdef EXEC_BACKEND
- read_nondefault_variables();
+ read_nondefault_variables();
#endif
+ } else
+ ProcessConfigFile(PGC_POSTMASTER);
/*
* Set up signal handlers and masks.
* 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 */
pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */
pqsignal(SIGTERM, die); /* cancel current query and exit */
pqsignal(SIGQUIT, quickdie); /* hard crash time */
- pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
+ pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
/*
* Ignore failure to write to frontend. Note: if frontend closes
/* noninteractive case: nothing should be left after switches */
if (errs || argc != optind || dbname == NULL)
{
- elog(WARNING, "%s: invalid command line arguments\nTry -? for help.",
- argv[0]);
- proc_exit(0); /* not 1, that causes system-wide
- * restart... */
+ ereport(FATAL,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid command-line arguments for server process"),
+ errhint("Try \"%s --help\" for more information.", argv[0])));
}
+
+ XLOGPathInit();
+
BaseInit();
-#ifdef EXECBACKEND
- AttachSharedMemoryAndSemaphores();
-#endif
}
else
{
/* interactive case: database name can be last arg on command line */
if (errs || argc - optind > 1)
{
- elog(WARNING, "%s: invalid command line arguments\nTry -? for help.",
- argv[0]);
- proc_exit(1);
+ ereport(FATAL,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("%s: invalid command-line arguments",
+ argv[0]),
+ errhint("Try \"%s --help\" for more information.", argv[0])));
}
else if (argc - optind == 1)
dbname = argv[optind];
else if ((dbname = username) == NULL)
{
- elog(WARNING, "%s: user name undefined and no database specified",
- argv[0]);
- proc_exit(1);
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%s: no database nor user name specified",
+ argv[0])));
}
/*
* pathname. (If under postmaster, this was done already.)
*/
if (FindExec(pg_pathname, argv[0], "postgres") < 0)
- elog(FATAL, "%s: could not locate executable, bailing out...",
- argv[0]);
+ ereport(FATAL,
+ (errmsg("%s: could not locate postgres executable",
+ argv[0])));
/*
* Validate we have been given a reasonable-looking DataDir (if
/*
* Create lockfile for data directory.
*/
- if (!CreateDataDirLockFile(DataDir, false))
- proc_exit(1);
+ CreateDataDirLockFile(DataDir, false);
XLOGPathInit();
BaseInit();
on_shmem_exit(DumpFreeSpaceMap, 0);
}
- /*
- * Set up additional info.
- */
-
-#ifdef CYR_RECODE
- SetCharSet();
-#endif
-
/*
* General initialization.
*
* putting it inside InitPostgres() instead. In particular, anything
* that involves database access should be there, not here.
*/
- elog(DEBUG2, "InitPostgres");
+ ereport(DEBUG3,
+ (errmsg_internal("InitPostgres")));
InitPostgres(dbname, username);
SetProcessingMode(NormalProcessing);
/* Need not flush since ReadyForQuery will do it. */
}
- if (!IsUnderPostmaster)
- {
- puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.341 $ $Date: 2003/05/09 15:57:24 $\n");
- }
+ /* Welcome banner for standalone case */
+ if (whereToSendOutput == Debug)
+ printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION);
/*
* Create the memory context we will use in the main loop.
*
* Make sure we're not interrupted while cleaning up. Also forget
* any pending QueryCancel request, since we're aborting anyway.
- * Force InterruptHoldoffCount to a known state in case we elog'd
- * from inside a holdoff section.
+ * Force InterruptHoldoffCount to a known state in case we
+ * ereport'd from inside a holdoff section.
*/
ImmediateInterruptOK = false;
QueryCancelPending = false;
InterruptHoldoffCount = 1;
CritSectionCount = 0; /* should be unnecessary, but... */
disable_sig_alarm(true);
- QueryCancelPending = false; /* again in case timeout occurred */
+ QueryCancelPending = false; /* again in case timeout occurred */
DisableNotifyInterrupt();
debug_query_string = NULL;
MemoryContextSwitchTo(ErrorContext);
/* Do the recovery */
- elog(DEBUG1, "AbortCurrentTransaction");
+ ereport(DEBUG2,
+ (errmsg_internal("AbortCurrentTransaction")));
AbortCurrentTransaction();
/*
*/
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextResetAndDeleteChildren(ErrorContext);
+ ActivePortal = NULL;
PortalContext = NULL;
QueryContext = NULL;
InError = false;
xact_started = false;
+ /*
+ * Clear flag that causes accounting for cost based vacuum.
+ */
+ VacuumCostActive = false;
+
/*
* If we were handling an extended-query-protocol message,
- * initiate skip till next Sync.
+ * initiate skip till next Sync. This also causes us not to issue
+ * ReadyForQuery (until we get Sync).
*/
if (doing_extended_query_message)
ignore_till_sync = true;
RESUME_INTERRUPTS();
}
- Warn_restart_ready = true; /* we can now handle elog(ERROR) */
+ Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
PG_SETMASK(&UnBlockSig);
- send_rfq = true; /* initially, or after error */
+ if (!ignore_till_sync)
+ send_rfq = true; /* initially, or after error */
/*
* Non-error queries loop here.
for (;;)
{
/*
- * At top of loop, reset extended-query-message flag, so that
- * any errors encountered in "idle" state don't provoke skip.
+ * 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;
MemoryContextSwitchTo(MessageContext);
MemoryContextResetAndDeleteChildren(MessageContext);
- input_message = makeStringInfo();
+ initStringInfo(&input_message);
/*
- * (1) tell the frontend we're ready for a new query.
+ * (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
+ * those every time through the message loop because it'd slow down
+ * processing of batched messages.
*/
if (send_rfq)
{
- ReadyForQuery(whereToSendOutput);
- send_rfq = false;
- }
+ pgstat_report_tabstat();
- /* ----------
- * Tell the statistics collector what we've collected
- * so far.
- * ----------
- */
- pgstat_report_tabstat();
+ if (IsTransactionOrTransactionBlock())
+ {
+ set_ps_display("idle in transaction");
+ pgstat_report_activity("<IDLE> in transaction");
+ }
+ else
+ {
+ set_ps_display("idle");
+ pgstat_report_activity("<IDLE>");
+ }
- if (IsTransactionBlock())
- {
- set_ps_display("idle in transaction");
- pgstat_report_activity("<IDLE> in transaction");
- }
- else
- {
- set_ps_display("idle");
- pgstat_report_activity("<IDLE>");
+ ReadyForQuery(whereToSendOutput);
+ send_rfq = false;
}
/*
/*
* (3) read a command (loop blocks here)
*/
- firstchar = ReadCommand(input_message);
+ if (!in_fatal_exit)
+ firstchar = ReadCommand(&input_message);
+ else
+ firstchar = EOF;
/*
* (4) disable async signal conditions again.
if (got_SIGHUP)
{
got_SIGHUP = false;
+#ifdef EXEC_BACKEND
+ read_nondefault_variables();
+#else
ProcessConfigFile(PGC_SIGHUP);
+#endif
}
/*
- * (6) process the command. But ignore it if we're skipping till Sync.
+ * (6) process the command. But ignore it if we're skipping till
+ * Sync.
*/
- if (ignore_till_sync)
+ if (ignore_till_sync && firstchar != EOF)
continue;
switch (firstchar)
{
const char *query_string;
- query_string = pq_getmsgstring(input_message);
- pq_getmsgend(input_message);
+ query_string = pq_getmsgstring(&input_message);
+ pq_getmsgend(&input_message);
exec_simple_query(query_string);
int numParams;
Oid *paramTypes = NULL;
- stmt_name = pq_getmsgstring(input_message);
- query_string = pq_getmsgstring(input_message);
- numParams = pq_getmsgint(input_message, 2);
+ stmt_name = pq_getmsgstring(&input_message);
+ query_string = pq_getmsgstring(&input_message);
+ numParams = pq_getmsgint(&input_message, 2);
if (numParams > 0)
{
- int i;
+ int i;
paramTypes = (Oid *) palloc(numParams * sizeof(Oid));
for (i = 0; i < numParams; i++)
- paramTypes[i] = pq_getmsgint(input_message, 4);
+ paramTypes[i] = pq_getmsgint(&input_message, 4);
}
- pq_getmsgend(input_message);
+ pq_getmsgend(&input_message);
exec_parse_message(query_string, stmt_name,
paramTypes, numParams);
break;
case 'B': /* bind */
+
/*
- * this message is complex enough that it seems best to put
- * the field extraction out-of-line
+ * this message is complex enough that it seems best to
+ * put the field extraction out-of-line
*/
- exec_bind_message(input_message);
+ exec_bind_message(&input_message);
break;
case 'E': /* execute */
{
const char *portal_name;
- int max_rows;
+ int max_rows;
- portal_name = pq_getmsgstring(input_message);
- max_rows = pq_getmsgint(input_message, 4);
- pq_getmsgend(input_message);
+ portal_name = pq_getmsgstring(&input_message);
+ max_rows = pq_getmsgint(&input_message, 4);
+ pq_getmsgend(&input_message);
exec_execute_message(portal_name, max_rows);
}
/* start an xact for this function invocation */
start_xact_command();
- if (HandleFunctionRequest(input_message) == EOF)
+ /* switch back to message context */
+ MemoryContextSwitchTo(MessageContext);
+
+ if (HandleFunctionRequest(&input_message) == EOF)
{
/* lost frontend connection during F message input */
/*
- * Reset whereToSendOutput to prevent elog from
+ * Reset whereToSendOutput to prevent ereport from
* attempting to send any more messages to client.
*/
if (whereToSendOutput == Remote)
}
/* commit the function-invocation transaction */
- finish_xact_command(false);
+ finish_xact_command();
send_rfq = true;
break;
- case 'C': /* close */
+ case 'C': /* close */
{
- int close_type;
+ int close_type;
const char *close_target;
- close_type = pq_getmsgbyte(input_message);
- close_target = pq_getmsgstring(input_message);
- pq_getmsgend(input_message);
+ close_type = pq_getmsgbyte(&input_message);
+ close_target = pq_getmsgstring(&input_message);
+ pq_getmsgend(&input_message);
switch (close_type)
{
}
break;
default:
- elog(ERROR, "Invalid Close message subtype %d",
- close_type);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid CLOSE message subtype %d",
+ close_type)));
break;
}
if (whereToSendOutput == Remote)
- pq_putemptymessage('3'); /* CloseComplete */
+ pq_putemptymessage('3'); /* CloseComplete */
}
break;
case 'D': /* describe */
{
- int describe_type;
+ int describe_type;
const char *describe_target;
- describe_type = pq_getmsgbyte(input_message);
- describe_target = pq_getmsgstring(input_message);
- pq_getmsgend(input_message);
+ describe_type = pq_getmsgbyte(&input_message);
+ describe_target = pq_getmsgstring(&input_message);
+ pq_getmsgend(&input_message);
switch (describe_type)
{
exec_describe_portal_message(describe_target);
break;
default:
- elog(ERROR, "Invalid Describe message subtype %d",
- describe_type);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid DESCRIBE message subtype %d",
+ describe_type)));
break;
}
}
break;
- case 'H': /* flush */
- pq_getmsgend(input_message);
+ case 'H': /* flush */
+ pq_getmsgend(&input_message);
if (whereToSendOutput == Remote)
pq_flush();
break;
- case 'S': /* sync */
- pq_getmsgend(input_message);
- finish_xact_command(false);
+ case 'S': /* sync */
+ pq_getmsgend(&input_message);
+ finish_xact_command();
send_rfq = true;
break;
case EOF:
/*
- * Reset whereToSendOutput to prevent elog from attempting
- * to send any more messages to client.
+ * Reset whereToSendOutput to prevent ereport from
+ * attempting to send any more messages to client.
*/
if (whereToSendOutput == Remote)
whereToSendOutput = None;
* Otherwise it will fail to be called during other
* backend-shutdown scenarios.
*/
- proc_exit(0);
+ proc_exit(!in_fatal_exit ? 0 : proc_exit_inprogress ||
+ !IsUnderPostmaster);
+
+ case 'd': /* copy data */
+ case 'c': /* copy done */
+ case 'f': /* copy fail */
- case 'd': /* copy data */
- case 'c': /* copy done */
- case 'f': /* copy fail */
/*
- * Accept but ignore these messages, per protocol spec;
- * we probably got here because a COPY failed, and the
+ * Accept but ignore these messages, per protocol spec; we
+ * probably got here because a COPY failed, and the
* frontend is still sending data.
*/
break;
default:
- elog(FATAL, "Socket command type %c unknown", firstchar);
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d",
+ firstchar)));
}
-
-#ifdef MEMORY_CONTEXT_CHECKING
-
- /*
- * Check all memory after each backend loop. This is a rather
- * weird place to do it, perhaps.
- */
- MemoryContextCheck(TopMemoryContext);
-#endif
} /* end of input-reading loop */
/* can't get here because the above loop never exits */
#include <sys/resource.h>
#endif /* HAVE_GETRUSAGE */
-struct rusage Save_r;
-struct timeval Save_t;
+static struct rusage Save_r;
+static struct timeval Save_t;
void
ResetUsage(void)
(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),
- (long) (r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec),
+ (long) (r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec),
(long) (r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec),
- (long) (r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec));
+ (long) (r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec));
appendStringInfo(&str,
"!\t[%ld.%06ld user %ld.%06ld sys total]\n",
(long) user.tv_sec,
if (str.data[str.len - 1] == '\n')
str.data[--str.len] = '\0';
- elog(LOG, "%s\n%s", title, str.data);
+ ereport(LOG,
+ (errmsg_internal("%s", title),
+ errdetail("%s", str.data)));
pfree(str.data);
}
+
+/*
+ * on_proc_exit handler to log end of session
+ */
+static void
+log_disconnections(int code, Datum arg)
+{
+ Port *port = MyProcPort;
+ struct timeval end;
+ int hours, minutes, seconds;
+
+ char session_time[20];
+ char uname[6+NAMEDATALEN];
+ char dbname[10+NAMEDATALEN];
+ char remote_host[7 + NI_MAXHOST];
+ char remote_port[7 + NI_MAXSERV];
+
+ snprintf(uname, sizeof(uname)," user=%s",port->user_name);
+ snprintf(dbname, sizeof(dbname)," database=%s",port->database_name);
+ snprintf(remote_host,sizeof(remote_host)," host=%s",
+ port->remote_host);
+ snprintf(remote_port,sizeof(remote_port)," port=%s",port->remote_port);
+
+
+ gettimeofday(&end,NULL);
+
+ if (end.tv_usec < port->session_start.tv_usec)
+ {
+ end.tv_sec--;
+ end.tv_usec += 1000000;
+ }
+ end.tv_sec -= port->session_start.tv_sec;
+ end.tv_usec -= port->session_start.tv_usec;
+
+ hours = end.tv_sec / 3600;
+ end.tv_sec %= 3600;
+ minutes = end.tv_sec / 60;
+ seconds = end.tv_sec % 60;
+
+ /* if time has gone backwards for some reason say so, or print time */
+
+ if (end.tv_sec < 0)
+ snprintf(session_time,sizeof(session_time),"negative!");
+ else
+ /* for stricter accuracy here we could round - this is close enough */
+ snprintf(session_time, sizeof(session_time),
+ "%d:%02d:%02d.%02d",
+ hours, minutes, seconds, (int) (end.tv_usec/10000));
+
+ ereport(
+ LOG,
+ (errmsg("disconnection: session time: %s%s%s%s%s",
+ session_time,uname,dbname,remote_host,remote_port)));
+
+}