Adjust things so that the query_string of a cached plan and the sourceText of
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 18 Jul 2008 20:26:06 +0000 (20:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 18 Jul 2008 20:26:06 +0000 (20:26 +0000)
a portal are never NULL, but reliably provide the source text of the query.
It turns out that there was only one place that was really taking a short-cut,
which was the 'EXECUTE' utility statement.  That doesn't seem like a
sufficiently critical performance hotspot to justify not offering a guarantee
of validity of the portal source text.  Fix it to copy the source text over
from the cached plan.  Add Asserts in the places that set up cached plans and
portals to reject null source strings, and simplify a bunch of places that
formerly needed to guard against nulls.

There may be a few places that cons up statements for execution without
having any source text at all; I found one such in ConvertTriggerToFK().
It seems sufficient to inject a phony source string in such a case,
for instance
        ProcessUtility((Node *) atstmt,
                       "(generated ALTER TABLE ADD FOREIGN KEY command)",
                       NULL, false, None_Receiver, NULL);

We should take a second look at the usage of debug_query_string,
particularly the recently added current_query() SQL function.

ITAGAKI Takahiro and Tom Lane

src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/commands/trigger.c
src/backend/executor/spi.c
src/backend/parser/analyze.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/backend/utils/cache/plancache.c
src/backend/utils/mmgr/portalmem.c
src/include/utils/plancache.h
src/include/utils/portal.h

index f8c0db346011006681a9d2de6ecbe15d7188d69a..9047cbcdece8a99272eb7cae4b4d4b2bc20feed1 100644 (file)
@@ -68,7 +68,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
                RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
 
        /*
-        * Create a portal and copy the plan into its memory context.
+        * Create a portal and copy the plan and queryString into its memory.
         */
        portal = CreatePortal(cstmt->portalname, false, false);
 
@@ -77,8 +77,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
        stmt = copyObject(stmt);
        stmt->utilityStmt = NULL;       /* make it look like plain SELECT */
 
-       if (queryString)                        /* copy the source text too for safety */
-               queryString = pstrdup(queryString);
+       queryString = pstrdup(queryString);
 
        PortalDefineQuery(portal,
                                          NULL,
index fe86d4d33e4c2627026a30c04ec5cc17b654e803..03713ee3cbdb887e2ae0f4131885dea0dc5ecb92 100644 (file)
@@ -174,6 +174,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
        ParamListInfo paramLI = NULL;
        EState     *estate = NULL;
        Portal          portal;
+       char       *query_string;
 
        /* Look it up in the hash table */
        entry = FetchPreparedStatement(stmt->name, true);
@@ -203,6 +204,10 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
        /* Don't display the portal in pg_cursors, it is for internal use only */
        portal->visible = false;
 
+       /* Copy the plan's saved query string into the portal's memory */
+       query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
+                                                                          entry->plansource->query_string);
+
        /*
         * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
         * so that we can modify its destination (yech, but this has always been
@@ -249,13 +254,9 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
                plan_list = cplan->stmt_list;
        }
 
-       /*
-        * Note: we don't bother to copy the source query string into the portal.
-        * Any errors it might be useful for will already have been reported.
-        */
        PortalDefineQuery(portal,
                                          NULL,
-                                         NULL,
+                                         query_string,
                                          entry->plansource->commandTag,
                                          plan_list,
                                          cplan);
@@ -777,12 +778,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
                        MemSet(nulls, 0, sizeof(nulls));
 
                        values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
-
-                       if (prep_stmt->plansource->query_string == NULL)
-                               nulls[1] = true;
-                       else
-                               values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
-
+                       values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
                        values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
                        values[3] = build_regtype_array(prep_stmt->plansource->param_types,
                                                                                  prep_stmt->plansource->num_params);
index 4470021de2e5f65c91c6e2905bfb466167560e56..5b7199c0a8869123661b08facaea863f28b004ad 100644 (file)
@@ -728,7 +728,8 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
 
                /* ... and execute it */
                ProcessUtility((Node *) atstmt,
-                                          NULL, NULL, false, None_Receiver, NULL);
+                                          "(generated ALTER TABLE ADD FOREIGN KEY command)",
+                                          NULL, false, None_Receiver, NULL);
 
                /* Remove the matched item from the list */
                info_list = list_delete_ptr(info_list, info);
