]> granicus.if.org Git - postgresql/blobdiff - src/backend/tcop/postgres.c
pgindent run for 8.3.
[postgresql] / src / backend / tcop / postgres.c
index d640c3b602c74ac667e618636772551d5413fc17..43435966c95033162048422816f6dfadec1541ce 100644 (file)
@@ -3,12 +3,12 @@
  * postgres.c
  *       POSTGRES C Backend Interface
  *
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.440 2004/12/31 22:01:16 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.538 2007/11/15 21:14:38 momjian Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
 #include <signal.h>
 #include <fcntl.h>
 #include <sys/socket.h>
-#if HAVE_SYS_SELECT_H
+#ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
 
+#ifndef HAVE_GETRUSAGE
+#include "rusagestub.h"
+#endif
+
 #include "access/printtup.h"
-#include "access/xlog.h"
+#include "access/xact.h"
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
-#include "commands/trigger.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/print.h"
-#include "optimizer/cost.h"
 #include "optimizer/planner.h"
 #include "parser/analyze.h"
 #include "parser/parser.h"
+#include "postmaster/autovacuum.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/freespace.h"
 #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/guc.h"
+#include "utils/flatfiles.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
@@ -70,22 +76,22 @@ extern char *optarg;
  *             global variables
  * ----------------
  */
-const char *debug_query_string; /* for pgmonitor and
-                                                                * log_min_error_statement */
+const char *debug_query_string; /* for pgmonitor and log_min_error_statement */
 
 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
-CommandDest whereToSendOutput = Debug;
+CommandDest whereToSendOutput = DestDebug;
 
 /* flag for logging end of session */
 bool           Log_disconnections = false;
 
 LogStmtLevel log_statement = LOGSTMT_NONE;
 
-/* flag indicating if the statement satisfies log_statement */
-bool           statement_logged;
-
 /* GUC variable for maximum stack depth (measured in kilobytes) */
-int                    max_stack_depth = 2048;
+int                    max_stack_depth = 100;
+
+/* wait N seconds to allow attach from a debugger */
+int                    PostAuthDelay = 0;
+
 
 
 /* ----------------
@@ -94,10 +100,13 @@ int                        max_stack_depth = 2048;
  */
 
 /* max_stack_depth converted to bytes for speed of checking */
-static int     max_stack_depth_bytes = 2048 * 1024;
+static long max_stack_depth_bytes = 100 * 1024L;
 
-/* stack base pointer (initialized by PostgresMain) */
-static char *stack_base_ptr = NULL;
+/*
+ * Stack base pointer -- initialized by PostgresMain. This is not static
+ * so that PL/Java can modify it.
+ */
+char      *stack_base_ptr = NULL;
 
 
 /*
@@ -113,6 +122,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.
@@ -125,8 +141,10 @@ static bool ignore_till_sync = false;
  * We keep it separate from the hashtable kept by commands/prepare.c
  * in order to reduce overhead for short-lived queries.
  */
+static CachedPlanSource *unnamed_stmt_psrc = NULL;
+
+/* workspace for building a new unnamed statement in */
 static MemoryContext unnamed_stmt_context = NULL;
-static PreparedStatement *unnamed_stmt_pstmt = NULL;
 
 
 static bool EchoQuery = false; /* default don't echo */
@@ -136,9 +154,7 @@ static bool EchoQuery = false;      /* default don't echo */
  * tcop/tcopdebug.h
  */
 #ifndef TCOP_DONTUSENEWLINE
-static int     UseNewLine = 1;         /* Use newlines query delimiters (the
-                                                                * default) */
-
+static int     UseNewLine = 1;         /* Use newlines query delimiters (the default) */
 #else
 static int     UseNewLine = 0;         /* Use EOF as query delimiters */
 #endif   /* TCOP_DONTUSENEWLINE */
@@ -149,12 +165,20 @@ static int        UseNewLine = 0;         /* Use EOF as query delimiters */
  * ----------------------------------------------------------------
  */
 static int     InteractiveBackend(StringInfo inBuf);
+static int     interactive_getc(void);
 static int     SocketBackend(StringInfo inBuf);
 static int     ReadCommand(StringInfo inBuf);
+static List *pg_rewrite_query(Query *query);
+static bool check_log_statement(List *stmt_list);
+static int     errdetail_execute(List *raw_parsetree_list);
+static int     errdetail_params(ParamListInfo params);
 static void start_xact_command(void);
 static void finish_xact_command(void);
+static bool IsTransactionExitStmt(Node *parsetree);
+static bool IsTransactionExitStmtList(List *parseTrees);
+static bool IsTransactionStmtList(List *parseTrees);
+static void drop_unnamed_stmt(void);
 static void SigHupHandler(SIGNAL_ARGS);
-static void FloatExceptionHandler(SIGNAL_ARGS);
 static void log_disconnections(int code, Datum arg);
 
 
@@ -186,69 +210,63 @@ InteractiveBackend(StringInfo inBuf)
        printf("backend> ");
        fflush(stdout);
 
-       /* Reset inBuf to empty */
-       inBuf->len = 0;
-       inBuf->data[0] = '\0';
-       inBuf->cursor = 0;
+       resetStringInfo(inBuf);
 
-       for (;;)
+       if (UseNewLine)
        {
-               if (UseNewLine)
+               /*
+                * if we are using \n as a delimiter, then read characters until the
+                * \n.
+                */
+               while ((c = interactive_getc()) != EOF)
                {
-                       /*
-                        * if we are using \n as a delimiter, then read characters
-                        * until the \n.
-                        */
-                       while ((c = getc(stdin)) != EOF)
+                       if (c == '\n')
                        {
-                               if (c == '\n')
+                               if (backslashSeen)
                                {
-                                       if (backslashSeen)
-                                       {
-                                               /* discard backslash from inBuf */
-                                               inBuf->data[--inBuf->len] = '\0';
-                                               backslashSeen = false;
-                                               continue;
-                                       }
-                                       else
-                                       {
-                                               /* keep the newline character */
-                                               appendStringInfoChar(inBuf, '\n');
-                                               break;
-                                       }
+                                       /* discard backslash from inBuf */
+                                       inBuf->data[--inBuf->len] = '\0';
+                                       backslashSeen = false;
+                                       continue;
                                }
-                               else if (c == '\\')
-                                       backslashSeen = true;
                                else
-                                       backslashSeen = false;
-
-                               appendStringInfoChar(inBuf, (char) c);
+                               {
+                                       /* keep the newline character */
+                                       appendStringInfoChar(inBuf, '\n');
+                                       break;
+                               }
                        }
+                       else if (c == '\\')
+                               backslashSeen = true;
+                       else
+                               backslashSeen = false;
 
-                       if (c == EOF)
-                               end = true;
-               }
-               else
-               {
-                       /*
-                        * otherwise read characters until EOF.
-                        */
-                       while ((c = getc(stdin)) != EOF)
-                               appendStringInfoChar(inBuf, (char) c);
-
-                       if (inBuf->len == 0)
-                               end = true;
+                       appendStringInfoChar(inBuf, (char) c);
                }
 
-               if (end)
-                       return EOF;
-
+               if (c == EOF)
+                       end = true;
+       }
+       else
+       {
                /*
-                * otherwise we have a user query so process it.
+                * otherwise read characters until EOF.
                 */
-               break;
+               while ((c = interactive_getc()) != EOF)
+                       appendStringInfoChar(inBuf, (char) c);
+
+               /* No input before EOF signal means time to quit. */
+               if (inBuf->len == 0)
+                       end = true;
        }
 
+       if (end)
+               return EOF;
+
+       /*
+        * otherwise we have a user query so process it.
+        */
+
        /* Add '\0' to make it look the same as message case. */
        appendStringInfoChar(inBuf, (char) '\0');
 
@@ -262,6 +280,24 @@ InteractiveBackend(StringInfo inBuf)
        return 'Q';
 }
 
+/*
+ * interactive_getc -- collect one character from stdin
+ *
+ * Even though we are not reading from a "client" process, we still want to
+ * respond to signals, particularly SIGTERM/SIGQUIT.  Hence we must use
+ * prepare_for_client_read and client_read_ended.
+ */
+static int
+interactive_getc(void)
+{
+       int                     c;
+
+       prepare_for_client_read();
+       c = getc(stdin);
+       client_read_ended();
+       return c;
+}
+
 /* ----------------
  *     SocketBackend()         Is called for frontend-backend connections
  *
@@ -289,12 +325,12 @@ SocketBackend(StringInfo inBuf)
        }
 
        /*
-        * 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.
+        * Validate message type code before trying to read body; if we have lost
+        * sync, better to say "command unknown" than to run out of memory because
+        * we used garbage as a length word.
         *
-        * This also gives us a place to set the doing_extended_query_message
-        * flag as soon as possible.
+        * This also gives us a place to set the doing_extended_query_message flag
+        * as soon as possible.
         */
        switch (qtype)
        {
@@ -307,7 +343,7 @@ SocketBackend(StringInfo inBuf)
                                {
                                        ereport(COMMERROR,
                                                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                                errmsg("unexpected EOF on client connection")));
+                                                        errmsg("unexpected EOF on client connection")));
                                        return EOF;
                                }
                        }
@@ -334,7 +370,7 @@ SocketBackend(StringInfo inBuf)
                        if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
                                ereport(FATAL,
                                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                        errmsg("invalid frontend message type %d", qtype)));
+                                                errmsg("invalid frontend message type %d", qtype)));
                        break;
 
                case 'S':                               /* sync */
@@ -346,7 +382,7 @@ SocketBackend(StringInfo inBuf)
                        if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
                                ereport(FATAL,
                                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                        errmsg("invalid frontend message type %d", qtype)));
+                                                errmsg("invalid frontend message type %d", qtype)));
                        break;
 
                case 'd':                               /* copy data */
@@ -357,15 +393,15 @@ SocketBackend(StringInfo inBuf)
                        if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
                                ereport(FATAL,
                                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                        errmsg("invalid frontend message type %d", qtype)));
+                                                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.
                         */
                        ereport(FATAL,
                                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -374,9 +410,9 @@ SocketBackend(StringInfo inBuf)
        }
 
        /*
-        * In protocol version 3, all frontend messages have a length word
-        * next after the type code; we can read the message contents
-        * independently of the type.
+        * In protocol version 3, all frontend messages have a length word next
+        * after the type code; we can read the message contents independently of
+        * the type.
         */
        if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
        {
@@ -399,13 +435,59 @@ ReadCommand(StringInfo inBuf)
 {
        int                     result;
 
-       if (whereToSendOutput == Remote)
+       if (whereToSendOutput == DestRemote)
                result = SocketBackend(inBuf);
        else
                result = InteractiveBackend(inBuf);
        return result;
 }
 
+/*
+ * prepare_for_client_read -- set up to possibly block on client input
+ *
+ * This must be called immediately before any low-level read from the
+ * client connection.  It is necessary to do it at a sufficiently low level
+ * that there won't be any other operations except the read kernel call
+ * itself between this call and the subsequent client_read_ended() call.
+ * In particular there mustn't be use of malloc() or other potentially
+ * non-reentrant libc functions.  This restriction makes it safe for us
+ * to allow interrupt service routines to execute nontrivial code while
+ * we are waiting for input.
+ */
+void
+prepare_for_client_read(void)
+{
+       if (DoingCommandRead)
+       {
+               /* Enable immediate processing of asynchronous signals */
+               EnableNotifyInterrupt();
+               EnableCatchupInterrupt();
+
+               /* Allow "die" interrupt to be processed while waiting */
+               ImmediateInterruptOK = true;
+
+               /* And don't forget to detect one that already arrived */
+               QueryCancelPending = false;
+               CHECK_FOR_INTERRUPTS();
+       }
+}
+
+/*
+ * client_read_ended -- get out of the client-input state
+ */
+void
+client_read_ended(void)
+{
+       if (DoingCommandRead)
+       {
+               ImmediateInterruptOK = false;
+               QueryCancelPending = false;             /* forget any CANCEL signal */
+
+               DisableNotifyInterrupt();
+               DisableCatchupInterrupt();
+       }
+}
+
 
 /*
  * Parse a query string and pass it through the rewriter.
@@ -440,6 +522,7 @@ pg_parse_and_rewrite(const char *query_string,      /* string to execute */
 
                querytree_list = list_concat(querytree_list,
                                                                         pg_analyze_and_rewrite(parsetree,
+                                                                                                                       query_string,
                                                                                                                        paramTypes,
                                                                                                                        numParams));
        }
@@ -464,71 +547,28 @@ List *
 pg_parse_query(const char *query_string)
 {
        List       *raw_parsetree_list;
-       ListCell   *parsetree_item;
-
-       statement_logged = false;
-       if (log_statement == LOGSTMT_ALL)
-       {
-               ereport(LOG,
-                               (errmsg("statement: %s", query_string)));
-               statement_logged = true;
-       }
 
        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)));
-                               statement_logged = true;
-                               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)));
-                               statement_logged = true;
-                               break;
-                       }
-               }
-       }
-
        if (log_parser_stats)
                ShowUsage("PARSER STATISTICS");
 
+#ifdef COPY_PARSE_PLAN_TREES
+       /* Optional debugging check: pass raw parsetrees through copyObject() */
+       {
+               List       *new_list = (List *) copyObject(raw_parsetree_list);
+
+               /* This checks both copyObject() and the equal() routines... */
+               if (!equal(new_list, raw_parsetree_list))
+                       elog(WARNING, "copyObject() failed to produce an equal raw parse tree");
+               else
+                       raw_parsetree_list = new_list;
+       }
+#endif
+
        return raw_parsetree_list;
 }
 
@@ -542,8 +582,10 @@ pg_parse_query(const char *query_string)
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
 List *
-pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
+pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
+                                          Oid *paramTypes, int numParams)
 {
+       Query      *query;
        List       *querytree_list;
 
        /*
@@ -552,7 +594,7 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
        if (log_parser_stats)
                ResetUsage();
 
-       querytree_list = parse_analyze(parsetree, paramTypes, numParams);
+       query = parse_analyze(parsetree, query_string, paramTypes, numParams);
 
        if (log_parser_stats)
                ShowUsage("PARSE ANALYSIS STATISTICS");
@@ -560,66 +602,55 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
        /*
         * (2) Rewrite the queries, as necessary
         */
-       querytree_list = pg_rewrite_queries(querytree_list);
+       querytree_list = pg_rewrite_query(query);
 
        return querytree_list;
 }
 
 /*
- * Perform rewriting of a list of queries produced by parse analysis.
+ * Perform rewriting of a query produced by parse analysis.
+ *
+ * Note: query must just have come from the parser, because we do not do
+ * AcquireRewriteLocks() on it.
  */
