X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Ftcop%2Fpostgres.c;h=ed5f050242091d4c0e7ccf0aa9d080a13c709436;hb=a536b2dd80f29464b0461e3980043ec4a822e820;hp=5658e10d4533183d5197f202e6cd073a2784adcf;hpb=09a893117a9fe0ae5e7e5c6a6a5496e9e6826f4c;p=postgresql diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 5658e10d45..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-2004, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.433 2004/09/26 00:26:25 tgl 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 @@ -55,6 +55,7 @@ #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" @@ -110,6 +111,13 @@ static volatile sig_atomic_t 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. @@ -148,6 +156,9 @@ 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); @@ -403,6 +414,52 @@ ReadCommand(StringInfo 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. @@ -461,68 +518,96 @@ List * pg_parse_query(const char *query_string) { List *raw_parsetree_list; - ListCell *parsetree_item; - - 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) + if (log_parser_stats) + ShowUsage("PARSER STATISTICS"); + + 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) { - 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)) { - 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)) + ExecuteStmt *stmt = (ExecuteStmt *) parsetree; + PreparedStatement *entry; + + if ((entry = FetchPreparedStatement(stmt->name, false)) != NULL && + entry->query_string) { - ereport(LOG, - (errmsg("statement: %s", query_string))); - break; + *prepare_string = palloc(strlen(entry->query_string) + + strlen(" [client PREPARE: %s]") - 1); + sprintf(*prepare_string, " [client PREPARE: %s]", + entry->query_string); } } } - - if (log_parser_stats) - ShowUsage("PARSER STATISTICS"); - - return raw_parsetree_list; + + 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. @@ -558,8 +643,11 @@ 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; @@ -725,12 +813,13 @@ exec_simple_query(const char *query_string) MemoryContext oldcontext; List *parsetree_list; ListCell *parsetree_item; - struct timeval start_t, - stop_t; + 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. */ @@ -786,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. */ @@ -834,6 +927,7 @@ exec_simple_query(const char *query_string) TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_PREPARE || stmt->kind == TRANS_STMT_ROLLBACK || stmt->kind == TRANS_STMT_ROLLBACK_TO) allowit = true; @@ -1001,9 +1095,11 @@ 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 + @@ -1018,16 +1114,20 @@ exec_simple_query(const char *query_string) (save_log_min_duration_statement > 0 && usecs >= save_log_min_duration_statement * 1000)) ereport(LOG, - (errmsg("duration: %ld.%03ld ms statement: %s", + (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))); + 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; } @@ -1063,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.) @@ -1155,6 +1259,7 @@ exec_parse_message(const char *query_string, /* string to execute */ TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_PREPARE || stmt->kind == TRANS_STMT_ROLLBACK || stmt->kind == TRANS_STMT_ROLLBACK_TO) allowit = true; @@ -1360,6 +1465,10 @@ exec_bind_message(StringInfo input_message) else portal = CreatePortal(portal_name, false, false); + if (log_statement == LOGSTMT_ALL) + ereport(LOG, + (errmsg("statement: %s", portal_name))); + /* * Fetch parameters, if any, and store in the portal's memory context. * @@ -1462,9 +1571,10 @@ exec_bind_message(StringInfo input_message) getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); params[i].value = - OidFunctionCall2(typreceive, + OidFunctionCall3(typreceive, PointerGetDatum(&pbuf), - ObjectIdGetDatum(typioparam)); + ObjectIdGetDatum(typioparam), + Int32GetDatum(-1)); /* Trouble if it didn't eat the whole buffer */ if (pbuf.cursor != pbuf.len) @@ -1568,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; @@ -1604,6 +1718,24 @@ 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 */ @@ -1619,6 +1751,7 @@ exec_execute_message(const char *portal_name, long max_rows) is_trans_stmt = true; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_PREPARE || stmt->kind == TRANS_STMT_ROLLBACK || stmt->kind == TRANS_STMT_ROLLBACK_TO) is_trans_exit = true; @@ -1698,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; } @@ -1749,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 *) linitial(pstmt->query_list))->targetList; - else - targetlist = NIL; - SendRowDescriptionMessage(tupdesc, targetlist, NULL); - } + SendRowDescriptionMessage(tupdesc, + FetchPreparedStatementTargetList(pstmt), + NULL); else pq_putemptymessage('n'); /* NoData */ @@ -1783,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 *) linitial(portal->parseTrees))->targetList; - else - targetlist = NIL; - SendRowDescriptionMessage(portal->tupDesc, targetlist, + SendRowDescriptionMessage(portal->tupDesc, + FetchPortalTargetList(portal), portal->formats); - } else pq_putemptymessage('n'); /* NoData */ } @@ -1947,7 +2111,7 @@ 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; @@ -2109,35 +2273,70 @@ assign_max_stack_depth(int newval, bool doit, GucSource source) static void 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 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 .\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); } @@ -2156,13 +2355,15 @@ PostgresMain(int argc, char *argv[], const char *username) { int flag; const char *dbname = NULL; - char *userPGDATA = 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; char stack_base; @@ -2170,6 +2371,10 @@ PostgresMain(int argc, char *argv[], const char *username) 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. @@ -2226,10 +2431,7 @@ PostgresMain(int argc, char *argv[], const char *username) EchoQuery = false; if (!IsUnderPostmaster) - { InitializeGUCOptions(); - userPGDATA = getenv("PGDATA"); - } /* ---------------- * parse command line arguments @@ -2250,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:D:d:Eef:FiNOPo:p:S:st:v:W:-:")) != -1) + { switch (flag) { case 'A': @@ -2274,46 +2477,13 @@ PostgresMain(int argc, char *argv[], const char *username) SetConfigOption("shared_buffers", optarg, ctx, gucsource); break; - case 'D': /* PGDATA directory */ + case 'D': /* PGDATA or config directory */ if (secure) - userPGDATA = 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': @@ -2354,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; @@ -2440,8 +2613,15 @@ PostgresMain(int argc, char *argv[], const char *username) /* * s - report usage statistics (timings) after each query + * + * Since log options are SUSET, we need to postpone unless + * still in secure context */ - SetConfigOption("log_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': @@ -2474,7 +2654,12 @@ PostgresMain(int argc, char *argv[], const char *username) break; } if (tmp) - SetConfigOption(tmp, "true", ctx, gucsource); + { + if (ctx == PGC_BACKEND) + PendingConfigOption(tmp, "true"); + else + SetConfigOption(tmp, "true", ctx, gucsource); + } break; case 'v': @@ -2511,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); @@ -2522,29 +2714,11 @@ 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); - SetConfigOption("log_disconnections", "true", debug_context, gucsource); } - if (debug_flag >= 2) - SetConfigOption("log_statement", "all", 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) { @@ -2561,38 +2735,18 @@ PostgresMain(int argc, char *argv[], const char *username) value = lfirst(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 (!IsUnderPostmaster) - { - if (!userPGDATA) - { - write_stderr("%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); + if (IsSuperuserConfigOption(name)) + PendingConfigOption(name, value); + else + SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT); } - SetDataDir(userPGDATA); } - Assert(DataDir); /* Acquire configuration parameters, unless inherited from postmaster */ if (!IsUnderPostmaster) { - ProcessConfigFile(PGC_POSTMASTER); - - /* If timezone is not set, determine what the OS uses */ - pg_timezone_initialize(); + if (!SelectConfigFiles(userDoption, argv[0])) + proc_exit(1); } /* @@ -2656,8 +2810,6 @@ PostgresMain(int argc, char *argv[], const char *username) errhint("Try \"%s --help\" for more information.", argv[0]))); } - XLOGPathInit(); - BaseInit(); } else @@ -2685,14 +2837,17 @@ PostgresMain(int argc, char *argv[], const char *username) * 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(); /* @@ -2708,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); } /* @@ -2719,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. */ @@ -2761,6 +2960,12 @@ 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 * @@ -2808,6 +3013,7 @@ PostgresMain(int argc, char *argv[], const char *username) * not in other exception-catching places since these interrupts * are only enabled while we wait for client input. */ + DoingCommandRead = false; DisableNotifyInterrupt(); DisableCatchupInterrupt(); @@ -2889,12 +3095,11 @@ 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. + * 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"); @@ -2902,6 +3107,8 @@ PostgresMain(int argc, char *argv[], const char *username) } else { + pgstat_report_tabstat(); + set_ps_display("idle"); pgstat_report_activity(""); } @@ -2911,21 +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(); - 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(); + QueryCancelPending = false; /* forget any earlier CANCEL signal */ + DoingCommandRead = true; /* * (3) read a command (loop blocks here) @@ -2935,11 +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(); - DisableCatchupInterrupt(); + DoingCommandRead = false; /* * (5) check for any other interesting events that happened while @@ -3341,10 +3536,10 @@ log_disconnections(int code, Datum arg) 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; + 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 */