X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Ftcop%2Fpostgres.c;h=ed5f050242091d4c0e7ccf0aa9d080a13c709436;hb=a536b2dd80f29464b0461e3980043ec4a822e820;hp=70ac378f0e67bcb3bf6e266f5e23535ca5d486f9;hpb=9d77708d83ee3c2f4a67d169e274e7b208008b7c;p=postgresql diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 70ac378f0e..ed5f050242 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3,12 +3,12 @@ * postgres.c * POSTGRES C Backend Interface * - * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2005, 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.374 2003/10/18 22:59:08 petere Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.455 2005/07/21 03:56:11 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -21,11 +21,8 @@ #include #include -#include -#include #include #include -#include #if HAVE_SYS_SELECT_H #include #endif @@ -53,10 +50,12 @@ #include "storage/ipc.h" #include "storage/pg_shmem.h" #include "storage/proc.h" +#include "storage/sinval.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" +#include "utils/flatfiles.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -68,7 +67,6 @@ extern int optind; extern char *optarg; - /* ---------------- * global variables * ---------------- @@ -79,28 +77,33 @@ const char *debug_query_string; /* for pgmonitor and /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */ CommandDest whereToSendOutput = Debug; -/* note: these declarations had better match tcopprot.h */ -sigjmp_buf Warn_restart; +/* flag for logging end of session */ +bool Log_disconnections = false; -bool Warn_restart_ready = false; -bool InError = false; +LogStmtLevel log_statement = LOGSTMT_NONE; + +/* GUC variable for maximum stack depth (measured in kilobytes) */ +int max_stack_depth = 2048; -/* - * Flags for expensive function optimization -- JMH 3/9/92 - */ -int XfuncMode = 0; /* ---------------- * 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 * reading in the signal handler, ey?) */ -static volatile bool got_SIGHUP = false; +static volatile sig_atomic_t got_SIGHUP = false; /* * Flag to keep track of whether we have started a transaction. @@ -108,6 +111,13 @@ static volatile bool got_SIGHUP = false; */ 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. @@ -146,10 +156,14 @@ static int UseNewLine = 0; /* Use EOF as query delimiters */ 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 void SigHupHandler(SIGNAL_ARGS); static void FloatExceptionHandler(SIGNAL_ARGS); +static void log_disconnections(int code, Datum arg); /* ---------------------------------------------------------------- @@ -393,13 +407,59 @@ ReadCommand(StringInfo inBuf) { int result; - if (IsUnderPostmaster) + if (whereToSendOutput == Remote) 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. @@ -417,7 +477,7 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ { List *raw_parsetree_list; List *querytree_list; - List *list_item; + ListCell *list_item; /* * (1) parse the request string into a list of raw parse trees. @@ -432,10 +492,10 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ { Node *parsetree = (Node *) lfirst(list_item); - querytree_list = nconc(querytree_list, - pg_analyze_and_rewrite(parsetree, - paramTypes, - numParams)); + querytree_list = list_concat(querytree_list, + pg_analyze_and_rewrite(parsetree, + paramTypes, + numParams)); } return querytree_list; @@ -459,10 +519,6 @@ pg_parse_query(const char *query_string) { List *raw_parsetree_list; - if (log_statement) - ereport(LOG, - (errmsg("statement: %s", query_string))); - if (log_parser_stats) ResetUsage(); @@ -474,6 +530,84 @@ pg_parse_query(const char *query_string) return raw_parsetree_list; } +static bool +log_after_parse(List *raw_parsetree_list, const char *query_string, + char **prepare_string) +{ + ListCell *parsetree_item; + bool log_this_statement = (log_statement == LOGSTMT_ALL); + + *prepare_string = NULL; + + /* Check if we need to log the statement, and get prepare_string. */ + 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) && + ((SelectStmt *) parsetree)->into == NULL) + 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 */ + log_this_statement = true; + + commandTag = CreateCommandTag(parsetree); + if ((log_statement == LOGSTMT_MOD || + log_statement == LOGSTMT_DDL) && + (strncmp(commandTag, "CREATE ", strlen("CREATE ")) == 0 || + IsA(parsetree, SelectStmt) || /* SELECT INTO, CREATE AS */ + strncmp(commandTag, "ALTER ", strlen("ALTER ")) == 0 || + strncmp(commandTag, "DROP ", strlen("DROP ")) == 0 || + IsA(parsetree, GrantStmt) || /* GRANT or REVOKE */ + IsA(parsetree, CommentStmt))) + log_this_statement = true; + + /* + * For the first EXECUTE we find, record the client statement + * used by the PREPARE. + */ + if (IsA(parsetree, ExecuteStmt)) + { + ExecuteStmt *stmt = (ExecuteStmt *) parsetree; + PreparedStatement *entry; + + if ((entry = FetchPreparedStatement(stmt->name, false)) != NULL && + entry->query_string) + { + *prepare_string = palloc(strlen(entry->query_string) + + strlen(" [client PREPARE: %s]") - 1); + sprintf(*prepare_string, " [client PREPARE: %s]", + entry->query_string); + } + } + } + + 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. @@ -509,12 +643,15 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams) /* * 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. */ -List * +static List * pg_rewrite_queries(List *querytree_list) { List *new_list = NIL; - List *list_item; + ListCell *list_item; if (log_parser_stats) ResetUsage(); @@ -541,7 +678,7 @@ pg_rewrite_queries(List *querytree_list) /* rewrite regular queries */ List *rewritten = QueryRewrite(querytree); - new_list = nconc(new_list, rewritten); + new_list = list_concat(new_list, rewritten); } } @@ -574,7 +711,7 @@ pg_rewrite_queries(List *querytree_list) /* Generate a plan for a single already-rewritten query. */ Plan * -pg_plan_query(Query *querytree) +pg_plan_query(Query *querytree, ParamListInfo boundParams) { Plan *plan; @@ -586,7 +723,7 @@ pg_plan_query(Query *querytree) ResetUsage(); /* call the optimizer */ - plan = planner(querytree, false, 0); + plan = planner(querytree, false, 0, boundParams); if (log_planner_stats) ShowUsage("PLANNER STATISTICS"); @@ -631,10 +768,11 @@ pg_plan_query(Query *querytree) * statements in the rewriter's output.) */ List * -pg_plan_queries(List *querytrees, bool needSnapshot) +pg_plan_queries(List *querytrees, ParamListInfo boundParams, + bool needSnapshot) { List *plan_list = NIL; - List *query_list; + ListCell *query_list; foreach(query_list, querytrees) { @@ -650,10 +788,10 @@ pg_plan_queries(List *querytrees, bool needSnapshot) { if (needSnapshot) { - SetQuerySnapshot(); + ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); needSnapshot = false; } - plan = pg_plan_query(query); + plan = pg_plan_query(query, boundParams); } plan_list = lappend(plan_list, plan); @@ -673,14 +811,15 @@ exec_simple_query(const char *query_string) { CommandDest dest = whereToSendOutput; MemoryContext oldcontext; - List *parsetree_list, - *parsetree_item; - struct timeval start_t, - stop_t; + List *parsetree_list; + ListCell *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; - + char *prepare_string = NULL; + bool was_logged = false; + /* * Report query to various monitoring facilities. */ @@ -736,6 +875,10 @@ exec_simple_query(const char *query_string) */ parsetree_list = pg_parse_query(query_string); + if (log_statement != LOGSTMT_NONE || save_log_min_duration_statement != -1) + was_logged = log_after_parse(parsetree_list, query_string, + &prepare_string); + /* * Switch back to transaction context to enter the loop. */ @@ -784,7 +927,9 @@ exec_simple_query(const char *query_string) TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) + stmt->kind == TRANS_STMT_PREPARE || + stmt->kind == TRANS_STMT_ROLLBACK || + stmt->kind == TRANS_STMT_ROLLBACK_TO) allowit = true; } @@ -792,7 +937,7 @@ exec_simple_query(const char *query_string) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"))); } /* Make sure we are in a transaction command */ @@ -811,7 +956,7 @@ exec_simple_query(const char *query_string) querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0); - plantree_list = pg_plan_queries(querytree_list, true); + plantree_list = pg_plan_queries(querytree_list, NULL, true); /* If we got a cancel signal in analysis or planning, quit */ CHECK_FOR_INTERRUPTS(); @@ -832,7 +977,7 @@ exec_simple_query(const char *query_string) /* * Start the portal. No parameters here. */ - PortalStart(portal, NULL); + PortalStart(portal, NULL, InvalidSnapshot); /* * Select the appropriate output format: text unless we are doing @@ -889,7 +1034,7 @@ exec_simple_query(const char *query_string) */ finish_xact_command(); } - else if (lnext(parsetree_item) == NIL) + else if (lnext(parsetree_item) == NULL) { /* * If this is the last parsetree of the query string, close @@ -950,33 +1095,39 @@ exec_simple_query(const char *query_string) stop_t.tv_sec--; stop_t.tv_usec += 1000000; } - usecs = (long) (stop_t.tv_sec - start_t.tv_sec) * 1000000 + (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) + /* Only print duration if we previously printed the statement. */ + if (was_logged && 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))); + (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. + * 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))); + (errmsg("duration: %ld.%03ld ms statement: %s%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, + prepare_string ? prepare_string : ""))); } if (save_log_statement_stats) ShowUsage("QUERY STATISTICS"); + if (prepare_string != NULL) + pfree(prepare_string); + debug_query_string = NULL; } @@ -1012,6 +1163,10 @@ exec_parse_message(const char *query_string, /* string to execute */ if (save_log_statement_stats) ResetUsage(); + if (log_statement == LOGSTMT_ALL) + ereport(LOG, + (errmsg("statement: PREPARE %s AS %s", stmt_name, query_string))); + /* * Start up a transaction command so we can run parse analysis etc. * (Note that this will normally change current memory context.) @@ -1072,14 +1227,14 @@ exec_parse_message(const char *query_string, /* string to execute */ * 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) + if (list_length(parsetree_list) > 1) 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); + Node *parsetree = (Node *) linitial(parsetree_list); int i; /* @@ -1104,7 +1259,9 @@ exec_parse_message(const char *query_string, /* string to execute */ TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) + stmt->kind == TRANS_STMT_PREPARE || + stmt->kind == TRANS_STMT_ROLLBACK || + stmt->kind == TRANS_STMT_ROLLBACK_TO) allowit = true; } @@ -1112,7 +1269,7 @@ exec_parse_message(const char *query_string, /* string to execute */ ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"))); } /* @@ -1139,9 +1296,9 @@ exec_parse_message(const char *query_string, /* string to execute */ if (ptype == InvalidOid || ptype == UNKNOWNOID) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_DATATYPE), - errmsg("could not determine data type of parameter $%d", - i + 1))); - param_list = lappendo(param_list, ptype); + errmsg("could not determine data type of parameter $%d", + i + 1))); + param_list = lappend_oid(param_list, ptype); } if (log_parser_stats) @@ -1149,7 +1306,14 @@ exec_parse_message(const char *query_string, /* string to execute */ querytree_list = pg_rewrite_queries(querytree_list); - plantree_list = pg_plan_queries(querytree_list, true); + /* + * 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 { @@ -1235,6 +1399,7 @@ exec_bind_message(StringInfo input_message) PreparedStatement *pstmt; Portal portal; ParamListInfo params; + bool isaborted = IsAbortedTransactionBlockState(); pgstat_report_activity(""); @@ -1285,11 +1450,11 @@ exec_bind_message(StringInfo input_message) errmsg("unnamed prepared statement does not exist"))); } - if (numParams != length(pstmt->argtype_list)) + 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", - numParams, stmt_name, length(pstmt->argtype_list)))); + numParams, stmt_name, list_length(pstmt->argtype_list)))); /* * Create the portal. Allow silent replacement of an existing portal @@ -1300,12 +1465,9 @@ exec_bind_message(StringInfo input_message) else portal = CreatePortal(portal_name, false, false); - PortalDefineQuery(portal, - pstmt->query_string, - pstmt->commandTag, - pstmt->query_list, - pstmt->plan_list, - pstmt->context); + if (log_statement == LOGSTMT_ALL) + ereport(LOG, + (errmsg("statement: %s", portal_name))); /* * Fetch parameters, if any, and store in the portal's memory context. @@ -1316,8 +1478,7 @@ exec_bind_message(StringInfo input_message) */ if (numParams > 0) { - bool isaborted = IsAbortedTransactionBlockState(); - List *l; + ListCell *l; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); @@ -1328,7 +1489,7 @@ exec_bind_message(StringInfo input_message) i = 0; foreach(l, pstmt->argtype_list) { - Oid ptype = lfirsto(l); + Oid ptype = lfirst_oid(l); int32 plength; bool isNull; @@ -1376,11 +1537,11 @@ exec_bind_message(StringInfo input_message) if (pformat == 0) { - Oid typInput; - Oid typElem; + Oid typinput; + Oid typioparam; char *pstring; - getTypeInputInfo(ptype, &typInput, &typElem); + getTypeInputInfo(ptype, &typinput, &typioparam); /* * We have to do encoding conversion before @@ -1390,9 +1551,9 @@ exec_bind_message(StringInfo input_message) pg_client_to_server((unsigned char *) pbuf.data, plength); params[i].value = - OidFunctionCall3(typInput, + OidFunctionCall3(typinput, CStringGetDatum(pstring), - ObjectIdGetDatum(typElem), + ObjectIdGetDatum(typioparam), Int32GetDatum(-1)); /* Free result of encoding conversion, if any */ if (pstring != pbuf.data) @@ -1400,19 +1561,20 @@ exec_bind_message(StringInfo input_message) } else if (pformat == 1) { - Oid typReceive; - Oid typElem; + Oid typreceive; + Oid typioparam; /* * Call the parameter type's binary input * converter */ - getTypeBinaryInputInfo(ptype, &typReceive, &typElem); + getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); params[i].value = - OidFunctionCall2(typReceive, + OidFunctionCall3(typreceive, PointerGetDatum(&pbuf), - ObjectIdGetDatum(typElem)); + ObjectIdGetDatum(typioparam), + Int32GetDatum(-1)); /* Trouble if it didn't eat the whole buffer */ if (pbuf.cursor != pbuf.len) @@ -1436,6 +1598,7 @@ exec_bind_message(StringInfo input_message) params[i].kind = PARAM_NUM; params[i].id = i + 1; + params[i].ptype = ptype; params[i].isnull = isNull; i++; @@ -1460,9 +1623,33 @@ exec_bind_message(StringInfo input_message) pq_getmsgend(input_message); /* - * Start portal execution. + * 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. + * + * 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 && + !isaborted) + { + MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context); + + pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true); + MemoryContextSwitchTo(oldContext); + } + + /* + * Define portal and start execution. */ - PortalStart(portal, params); + 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. @@ -1491,6 +1678,10 @@ exec_execute_message(const char *portal_name, long max_rows) bool is_trans_exit = false; bool completed; char completionTag[COMPLETION_TAG_BUFSIZE]; + 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; /* Adjust destination to tell printtup.c what to do */ dest = whereToSendOutput; @@ -1527,12 +1718,30 @@ exec_execute_message(const char *portal_name, long max_rows) set_ps_display(portal->commandTag); + /* + * 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 || save_log_min_duration_statement != -1) + gettimeofday(&start_t, NULL); + + if (save_log_statement_stats) + ResetUsage(); + + if (log_statement == LOGSTMT_ALL) + /* We have the portal, so output the source query. */ + ereport(LOG, + (errmsg("statement: EXECUTE %s [PREPARE: %s]", portal_name, + portal->sourceText ? portal->sourceText : ""))); + BeginCommand(portal->commandTag, dest); /* Check for transaction-control commands */ - if (length(portal->parseTrees) == 1) + if (list_length(portal->parseTrees) == 1) { - Query *query = (Query *) lfirst(portal->parseTrees); + Query *query = (Query *) linitial(portal->parseTrees); if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL && @@ -1542,7 +1751,9 @@ exec_execute_message(const char *portal_name, long max_rows) is_trans_stmt = true; if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) + stmt->kind == TRANS_STMT_PREPARE || + stmt->kind == TRANS_STMT_ROLLBACK || + stmt->kind == TRANS_STMT_ROLLBACK_TO) is_trans_exit = true; } } @@ -1570,7 +1781,7 @@ exec_execute_message(const char *portal_name, long max_rows) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"))); } /* Check for cancel signal before we start execution */ @@ -1620,6 +1831,50 @@ exec_execute_message(const char *portal_name, long max_rows) pq_putemptymessage('s'); } + /* + * Combine processing here as we need to calculate the query duration + * in both instances. + */ + 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; + } + usecs = (long) (stop_t.tv_sec - start_t.tv_sec) * 1000000 + + (long) (stop_t.tv_usec - start_t.tv_usec); + + /* Only print duration if we previously printed the statement. */ + if (log_statement == LOGSTMT_ALL && 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: EXECUTE %s [PREPARE: %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, + portal_name, + portal->sourceText ? portal->sourceText : ""))); + } + + if (save_log_statement_stats) + ShowUsage("QUERY STATISTICS"); + debug_query_string = NULL; } @@ -1633,7 +1888,7 @@ exec_describe_statement_message(const char *stmt_name) { PreparedStatement *pstmt; TupleDesc tupdesc; - List *l; + ListCell *l; StringInfoData buf; /* Find prepared statement */ @@ -1656,11 +1911,11 @@ exec_describe_statement_message(const char *stmt_name) * First describe the parameters... */ pq_beginmessage(&buf, 't'); /* parameter description message type */ - pq_sendint(&buf, length(pstmt->argtype_list), 2); + pq_sendint(&buf, list_length(pstmt->argtype_list), 2); foreach(l, pstmt->argtype_list) { - Oid ptype = lfirsto(l); + Oid ptype = lfirst_oid(l); pq_sendint(&buf, (int) ptype, 4); } @@ -1671,15 +1926,9 @@ exec_describe_statement_message(const char *stmt_name) */ tupdesc = FetchPreparedStatementResultDesc(pstmt); if (tupdesc) - { - List *targetlist; - - if (ChoosePortalStrategy(pstmt->query_list) == PORTAL_ONE_SELECT) - targetlist = ((Query *) lfirst(pstmt->query_list))->targetList; - else - targetlist = NIL; - SendRowDescriptionMessage(tupdesc, targetlist, NULL); - } + SendRowDescriptionMessage(tupdesc, + FetchPreparedStatementTargetList(pstmt), + NULL); else pq_putemptymessage('n'); /* NoData */ @@ -1705,16 +1954,9 @@ exec_describe_portal_message(const char *portal_name) return; /* can't actually do anything... */ if (portal->tupDesc) - { - List *targetlist; - - if (portal->strategy == PORTAL_ONE_SELECT) - targetlist = ((Query *) lfirst(portal->parseTrees))->targetList; - else - targetlist = NIL; - SendRowDescriptionMessage(portal->tupDesc, targetlist, + SendRowDescriptionMessage(portal->tupDesc, + FetchPortalTargetList(portal), portal->formats); - } else pq_putemptymessage('n'); /* NoData */ } @@ -1745,9 +1987,6 @@ finish_xact_command(void) { if (xact_started) { - /* Invoke IMMEDIATE constraint triggers */ - DeferredTriggerEndQuery(); - /* Cancel any active statement timeout before committing */ disable_sig_alarm(true); @@ -1792,15 +2031,15 @@ quickdie(SIGNAL_ARGS) /* * Ideally this should be ereport(FATAL), but then we'd not get - * control back (perhaps could fix by doing local sigsetjmp?) + * control back... */ 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."), + 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."))); @@ -1843,6 +2082,7 @@ die(SIGNAL_ARGS) /* until we are done getting ready for it */ InterruptHoldoffCount++; DisableNotifyInterrupt(); + DisableCatchupInterrupt(); /* Make sure CheckDeadLock won't run while shutting down... */ LockWaitCancel(); InterruptHoldoffCount--; @@ -1871,16 +2111,15 @@ authdie(SIGNAL_ARGS) * Query-cancel signal from postmaster: abort current transaction * at soonest convenient time */ -static void +void StatementCancelHandler(SIGNAL_ARGS) { int save_errno = errno; /* - * Don't joggle the elbow of proc_exit, nor an already-in-progress - * abort + * Don't joggle the elbow of proc_exit */ - if (!proc_exit_inprogress && !InError) + if (!proc_exit_inprogress) { InterruptPending = true; QueryCancelPending = true; @@ -1899,6 +2138,7 @@ StatementCancelHandler(SIGNAL_ARGS) if (LockWaitCancel()) { DisableNotifyInterrupt(); + DisableCatchupInterrupt(); InterruptHoldoffCount--; ProcessInterrupts(); } @@ -1950,6 +2190,7 @@ ProcessInterrupts(void) QueryCancelPending = false; /* ProcDie trumps QueryCancel */ ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); + DisableCatchupInterrupt(); ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating connection due to administrator command"))); @@ -1959,6 +2200,7 @@ ProcessInterrupts(void) QueryCancelPending = false; ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); + DisableCatchupInterrupt(); ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling query due to user request"))); @@ -1967,41 +2209,136 @@ ProcessInterrupts(void) } +/* + * 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) +usage(const char *progname) { - printf(gettext("%s is the PostgreSQL stand-alone backend. It is not\nintended to be used by normal users.\n\n"), progname); + printf(_("%s is the PostgreSQL stand-alone backend. It is not\nintended to be used by normal users.\n\n"), progname); - printf(gettext("Usage:\n %s [OPTION]... [DBNAME]\n\n"), progname); - printf(gettext("Options:\n")); + printf(_("Usage:\n %s [OPTION]... [DBNAME]\n\n"), progname); + printf(_("Options:\n")); #ifdef USE_ASSERT_CHECKING - printf(gettext(" -A 1|0 enable/disable run-time assert checking\n")); + printf(_(" -A 1|0 enable/disable run-time assert checking\n")); #endif - 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 SORT-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 .\n")); + printf(_(" -B NBUFFERS number of shared buffers\n")); + 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 input format (DMY)\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 WORK-MEM set amount of memory for sorts (in kB)\n")); + printf(_(" --describe-config describe configuration parameters, then exit\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 .\n")); } +/* + * 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); +} + /* ---------------------------------------------------------------- * PostgresMain @@ -2018,18 +2355,26 @@ PostgresMain(int argc, char *argv[], const char *username) { int flag; const char *dbname = NULL; - char *potential_DataDir = NULL; + char *userDoption = NULL; bool secure; int errs = 0; - int debug_flag = 0; - GucContext ctx, - debug_context; + 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; char *tmp; int firstchar; - StringInfoData input_message; + char stack_base; + StringInfoData input_message; + sigjmp_buf local_sigjmp_buf; volatile bool send_rfq = true; +#define PendingConfigOption(name,val) \ + (guc_names = lappend(guc_names, pstrdup(name)), \ + guc_values = lappend(guc_values, pstrdup(val))) + /* * Catch standard options before doing much else. This even works on * systems without getopt_long. @@ -2043,7 +2388,7 @@ PostgresMain(int argc, char *argv[], const char *username) } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { - puts("postgres (PostgreSQL) " PG_VERSION); + puts(PG_VERSIONSTR); exit(0); } } @@ -2052,7 +2397,6 @@ PostgresMain(int argc, char *argv[], const char *username) * initialize globals (already done if under postmaster, but not if * standalone; cheap enough to do over) */ - MyProcPid = getpid(); /* @@ -2067,17 +2411,27 @@ PostgresMain(int argc, char *argv[], const char *username) 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]); + } + + if (pkglib_path[0] == '\0') + get_pkglib_path(my_exec_path, pkglib_path); + /* * Set default values for command-line options. */ - Noversion = false; EchoQuery = false; - if (!IsUnderPostmaster /* when exec || ExecBackend */ ) - { + if (!IsUnderPostmaster) InitializeGUCOptions(); - potential_DataDir = getenv("PGDATA"); - } /* ---------------- * parse command line arguments @@ -2098,10 +2452,11 @@ PostgresMain(int argc, char *argv[], const char *username) /* all options are allowed until '-p' */ secure = true; - ctx = debug_context = PGC_POSTMASTER; + ctx = 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:-:")) != -1) + { switch (flag) { case 'A': @@ -2122,54 +2477,13 @@ PostgresMain(int argc, char *argv[], const char *username) SetConfigOption("shared_buffers", optarg, ctx, gucsource); break; - case 'C': - - /* - * don't print version string - */ - Noversion = true; - break; - - case 'D': /* PGDATA directory */ + case 'D': /* PGDATA or config directory */ if (secure) - potential_DataDir = optarg; + userDoption = optarg; break; case 'd': /* debug level */ - { - /* - * 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) - { - 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); - } - } + debug_flag = atoi(optarg); break; case 'E': @@ -2210,6 +2524,9 @@ PostgresMain(int argc, char *argv[], const char *username) case 'i': /* indexscan */ tmp = "enable_indexscan"; break; + case 'b': /* bitmapscan */ + tmp = "enable_bitmapscan"; + break; case 't': /* tidscan */ tmp = "enable_tidscan"; break; @@ -2251,9 +2568,9 @@ PostgresMain(int argc, char *argv[], const char *username) /* * 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. + * 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. */ IgnoreSystemIndexes(true); break; @@ -2275,23 +2592,8 @@ PostgresMain(int argc, char *argv[], const char *username) */ 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; @@ -2304,15 +2606,22 @@ PostgresMain(int argc, char *argv[], const char *username) /* * 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 + * + * Since log options are SUSET, we need to postpone unless + * still in secure context */ - SetConfigOption("show_statement_stats", "true", ctx, gucsource); + if (ctx == PGC_BACKEND) + PendingConfigOption("log_statement_stats", "true"); + else + SetConfigOption("log_statement_stats", "true", + ctx, gucsource); break; case 't': @@ -2338,14 +2647,19 @@ PostgresMain(int argc, char *argv[], const char *username) errs++; break; case 'e': - tmp = "show_executor_stats"; + tmp = "log_executor_stats"; break; default: errs++; break; } if (tmp) - SetConfigOption(tmp, "true", ctx, gucsource); + { + if (ctx == PGC_BACKEND) + PendingConfigOption(tmp, "true"); + else + SetConfigOption(tmp, "true", ctx, gucsource); + } break; case 'v': @@ -2358,40 +2672,7 @@ PostgresMain(int argc, char *argv[], const char *username) /* * wait N seconds to allow attach from a debugger */ - sleep(atoi(optarg)); - break; - - case 'x': -#ifdef NOT_USED /* planner/xfunc.h */ - - /* - * control joey hellerstein's expensive function - * optimization - */ - if (XfuncMode != 0) - { - elog(WARNING, "only one -x flag is allowed"); - errs++; - break; - } - if (strcmp(optarg, "off") == 0) - XfuncMode = XFUNC_OFF; - else if (strcmp(optarg, "nor") == 0) - XfuncMode = XFUNC_NOR; - else if (strcmp(optarg, "nopull") == 0) - XfuncMode = XFUNC_NOPULL; - else if (strcmp(optarg, "nopm") == 0) - XfuncMode = XFUNC_NOPM; - else if (strcmp(optarg, "pullall") == 0) - XfuncMode = XFUNC_PULLALL; - else if (strcmp(optarg, "wait") == 0) - XfuncMode = XFUNC_WAIT; - else - { - elog(WARNING, "use -x {off,nor,nopull,nopm,pullall,wait}"); - errs++; - } -#endif + pg_usleep(atoi(optarg) * 1000000L); break; case 'c': @@ -2415,7 +2696,14 @@ PostgresMain(int argc, char *argv[], const char *username) optarg))); } - SetConfigOption(name, value, ctx, gucsource); + /* + * 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); free(name); if (value) free(value); @@ -2426,81 +2714,40 @@ PostgresMain(int argc, char *argv[], const char *username) errs++; break; } - - - /* - * -d is not the same as setting log_min_messages because it enables - * other output options. - */ - if (debug_flag >= 1) - SetConfigOption("log_connections", "true", debug_context, gucsource); - if (debug_flag >= 2) - SetConfigOption("log_statement", "true", debug_context, gucsource); - if (debug_flag >= 3) - SetConfigOption("debug_print_parse", "true", debug_context, gucsource); - if (debug_flag >= 4) - SetConfigOption("debug_print_plan", "true", debug_context, gucsource); - if (debug_flag >= 5) - SetConfigOption("debug_print_rewritten", "true", debug_context, gucsource); + } /* * Process any additional GUC variable settings passed in startup - * packet. + * packet. These are handled exactly like command-line variables. */ if (MyProcPort != NULL) { - List *gucopts = MyProcPort->guc_options; + ListCell *gucopts = list_head(MyProcPort->guc_options); while (gucopts) { - char *name, - *value; + char *name; + char *value; name = lfirst(gucopts); gucopts = lnext(gucopts); + value = lfirst(gucopts); gucopts = lnext(gucopts); - SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT); - } - } - /* - * Post-processing for command line options. - */ - if (log_statement_stats && - (log_parser_stats || log_planner_stats || log_executor_stats)) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("statement-level statistics are disabled because parser, planner, or executor statistics are on"))); - SetConfigOption("show_statement_stats", "false", ctx, gucsource); + 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 (!potential_DataDir) - { - 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]); + if (!SelectConfigFiles(userDoption, argv[0])) proc_exit(1); - } - SetDataDir(potential_DataDir); } - Assert(DataDir); - - /* Acquire configuration parameters */ - if (IsUnderPostmaster) - { -#ifdef EXEC_BACKEND - read_nondefault_variables(); -#endif - } - else - ProcessConfigFile(PGC_POSTMASTER); /* * Set up signal handlers and masks. @@ -2516,7 +2763,6 @@ PostgresMain(int argc, char *argv[], const char *username) * 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 */ @@ -2530,9 +2776,8 @@ PostgresMain(int argc, char *argv[], const char *username) * midst of output during who-knows-what operation... */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, SIG_IGN); /* this signal available for use */ - - pqsignal(SIGUSR2, Async_NotifyHandler); /* flush also sinval cache */ + pqsignal(SIGUSR1, CatchupInterruptHandler); + pqsignal(SIGUSR2, NotifyInterruptHandler); pqsignal(SIGFPE, FloatExceptionHandler); /* @@ -2561,13 +2806,11 @@ PostgresMain(int argc, char *argv[], const char *username) { ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid command-line arguments for server process"), - errhint("Try \"%s --help\" for more information.", argv[0]))); + errmsg("invalid command-line arguments for server process"), + errhint("Try \"%s --help\" for more information.", argv[0]))); } + BaseInit(); -#ifdef EXECBACKEND - AttachSharedMemoryAndSemaphores(); -#endif } else { @@ -2578,7 +2821,7 @@ PostgresMain(int argc, char *argv[], const char *username) (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s: invalid command-line arguments", argv[0]), - errhint("Try \"%s --help\" for more information.", argv[0]))); + errhint("Try \"%s --help\" for more information.", argv[0]))); } else if (argc - optind == 1) dbname = argv[optind]; @@ -2590,27 +2833,21 @@ PostgresMain(int argc, char *argv[], const char *username) argv[0]))); } - /* - * On some systems our dynloader code needs the executable's - * pathname. (If under postmaster, this was done already.) - */ - if (FindExec(pg_pathname, argv[0], "postgres") < 0) - ereport(FATAL, - (errmsg("%s: could not locate postgres executable", - argv[0]))); - /* * 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(); + /* * Create lockfile for data directory. */ - CreateDataDirLockFile(DataDir, false); + CreateDataDirLockFile(false); - XLOGPathInit(); BaseInit(); /* @@ -2626,6 +2863,12 @@ PostgresMain(int argc, char *argv[], const char *username) */ LoadFreeSpaceMap(); on_shmem_exit(DumpFreeSpaceMap, 0); + + /* + * 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); } /* @@ -2637,10 +2880,48 @@ PostgresMain(int argc, char *argv[], const char *username) */ ereport(DEBUG3, (errmsg_internal("InitPostgres"))); - InitPostgres(dbname, username); + 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(); + + /* + * 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. */ @@ -2656,11 +2937,9 @@ PostgresMain(int argc, char *argv[], const char *username) /* Need not flush since ReadyForQuery will do it. */ } - if (!IsUnderPostmaster) - { - puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.374 $ $Date: 2003/10/18 22:59:08 $\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. @@ -2681,45 +2960,78 @@ PostgresMain(int argc, char *argv[], const char *username) */ pgstat_bestart(); + /* + * Remember stand-alone backend startup time + */ + if (!IsUnderPostmaster) + PgStartTime = GetCurrentTimestamp(); + /* * POSTGRES main processing loop begins here * * 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(Warn_restart, 1) != 0) + if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* * NOTE: if you are tempted to add more code in this if-block, - * consider the probability that it should be in - * AbortTransaction() instead. - * - * 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 - * ereport'd from inside a holdoff section. + * consider the high probability that it should be in + * AbortTransaction() instead. The only stuff done directly here + * 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(); + + /* + * Forget any pending QueryCancel request, since we're returning + * to the idle loop anyway, and cancel the statement timer if + * running. */ - ImmediateInterruptOK = false; QueryCancelPending = false; - InterruptHoldoffCount = 1; - CritSectionCount = 0; /* should be unnecessary, but... */ disable_sig_alarm(true); QueryCancelPending = false; /* again in case timeout occurred */ + + /* + * 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(); - debug_query_string = NULL; + DisableCatchupInterrupt(); + + /* Make sure libpq is in a good state */ + pq_comm_reset(); + + /* Report the error to the client and/or server log */ + EmitErrorReport(); /* - * Make sure we are in a valid memory context during recovery. - * - * We use ErrorContext in hopes that it will have some free space - * even if we're otherwise up against it... + * Make sure debug_query_string gets reset before we possibly + * clobber the storage it points at. */ - MemoryContextSwitchTo(ErrorContext); + debug_query_string = NULL; - /* Do the recovery */ - ereport(DEBUG2, - (errmsg_internal("AbortCurrentTransaction"))); + /* + * Abort the current transaction in order to recover. + */ AbortCurrentTransaction(); /* @@ -2727,17 +3039,9 @@ PostgresMain(int argc, char *argv[], const char *username) * for next time. */ MemoryContextSwitchTo(TopMemoryContext); - MemoryContextResetAndDeleteChildren(ErrorContext); - PortalContext = NULL; + FlushErrorState(); QueryContext = NULL; - /* - * Clear flag to indicate that we got out of error recovery mode - * successfully. (Flag was set in elog.c before longjmp().) - */ - InError = false; - xact_started = false; - /* * If we were handling an extended-query-protocol message, * initiate skip till next Sync. This also causes us not to issue @@ -2746,13 +3050,15 @@ PostgresMain(int argc, char *argv[], const char *username) if (doing_extended_query_message) ignore_till_sync = true; - /* - * Exit interrupt holdoff section we implicitly established above. - */ + /* We don't have a transaction command open anymore */ + xact_started = false; + + /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); } - Warn_restart_ready = true; /* we can now handle ereport(ERROR) */ + /* We can now handle ereport(ERROR) */ + PG_exception_stack = &local_sigjmp_buf; PG_SETMASK(&UnBlockSig); @@ -2788,13 +3094,12 @@ PostgresMain(int argc, char *argv[], const char *username) * * 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. + * 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_rfq) { - pgstat_report_tabstat(); - if (IsTransactionOrTransactionBlock()) { set_ps_display("idle in transaction"); @@ -2802,6 +3107,8 @@ PostgresMain(int argc, char *argv[], const char *username) } else { + pgstat_report_tabstat(); + set_ps_display("idle"); pgstat_report_activity(""); } @@ -2811,20 +3118,13 @@ PostgresMain(int argc, char *argv[], const char *username) } /* - * (2) deal with pending asynchronous NOTIFY from other backends, - * and enable async.c's signal handler to execute NOTIFY directly. - * Then set up other stuff needed before blocking for input. + * (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 */ - - EnableNotifyInterrupt(); - - /* 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(); + QueryCancelPending = false; /* forget any earlier CANCEL signal */ + DoingCommandRead = true; /* * (3) read a command (loop blocks here) @@ -2834,10 +3134,7 @@ PostgresMain(int argc, char *argv[], const char *username) /* * (4) disable async signal conditions again. */ - ImmediateInterruptOK = false; - QueryCancelPending = false; /* forget any CANCEL signal */ - - DisableNotifyInterrupt(); + DoingCommandRead = false; /* * (5) check for any other interesting events that happened while @@ -2928,6 +3225,9 @@ PostgresMain(int argc, char *argv[], const char *username) /* switch back to message context */ MemoryContextSwitchTo(MessageContext); + /* set snapshot in case function needs one */ + ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); + if (HandleFunctionRequest(&input_message) == EOF) { /* lost frontend connection during F message input */ @@ -3090,8 +3390,8 @@ PostgresMain(int argc, char *argv[], const char *username) #include #endif /* HAVE_GETRUSAGE */ -struct rusage Save_r; -struct timeval Save_t; +static struct rusage Save_r; +static struct timeval Save_t; void ResetUsage(void) @@ -3201,3 +3501,63 @@ ShowUsage(const char *title) 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 / SECS_PER_HOUR; + end.tv_sec %= SECS_PER_HOUR; + minutes = end.tv_sec / SECS_PER_MINUTE; + seconds = end.tv_sec % SECS_PER_MINUTE; + + /* 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))); + +}