-List *
-pg_rewrite_queries(List *querytree_list)
+static List *
+pg_rewrite_query(Query *query)
 {
-       List       *new_list = NIL;
-       ListCell   *list_item;
+       List       *querytree_list;
 
        if (log_parser_stats)
                ResetUsage();
 
-       /*
-        * rewritten queries are collected in new_list.  Note there may be
-        * more or fewer than in the original list.
-        */
-       foreach(list_item, querytree_list)
-       {
-               Query      *querytree = (Query *) lfirst(list_item);
-
-               if (Debug_print_parse)
-                       elog_node_display(DEBUG1, "parse tree", querytree,
-                                                         Debug_pretty_print);
-
-               if (querytree->commandType == CMD_UTILITY)
-               {
-                       /* don't rewrite utilities, just dump 'em into new_list */
-                       new_list = lappend(new_list, querytree);
-               }
-               else
-               {
-                       /* rewrite regular queries */
-                       List       *rewritten = QueryRewrite(querytree);
+       if (Debug_print_parse)
+               elog_node_display(DEBUG1, "parse tree", query,
+                                                 Debug_pretty_print);
 
-                       new_list = list_concat(new_list, rewritten);
-               }
+       if (query->commandType == CMD_UTILITY)
+       {
+               /* don't rewrite utilities, just dump 'em into result list */
+               querytree_list = list_make1(query);
+       }
+       else
+       {
+               /* rewrite regular queries */
+               querytree_list = QueryRewrite(query);
        }
-
-       querytree_list = new_list;
 
        if (log_parser_stats)
                ShowUsage("REWRITER STATISTICS");
 
 #ifdef COPY_PARSE_PLAN_TREES
+       /* Optional debugging check: pass querytree output through copyObject() */
+       {
+               List       *new_list;
 
-       /*
-        * Optional debugging check: pass querytree output through
-        * copyObject()
-        */
-       new_list = (List *) copyObject(querytree_list);
-       /* This checks both copyObject() and the equal() routines... */
-       if (!equal(new_list, querytree_list))
-               elog(WARNING, "copyObject() failed to produce an equal parse tree");
-       else
-               querytree_list = new_list;
+               new_list = (List *) copyObject(querytree_list);
+               /* This checks both copyObject() and the equal() routines... */
+               if (!equal(new_list, querytree_list))
+                       elog(WARNING, "copyObject() failed to produce equal parse tree");
+               else
+                       querytree_list = new_list;
+       }
 #endif
 
        if (Debug_print_rewritten)
@@ -630,11 +661,14 @@ pg_rewrite_queries(List *querytree_list)
 }
 
 
-/* Generate a plan for a single already-rewritten query. */
-Plan *
-pg_plan_query(Query *querytree, ParamListInfo boundParams)
+/*
+ * Generate a plan for a single already-rewritten query.
+ * This is a thin wrapper around planner() and takes the same parameters.
+ */
+PlannedStmt *
+pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 {
-       Plan       *plan;
+       PlannedStmt *plan;
 
        /* Utility commands have no plans. */
        if (querytree->commandType == CMD_UTILITY)
@@ -644,7 +678,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
                ResetUsage();
 
        /* call the optimizer */
-       plan = planner(querytree, false, 0, boundParams);
+       plan = planner(querytree, cursorOptions, boundParams);
 
        if (log_planner_stats)
                ShowUsage("PLANNER STATISTICS");
@@ -652,11 +686,11 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
 #ifdef COPY_PARSE_PLAN_TREES
        /* Optional debugging check: pass plan output through copyObject() */
        {
-               Plan       *new_plan = (Plan *) copyObject(plan);
+               PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
 
                /*
-                * equal() currently does not have routines to compare Plan nodes,
-                * so don't try to test equality here.  Perhaps fix someday?
+                * equal() currently does not have routines to compare Plan nodes, so
+                * don't try to test equality here.  Perhaps fix someday?
                 */
 #ifdef NOT_USED
                /* This checks both copyObject() and the equal() routines... */
@@ -687,23 +721,26 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
  * utility statements depend on not having frozen the snapshot yet.
  * (We assume that such statements cannot appear together with plannable
  * statements in the rewriter's output.)
+ *
+ * Normal optimizable statements generate PlannedStmt entries in the result
+ * list.  Utility statements are simply represented by their statement nodes.
  */
 List *
-pg_plan_queries(List *querytrees, ParamListInfo boundParams,
+pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams,
                                bool needSnapshot)
 {
-       List       *plan_list = NIL;
+       List       *stmt_list = NIL;
        ListCell   *query_list;
 
        foreach(query_list, querytrees)
        {
                Query      *query = (Query *) lfirst(query_list);
-               Plan       *plan;
+               Node       *stmt;
 
                if (query->commandType == CMD_UTILITY)
                {
                        /* Utility commands have no plans. */
-                       plan = NULL;
+                       stmt = query->utilityStmt;
                }
                else
                {
@@ -712,13 +749,13 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                                ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
                                needSnapshot = false;
                        }
-                       plan = pg_plan_query(query, boundParams);
+                       stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
                }
 
-               plan_list = lappend(plan_list, plan);
+               stmt_list = lappend(stmt_list, stmt);
        }
 
-       return plan_list;
+       return stmt_list;
 }
 
 
@@ -734,11 +771,10 @@ exec_simple_query(const char *query_string)
        MemoryContext oldcontext;
        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;
+       bool            was_logged = false;
+       bool            isTopLevel;
+       char            msec_str[32];
 
        /*
         * Report query to various monitoring facilities.
@@ -748,58 +784,64 @@ exec_simple_query(const char *query_string)
        pgstat_report_activity(query_string);
 
        /*
-        * 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.
+        * We use save_log_statement_stats so ShowUsage doesn't report incorrect
+        * results because ResetUsage wasn't called.
         */
-       if (save_log_duration || save_log_min_duration_statement != -1)
-               gettimeofday(&start_t, NULL);
-
        if (save_log_statement_stats)
                ResetUsage();
 
        /*
         * Start up a transaction command.      All queries generated by the
         * query_string will be in this same command block, *unless* we find a
-        * BEGIN/COMMIT/ABORT statement; we have to force a new xact command
-        * after one of those, else bad things will happen in xact.c. (Note
-        * that this will normally change current memory context.)
+        * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after
+        * one of those, else bad things will happen in xact.c. (Note that this
+        * will normally change current memory context.)
         */
        start_xact_command();
 
        /*
-        * 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)
-       {
-               DropDependentPortals(unnamed_stmt_context);
-               MemoryContextDelete(unnamed_stmt_context);
-       }
-       unnamed_stmt_context = NULL;
+       drop_unnamed_stmt();
 
        /*
         * Switch to appropriate context for constructing parsetrees.
         */
        oldcontext = MemoryContextSwitchTo(MessageContext);
 
-       QueryContext = CurrentMemoryContext;
-
        /*
-        * Do basic parsing of the query or queries (this should be safe even
-        * if we are in aborted transaction state!)
+        * Do basic parsing of the query or queries (this should be safe even if
+        * we are in aborted transaction state!)
         */
        parsetree_list = pg_parse_query(query_string);
 
+       /* Log immediately if dictated by log_statement */
+       if (check_log_statement(parsetree_list))
+       {
+               ereport(LOG,
+                               (errmsg("statement: %s", query_string),
+                                errhidestmt(true),
+                                errdetail_execute(parsetree_list)));
+               was_logged = true;
+       }
+
        /*
         * Switch back to transaction context to enter the loop.
         */
        MemoryContextSwitchTo(oldcontext);
 
+       /*
+        * We'll tell PortalRun it's a top-level command iff there's exactly one
+        * raw parsetree.  If more than one, it's effectively a transaction block
+        * and we want PreventTransactionChain to reject unsafe commands. (Note:
+        * we're assuming that query rewrite cannot add commands that are
+        * significant to PreventTransactionChain.)
+        */
+       isTopLevel = (list_length(parsetree_list) == 1);
+
        /*
         * Run through the raw parsetree(s) and process each one.
         */
@@ -815,45 +857,31 @@ exec_simple_query(const char *query_string)
                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);
 
-               set_ps_display(commandTag);
+               set_ps_display(commandTag, false);
 
                BeginCommand(commandTag, dest);
 
                /*
                 * If we are in an aborted transaction, reject all commands except
-                * COMMIT/ABORT.  It is important that this test occur before we
-                * try to do parse analysis, rewrite, or planning, since all those
-                * phases try to do database accesses, which may fail in abort
-                * state. (It might be safe to allow some additional utility
-                * commands in this state, but not many...)
+                * COMMIT/ABORT.  It is important that this test occur before we try
+                * to do parse analysis, rewrite, or planning, since all those phases
+                * try to do database accesses, which may fail in abort state. (It
+                * might be safe to allow some additional utility commands in this
+                * state, but not many...)
                 */
-               if (IsAbortedTransactionBlockState())
-               {
-                       bool            allowit = false;
-
-                       if (IsA(parsetree, TransactionStmt))
-                       {
-                               TransactionStmt *stmt = (TransactionStmt *) parsetree;
-
-                               if (stmt->kind == TRANS_STMT_COMMIT ||
-                                       stmt->kind == TRANS_STMT_ROLLBACK ||
-                                       stmt->kind == TRANS_STMT_ROLLBACK_TO)
-                                       allowit = true;
-                       }
-
-                       if (!allowit)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
-                                                errmsg("current transaction is aborted, "
-                                       "commands ignored until end of transaction block")));
-               }
+               if (IsAbortedTransactionBlockState() &&
+                       !IsTransactionExitStmt(parsetree))
+                       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 */
                start_xact_command();
@@ -869,9 +897,10 @@ exec_simple_query(const char *query_string)
                 */
                oldcontext = MemoryContextSwitchTo(MessageContext);
 
-               querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0);
+               querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+                                                                                               NULL, 0);
 
-               plantree_list = pg_plan_queries(querytree_list, NULL, true);
+               plantree_list = pg_plan_queries(querytree_list, 0, NULL, true);
 
                /* If we got a cancel signal in analysis or planning, quit */
                CHECK_FOR_INTERRUPTS();
@@ -881,13 +910,15 @@ exec_simple_query(const char *query_string)
                 * already is one, silently drop it.
                 */
                portal = CreatePortal("", true, true);
+               /* Don't display the portal in pg_cursors */
+               portal->visible = false;
 
                PortalDefineQuery(portal,
+                                                 NULL,
                                                  query_string,
                                                  commandTag,
-                                                 querytree_list,
                                                  plantree_list,
-                                                 MessageContext);
+                                                 NULL);
 
                /*
                 * Start the portal.  No parameters here.
@@ -895,10 +926,10 @@ exec_simple_query(const char *query_string)
                PortalStart(portal, NULL, InvalidSnapshot);
 
                /*
-                * 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...)
+                * Select the appropriate output format: text unless we are doing a
+                * FETCH from a binary cursor.  (Pretty grotty to have to do this here
+                * --- but it avoids grottiness in other places.  Ah, the joys of
+                * backward compatibility...)
                 */
                format = 0;                             /* TEXT is default */
                if (IsA(parsetree, FetchStmt))
@@ -927,11 +958,11 @@ exec_simple_query(const char *query_string)
                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,
+                                                isTopLevel,
                                                 receiver,
                                                 receiver,
                                                 completionTag);
@@ -943,24 +974,22 @@ exec_simple_query(const char *query_string)
                if (IsA(parsetree, TransactionStmt))
                {
                        /*
-                        * 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();
                }
                else if (lnext(parsetree_item) == NULL)
                {
                        /*
-                        * 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();
                }
@@ -974,11 +1003,10 @@ exec_simple_query(const char *query_string)
                }
 
                /*
-                * Tell client that we're done with this query.  Note we emit
-                * exactly one EndCommand report for each raw parsetree, thus one
-                * for each SQL command the client sent, regardless of rewriting.
-                * (But a command aborted by error will not send an EndCommand
-                * report at all.)
+                * Tell client that we're done with this query.  Note we emit exactly
+                * one EndCommand report for each raw parsetree, thus one for each SQL
+                * command the client sent, regardless of rewriting. (But a command
+                * aborted by error will not send an EndCommand report at all.)
                 */
                EndCommand(completionTag, dest);
        }                                                       /* end loop over parsetrees */
@@ -994,45 +1022,23 @@ exec_simple_query(const char *query_string)
        if (!parsetree_list)
                NullCommand(dest);
 
-       QueryContext = NULL;
-
        /*
-        * Combine processing here as we need to calculate the query duration
-        * in both instances.
+        * Emit duration logging if appropriate.
         */
-       if (save_log_duration || save_log_min_duration_statement != -1)
+       switch (check_log_duration(msec_str, was_logged))
        {
-               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 (statement_logged && save_log_duration)
+               case 1:
                        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))
+                                       (errmsg("duration: %s ms", msec_str),
+                                        errhidestmt(true)));
+                       break;
+               case 2:
                        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: %s ms  statement: %s",
+                                                       msec_str, query_string),
+                                        errhidestmt(true),
+                                        errdetail_execute(parsetree_list)));
+                       break;
        }
 
        if (save_log_statement_stats)