index 6770138143f92a425ea2a9c7cbb6b810f4cb205a..748483cad09798e2214256ca0b80705ebde940a6 100644 (file)
@@ -1057,10 +1057,8 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
         */
        oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-       /* Copy the plan's query string, if available, into the portal */
-       query_string = plansource->query_string;
-       if (query_string)
-               query_string = pstrdup(query_string);
+       /* Copy the plan's query string into the portal */
+       query_string = pstrdup(plansource->query_string);
 
        /* If the plan has parameters, copy them into the portal */
        if (plan->nargs > 0)
index 340fc860433ebe277d304db111b142c3ba094b40..998ff7ee01bbbddf4ea401bff0df30f98377e0e1 100644 (file)
@@ -72,9 +72,6 @@ static bool check_parameter_resolution_walker(Node *node,
  * parse_analyze
  *             Analyze a raw parse tree and transform it to Query form.
  *
- * If available, pass the source text from which the raw parse tree was
- * generated; it's OK to pass NULL if this is not available.
- *
  * Optionally, information about $n parameter types can be supplied.
  * References to $n indexes not defined by paramTypes[] are disallowed.
  *
@@ -89,6 +86,8 @@ parse_analyze(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
+       Assert(sourceText != NULL);                             /* required as of 8.4 */
+
        pstate->p_sourcetext = sourceText;
        pstate->p_paramtypes = paramTypes;
        pstate->p_numparams = numParams;
@@ -115,6 +114,8 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
+       Assert(sourceText != NULL);                             /* required as of 8.4 */
+
        pstate->p_sourcetext = sourceText;
        pstate->p_paramtypes = *paramTypes;
        pstate->p_numparams = *numParams;
index c2809eaf84162f86c35ff834d45775478d0ede5e..3e12c13efc76b793e0b54d0a7bdb5b5f69dbb3f1 100644 (file)
@@ -1388,9 +1388,9 @@ exec_bind_message(StringInfo input_message)
        /*
         * Report query to various monitoring facilities.
         */
-       debug_query_string = psrc->query_string ? psrc->query_string : "<BIND>";
+       debug_query_string = psrc->query_string;
 
-       pgstat_report_activity(debug_query_string);
+       pgstat_report_activity(psrc->query_string);
 
        set_ps_display("BIND", false);
 
@@ -1466,10 +1466,8 @@ exec_bind_message(StringInfo input_message)
         */
        oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-       /* Copy the plan's query string, if available, into the portal */
-       query_string = psrc->query_string;
-       if (query_string)
-               query_string = pstrdup(query_string);
+       /* Copy the plan's query string into the portal */
+       query_string = pstrdup(psrc->query_string);
 
        /* Likewise make a copy of the statement name, unless it's unnamed */
        if (stmt_name[0])
@@ -1714,7 +1712,7 @@ exec_bind_message(StringInfo input_message)
                                                        *stmt_name ? stmt_name : "<unnamed>",
                                                        *portal_name ? "/" : "",
                                                        *portal_name ? portal_name : "",
-                       psrc->query_string ? psrc->query_string : "<source not stored>"),
+                                                       psrc->query_string),
                                         errhidestmt(true),
                                         errdetail_params(params)));
                        break;