@@ -1054,12 +1060,14 @@ exec_parse_message(const char *query_string,    /* string to execute */
 {
        MemoryContext oldcontext;
        List       *parsetree_list;
+       Node       *raw_parse_tree;
        const char *commandTag;
        List       *querytree_list,
-                          *plantree_list,
-                          *param_list;
+                          *stmt_list;
        bool            is_named;
+       bool            fully_planned;
        bool            save_log_statement_stats = log_statement_stats;
+       char            msec_str[32];
 
        /*
         * Report query to various monitoring facilities.
@@ -1068,15 +1076,20 @@ exec_parse_message(const char *query_string,    /* string to execute */
 
        pgstat_report_activity(query_string);
 
-       set_ps_display("PARSE");
+       set_ps_display("PARSE", false);
 
        if (save_log_statement_stats)
                ResetUsage();
 
+       ereport(DEBUG2,
+                       (errmsg("parse %s: %s",
+                                       *stmt_name ? stmt_name : "<unnamed>",
+                                       query_string)));
+
        /*
-        * Start up a transaction command so we can run parse analysis etc.
-        * (Note that this will normally change current memory context.)
-        * Nothing happens if we are already in one.
+        * Start up a transaction command so we can run parse analysis etc. (Note
+        * that this will normally change current memory context.) Nothing happens
+        * if we are already in one.
         */
        start_xact_command();
 
@@ -1086,13 +1099,12 @@ exec_parse_message(const char *query_string,    /* string to execute */
         * 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.
+        * statement's plancache entry; 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 create the plancache
+        * entry's context here, and do all the parsing work therein.
         */
        is_named = (stmt_name[0] != '\0');
        if (is_named)
@@ -1103,16 +1115,10 @@ exec_parse_message(const char *query_string,    /* string to execute */
        else
        {
                /* Unnamed prepared statement --- release any prior unnamed stmt */
-               unnamed_stmt_pstmt = NULL;
-               if (unnamed_stmt_context)
-               {
-                       DropDependentPortals(unnamed_stmt_context);
-                       MemoryContextDelete(unnamed_stmt_context);
-               }
-               unnamed_stmt_context = NULL;
-               /* create context for parsing/planning */
+               drop_unnamed_stmt();
+               /* Create context for parsing/planning */
                unnamed_stmt_context =
-                       AllocSetContextCreate(TopMemoryContext,
+                       AllocSetContextCreate(CacheMemoryContext,
                                                                  "unnamed prepared statement",
                                                                  ALLOCSET_DEFAULT_MINSIZE,
                                                                  ALLOCSET_DEFAULT_INITSIZE,
@@ -1120,80 +1126,69 @@ exec_parse_message(const char *query_string,    /* string to execute */
                oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
        }
 
-       QueryContext = CurrentMemoryContext;
-
        /*
-        * Do basic parsing of the query or queries (this should be safe even
-        * if we are in aborted transaction state!)
+        * Do basic parsing of the query or queries (this should be safe even if
+        * we are in aborted transaction state!)
         */
        parsetree_list = pg_parse_query(query_string);
 
        /*
-        * 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 (list_length(parsetree_list) > 1)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("cannot insert multiple commands into a prepared statement")));
+               errmsg("cannot insert multiple commands into a prepared statement")));
 
        if (parsetree_list != NIL)
        {
-               Node       *parsetree = (Node *) linitial(parsetree_list);
+               Query      *query;
                int                     i;
 
+               raw_parse_tree = (Node *) linitial(parsetree_list);
+
                /*
                 * Get the command name for possible use in status display.
                 */
-               commandTag = CreateCommandTag(parsetree);
+               commandTag = CreateCommandTag(raw_parse_tree);
 
                /*
                 * 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
-                * commands in this state, but not many...)
+                * 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())
-               {
-                       bool            allowit = false;
-
-                       if (IsA(parsetree, TransactionStmt))
-                       {
-                               TransactionStmt *stmt = (TransactionStmt *) parsetree;
-
-                               if (stmt->kind == TRANS_STMT_COMMIT ||
-                                       stmt->kind == TRANS_STMT_ROLLBACK ||
-                                       stmt->kind == TRANS_STMT_ROLLBACK_TO)
-                                       allowit = true;
-                       }
-
-                       if (!allowit)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
-                                                errmsg("current transaction is aborted, "
-                                       "commands ignored until end of transaction block")));
-               }
+               if (IsAbortedTransactionBlockState() &&
+                       !IsTransactionExitStmt(raw_parse_tree))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                                        errmsg("current transaction is aborted, "
+                                               "commands ignored until end of transaction block")));
 
                /*
                 * OK to analyze, rewrite, and plan this query.  Note that the
-                * originally specified parameter set is not required to be
-                * complete, so we have to use parse_analyze_varparams().
+                * originally specified parameter set is not required to be complete,
+                * so we have to use parse_analyze_varparams().
+                *
+                * XXX must use copyObject here since parse analysis scribbles on its
+                * input, and we need the unmodified raw parse tree for possible
+                * replanning later.
                 */
                if (log_parser_stats)
                        ResetUsage();
 
-               querytree_list = parse_analyze_varparams(parsetree,
-                                                                                                &paramTypes,
-                                                                                                &numParams);
+               query = parse_analyze_varparams(copyObject(raw_parse_tree),
+                                                                               query_string,
+                                                                               &paramTypes,
+                                                                               &numParams);
 
                /*
-                * Check all parameter types got determined, and convert array
-                * representation to a list for storage.
+                * Check all parameter types got determined.
                 */
-               param_list = NIL;
                for (i = 0; i < numParams; i++)
                {
                        Oid                     ptype = paramTypes[i];
@@ -1201,32 +1196,37 @@ 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 = lappend_oid(param_list, ptype);
+                                        errmsg("could not determine data type of parameter $%d",
+                                                       i + 1)));
                }
 
                if (log_parser_stats)
                        ShowUsage("PARSE ANALYSIS STATISTICS");
 
-               querytree_list = pg_rewrite_queries(querytree_list);
+               querytree_list = pg_rewrite_query(query);
 
                /*
-                * If this is the unnamed statement and it has parameters, defer
-                * query planning until Bind.  Otherwise do it now.
+                * 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;
+               {
+                       stmt_list = querytree_list;
+                       fully_planned = false;
+               }
                else
-                       plantree_list = pg_plan_queries(querytree_list, NULL, true);
+               {
+                       stmt_list = pg_plan_queries(querytree_list, 0, NULL, true);
+                       fully_planned = true;
+               }
        }
        else
        {
                /* Empty input string.  This is legal. */
+               raw_parse_tree = NULL;
                commandTag = NULL;
-               querytree_list = NIL;
-               plantree_list = NIL;
-               param_list = NIL;
+               stmt_list = NIL;
+               fully_planned = true;
        }
 
        /* If we got a cancel signal in analysis or planning, quit */
@@ -1238,47 +1238,80 @@ exec_parse_message(const char *query_string,    /* string to execute */
        if (is_named)
        {
                StorePreparedStatement(stmt_name,
+                                                          raw_parse_tree,
                                                           query_string,
                                                           commandTag,
-                                                          querytree_list,
-                                                          plantree_list,
-                                                          param_list);
+                                                          paramTypes,
+                                                          numParams,
+                                                          0,           /* default cursor options */
+                                                          stmt_list,
+                                                          false);
        }
        else
        {
-               PreparedStatement *pstmt;
+               /*
+                * paramTypes and query_string need to be copied into
+                * unnamed_stmt_context.  The rest is there already
+                */
+               Oid                *newParamTypes;
 
-               pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement));
-               /* query_string needs to be copied into unnamed_stmt_context */
-               pstmt->query_string = pstrdup(query_string);
-               /* the rest is there already */
-               pstmt->commandTag = commandTag;
-               pstmt->query_list = querytree_list;
-               pstmt->plan_list = plantree_list;
-               pstmt->argtype_list = param_list;
-               pstmt->context = unnamed_stmt_context;
-               /* Now the unnamed statement is complete and valid */
-               unnamed_stmt_pstmt = pstmt;
+               if (numParams > 0)
+               {
+                       newParamTypes = (Oid *) palloc(numParams * sizeof(Oid));
+                       memcpy(newParamTypes, paramTypes, numParams * sizeof(Oid));
+               }
+               else
+                       newParamTypes = NULL;
+
+               unnamed_stmt_psrc = FastCreateCachedPlan(raw_parse_tree,
+                                                                                                pstrdup(query_string),
+                                                                                                commandTag,
+                                                                                                newParamTypes,
+                                                                                                numParams,
+                                                                                                0,             /* cursor options */
+                                                                                                stmt_list,
+                                                                                                fully_planned,
+                                                                                                true,
+                                                                                                unnamed_stmt_context);
+               /* context now belongs to the plancache entry */
+               unnamed_stmt_context = NULL;
        }
 
        MemoryContextSwitchTo(oldcontext);
 
-       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();
 
        /*
         * Send ParseComplete.
         */
-       if (whereToSendOutput == Remote)
+       if (whereToSendOutput == DestRemote)
                pq_putemptymessage('1');
 
+       /*
+        * Emit duration logging if appropriate.
+        */
+       switch (check_log_duration(msec_str, false))
+       {
+               case 1:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms", msec_str),
+                                        errhidestmt(true)));
+                       break;
+               case 2:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms  parse %s: %s",
+                                                       msec_str,
+                                                       *stmt_name ? stmt_name : "<unnamed>",
+                                                       query_string),
+                                        errhidestmt(true)));
+                       break;
+       }
+
        if (save_log_statement_stats)
                ShowUsage("PARSE MESSAGE STATISTICS");
 
@@ -1300,34 +1333,69 @@ exec_bind_message(StringInfo input_message)
        int                     numParams;
        int                     numRFormats;
        int16      *rformats = NULL;
-       int                     i;
-       PreparedStatement *pstmt;
+       CachedPlanSource *psrc;
+       CachedPlan *cplan;
        Portal          portal;
        ParamListInfo params;
-       bool            isaborted = IsAbortedTransactionBlockState();
+       List       *plan_list;
+       bool            save_log_statement_stats = log_statement_stats;
+       char            msec_str[32];
+
+       /* Get the fixed part of the message */
+       portal_name = pq_getmsgstring(input_message);
+       stmt_name = pq_getmsgstring(input_message);
+
+       ereport(DEBUG2,
+                       (errmsg("bind %s to %s",
+                                       *portal_name ? portal_name : "<unnamed>",
+                                       *stmt_name ? stmt_name : "<unnamed>")));
+
+       /* Find prepared statement */
+       if (stmt_name[0] != '\0')
+       {
+               PreparedStatement *pstmt;
+
+               pstmt = FetchPreparedStatement(stmt_name, true);
+               psrc = pstmt->plansource;
+       }
+       else
+       {
+               /* special-case the unnamed statement */
+               psrc = unnamed_stmt_psrc;
+               if (!psrc)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
+                                        errmsg("unnamed prepared statement does not exist")));
+       }
+
+       /*
+        * Report query to various monitoring facilities.
+        */
+       debug_query_string = psrc->query_string ? psrc->query_string : "<BIND>";
 
-       pgstat_report_activity("<BIND>");
+       pgstat_report_activity(debug_query_string);
 
-       set_ps_display("BIND");
+       set_ps_display("BIND", false);
+
+       if (save_log_statement_stats)
+               ResetUsage();
 
        /*
-        * 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();
 
        /* Switch back to message context */
        MemoryContextSwitchTo(MessageContext);
 
-       /* Get the fixed part of the message */
-       portal_name = pq_getmsgstring(input_message);
-       stmt_name = pq_getmsgstring(input_message);
-
        /* Get the parameter format codes */
        numPFormats = pq_getmsgint(input_message, 2);
        if (numPFormats > 0)
        {
+               int                     i;
+
                pformats = (int16 *) palloc(numPFormats * sizeof(int16));
                for (i = 0; i < numPFormats; i++)
                        pformats[i] = pq_getmsgint(input_message, 2);
@@ -1339,31 +1407,34 @@ exec_bind_message(StringInfo input_message)
        if (numPFormats > 1 && numPFormats != numParams)
                ereport(ERROR,
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
-               errmsg("bind message has %d parameter formats but %d parameters",
-                          numPFormats, numParams)));
+                       errmsg("bind message has %d parameter formats but %d parameters",
+                                  numPFormats, numParams)));
 
-       /* Find prepared statement */
-       if (stmt_name[0] != '\0')
-               pstmt = FetchPreparedStatement(stmt_name, true);
-       else
-       {
-               /* special-case the unnamed statement */
-               pstmt = unnamed_stmt_pstmt;
-               if (!pstmt)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
-                                  errmsg("unnamed prepared statement does not exist")));
-       }
-
-       if (numParams != list_length(pstmt->argtype_list))
+       if (numParams != psrc->num_params)
                ereport(ERROR,
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
                                 errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
-                          numParams, stmt_name, list_length(pstmt->argtype_list))));
+                                               numParams, stmt_name, psrc->num_params)));
 
        /*
-        * Create the portal.  Allow silent replacement of an existing portal
-        * only if the unnamed portal is specified.
+        * If we are in aborted transaction state, the only portals we can
+        * actually run are those containing COMMIT or ROLLBACK commands. We
+        * disallow binding anything else to avoid problems with infrastructure
+        * that expects to run inside a valid transaction.      We also disallow
+        * binding any parameters, since we can't risk calling user-defined I/O
+        * functions.
+        */
+       if (IsAbortedTransactionBlockState() &&
+               (!IsTransactionExitStmt(psrc->raw_parse_tree) ||
+                numParams != 0))
+               ereport(ERROR,
+                               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                                errmsg("current transaction is aborted, "
+                                               "commands ignored until end of transaction block")));
+
+       /*
+        * Create the portal.  Allow silent replacement of an existing portal only
+        * if the unnamed portal is specified.
         */
        if (portal_name[0] == '\0')
                portal = CreatePortal(portal_name, true, true);
@@ -1372,27 +1443,28 @@ exec_bind_message(StringInfo input_message)
 
        /*
         * 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.
         */
        if (numParams > 0)
        {
-               ListCell   *l;
                MemoryContext oldContext;
+               int                     paramno;
 
                oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-               params = (ParamListInfo)
-                       palloc0((numParams + 1) * sizeof(ParamListInfoData));
+               /* sizeof(ParamListInfoData) includes the first array element */
+               params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
+                                                                  (numParams - 1) *sizeof(ParamExternData));
+               params->numParams = numParams;
 
-               i = 0;
-               foreach(l, pstmt->argtype_list)
+               for (paramno = 0; paramno < numParams; paramno++)
                {
-                       Oid                     ptype = lfirst_oid(l);
+                       Oid                     ptype = psrc->param_types[paramno];
                        int32           plength;
+                       Datum           pval;
                        bool            isNull;
+                       StringInfoData pbuf;
+                       char            csave;
+                       int16           pformat;
 
                        plength = pq_getmsgint(input_message, 4);
                        isNull = (plength == -1);
@@ -1401,110 +1473,108 @@ exec_bind_message(StringInfo input_message)
                        {
                                const char *pvalue = pq_getmsgbytes(input_message, plength);
 
-                               if (isaborted)
-                               {
-                                       /* We don't bother to check the format in this case */
-                                       isNull = true;
-                               }
+                               /*
+                                * Rather than copying data around, we just set up a phony
+                                * StringInfo pointing to the correct portion of the message
+                                * buffer.      We assume we can scribble on the message buffer so
+                                * as to maintain the convention that StringInfos have a
+                                * trailing null.  This is grotty but is a big win when
+                                * dealing with very large parameter strings.
+                                */
+                               pbuf.data = (char *) pvalue;
+                               pbuf.maxlen = plength + 1;
+                               pbuf.len = plength;
+                               pbuf.cursor = 0;
+
+                               csave = pbuf.data[plength];
+                               pbuf.data[plength] = '\0';
+                       }
+                       else
+                       {
+                               pbuf.data = NULL;               /* keep compiler quiet */
+                               csave = 0;
+                       }
+
+                       if (numPFormats > 1)
+                               pformat = pformats[paramno];
+                       else if (numPFormats > 0)
+                               pformat = pformats[0];
+                       else
+                               pformat = 0;    /* default = text */
+
+                       if (pformat == 0)       /* text mode */
+                       {
+                               Oid                     typinput;
+                               Oid                     typioparam;
+                               char       *pstring;
+
+                               getTypeInputInfo(ptype, &typinput, &typioparam);
+
+                               /*
+                                * We have to do encoding conversion before calling the
+                                * typinput routine.
+                                */
+                               if (isNull)
+                                       pstring = NULL;
                                else
-                               {
-                                       int16           pformat;
-                                       StringInfoData pbuf;
-                                       char            csave;
-
-                                       if (numPFormats > 1)
-                                               pformat = pformats[i];
-                                       else if (numPFormats > 0)
-                                               pformat = pformats[0];
-                                       else
-                                               pformat = 0;    /* default = text */
+                                       pstring = pg_client_to_server(pbuf.data, plength);
 
-                                       /*
-                                        * Rather than copying data around, we just set up a
-                                        * phony StringInfo pointing to the correct portion of
-                                        * the message buffer.  We assume we can scribble on
-                                        * the message buffer so as to maintain the convention
-                                        * that StringInfos have a trailing null.  This is
-                                        * grotty but is a big win when dealing with very
-                                        * large parameter strings.
-                                        */
-                                       pbuf.data = (char *) pvalue;
-                                       pbuf.maxlen = plength + 1;
-                                       pbuf.len = plength;
-                                       pbuf.cursor = 0;
+                               pval = OidInputFunctionCall(typinput, pstring, typioparam, -1);
+
+                               /* Free result of encoding conversion, if any */
+                               if (pstring && pstring != pbuf.data)
+                                       pfree(pstring);
+                       }
+                       else if (pformat == 1)          /* binary mode */
+                       {
+                               Oid                     typreceive;
+                               Oid                     typioparam;
+                               StringInfo      bufptr;
 
-                                       csave = pbuf.data[plength];
-                                       pbuf.data[plength] = '\0';
+                               /*
+                                * Call the parameter type's binary input converter
+                                */
+                               getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
 
-                                       if (pformat == 0)
-                                       {
-                                               Oid                     typinput;
-                                               Oid                     typioparam;
-                                               char       *pstring;
-
-                                               getTypeInputInfo(ptype, &typinput, &typioparam);
-
-                                               /*
-                                                * We have to do encoding conversion before
-                                                * calling the typinput routine.
-                                                */
-                                               pstring = (char *)
-                                                       pg_client_to_server((unsigned char *) pbuf.data,
-                                                                                               plength);
-                                               params[i].value =
-                                                       OidFunctionCall3(typinput,
-                                                                                        CStringGetDatum(pstring),
-                                                                                        ObjectIdGetDatum(typioparam),
-                                                                                        Int32GetDatum(-1));
-                                               /* Free result of encoding conversion, if any */
-                                               if (pstring != pbuf.data)
-                                                       pfree(pstring);
-                                       }
-                                       else if (pformat == 1)
-                                       {
-                                               Oid                     typreceive;
-                                               Oid                     typioparam;
-
-                                               /*
-                                                * Call the parameter type's binary input
-                                                * converter
-                                                */
-                                               getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
-
-                                               params[i].value =
-                                                       OidFunctionCall2(typreceive,
-                                                                                        PointerGetDatum(&pbuf),
-                                                                                  ObjectIdGetDatum(typioparam));
-
-                                               /* 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
-                                       {
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                                errmsg("unsupported format code: %d",
-                                                                               pformat)));
-                                       }
+                               if (isNull)
+                                       bufptr = NULL;
+                               else
+                                       bufptr = &pbuf;
 
-                                       /* Restore message buffer contents */
-                                       pbuf.data[plength] = csave;
-                               }
+                               pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1);
+
+                               /* Trouble if it didn't eat the whole buffer */
+                               if (!isNull && pbuf.cursor != pbuf.len)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+                                                        errmsg("incorrect binary data format in bind parameter %d",
+                                                                       paramno + 1)));
+                       }
+                       else
+                       {
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("unsupported format code: %d",
+                                                               pformat)));
+                               pval = 0;               /* keep compiler quiet */
                        }
 
-                       params[i].kind = PARAM_NUM;
-                       params[i].id = i + 1;
-                       params[i].ptype = ptype;
-                       params[i].isnull = isNull;
+                       /* Restore message buffer contents */
+                       if (!isNull)
+                               pbuf.data[plength] = csave;
 
-                       i++;
-               }
+                       params->params[paramno].value = pval;
+                       params->params[paramno].isnull = isNull;
 
-               params[i].kind = PARAM_INVALID;
+                       /*
+                        * We mark the params as CONST.  This has no effect if we already
+                        * did planning, but if we didn't, it licenses the planner to
+                        * substitute the parameters directly into the one-shot plan we
+                        * will generate below.
+                        */
+                       params->params[paramno].pflags = PARAM_FLAG_CONST;
+                       params->params[paramno].ptype = ptype;
+               }
 
                MemoryContextSwitchTo(oldContext);
        }
@@ -1515,6 +1585,8 @@ exec_bind_message(StringInfo input_message)
        numRFormats = pq_getmsgint(input_message, 2);
        if (numRFormats > 0)
        {
+               int                     i;
+
                rformats = (int16 *) palloc(numRFormats * sizeof(int16));
                for (i = 0; i < numRFormats; i++)
                        rformats[i] = pq_getmsgint(input_message, 2);
@@ -1522,32 +1594,62 @@ exec_bind_message(StringInfo input_message)
 
        pq_getmsgend(input_message);
 
-       /*
-        * 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)
+       if (psrc->fully_planned)
        {
-               MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
+               /*
+                * Revalidate the cached plan; this may result in replanning.  Any
+                * cruft will be generated in MessageContext.  The plan refcount will
+                * be assigned to the Portal, so it will be released at portal
+                * destruction.
+                */
+               cplan = RevalidateCachedPlan(psrc, false);
+               plan_list = cplan->stmt_list;
+       }
+       else
+       {
+               MemoryContext oldContext;
+               List       *query_list;
 
-               pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true);
+               /*
+                * Revalidate the cached plan; this may result in redoing parse
+                * analysis and rewriting (but not planning).  Any cruft will be
+                * generated in MessageContext.  The plan refcount is assigned to
+                * CurrentResourceOwner.
+                */
+               cplan = RevalidateCachedPlan(psrc, true);
+
+               /*
+                * We didn't plan the query before, so do it now.  This allows the
+                * planner to make use of the concrete parameter values we now have.
+                * Because we use PARAM_FLAG_CONST, the plan is good only for this set
+                * of param values, and so we generate the plan in the portal's own
+                * memory context where it will be thrown away after use. As in
+                * exec_parse_message, we make no attempt to recover planner temporary
+                * memory until the end of the operation.
+                *
+                * XXX because the planner has a bad habit of scribbling on its input,
+                * we have to make a copy of the parse trees.  FIXME someday.
+                */
+               oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+               query_list = copyObject(cplan->stmt_list);
+               plan_list = pg_plan_queries(query_list, 0, params, true);
                MemoryContextSwitchTo(oldContext);
+
+               /* We no longer need the cached plan refcount ... */
+               ReleaseCachedPlan(cplan, true);
+               /* ... and we don't want the portal to depend on it, either */
+               cplan = NULL;
        }
 
        /*
         * Define portal and start execution.
         */
        PortalDefineQuery(portal,
-                                         pstmt->query_string,
-                                         pstmt->commandTag,
-                                         pstmt->query_list,
-                                         pstmt->plan_list,
-                                         pstmt->context);
+                                         stmt_name[0] ? stmt_name : NULL,
+                                         psrc->query_string,
+                                         psrc->commandTag,
+                                         plan_list,
+                                         cplan);
 
        PortalStart(portal, params, InvalidSnapshot);
 
@@ -1559,8 +1661,36 @@ exec_bind_message(StringInfo input_message)
        /*
         * Send BindComplete.
         */
-       if (whereToSendOutput == Remote)
+       if (whereToSendOutput == DestRemote)
                pq_putemptymessage('2');
+
+       /*
+        * Emit duration logging if appropriate.
+        */
+       switch (check_log_duration(msec_str, false))
+       {
+               case 1:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms", msec_str),
+                                        errhidestmt(true)));
+                       break;
+               case 2:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms  bind %s%s%s: %s",
+                                                       msec_str,
+                                                       *stmt_name ? stmt_name : "<unnamed>",
+                                                       *portal_name ? "/" : "",
+                                                       *portal_name ? portal_name : "",
+                       psrc->query_string ? psrc->query_string : "<source not stored>"),
+                                        errhidestmt(true),
+                                        errdetail_params(params)));
+                       break;
+       }
+
+       if (save_log_statement_stats)
+               ShowUsage("BIND MESSAGE STATISTICS");
+
+       debug_query_string = NULL;
 }
 
 /*
@@ -1574,15 +1704,21 @@ exec_execute_message(const char *portal_name, long max_rows)
        CommandDest dest;
        DestReceiver *receiver;
        Portal          portal;
-       bool            is_trans_stmt = false;
-       bool            is_trans_exit = false;
        bool            completed;
        char            completionTag[COMPLETION_TAG_BUFSIZE];
+       const char *sourceText;
+       const char *prepStmtName;
+       ParamListInfo portalParams;
+       bool            save_log_statement_stats = log_statement_stats;
+       bool            is_xact_command;
+       bool            execute_is_fetch;
+       bool            was_logged = false;
+       char            msec_str[32];
 
        /* Adjust destination to tell printtup.c what to do */
        dest = whereToSendOutput;
-       if (dest == Remote)
-               dest = RemoteExecute;
+       if (dest == DestRemote)
+               dest = DestRemoteExecute;
 
        portal = GetPortalByName(portal_name);
        if (!PortalIsValid(portal))
@@ -1596,49 +1732,60 @@ exec_execute_message(const char *portal_name, long max_rows)
         */
        if (portal->commandTag == NULL)
        {
-               Assert(portal->parseTrees == NIL);
+               Assert(portal->stmts == NIL);
                NullCommand(dest);
                return;
        }
 
-       if (portal->sourceText)
+       /* Does the portal contain a transaction command? */
+       is_xact_command = IsTransactionStmtList(portal->stmts);
+
+       /*
+        * We must copy the sourceText and prepStmtName into MessageContext in
+        * case the portal is destroyed during finish_xact_command. Can avoid the
+        * copy if it's not an xact command, though.
+        */
+       if (is_xact_command)
        {
-               debug_query_string = portal->sourceText;
-               pgstat_report_activity(portal->sourceText);
+               sourceText = portal->sourceText ? pstrdup(portal->sourceText) : NULL;
+               if (portal->prepStmtName)
+                       prepStmtName = pstrdup(portal->prepStmtName);
+               else
+                       prepStmtName = "<unnamed>";
+
+               /*
+                * An xact command shouldn't have any parameters, which is a good
+                * thing because they wouldn't be around after finish_xact_command.
+                */
+               portalParams = NULL;
        }
        else
        {
-               debug_query_string = "execute message";
-               pgstat_report_activity("<EXECUTE>");
+               sourceText = portal->sourceText;
+               if (portal->prepStmtName)
+                       prepStmtName = portal->prepStmtName;
+               else
+                       prepStmtName = "<unnamed>";
+               portalParams = portal->portalParams;
        }
 
-       set_ps_display(portal->commandTag);
+       /*
+        * Report query to various monitoring facilities.
+        */
+       debug_query_string = sourceText ? sourceText : "<EXECUTE>";
 
-       BeginCommand(portal->commandTag, dest);
+       pgstat_report_activity(debug_query_string);
 
-       /* Check for transaction-control commands */
-       if (list_length(portal->parseTrees) == 1)
-       {
-               Query      *query = (Query *) linitial(portal->parseTrees);
+       set_ps_display(portal->commandTag, false);
 
-               if (query->commandType == CMD_UTILITY &&
-                       query->utilityStmt != NULL &&
-                       IsA(query->utilityStmt, TransactionStmt))
-               {
-                       TransactionStmt *stmt = (TransactionStmt *) query->utilityStmt;
+       if (save_log_statement_stats)
+               ResetUsage();
 
-                       is_trans_stmt = true;
-                       if (stmt->kind == TRANS_STMT_COMMIT ||
-                               stmt->kind == TRANS_STMT_ROLLBACK ||
-                               stmt->kind == TRANS_STMT_ROLLBACK_TO)
-                               is_trans_exit = true;
-               }
-       }
+       BeginCommand(portal->commandTag, dest);
 
        /*
-        * 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);
 
@@ -1649,18 +1796,42 @@ exec_execute_message(const char *portal_name, long max_rows)
        start_xact_command();
 
        /*
-        * If we are in aborted transaction state, the only portals we can
-        * actually run are those containing COMMIT or ROLLBACK commands.
+        * If we re-issue an Execute protocol request against an existing portal,
+        * then we are only fetching more rows rather than completely re-executing
+        * the query from the start. atStart is never reset for a v3 portal, so we
+        * are safe to use this check.
         */
-       if (IsAbortedTransactionBlockState())
+       execute_is_fetch = !portal->atStart;
+
+       /* Log immediately if dictated by log_statement */
+       if (check_log_statement(portal->stmts))
        {
-               if (!is_trans_exit)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
-                                        errmsg("current transaction is aborted, "
-                                       "commands ignored until end of transaction block")));
+               ereport(LOG,
+                               (errmsg("%s %s%s%s%s%s",
+                                               execute_is_fetch ?
+                                               _("execute fetch from") :
+                                               _("execute"),
+                                               prepStmtName,
+                                               *portal_name ? "/" : "",
+                                               *portal_name ? portal_name : "",
+                                               sourceText ? ": " : "",
+                                               sourceText ? sourceText : ""),
+                                errhidestmt(true),
+                                errdetail_params(portalParams)));
+               was_logged = true;
        }
 