@@ -1780,7 +1778,7 @@ exec_execute_message(const char *portal_name, long max_rows)
         */
        if (is_xact_command)
        {
-               sourceText = portal->sourceText ? pstrdup(portal->sourceText) : NULL;
+               sourceText = pstrdup(portal->sourceText);
                if (portal->prepStmtName)
                        prepStmtName = pstrdup(portal->prepStmtName);
                else
@@ -1805,9 +1803,9 @@ exec_execute_message(const char *portal_name, long max_rows)
        /*
         * Report query to various monitoring facilities.
         */
-       debug_query_string = sourceText ? sourceText : "<EXECUTE>";
+       debug_query_string = sourceText;
 
-       pgstat_report_activity(debug_query_string);
+       pgstat_report_activity(sourceText);
 
        set_ps_display(portal->commandTag, false);
 
@@ -1840,15 +1838,14 @@ exec_execute_message(const char *portal_name, long max_rows)
        if (check_log_statement(portal->stmts))
        {
                ereport(LOG,
-                               (errmsg("%s %s%s%s%s%s",
+                               (errmsg("%s %s%s%s%s",
                                                execute_is_fetch ?
                                                _("execute fetch from") :
                                                _("execute"),
                                                prepStmtName,
                                                *portal_name ? "/" : "",
                                                *portal_name ? portal_name : "",
-                                               sourceText ? ": " : "",
-                                               sourceText ? sourceText : ""),
+                                               sourceText),
                                 errhidestmt(true),
                                 errdetail_params(portalParams)));
                was_logged = true;
@@ -1924,7 +1921,7 @@ exec_execute_message(const char *portal_name, long max_rows)
                        break;
                case 2:
                        ereport(LOG,
-                                       (errmsg("duration: %s ms  %s %s%s%s%s%s",
+                                       (errmsg("duration: %s ms  %s %s%s%s%s",
                                                        msec_str,
                                                        execute_is_fetch ?
                                                        _("execute fetch from") :
@@ -1932,8 +1929,7 @@ exec_execute_message(const char *portal_name, long max_rows)
                                                        prepStmtName,
                                                        *portal_name ? "/" : "",
                                                        *portal_name ? portal_name : "",
-                                                       sourceText ? ": " : "",
-                                                       sourceText ? sourceText : ""),
+                                                       sourceText),
                                         errhidestmt(true),
                                         errdetail_params(portalParams)));
                        break;
@@ -2049,7 +2045,7 @@ errdetail_execute(List *raw_parsetree_list)
                        PreparedStatement *pstmt;
 
                        pstmt = FetchPreparedStatement(stmt->name, false);
-                       if (pstmt && pstmt->plansource->query_string)
+                       if (pstmt)
                        {
                                errdetail("prepare: %s", pstmt->plansource->query_string);
                                return 0;
index 5109dc032f088fedc01bdf68ee4e89d37d18f192..d68b797eb16497f627f64e33b9e1d8182cb86cd5 100644 (file)
@@ -218,13 +218,17 @@ check_xact_readonly(Node *parsetree)
  *             general utility function invoker
  *
  *     parsetree: the parse tree for the utility statement
- *     queryString: original source text of command (NULL if not available)
+ *     queryString: original source text of command
  *     params: parameters to use during execution
  *     isTopLevel: true if executing a "top level" (interactively issued) command
  *     dest: where to send results
  *     completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
  *             in which to store a command completion status string.
  *
+ * Notes: as of PG 8.4, caller MUST supply a queryString; it is not
+ * allowed anymore to pass NULL.  (If you really don't have source text,
+ * you can pass a constant string, perhaps "(query not available)".)
+ *
  * completionTag is only set nonempty if we want to return a nondefault status.
  *
  * completionTag may be NULL if caller doesn't want a status string.
@@ -237,6 +241,8 @@ ProcessUtility(Node *parsetree,
                           DestReceiver *dest,
                           char *completionTag)
 {
+       Assert(queryString != NULL);                            /* required as of 8.4 */
+
        check_xact_readonly(parsetree);
 
        if (completionTag)
index 3005d2008f45cb903505e176fc9e9d409178604b..f16de7055438e5c9d601504c47ea16d38aff5315 100644 (file)
@@ -104,8 +104,7 @@ InitPlanCache(void)
  * about all that we do here is copy it into permanent storage.
  *
  * raw_parse_tree: output of raw_parser()
- * query_string: original query text (can be NULL if not available, but
- *             that is discouraged because it degrades error message quality)
+ * query_string: original query text (as of PG 8.4, must not be NULL)
  * commandTag: compile-time-constant tag for query, or NULL if empty query
  * param_types: array of parameter type OIDs, or NULL if none
  * num_params: number of parameters
@@ -130,6 +129,8 @@ CreateCachedPlan(Node *raw_parse_tree,
        MemoryContext source_context;
        MemoryContext oldcxt;
 
+       Assert(query_string != NULL);                           /* required as of 8.4 */
+
        /*
         * Make a dedicated memory context for the CachedPlanSource and its
         * subsidiary data.  We expect it can be pretty small.
@@ -152,7 +153,7 @@ CreateCachedPlan(Node *raw_parse_tree,
        oldcxt = MemoryContextSwitchTo(source_context);
        plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
        plansource->raw_parse_tree = copyObject(raw_parse_tree);
-       plansource->query_string = query_string ? pstrdup(query_string) : NULL;
+       plansource->query_string = pstrdup(query_string);
        plansource->commandTag = commandTag;            /* no copying needed */
        if (num_params > 0)
        {
@@ -228,6 +229,8 @@ FastCreateCachedPlan(Node *raw_parse_tree,
        OverrideSearchPath *search_path;
        MemoryContext oldcxt;
 
+       Assert(query_string != NULL);                           /* required as of 8.4 */
+
        /*
         * Fetch current search_path into given context, but do any recalculation
         * work required in caller's context.
index 1117af42a4f8dfc8829d9f49fab71a62fb598d7d..5184bbc360045d05be002755f2259a363388565b 100644 (file)
@@ -271,7 +271,11 @@ CreateNewPortal(void)
  * PortalDefineQuery
  *             A simple subroutine to establish a portal's query.
  *
- * Notes: commandTag shall be NULL if and only if the original query string
+ * Notes: as of PG 8.4, caller MUST supply a sourceText string; it is not
+ * allowed anymore to pass NULL.  (If you really don't have source text,
+ * you can pass a constant string, perhaps "(query not available)".)
+ *
+ * commandTag shall be NULL if and only if the original query string
  * (before rewriting) was an empty string.     Also, the passed commandTag must
  * be a pointer to a constant string, since it is not copied.
  *
@@ -284,7 +288,7 @@ CreateNewPortal(void)
  * copying them into the portal's heap context.
  *
  * The caller is also responsible for ensuring that the passed prepStmtName
- * and sourceText (if not NULL) have adequate lifetime.
+ * (if not NULL) and sourceText have adequate lifetime.
  *
  * NB: this function mustn't do much beyond storing the passed values; in
  * particular don't do anything that risks elog(ERROR).  If that were to
@@ -302,7 +306,8 @@ PortalDefineQuery(Portal portal,
        AssertArg(PortalIsValid(portal));
        AssertState(portal->status == PORTAL_NEW);
 
-       Assert(commandTag != NULL || stmts == NIL);
+       AssertArg(sourceText != NULL);
+       AssertArg(commandTag != NULL || stmts == NIL);
 
        portal->prepStmtName = prepStmtName;
        portal->sourceText = sourceText;
@@ -927,10 +932,7 @@ pg_cursor(PG_FUNCTION_ARGS)
                MemSet(nulls, 0, sizeof(nulls));
 
                values[0] = CStringGetTextDatum(portal->name);
-               if (!portal->sourceText)
-                       nulls[1] = true;
-               else
-                       values[1] = CStringGetTextDatum(portal->sourceText);
+               values[1] = CStringGetTextDatum(portal->sourceText);
                values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
                values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
                values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
index 72a0052843b4ba4401535614096598cc13727bb0..3fcf5d4f03a262f69cac067cabb6ad659044b0e2 100644 (file)
@@ -20,8 +20,7 @@
 /*
  * CachedPlanSource represents the portion of a cached plan that persists
  * across invalidation/replan cycles.  It stores a raw parse tree (required),
- * the original source text (optional, but highly recommended to improve
- * error reports), and adjunct data.
+ * the original source text (also required, as of 8.4), and adjunct data.
  *
  * Normally, both the struct itself and the subsidiary data live in the
  * context denoted by the context field, while the linked-to CachedPlan, if
@@ -47,7 +46,7 @@
 typedef struct CachedPlanSource
 {
        Node       *raw_parse_tree; /* output of raw_parser() */
-       char       *query_string;       /* text of query, or NULL */
+       char       *query_string;       /* text of query (as of 8.4, never NULL) */
        const char *commandTag;         /* command tag (a constant!), or NULL */
        Oid                *param_types;        /* array of parameter type OIDs, or NULL */
        int                     num_params;             /* length of param_types array */
index bb55d39ced3e909d5e0e41efba8219fd37ff8fdf..7e3dd7d903ffb219008cdcb552ed632eb416ec55 100644 (file)
@@ -123,7 +123,7 @@ typedef struct PortalData
         */
 
        /* The query or queries the portal will execute */
-       const char *sourceText;         /* text of query, if known (may be NULL) */
+       const char *sourceText;         /* text of query (as of 8.4, never NULL) */
        const char *commandTag;         /* command tag for original query */
        List       *stmts;                      /* PlannedStmts and/or utility statements */
        CachedPlan *cplan;                      /* CachedPlan, if stmts are from one */