+       /*
+        * If we are in aborted transaction state, the only portals we can
+        * actually run are those containing COMMIT or ROLLBACK commands.
+        */
+       if (IsAbortedTransactionBlockState() &&
+               !IsTransactionExitStmtList(portal->stmts))
+               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 */
        CHECK_FOR_INTERRUPTS();
 
@@ -1672,6 +1843,7 @@ exec_execute_message(const char *portal_name, long max_rows)
 
        completed = PortalRun(portal,
                                                  max_rows,
+                                                 true, /* always top level */
                                                  receiver,
                                                  receiver,
                                                  completionTag);
@@ -1680,12 +1852,11 @@ exec_execute_message(const char *portal_name, long max_rows)
 
        if (completed)
        {
-               if (is_trans_stmt)
+               if (is_xact_command)
                {
                        /*
                         * If this was a transaction control statement, commit it.      We
-                        * will start a new xact command for the next command (if
-                        * any).
+                        * will start a new xact command for the next command (if any).
                         */
                        finish_xact_command();
                }
@@ -1704,13 +1875,222 @@ exec_execute_message(const char *portal_name, long max_rows)
        else
        {
                /* Portal run not complete, so send PortalSuspended */
-               if (whereToSendOutput == Remote)
+               if (whereToSendOutput == DestRemote)
                        pq_putemptymessage('s');
        }
 
+       /*
+        * Emit duration logging if appropriate.
+        */
+       switch (check_log_duration(msec_str, was_logged))
+       {
+               case 1:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms", msec_str),
+                                        errhidestmt(true)));
+                       break;
+               case 2:
+                       ereport(LOG,
+                                       (errmsg("duration: %s ms  %s %s%s%s%s%s",
+                                                       msec_str,
+                                                       execute_is_fetch ?
+                                                       _("execute fetch from") :
+                                                       _("execute"),
+                                                       prepStmtName,
+                                                       *portal_name ? "/" : "",
+                                                       *portal_name ? portal_name : "",
+                                                       sourceText ? ": " : "",
+                                                       sourceText ? sourceText : ""),
+                                        errhidestmt(true),
+                                        errdetail_params(portalParams)));
+                       break;
+       }
+
+       if (save_log_statement_stats)
+               ShowUsage("EXECUTE MESSAGE STATISTICS");
+
        debug_query_string = NULL;
 }
 
+/*
+ * check_log_statement
+ *             Determine whether command should be logged because of log_statement
+ *
+ * parsetree_list can be either raw grammar output or a list of planned
+ * statements
+ */
+static bool
+check_log_statement(List *stmt_list)
+{
+       ListCell   *stmt_item;
+
+       if (log_statement == LOGSTMT_NONE)
+               return false;
+       if (log_statement == LOGSTMT_ALL)
+               return true;
+
+       /* Else we have to inspect the statement(s) to see whether to log */
+       foreach(stmt_item, stmt_list)
+       {
+               Node       *stmt = (Node *) lfirst(stmt_item);
+
+               if (GetCommandLogLevel(stmt) <= log_statement)
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * check_log_duration
+ *             Determine whether current command's duration should be logged
+ *
+ * Returns:
+ *             0 if no logging is needed
+ *             1 if just the duration should be logged
+ *             2 if duration and query details should be logged
+ *
+ * If logging is needed, the duration in msec is formatted into msec_str[],
+ * which must be a 32-byte buffer.
+ *
+ * was_logged should be TRUE if caller already logged query details (this
+ * essentially prevents 2 from being returned).
+ */
+int
+check_log_duration(char *msec_str, bool was_logged)
+{
+       if (log_duration || log_min_duration_statement >= 0)
+       {
+               long            secs;
+               int                     usecs;
+               int                     msecs;
+               bool            exceeded;
+
+               TimestampDifference(GetCurrentStatementStartTimestamp(),
+                                                       GetCurrentTimestamp(),
+                                                       &secs, &usecs);
+               msecs = usecs / 1000;
+
+               /*
+                * This odd-looking test for log_min_duration_statement being exceeded
+                * is designed to avoid integer overflow with very long durations:
+                * don't compute secs * 1000 until we've verified it will fit in int.
+                */
+               exceeded = (log_min_duration_statement == 0 ||
+                                       (log_min_duration_statement > 0 &&
+                                        (secs > log_min_duration_statement / 1000 ||
+                                         secs * 1000 + msecs >= log_min_duration_statement)));
+
+               if (exceeded || log_duration)
+               {
+                       snprintf(msec_str, 32, "%ld.%03d",
+                                        secs * 1000 + msecs, usecs % 1000);
+                       if (exceeded && !was_logged)
+                               return 2;
+                       else
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * errdetail_execute
+ *
+ * Add an errdetail() line showing the query referenced by an EXECUTE, if any.
+ * The argument is the raw parsetree list.
+ */
+static int
+errdetail_execute(List *raw_parsetree_list)
+{
+       ListCell   *parsetree_item;
+
+       foreach(parsetree_item, raw_parsetree_list)
+       {
+               Node       *parsetree = (Node *) lfirst(parsetree_item);
+
+               if (IsA(parsetree, ExecuteStmt))
+               {
+                       ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
+                       PreparedStatement *pstmt;
+
+                       pstmt = FetchPreparedStatement(stmt->name, false);
+                       if (pstmt && pstmt->plansource->query_string)
+                       {
+                               errdetail("prepare: %s", pstmt->plansource->query_string);
+                               return 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * errdetail_params
+ *
+ * Add an errdetail() line showing bind-parameter data, if available.
+ */
+static int
+errdetail_params(ParamListInfo params)
+{
+       /* We mustn't call user-defined I/O functions when in an aborted xact */
+       if (params && params->numParams > 0 && !IsAbortedTransactionBlockState())
+       {
+               StringInfoData param_str;
+               MemoryContext oldcontext;
+               int                     paramno;
+
+               /* Make sure any trash is generated in MessageContext */
+               oldcontext = MemoryContextSwitchTo(MessageContext);
+
+               initStringInfo(&param_str);
+
+               for (paramno = 0; paramno < params->numParams; paramno++)
+               {
+                       ParamExternData *prm = &params->params[paramno];
+                       Oid                     typoutput;
+                       bool            typisvarlena;
+                       char       *pstring;
+                       char       *p;
+
+                       appendStringInfo(&param_str, "%s$%d = ",
+                                                        paramno > 0 ? ", " : "",
+                                                        paramno + 1);
+
+                       if (prm->isnull || !OidIsValid(prm->ptype))
+                       {
+                               appendStringInfoString(&param_str, "NULL");
+                               continue;
+                       }
+
+                       getTypeOutputInfo(prm->ptype, &typoutput, &typisvarlena);
+
+                       pstring = OidOutputFunctionCall(typoutput, prm->value);
+
+                       appendStringInfoCharMacro(&param_str, '\'');
+                       for (p = pstring; *p; p++)
+                       {
+                               if (*p == '\'') /* double single quotes */
+                                       appendStringInfoCharMacro(&param_str, *p);
+                               appendStringInfoCharMacro(&param_str, *p);
+                       }
+                       appendStringInfoCharMacro(&param_str, '\'');
+
+                       pfree(pstring);
+               }
+
+               errdetail("parameters: %s", param_str.data);
+
+               pfree(param_str.data);
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       return 0;
+}
+
 /*
  * exec_describe_statement_message
  *
@@ -1719,36 +2099,69 @@ exec_execute_message(const char *portal_name, long max_rows)
 static void
 exec_describe_statement_message(const char *stmt_name)
 {
-       PreparedStatement *pstmt;
-       TupleDesc       tupdesc;
-       ListCell   *l;
+       CachedPlanSource *psrc;
        StringInfoData buf;
+       int                     i;
+
+       /*
+        * Start up a transaction command. (Note that this will normally change
+        * current memory context.) Nothing happens if we are already in one.
+        */
+       start_xact_command();
+
+       /* Switch back to message context */
+       MemoryContextSwitchTo(MessageContext);
 
        /* Find prepared statement */
        if (stmt_name[0] != '\0')
+       {
+               PreparedStatement *pstmt;
+
                pstmt = FetchPreparedStatement(stmt_name, true);
+               psrc = pstmt->plansource;
+       }
        else
        {
                /* special-case the unnamed statement */
-               pstmt = unnamed_stmt_pstmt;
-               if (!pstmt)
+               psrc = unnamed_stmt_psrc;
+               if (!psrc)
                        ereport(ERROR,
                                        (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
-                                  errmsg("unnamed prepared statement does not exist")));
+                                        errmsg("unnamed prepared statement does not exist")));
        }
 
-       if (whereToSendOutput != Remote)
+       /* Prepared statements shouldn't have changeable result descs */
+       Assert(psrc->fixed_result);
+
+       /*
+        * If we are in aborted transaction state, we can't run
+        * SendRowDescriptionMessage(), because that needs catalog accesses. (We
+        * can't do RevalidateCachedPlan, either, but that's a lesser problem.)
+        * Hence, refuse to Describe statements that return data.  (We shouldn't
+        * just refuse all Describes, since that might break the ability of some
+        * clients to issue COMMIT or ROLLBACK commands, if they use code that
+        * blindly Describes whatever it does.)  We can Describe parameters
+        * without doing anything dangerous, so we don't restrict that.
+        */
+       if (IsAbortedTransactionBlockState() &&
+               psrc->resultDesc)
+               ereport(ERROR,
+                               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                                errmsg("current transaction is aborted, "
+                                               "commands ignored until end of transaction block")));
+
+       if (whereToSendOutput != DestRemote)
                return;                                 /* can't actually do anything... */
 
        /*
         * First describe the parameters...
         */
        pq_beginmessage(&buf, 't'); /* parameter description message type */
-       pq_sendint(&buf, list_length(pstmt->argtype_list), 2);
+       pq_sendint(&buf, psrc->num_params, 2);
 
-       foreach(l, pstmt->argtype_list)
+       for (i = 0; i < psrc->num_params; i++)
        {
-               Oid                     ptype = lfirst_oid(l);
+               Oid                     ptype = psrc->param_types[i];
 
                pq_sendint(&buf, (int) ptype, 4);
        }
@@ -1757,16 +2170,20 @@ exec_describe_statement_message(const char *stmt_name)
        /*
         * Next send RowDescription or NoData to describe the result...
         */
-       tupdesc = FetchPreparedStatementResultDesc(pstmt);
-       if (tupdesc)
+       if (psrc->resultDesc)
        {
-               List       *targetlist;
+               CachedPlan *cplan;
+               List       *tlist;
 
-               if (ChoosePortalStrategy(pstmt->query_list) == PORTAL_ONE_SELECT)
-                       targetlist = ((Query *) linitial(pstmt->query_list))->targetList;
-               else
-                       targetlist = NIL;
-               SendRowDescriptionMessage(tupdesc, targetlist, NULL);
+               /* Make sure the plan is up to date */
+               cplan = RevalidateCachedPlan(psrc, true);
+
+               /* Get the primary statement and find out what it returns */
+               tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
+
+               SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
+
+               ReleaseCachedPlan(cplan, true);
        }
        else
                pq_putemptymessage('n');        /* NoData */
@@ -1783,26 +2200,43 @@ exec_describe_portal_message(const char *portal_name)
 {
        Portal          portal;
 
+       /*
+        * Start up a transaction command. (Note that this will normally change
+        * current memory context.) Nothing happens if we are already in one.
+        */
+       start_xact_command();
+
+       /* Switch back to message context */
+       MemoryContextSwitchTo(MessageContext);
+
        portal = GetPortalByName(portal_name);
        if (!PortalIsValid(portal))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_CURSOR),
                                 errmsg("portal \"%s\" does not exist", portal_name)));
 
-       if (whereToSendOutput != Remote)
+       /*
+        * If we are in aborted transaction state, we can't run
+        * SendRowDescriptionMessage(), because that needs catalog accesses.
+        * Hence, refuse to Describe portals that return data.  (We shouldn't just
+        * refuse all Describes, since that might break the ability of some
+        * clients to issue COMMIT or ROLLBACK commands, if they use code that
+        * blindly Describes whatever it does.)
+        */
+       if (IsAbortedTransactionBlockState() &&
+               portal->tupDesc)
+               ereport(ERROR,
+                               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                                errmsg("current transaction is aborted, "
+                                               "commands ignored until end of transaction block")));
+
+       if (whereToSendOutput != DestRemote)
                return;                                 /* can't actually do anything... */
 
        if (portal->tupDesc)
-       {
-               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 */
 }
@@ -1821,8 +2255,11 @@ start_xact_command(void)
                StartTransactionCommand();
 
                /* Set statement timeout running, if any */
+               /* NB: this mustn't be enabled until we are within an xact */
                if (StatementTimeout > 0)
                        enable_sig_alarm(StatementTimeout, true);
+               else
+                       cancel_from_timeout = false;
 
                xact_started = true;
        }
@@ -1842,20 +2279,105 @@ finish_xact_command(void)
 
                CommitTransactionCommand();
 
-#ifdef MEMORY_CONTEXT_CHECKING
-               /* Check all memory contexts that weren't freed during commit */
-               /* (those that were, were checked before being deleted) */
-               MemoryContextCheck(TopMemoryContext);
-#endif
+#ifdef MEMORY_CONTEXT_CHECKING
+               /* Check all memory contexts that weren't freed during commit */
+               /* (those that were, were checked before being deleted) */
+               MemoryContextCheck(TopMemoryContext);
+#endif
+
+#ifdef SHOW_MEMORY_STATS
+               /* Print mem stats after each commit for leak tracking */
+               MemoryContextStats(TopMemoryContext);
+#endif
+
+               xact_started = false;
+       }
+}
+
+
+/*
+ * Convenience routines for checking whether a statement is one of the
+ * ones that we allow in transaction-aborted state.
+ */
+
+/* Test a bare parsetree */
+static bool
+IsTransactionExitStmt(Node *parsetree)
+{
+       if (parsetree && IsA(parsetree, TransactionStmt))
+       {
+               TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+               if (stmt->kind == TRANS_STMT_COMMIT ||
+                       stmt->kind == TRANS_STMT_PREPARE ||
+                       stmt->kind == TRANS_STMT_ROLLBACK ||
+                       stmt->kind == TRANS_STMT_ROLLBACK_TO)
+                       return true;
+       }
+       return false;
+}
+
+/* Test a list that might contain Query nodes or bare parsetrees */
+static bool
+IsTransactionExitStmtList(List *parseTrees)
+{
+       if (list_length(parseTrees) == 1)
+       {
+               Node       *stmt = (Node *) linitial(parseTrees);
+
+               if (IsA(stmt, Query))
+               {
+                       Query      *query = (Query *) stmt;
+
+                       if (query->commandType == CMD_UTILITY &&
+                               IsTransactionExitStmt(query->utilityStmt))
+                               return true;
+               }
+               else if (IsTransactionExitStmt(stmt))
+                       return true;
+       }
+       return false;
+}
+
+/* Test a list that might contain Query nodes or bare parsetrees */
+static bool
+IsTransactionStmtList(List *parseTrees)
+{
+       if (list_length(parseTrees) == 1)
+       {
+               Node       *stmt = (Node *) linitial(parseTrees);
 
-#ifdef SHOW_MEMORY_STATS
-               /* Print mem stats after each commit for leak tracking */
-               if (ShowStats)
-                       MemoryContextStats(TopMemoryContext);
-#endif
+               if (IsA(stmt, Query))
+               {
+                       Query      *query = (Query *) stmt;
 
-               xact_started = false;
+                       if (query->commandType == CMD_UTILITY &&
+                               IsA(query->utilityStmt, TransactionStmt))
+                               return true;
+               }
+               else if (IsA(stmt, TransactionStmt))
+                       return true;
        }
+       return false;
+}
+
+/* Release any existing unnamed prepared statement */
+static void
+drop_unnamed_stmt(void)
+{
+       /* Release any completed unnamed statement */
+       if (unnamed_stmt_psrc)
+               DropCachedPlan(unnamed_stmt_psrc);
+       unnamed_stmt_psrc = NULL;
+
+       /*
+        * If we failed while trying to build a prior unnamed statement, we may
+        * have a memory context that wasn't assigned to a completed plancache
+        * entry.  If so, drop it to avoid a permanent memory leak.
+        */
+       if (unnamed_stmt_context)
+               MemoryContextDelete(unnamed_stmt_context);
+       unnamed_stmt_context = NULL;
 }
 
 
@@ -1876,30 +2398,30 @@ quickdie(SIGNAL_ARGS)
        PG_SETMASK(&BlockSig);
 
        /*
-        * Ideally this should be ereport(FATAL), but then we'd not get
-        * control back...
+        * Ideally this should be ereport(FATAL), but then we'd not get 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."),
+       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
-        * corrupted, so we don't want to try to clean up our transaction.
-        * Just nail the windows shut and get out of town.
+        * corrupted, so we don't want to try to clean up our transaction. Just
+        * nail the windows shut and get out of town.
         *
-        * Note we do exit(1) not exit(0).      This is to force the postmaster into
-        * a system reset cycle if some idiot DBA sends a manual SIGQUIT to a
-        * random backend.      This is necessary precisely because we don't clean
-        * up our shared memory state.
+        * Note we do exit(2) not exit(0).      This is to force the postmaster into a
+        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * backend.  This is necessary precisely because we don't clean up our
+        * shared memory state.
         */
-       exit(1);
+       exit(2);
 }
 
 /*
@@ -1918,8 +2440,8 @@ die(SIGNAL_ARGS)
                ProcDiePending = true;
 
                /*
-                * If it's safe to interrupt, and we're waiting for input or a
-                * lock, service the interrupt immediately
+                * If it's safe to interrupt, and we're waiting for input or a lock,
+                * service the interrupt immediately
                 */
                if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
                        CritSectionCount == 0)
@@ -1941,7 +2463,7 @@ die(SIGNAL_ARGS)
 
 /*
  * Timeout or shutdown signal from postmaster during client authentication.
- * Simply exit(0).
+ * Simply exit(1).
  *
  * XXX: possible future improvement: try to send a message indicating
  * why we are disconnecting.  Problem is to be sure we don't block while
@@ -1950,14 +2472,14 @@ die(SIGNAL_ARGS)
 void
 authdie(SIGNAL_ARGS)
 {
-       exit(0);
+       exit(1);
 }
 
 /*
  * Query-cancel signal from postmaster: abort current transaction
  * at soonest convenient time
  */
-static void
+void
 StatementCancelHandler(SIGNAL_ARGS)
 {
        int                     save_errno = errno;
@@ -1971,9 +2493,9 @@ StatementCancelHandler(SIGNAL_ARGS)
                QueryCancelPending = true;
 
                /*
-                * If it's safe to interrupt, and we're waiting for a lock,
-                * service the interrupt immediately.  No point in interrupting if
-                * we're waiting for input, however.
+                * If it's safe to interrupt, and we're waiting for a lock, service
+                * the interrupt immediately.  No point in interrupting if we're
+                * waiting for input, however.
                 */
                if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
                        CritSectionCount == 0)
@@ -1997,15 +2519,15 @@ StatementCancelHandler(SIGNAL_ARGS)
 }
 
 /* signal handler for floating point exception */
-static void
+void
 FloatExceptionHandler(SIGNAL_ARGS)
 {
        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.")));
+                        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 */
@@ -2037,9 +2559,14 @@ ProcessInterrupts(void)
                ImmediateInterruptOK = false;   /* not idle anymore */
                DisableNotifyInterrupt();
                DisableCatchupInterrupt();
-               ereport(FATAL,
-                               (errcode(ERRCODE_ADMIN_SHUTDOWN),
-                errmsg("terminating connection due to administrator command")));
+               if (IsAutoVacuumWorkerProcess())
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_ADMIN_SHUTDOWN),
+                                        errmsg("terminating autovacuum process due to administrator command")));
+               else
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_ADMIN_SHUTDOWN),
+                        errmsg("terminating connection due to administrator command")));
        }
        if (QueryCancelPending)
        {
@@ -2047,9 +2574,14 @@ ProcessInterrupts(void)
                ImmediateInterruptOK = false;   /* not idle anymore */
                DisableNotifyInterrupt();
                DisableCatchupInterrupt();
-               ereport(ERROR,
-                               (errcode(ERRCODE_QUERY_CANCELED),
-                                errmsg("canceling query due to user request")));
+               if (cancel_from_timeout)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_QUERY_CANCELED),
+                                        errmsg("canceling statement due to statement timeout")));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_QUERY_CANCELED),
+                                        errmsg("canceling statement due to user request")));
        }
        /* If we get here, do nothing (probably, QueryCancelPending was reset) */
 }
@@ -2061,28 +2593,21 @@ ProcessInterrupts(void)
  * 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 ...
+ * before hitting the hardware limit.
  */
 void
 check_stack_depth(void)
 {
        char            stack_top_loc;
-       int                     stack_depth;
+       long            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);
+       stack_depth = (long) (stack_base_ptr - &stack_top_loc);
 
        /*
-        * Take abs value, since stacks grow up on some machines, down on
-        * others
+        * Take abs value, since stacks grow up on some machines, down on others
         */
        if (stack_depth < 0)
                stack_depth = -stack_depth;
@@ -2091,9 +2616,9 @@ check_stack_depth(void)
         * 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.
+        * 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)
@@ -2101,56 +2626,33 @@ check_stack_depth(void)
                ereport(ERROR,
                                (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
                                 errmsg("stack depth limit exceeded"),
-                                errhint("Increase the configuration parameter \"max_stack_depth\".")));
+                errhint("Increase the configuration parameter \"max_stack_depth\", "
+                  "after ensuring the platform's stack depth limit is adequate.")));
        }
 }
 
-/* GUC assign hook to update max_stack_depth_bytes from max_stack_depth */
+/* GUC assign hook for max_stack_depth */
 bool
 assign_max_stack_depth(int newval, bool doit, GucSource source)
 {
-       /* Range check was already handled by guc.c */
+       long            newval_bytes = newval * 1024L;
+       long            stack_rlimit = get_stack_depth_rlimit();
+
+       if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
+       {
+               ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("\"max_stack_depth\" must not exceed %ldkB",
+                                               (stack_rlimit - STACK_DEPTH_SLOP) / 1024L),
+                                errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.")));
+               return false;
+       }
        if (doit)
-               max_stack_depth_bytes = newval * 1024;
+               max_stack_depth_bytes = newval_bytes;
        return true;
 }
 
 
-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(gettext("Usage:\n  %s [OPTION]... [DBNAME]\n\n"), progname);
-       printf(gettext("Options:\n"));
-#ifdef USE_ASSERT_CHECKING
-       printf(gettext("  -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 kB)\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"));
-}
-
-
 /*
  * set_debug_options --- apply "-d N" command line option
  *
@@ -2186,6 +2688,66 @@ set_debug_options(int debug_flag, GucContext context, GucSource source)
 }
 
 
+bool
+set_plan_disabling_options(const char *arg, GucContext context, GucSource source)
+{
+       char       *tmp = NULL;
+
+       switch (arg[0])
+       {
+               case 's':                               /* seqscan */
+                       tmp = "enable_seqscan";
+                       break;
+               case 'i':                               /* indexscan */
+                       tmp = "enable_indexscan";
+                       break;
+               case 'b':                               /* bitmapscan */
+                       tmp = "enable_bitmapscan";
+                       break;
+               case 't':                               /* tidscan */
+                       tmp = "enable_tidscan";
+                       break;
+               case 'n':                               /* nestloop */
+                       tmp = "enable_nestloop";
+                       break;
+               case 'm':                               /* mergejoin */
+                       tmp = "enable_mergejoin";
+                       break;
+               case 'h':                               /* hashjoin */
+                       tmp = "enable_hashjoin";
+                       break;
+       }
+       if (tmp)
+       {
+               SetConfigOption(tmp, "false", context, source);
+               return true;
+       }
+       else
+               return false;
+}
+
+
+const char *
+get_stats_option_name(const char *arg)
+{
+       switch (arg[0])
+       {
+               case 'p':
+                       if (optarg[1] == 'a')           /* "parser" */
+                               return "log_parser_stats";
+                       else if (optarg[1] == 'l')      /* "planner" */
+                               return "log_planner_stats";
+                       break;
+
+               case 'e':                               /* "executor" */
+                       return "log_executor_stats";
+                       break;
+       }
+
+       return NULL;
+}
+
+
 /* ----------------------------------------------------------------
  * PostgresMain
  *        postgres main loop -- all backends, interactive or otherwise start here
@@ -2204,47 +2766,30 @@ PostgresMain(int argc, char *argv[], const char *username)
        char       *userDoption = NULL;
        bool            secure;
        int                     errs = 0;
-       int                     debug_flag = -1;                /* -1 means not given */
-       List       *guc_names = NIL;            /* for SUSET options */
+       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;
        StringInfoData input_message;
        sigjmp_buf      local_sigjmp_buf;
-       volatile bool send_rfq = true;
+       volatile bool send_ready_for_query = 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.
-        */
-       if (!IsUnderPostmaster && argc > 1)
-       {
-               if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
-               {
-                       usage(argv[0]);
-                       exit(0);
-               }
-               if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
-               {
-                       puts(PG_VERSIONSTR);
-                       exit(0);
-               }
-       }
-
        /*
         * initialize globals (already done if under postmaster, but not if
         * standalone; cheap enough to do over)
         */
        MyProcPid = getpid();
 
+       MyStartTime = time(NULL);
+
        /*
         * Fire up essential subsystems: error and memory management
         *
@@ -2253,7 +2798,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
                MemoryContextInit();
 
-       set_ps_display("startup");
+       set_ps_display("startup", false);
 
        SetProcessingMode(InitProcessing);
 
@@ -2296,167 +2841,118 @@ PostgresMain(int argc, char *argv[], const char *username)
         * ----------------
         */
 
+       /* Ignore the initial --single argument, if present */
+       if (argc > 1 && strcmp(argv[1], "--single") == 0)
+       {
+               argv++;
+               argc--;
+       }
+
        /* all options are allowed until '-p' */
        secure = true;
        ctx = PGC_POSTMASTER;
        gucsource = PGC_S_ARGV;         /* initial switches came from command line */
 
-       while ((flag = getopt(argc, argv, "A:B:c:D:d:Eef:FiNOPo:p:S:st:v:W:-:")) != -1)
+       /*
+        * Parse command-line options.  CAUTION: keep this in sync with
+        * postmaster/postmaster.c (the option sets should not conflict) and with
+        * the common help() function in main/main.c.
+        */
+       while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:y:-:")) != -1)
        {
                switch (flag)
                {
                        case 'A':
-#ifdef USE_ASSERT_CHECKING
                                SetConfigOption("debug_assertions", optarg, ctx, gucsource);
-#else
-                               ereport(WARNING,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("assert checking is not compiled in")));
-#endif
                                break;
 
                        case 'B':
-
-                               /*
-                                * specify the size of buffer pool
-                                */
                                SetConfigOption("shared_buffers", optarg, ctx, gucsource);
                                break;
 
-                       case 'D':                       /* PGDATA or config directory */
+                       case 'D':
                                if (secure)
                                        userDoption = optarg;
                                break;
 
-                       case 'd':                       /* debug level */
+                       case 'd':
                                debug_flag = atoi(optarg);
                                break;
 
                        case 'E':
-
-                               /*
-                                * E - echo the query the user entered
-                                */
                                EchoQuery = true;
                                break;
 
                        case 'e':
-
-                               /*
-                                * Use European date input format (DMY)
-                                */
                                SetConfigOption("datestyle", "euro", ctx, gucsource);
                                break;
 
                        case 'F':
-
-                               /*
-                                * turn off fsync
-                                */
                                SetConfigOption("fsync", "false", ctx, gucsource);
                                break;
 
                        case 'f':
+                               if (!set_plan_disabling_options(optarg, ctx, gucsource))
+                                       errs++;
+                               break;
 
-                               /*
-                                * f - forbid generation of certain plans
-                                */
-                               tmp = NULL;
-                               switch (optarg[0])
-                               {
-                                       case 's':       /* seqscan */
-                                               tmp = "enable_seqscan";
-                                               break;
-                                       case 'i':       /* indexscan */
-                                               tmp = "enable_indexscan";
-                                               break;
-                                       case 't':       /* tidscan */
-                                               tmp = "enable_tidscan";
-                                               break;
-                                       case 'n':       /* nestloop */
-                                               tmp = "enable_nestloop";
-                                               break;
-                                       case 'm':       /* mergejoin */
-                                               tmp = "enable_mergejoin";
-                                               break;
-                                       case 'h':       /* hashjoin */
-                                               tmp = "enable_hashjoin";
-                                               break;
-                                       default:
-                                               errs++;
-                               }
-                               if (tmp)
-                                       SetConfigOption(tmp, "false", ctx, gucsource);
+                       case 'h':
+                               SetConfigOption("listen_addresses", optarg, ctx, gucsource);
                                break;
 
-                       case 'N':
+                       case 'i':
+                               SetConfigOption("listen_addresses", "*", ctx, gucsource);
+                               break;
 
-                               /*
-                                * N - Don't use newline as a query delimiter
-                                */
+                       case 'j':
                                UseNewLine = 0;
                                break;
 
-                       case 'O':
+                       case 'k':
+                               SetConfigOption("unix_socket_directory", optarg, ctx, gucsource);
+                               break;
 
-                               /*
-                                * allow system table structure modifications
-                                */
-                               if (secure)             /* XXX safe to allow from client??? */
-                                       allowSystemTableMods = true;
+                       case 'l':
+                               SetConfigOption("ssl", "true", ctx, gucsource);
                                break;
 
-                       case 'P':
+                       case 'N':
+                               SetConfigOption("max_connections", optarg, 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.
-                                */
-                               IgnoreSystemIndexes(true);
+                       case 'n':
+                               /* ignored for consistency with postmaster */
+                               break;
+
+                       case 'O':
+                               SetConfigOption("allow_system_table_mods", "true", ctx, gucsource);
                                break;
 
                        case 'o':
+                               errs++;
+                               break;
 
-                               /*
-                                * o - send output (stdout and stderr) to the given file
-                                */
-                               if (secure)
-                                       StrNCpy(OutputFileName, optarg, MAXPGPATH);
+                       case 'P':
+                               SetConfigOption("ignore_system_indexes", "true", ctx, gucsource);
                                break;
 
                        case 'p':
+                               SetConfigOption("port", optarg, ctx, gucsource);
+                               break;
 
-                               /*
-                                * p - special flag passed if backend was forked by a
-                                * postmaster.
-                                */
+                       case 'r':
+                               /* send output (stdout and stderr) to the given file */
                                if (secure)
-                               {
-                                       dbname = strdup(optarg);
-
-                                       secure = false;         /* subsequent switches are NOT
-                                                                                * secure */
-                                       ctx = PGC_BACKEND;
-                                       gucsource = PGC_S_CLIENT;
-                               }
+                                       strlcpy(OutputFileName, optarg, MAXPGPATH);
                                break;
 
                        case 'S':
-
-                               /*
-                                * S - amount of sort memory to use in 1k bytes
-                                */
                                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
                                 */
@@ -2467,43 +2963,25 @@ PostgresMain(int argc, char *argv[], const char *username)
                                                                        ctx, gucsource);
                                break;
 
+                       case 'T':
+                               /* ignored for consistency with postmaster */
+                               break;
+
                        case 't':
-                               /* ---------------
-                                *      tell postgres to report usage statistics (timings) for
-                                *      each query
-                                *
-                                *      -tpa[rser] = print stats for parser time of each query
-                                *      -tpl[anner] = print stats for planner time of each query
-                                *      -te[xecutor] = print stats for executor time of each query
-                                *      caution: -s can not be used together with -t.
-                                * ----------------
-                                */
-                               tmp = NULL;
-                               switch (optarg[0])
                                {
-                                       case 'p':
-                                               if (optarg[1] == 'a')
-                                                       tmp = "log_parser_stats";
-                                               else if (optarg[1] == 'l')
-                                                       tmp = "log_planner_stats";
+                                       const char *tmp = get_stats_option_name(optarg);
+
+                                       if (tmp)
+                                       {
+                                               if (ctx == PGC_BACKEND)
+                                                       PendingConfigOption(tmp, "true");
                                                else
-                                                       errs++;
-                                               break;
-                                       case 'e':
-                                               tmp = "log_executor_stats";
-                                               break;
-                                       default:
-                                               errs++;
-                                               break;
-                               }
-                               if (tmp)
-                               {
-                                       if (ctx == PGC_BACKEND)
-                                               PendingConfigOption(tmp, "true");
+                                                       SetConfigOption(tmp, "true", ctx, gucsource);
+                                       }
                                        else
-                                               SetConfigOption(tmp, "true", ctx, gucsource);
+                                               errs++;
+                                       break;
                                }
-                               break;
 
                        case 'v':
                                if (secure)
@@ -2511,11 +2989,24 @@ PostgresMain(int argc, char *argv[], const char *username)
                                break;
 
                        case 'W':
+                               SetConfigOption("post_auth_delay", optarg, ctx, gucsource);
+                               break;
+
+
+                       case 'y':
 
                                /*
-                                * wait N seconds to allow attach from a debugger
+                                * y - special flag passed if backend was forked by a
+                                * postmaster.
                                 */
-                               pg_usleep(atoi(optarg) * 1000000L);
+                               if (secure)
+                               {
+                                       dbname = strdup(optarg);
+
+                                       secure = false;         /* subsequent switches are NOT secure */
+                                       ctx = PGC_BACKEND;
+                                       gucsource = PGC_S_CLIENT;
+                               }
                                break;
 
                        case 'c':
@@ -2540,8 +3031,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                                        }
 
                                        /*
-                                        * If a SUSET option, must postpone evaluation, unless
-                                        * we are still reading secure switches.
+                                        * If a SUSET option, must postpone evaluation, unless we
+                                        * are still reading secure switches.
                                         */
                                        if (ctx == PGC_BACKEND && IsSuperuserConfigOption(name))
                                                PendingConfigOption(name, value);
@@ -2560,8 +3051,8 @@ PostgresMain(int argc, char *argv[], const char *username)
        }
 
        /*
-        * Process any additional GUC variable settings passed in startup
-        * packet.  These are handled exactly like command-line variables.
+        * Process any additional GUC variable settings passed in startup packet.
+        * These are handled exactly like command-line variables.
         */
        if (MyProcPort != NULL)
        {
@@ -2590,33 +3081,54 @@ PostgresMain(int argc, char *argv[], const char *username)
        {
                if (!SelectConfigFiles(userDoption, argv[0]))
                        proc_exit(1);
+               /* If timezone is not set, determine what the OS uses */
+               pg_timezone_initialize();
+               /* If timezone_abbreviations is not set, select default */
+               pg_timezone_abbrev_initialize();
        }
 
+       if (PostAuthDelay)
+               pg_usleep(PostAuthDelay * 1000000L);
+
+       /*
+        * You might expect to see a setsid() call here, but it's not needed,
+        * because if we are under a postmaster then BackendInitialize() did it.
+        */
+
        /*
         * Set up signal handlers and masks.
         *
         * Note that postmaster blocked all signals before forking child process,
-        * so there is no race condition whereby we might receive a signal
-        * before we have set up the handler.
+        * so there is no race condition whereby we might receive a signal before
+        * we have set up the handler.
         *
-        * Also note: it's best not to use any signals that are SIG_IGNored in
-        * the postmaster.      If such a signal arrives before we are able to
-        * change the handler to non-SIG_IGN, it'll get dropped.  Instead,
-        * make a dummy handler in the postmaster to reserve the signal. (Of
-        * course, this isn't an issue for signals that are locally generated,
-        * such as SIGALRM and SIGPIPE.)
+        * Also note: it's best not to use any signals that are SIG_IGNored in the
+        * postmaster.  If such a signal arrives before we are able to change the
+        * handler to non-SIG_IGN, it'll get dropped.  Instead, make a dummy
+        * handler in the postmaster to reserve the signal. (Of course, this isn't
+        * an issue for signals that are locally generated, such as SIGALRM and
+        * SIGPIPE.)
         */
        pqsignal(SIGHUP, SigHupHandler);        /* set flag to read config file */
        pqsignal(SIGINT, StatementCancelHandler);       /* cancel current query */
        pqsignal(SIGTERM, die);         /* cancel current query and exit */
-       pqsignal(SIGQUIT, quickdie);    /* hard crash time */
+
+       /*
+        * In a standalone backend, SIGQUIT can be generated from the keyboard
+        * easily, while SIGTERM cannot, so we make both signals do die() rather
+        * than quickdie().
+        */
+       if (IsUnderPostmaster)
+               pqsignal(SIGQUIT, quickdie);    /* hard crash time */
+       else
+               pqsignal(SIGQUIT, die); /* cancel current query and exit */
        pqsignal(SIGALRM, handle_sig_alarm);            /* timeout conditions */
 
        /*
         * Ignore failure to write to frontend. Note: if frontend closes
         * connection, we will notice it and exit cleanly when control next
-        * returns to outer loop.  This seems safer than forcing exit in the
-        * midst of output during who-knows-what operation...
+        * returns to outer loop.  This seems safer than forcing exit in the midst
+        * of output during who-knows-what operation...
         */
        pqsignal(SIGPIPE, SIG_IGN);
        pqsignal(SIGUSR1, CatchupInterruptHandler);
@@ -2624,24 +3136,24 @@ PostgresMain(int argc, char *argv[], const char *username)
        pqsignal(SIGFPE, FloatExceptionHandler);
 
        /*
-        * Reset some signals that are accepted by postmaster but not by
-        * backend
+        * Reset some signals that are accepted by postmaster but not by backend
         */
-       pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some
-                                                                * platforms */
+       pqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some platforms */
 
        pqinitmask();
 
-       /* We allow SIGQUIT (quickdie) at all times */
+       if (IsUnderPostmaster)
+       {
+               /* We allow SIGQUIT (quickdie) at all times */
 #ifdef HAVE_SIGPROCMASK
-       sigdelset(&BlockSig, SIGQUIT);
+               sigdelset(&BlockSig, SIGQUIT);
 #else
-       BlockSig &= ~(sigmask(SIGQUIT));
+               BlockSig &= ~(sigmask(SIGQUIT));
 #endif
+       }
 
        PG_SETMASK(&BlockSig);          /* block everything except SIGQUIT */
 
-
        if (IsUnderPostmaster)
        {
                /* noninteractive case: nothing should be left after switches */
@@ -2649,12 +3161,10 @@ 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])));
                }
 
-               XLOGPathInit();
-
                BaseInit();
        }
        else
@@ -2666,7 +3176,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];
@@ -2679,18 +3189,20 @@ 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).
+                * 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();
 
                /*
@@ -2701,23 +3213,42 @@ PostgresMain(int argc, char *argv[], const char *username)
                on_shmem_exit(ShutdownXLOG, 0);
 
                /*
-                * Read any existing FSM cache file, and register to write one out
-                * at exit.
+                * Read any existing FSM cache file, and register to write one out at
+                * exit.
                 */
                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);
        }
 
+       /*
+        * Create a per-backend PGPROC struct in shared memory, except in the
+        * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
+        * this before we can use LWLocks (and in the EXEC_BACKEND case we already
+        * had to do some stuff with LWLocks).
+        */
+#ifdef EXEC_BACKEND
+       if (!IsUnderPostmaster)
+               InitProcess();
+#else
+       InitProcess();
+#endif
+
        /*
         * General initialization.
         *
-        * NOTE: if you are tempted to add code in this vicinity, consider
-        * putting it inside InitPostgres() instead.  In particular, anything
-        * that involves database access should be there, not here.
+        * NOTE: if you are tempted to add code in this vicinity, consider putting
+        * it inside InitPostgres() instead.  In particular, anything that
+        * involves database access should be there, not here.
         */
        ereport(DEBUG3,
                        (errmsg_internal("InitPostgres")));
-       am_superuser = InitPostgres(dbname, username);
+       am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
 
        SetProcessingMode(NormalProcessing);
 
@@ -2753,16 +3284,22 @@ PostgresMain(int argc, char *argv[], const char *username)
        BeginReportingGUCOptions();
 
        /*
-        * Also set up handler to log session end; we have to wait till now
-        * to be sure Log_disconnections has its final value.
+        * 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);
 
+       /*
+        * process any libraries that should be preloaded at backend start (this
+        * likewise can't be done until GUC settings are complete)
+        */
+       process_local_preload_libraries();
+
        /*
         * Send this backend's cancellation info to the frontend.
         */
-       if (whereToSendOutput == Remote &&
+       if (whereToSendOutput == DestRemote &&
                PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
        {
                StringInfoData buf;
@@ -2775,7 +3312,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        }
 
        /* Welcome banner for standalone case */
-       if (whereToSendOutput == Debug)
+       if (whereToSendOutput == DestDebug)
                printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION);
 
        /*
@@ -2790,27 +3327,25 @@ PostgresMain(int argc, char *argv[], const char *username)
                                                                                   ALLOCSET_DEFAULT_INITSIZE,
                                                                                   ALLOCSET_DEFAULT_MAXSIZE);
 
-       /* ----------
-        * Tell the statistics collector that we're alive and
-        * to which database we belong.
-        * ----------
+       /*
+        * Remember stand-alone backend startup time
         */
-       pgstat_bestart();
+       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.
+        * 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.)
+        * exception stack, and so with PG_TRY there would be no exception handler
+        * in force at all during the CATCH part.  By leaving the outermost setjmp
+        * always active, we have at least some chance of recovering from an error
+        * during error recovery.  (If we get into an infinite loop thereby, it
+        * will soon be stopped by overflow of elog.c's internal state stack.)
         */
 
        if (sigsetjmp(local_sigjmp_buf, 1) != 0)
@@ -2819,9 +3354,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                 * NOTE: if you are tempted to add more code in this if-block,
                 * 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.
+                * 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 */
@@ -2831,19 +3365,19 @@ PostgresMain(int argc, char *argv[], const char *username)
                HOLD_INTERRUPTS();
 
                /*
-                * Forget any pending QueryCancel request, since we're returning
-                * to the idle loop anyway, and cancel the statement timer if
-                * running.
+                * Forget any pending QueryCancel request, since we're returning to
+                * the idle loop anyway, and cancel the statement timer if running.
                 */
                QueryCancelPending = false;
                disable_sig_alarm(true);
                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.
+                * Turn off these interrupts too.  This is only needed here and not in
+                * other exception-catching places since these interrupts are only
+                * enabled while we wait for client input.
                 */
+               DoingCommandRead = false;
                DisableNotifyInterrupt();
                DisableCatchupInterrupt();
 
@@ -2854,8 +3388,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                EmitErrorReport();
 
                /*
-                * Make sure debug_query_string gets reset before we possibly
-                * clobber the storage it points at.
+                * Make sure debug_query_string gets reset before we possibly clobber
+                * the storage it points at.
                 */
                debug_query_string = NULL;
 
@@ -2865,16 +3399,15 @@ PostgresMain(int argc, char *argv[], const char *username)
                AbortCurrentTransaction();
 
                /*
-                * Now return to normal top-level context and clear ErrorContext
-                * for next time.
+                * Now return to normal top-level context and clear ErrorContext for
+                * next time.
                 */
                MemoryContextSwitchTo(TopMemoryContext);
                FlushErrorState();
-               QueryContext = NULL;
 
                /*
-                * If we were handling an extended-query-protocol message,
-                * initiate skip till next Sync.  This also causes us not to issue
+                * If we were handling an extended-query-protocol message, initiate
+                * skip till next Sync.  This also causes us not to issue
                 * ReadyForQuery (until we get Sync).
                 */
                if (doing_extended_query_message)
@@ -2893,7 +3426,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        PG_SETMASK(&UnBlockSig);
 
        if (!ignore_till_sync)
-               send_rfq = true;                /* initially, or after error */
+               send_ready_for_query = true;    /* initially, or after error */
 
        /*
         * Non-error queries loop here.
@@ -2908,8 +3441,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                doing_extended_query_message = false;
 
                /*
-                * Release storage left over from prior query cycle, and create a
-                * new query input buffer in the cleared MessageContext.
+                * Release storage left over from prior query cycle, and create a new
+                * query input buffer in the cleared MessageContext.
                 */
                MemoryContextSwitchTo(MessageContext);
                MemoryContextResetAndDeleteChildren(MessageContext);
@@ -2917,52 +3450,44 @@ PostgresMain(int argc, char *argv[], const char *username)
                initStringInfo(&input_message);
 
                /*
-                * (1) If we've reached idle state, 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, and because we don't want
-                * to report uncommitted updates (that confuses autovacuum).
+                * 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)
+               if (send_ready_for_query)
                {
                        if (IsTransactionOrTransactionBlock())
                        {
-                               set_ps_display("idle in transaction");
+                               set_ps_display("idle in transaction", false);
                                pgstat_report_activity("<IDLE> in transaction");
                        }
                        else
                        {
-                               pgstat_report_tabstat();
+                               pgstat_report_tabstat(false);
 
-                               set_ps_display("idle");
+                               set_ps_display("idle", false);
                                pgstat_report_activity("<IDLE>");
                        }
 
                        ReadyForQuery(whereToSendOutput);
-                       send_rfq = false;
+                       send_ready_for_query = false;
                }
 
                /*
-                * (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)
@@ -2972,15 +3497,11 @@ 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
-                * we slept.
+                * (5) check for any other interesting events that happened while we
+                * slept.
                 */
                if (got_SIGHUP)
                {
@@ -3001,12 +3522,15 @@ PostgresMain(int argc, char *argv[], const char *username)
                                {
                                        const char *query_string;
 
+                                       /* Set statement_timestamp() */
+                                       SetCurrentStatementStartTimestamp();
+
                                        query_string = pq_getmsgstring(&input_message);
                                        pq_getmsgend(&input_message);
 
                                        exec_simple_query(query_string);
 
-                                       send_rfq = true;
+                                       send_ready_for_query = true;
                                }
                                break;
 
@@ -3017,6 +3541,9 @@ PostgresMain(int argc, char *argv[], const char *username)
                                        int                     numParams;
                                        Oid                *paramTypes = NULL;
 
+                                       /* Set statement_timestamp() */
+                                       SetCurrentStatementStartTimestamp();
+
                                        stmt_name = pq_getmsgstring(&input_message);
                                        query_string = pq_getmsgstring(&input_message);
                                        numParams = pq_getmsgint(&input_message, 2);
@@ -3036,10 +3563,12 @@ PostgresMain(int argc, char *argv[], const char *username)
                                break;
 
                        case 'B':                       /* bind */
+                               /* Set statement_timestamp() */
+                               SetCurrentStatementStartTimestamp();
 
                                /*
-                                * 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);
                                break;
@@ -3049,6 +3578,9 @@ PostgresMain(int argc, char *argv[], const char *username)
                                        const char *portal_name;
                                        int                     max_rows;
 
+                                       /* Set statement_timestamp() */
+                                       SetCurrentStatementStartTimestamp();
+
                                        portal_name = pq_getmsgstring(&input_message);
                                        max_rows = pq_getmsgint(&input_message, 4);
                                        pq_getmsgend(&input_message);
@@ -3058,18 +3590,27 @@ PostgresMain(int argc, char *argv[], const char *username)
                                break;
 
                        case 'F':                       /* fastpath function call */
+                               /* Set statement_timestamp() */
+                               SetCurrentStatementStartTimestamp();
+
                                /* Tell the collector what we're doing */
                                pgstat_report_activity("<FASTPATH> function call");
 
                                /* start an xact for this function invocation */
                                start_xact_command();
 
+                               /*
+                                * Note: we may at this point be inside an aborted
+                                * transaction.  We can't throw error for that until we've
+                                * finished reading the function-call message, so
+                                * HandleFunctionRequest() must check for it after doing so.
+                                * Be careful not to do anything that assumes we're inside a
+                                * valid transaction here.
+                                */
+
                                /* switch back to message context */
                                MemoryContextSwitchTo(MessageContext);
 
-                               /* set snapshot in case function needs one */
-                               ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
-
                                if (HandleFunctionRequest(&input_message) == EOF)
                                {
                                        /* lost frontend connection during F message input */
@@ -3078,8 +3619,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                                         * Reset whereToSendOutput to prevent ereport from
                                         * attempting to send any more messages to client.
                                         */
-                                       if (whereToSendOutput == Remote)
-                                               whereToSendOutput = None;
+                                       if (whereToSendOutput == DestRemote)
+                                               whereToSendOutput = DestNone;
 
                                        proc_exit(0);
                                }
@@ -3087,7 +3628,7 @@ PostgresMain(int argc, char *argv[], const char *username)
                                /* commit the function-invocation transaction */
                                finish_xact_command();
 
-                               send_rfq = true;
+                               send_ready_for_query = true;
                                break;
 
                        case 'C':                       /* close */
@@ -3107,13 +3648,7 @@ PostgresMain(int argc, char *argv[], const char *username)
                                                        else
                                                        {
                                                                /* special-case the unnamed statement */
-                                                               unnamed_stmt_pstmt = NULL;
-                                                               if (unnamed_stmt_context)
-                                                               {
-                                                                       DropDependentPortals(unnamed_stmt_context);
-                                                                       MemoryContextDelete(unnamed_stmt_context);
-                                                               }
-                                                               unnamed_stmt_context = NULL;
+                                                               drop_unnamed_stmt();
                                                        }
                                                        break;
                                                case 'P':
@@ -3128,12 +3663,12 @@ PostgresMain(int argc, char *argv[], const char *username)
                                                default:
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                                          errmsg("invalid CLOSE message subtype %d",
-                                                                         close_type)));
+                                                                  errmsg("invalid CLOSE message subtype %d",
+                                                                                 close_type)));
                                                        break;
                                        }
 
-                                       if (whereToSendOutput == Remote)
+                                       if (whereToSendOutput == DestRemote)
                                                pq_putemptymessage('3');                /* CloseComplete */
                                }
                                break;
@@ -3143,6 +3678,9 @@ PostgresMain(int argc, char *argv[], const char *username)
                                        int                     describe_type;
                                        const char *describe_target;
 
+                                       /* Set statement_timestamp() (needed for xact) */
+                                       SetCurrentStatementStartTimestamp();
+
                                        describe_type = pq_getmsgbyte(&input_message);
                                        describe_target = pq_getmsgstring(&input_message);
                                        pq_getmsgend(&input_message);
@@ -3158,8 +3696,8 @@ PostgresMain(int argc, char *argv[], const char *username)
                                                default:
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                                       errmsg("invalid DESCRIBE message subtype %d",
-                                                                  describe_type)));
+                                                               errmsg("invalid DESCRIBE message subtype %d",
+                                                                          describe_type)));
                                                        break;
                                        }
                                }
@@ -3167,37 +3705,37 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                        case 'H':                       /* flush */
                                pq_getmsgend(&input_message);
-                               if (whereToSendOutput == Remote)
+                               if (whereToSendOutput == DestRemote)
                                        pq_flush();
                                break;
 
                        case 'S':                       /* sync */
                                pq_getmsgend(&input_message);
                                finish_xact_command();
-                               send_rfq = true;
+                               send_ready_for_query = true;
                                break;
 
                                /*
-                                * 'X' means that the frontend is closing down the socket.
-                                * EOF means unexpected loss of frontend connection.
-                                * Either way, perform normal shutdown.
+                                * 'X' means that the frontend is closing down the socket. EOF
+                                * means unexpected loss of frontend connection. Either way,
+                                * perform normal shutdown.
                                 */
                        case 'X':
                        case EOF:
 
                                /*
-                                * Reset whereToSendOutput to prevent ereport 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;
+                               if (whereToSendOutput == DestRemote)
+                                       whereToSendOutput = DestNone;
 
                                /*
                                 * NOTE: if you are tempted to add more code here, DON'T!
                                 * Whatever you had in mind to do should be set up as an
-                                * on_proc_exit or on_shmem_exit callback, instead.
-                                * Otherwise it will fail to be called during other
-                                * backend-shutdown scenarios.
+                                * on_proc_exit or on_shmem_exit callback, instead. Otherwise
+                                * it will fail to be called during other backend-shutdown
+                                * scenarios.
                                 */
                                proc_exit(0);
 
@@ -3207,8 +3745,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                                /*
                                 * Accept but ignore these messages, per protocol spec; we
-                                * probably got here because a COPY failed, and the
-                                * frontend is still sending data.
+                                * probably got here because a COPY failed, and the frontend
+                                * is still sending data.
                                 */
                                break;
 
@@ -3226,11 +3764,41 @@ PostgresMain(int argc, char *argv[], const char *username)
        return 1;                                       /* keep compiler quiet */
 }
 
-#ifndef HAVE_GETRUSAGE
-#include "rusagestub.h"
-#else
-#include <sys/resource.h>
-#endif   /* HAVE_GETRUSAGE */
+
+/*
+ * Obtain platform stack depth limit (in bytes)
+ *
+ * Return -1 if unlimited or not known
+ */
+long
+get_stack_depth_rlimit(void)
+{
+#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_STACK)
+       static long val = 0;
+
+       /* This won't change after process launch, so check just once */
+       if (val == 0)
+       {
+               struct rlimit rlim;
+
+               if (getrlimit(RLIMIT_STACK, &rlim) < 0)
+                       val = -1;
+               else if (rlim.rlim_cur == RLIM_INFINITY)
+                       val = -1;
+               else
+                       val = rlim.rlim_cur;
+       }
+       return val;
+#else                                                  /* no getrlimit */
+#if defined(WIN32) || defined(__CYGWIN__)
+       /* On Windows we set the backend stack size in src/backend/Makefile */
+       return WIN32_STACK_RLIMIT;
+#else                                                  /* not windows ... give up */
+       return -1;
+#endif
+#endif
+}
+
 
 static struct rusage Save_r;
 static struct timeval Save_t;
@@ -3276,30 +3844,28 @@ ShowUsage(const char *title)
 
        /*
         * the only stats we don't show here are for memory usage -- i can't
-        * figure out how to interpret the relevant fields in the rusage
-        * struct, and they change names across o/s platforms, anyway. if you
-        * can figure out what the entries mean, you can somehow extract
-        * resident set size, shared text size, and unshared data and stack
-        * sizes.
+        * figure out how to interpret the relevant fields in the rusage struct,
+        * and they change names across o/s platforms, anyway. if you can figure
+        * out what the entries mean, you can somehow extract resident set size,
+        * shared text size, and unshared data and stack sizes.
         */
        initStringInfo(&str);
 
        appendStringInfo(&str, "! system usage stats:\n");
        appendStringInfo(&str,
-                       "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
+                               "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
                                         (long) (elapse_t.tv_sec - Save_t.tv_sec),
                                         (long) (elapse_t.tv_usec - Save_t.tv_usec),
                                         (long) (r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec),
-                                  (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,
                                         (long) user.tv_usec,
                                         (long) sys.tv_sec,
                                         (long) sys.tv_usec);
-/* BeOS has rusage but only has some fields, and not these... */
 #if defined(HAVE_GETRUSAGE)
        appendStringInfo(&str,
                                         "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
@@ -3308,21 +3874,21 @@ ShowUsage(const char *title)
                                         r.ru_oublock - Save_r.ru_oublock,
                                         r.ru_inblock, r.ru_oublock);
        appendStringInfo(&str,
-                 "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
+                         "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
                                         r.ru_majflt - Save_r.ru_majflt,
                                         r.ru_minflt - Save_r.ru_minflt,
                                         r.ru_majflt, r.ru_minflt,
                                         r.ru_nswap - Save_r.ru_nswap,
                                         r.ru_nswap);
        appendStringInfo(&str,
-        "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
+                "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
                                         r.ru_nsignals - Save_r.ru_nsignals,
                                         r.ru_nsignals,
                                         r.ru_msgrcv - Save_r.ru_msgrcv,
                                         r.ru_msgsnd - Save_r.ru_msgsnd,
                                         r.ru_msgrcv, r.ru_msgsnd);
        appendStringInfo(&str,
-                "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
+                        "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
                                         r.ru_nvcsw - Save_r.ru_nvcsw,
                                         r.ru_nivcsw - Save_r.ru_nivcsw,
                                         r.ru_nvcsw, r.ru_nivcsw);
@@ -3350,56 +3916,27 @@ static void
 log_disconnections(int code, Datum arg)
 {
        Port       *port = MyProcPort;
-       struct timeval end;
+       long            secs;
+       int                     usecs;
+       int                     msecs;
        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));
+       TimestampDifference(port->SessionStartTime,
+                                               GetCurrentTimestamp(),
+                                               &secs, &usecs);
+       msecs = usecs / 1000;
 
-       ereport(
-                       LOG,
-                       (errmsg("disconnection: session time: %s%s%s%s%s",
-                               session_time, uname, dbname, remote_host, remote_port)));
+       hours = secs / SECS_PER_HOUR;
+       secs %= SECS_PER_HOUR;
+       minutes = secs / SECS_PER_MINUTE;
+       seconds = secs % SECS_PER_MINUTE;
 
+       ereport(LOG,
+                       (errmsg("disconnection: session time: %d:%02d:%02d.%03d "
+                                       "user=%s database=%s host=%s%s%s",
+                                       hours, minutes, seconds, msecs,
+                                       port->user_name, port->database_name, port->remote_host,
+                                 port->remote_port[0] ? " port=" : "", port->remote_port)));
 }