summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorTom Lane2007-04-27 22:05:49 +0000
committerTom Lane2007-04-27 22:05:49 +0000
commitbbbe825f5f46d7ead60502f43d3b414719a41aa5 (patch)
treeb4dda033d2ecee0e6ba083621763e058ae0b7ba4 /src/backend/commands
parenta264671116ab9ba45fb20441c16fe0783e52857b (diff)
Modify processing of DECLARE CURSOR and EXPLAIN so that they can resolve the
types of unspecified parameters when submitted via extended query protocol. This worked in 8.2 but I had broken it during plancache changes. DECLARE CURSOR is now treated almost exactly like a plain SELECT through parse analysis, rewrite, and planning; only just before sending to the executor do we divert it away to ProcessUtility. This requires a special-case check in a number of places, but practically all of them were already special-casing SELECT INTO, so it's not too ugly. (Maybe it would be a good idea to merge the two by treating IntoClause as a form of utility statement? Not going to worry about that now, though.) That approach doesn't work for EXPLAIN, however, so for that I punted and used a klugy solution of running parse analysis an extra time if under extended query protocol.
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/copy.c5
-rw-r--r--src/backend/commands/explain.c67
-rw-r--r--src/backend/commands/portalcmds.c82
-rw-r--r--src/backend/commands/prepare.c24
4 files changed, 43 insertions, 135 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index be33bb76c4f..885411cf2c0 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.282 2007/04/18 02:28:22 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.283 2007/04/27 22:05:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1015,9 +1015,10 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
query = (Query *) linitial(rewritten);
Assert(query->commandType == CMD_SELECT);
+ Assert(query->utilityStmt == NULL);
/* Query mustn't use INTO, either */
- if (query->into)
+ if (query->intoClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("COPY (SELECT INTO) is not supported")));
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index bb7d4303598..592eeba4177 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.161 2007/04/16 01:14:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.162 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,8 +42,8 @@ typedef struct ExplainState
List *rtable; /* range table */
} ExplainState;
-static void ExplainOneQuery(Query *query, int cursorOptions,
- ExplainStmt *stmt, const char *queryString,
+static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
+ const char *queryString,
ParamListInfo params, TupOutputState *tstate);
static double elapsed_time(instr_time *starttime);
static void explain_outNode(StringInfo str,
@@ -102,8 +102,8 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
/* Explain every plan */
foreach(l, rewritten)
{
- ExplainOneQuery((Query *) lfirst(l), 0,
- stmt, queryString, params, tstate);
+ ExplainOneQuery((Query *) lfirst(l), stmt,
+ queryString, params, tstate);
/* put a blank line between plans */
if (lnext(l) != NULL)
do_text_output_oneline(tstate, "");
@@ -134,8 +134,7 @@ ExplainResultDesc(ExplainStmt *stmt)
* print out the execution plan for one Query
*/
static void
-ExplainOneQuery(Query *query, int cursorOptions,
- ExplainStmt *stmt, const char *queryString,
+ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString,
ParamListInfo params, TupOutputState *tstate)
{
PlannedStmt *plan;
@@ -150,7 +149,7 @@ ExplainOneQuery(Query *query, int cursorOptions,
}
/* plan the query */
- plan = planner(query, cursorOptions, params);
+ plan = planner(query, 0, params);
/*
* Update snapshot command ID to ensure this query sees results of any
@@ -187,52 +186,7 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
if (utilityStmt == NULL)
return;
- if (IsA(utilityStmt, DeclareCursorStmt))
- {
- DeclareCursorStmt *dcstmt = (DeclareCursorStmt *) utilityStmt;
- Oid *param_types;
- int num_params;
- Query *query;
- List *rewritten;
- ExplainStmt newstmt;
-
- /* Convert parameter type data to the form parser wants */
- getParamListTypes(params, &param_types, &num_params);
-
- /*
- * Run parse analysis and rewrite. Note this also acquires sufficient
- * locks on the source table(s).
- *
- * Because the parser and planner tend to scribble on their input, we
- * make a preliminary copy of the source querytree. This prevents
- * problems in the case that the DECLARE CURSOR is in a portal or
- * plpgsql function and is executed repeatedly. (See also the same
- * hack in COPY and PREPARE.) XXX FIXME someday.
- */
- rewritten = pg_analyze_and_rewrite((Node *) copyObject(dcstmt->query),
- queryString,
- param_types, num_params);
-
- /* We don't expect more or less than one result query */
- if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
- elog(ERROR, "unexpected rewrite result");
- query = (Query *) linitial(rewritten);
- if (query->commandType != CMD_SELECT)
- elog(ERROR, "unexpected rewrite result");
-
- /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
- if (query->into)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("DECLARE CURSOR cannot specify INTO")));
-
- /* do not actually execute the underlying query! */
- memcpy(&newstmt, stmt, sizeof(ExplainStmt));
- newstmt.analyze = false;
- ExplainOneQuery(query, dcstmt->options, &newstmt,
- queryString, params, tstate);
- }
- else if (IsA(utilityStmt, ExecuteStmt))
+ if (IsA(utilityStmt, ExecuteStmt))
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, stmt,
queryString, params, tstate);
else if (IsA(utilityStmt, NotifyStmt))
@@ -247,6 +201,11 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
* given a planned query, execute it if needed, and then print
* EXPLAIN output
*
+ * Since we ignore any DeclareCursorStmt that might be attached to the query,
+ * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
+ * query. This is different from pre-8.3 behavior but seems more useful than
+ * not running the query. No cursor will be created, however.
+ *
* This is exported because it's called back from prepare.c in the
* EXPLAIN EXECUTE case
*
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index eb381ebb705..939452650d0 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.64 2007/04/16 01:14:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.65 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,34 +26,34 @@
#include "access/xact.h"
#include "commands/portalcmds.h"
#include "executor/executor.h"
-#include "optimizer/planner.h"
-#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
-#include "tcop/tcopprot.h"
#include "utils/memutils.h"
/*
* PerformCursorOpen
* Execute SQL DECLARE CURSOR command.
+ *
+ * The query has already been through parse analysis, rewriting, and planning.
+ * When it gets here, it looks like a SELECT PlannedStmt, except that the
+ * utilityStmt field is set.
*/
void
-PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
+PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
const char *queryString, bool isTopLevel)
{
- Oid *param_types;
- int num_params;
- List *rewritten;
- Query *query;
- PlannedStmt *plan;
+ DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
Portal portal;
MemoryContext oldContext;
+ if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
+ elog(ERROR, "PerformCursorOpen called for non-cursor query");
+
/*
* Disallow empty-string cursor name (conflicts with protocol-level
* unnamed portal).
*/
- if (!stmt->portalname || stmt->portalname[0] == '\0')
+ if (!cstmt->portalname || cstmt->portalname[0] == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_NAME),
errmsg("invalid cursor name: must not be empty")));
@@ -63,70 +63,24 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
* been executed inside a transaction block (or else, it would have no
* user-visible effect).
*/
- if (!(stmt->options & CURSOR_OPT_HOLD))
+ if (!(cstmt->options & CURSOR_OPT_HOLD))
RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
/*
- * Don't allow both SCROLL and NO SCROLL to be specified
- */
- if ((stmt->options & CURSOR_OPT_SCROLL) &&
- (stmt->options & CURSOR_OPT_NO_SCROLL))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("cannot specify both SCROLL and NO SCROLL")));
-
- /* Convert parameter type data to the form parser wants */
- getParamListTypes(params, &param_types, &num_params);
-
- /*
- * Run parse analysis and rewrite. Note this also acquires sufficient
- * locks on the source table(s).
- *
- * Because the parser and planner tend to scribble on their input, we
- * make a preliminary copy of the source querytree. This prevents
- * problems in the case that the DECLARE CURSOR is in a portal or plpgsql
- * function and is executed repeatedly. (See also the same hack in
- * COPY and PREPARE.) XXX FIXME someday.
- */
- rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
- queryString, param_types, num_params);
-
- /* We don't expect more or less than one result query */
- if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
- elog(ERROR, "unexpected rewrite result");
- query = (Query *) linitial(rewritten);
- if (query->commandType != CMD_SELECT)
- elog(ERROR, "unexpected rewrite result");
-
- /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
- if (query->into)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("DECLARE CURSOR cannot specify INTO")));
-
- if (query->rowMarks != NIL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
- errdetail("Cursors must be READ ONLY.")));
-
- /* plan the query */
- plan = planner(query, stmt->options, params);
-
- /*
* Create a portal and copy the plan into its memory context.
*/
- portal = CreatePortal(stmt->portalname, false, false);
+ portal = CreatePortal(cstmt->portalname, false, false);
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
- plan = copyObject(plan);
+ stmt = copyObject(stmt);
+ stmt->utilityStmt = NULL; /* make it look like plain SELECT */
PortalDefineQuery(portal,
NULL,
queryString,
"SELECT", /* cursor's query is always a SELECT */
- list_make1(plan),
+ list_make1(stmt),
NULL);
/*----------
@@ -150,10 +104,10 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
* based on whether it would require any additional runtime overhead to do
* so.
*/
- portal->cursorOptions = stmt->options;
+ portal->cursorOptions = cstmt->options;
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{
- if (ExecSupportsBackwardScan(plan->planTree))
+ if (ExecSupportsBackwardScan(stmt->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
else
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index e9b953f709a..997f66c8188 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,7 +10,7 @@
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.74 2007/04/26 23:24:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.75 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -57,7 +57,6 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
int nargs;
List *queries;
Query *query;
- const char *commandTag;
List *query_list,
*plan_list;
int i;
@@ -137,22 +136,15 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
switch (query->commandType)
{
case CMD_SELECT:
- commandTag = "SELECT";
- break;
case CMD_INSERT:
- commandTag = "INSERT";
- break;
case CMD_UPDATE:
- commandTag = "UPDATE";
- break;
case CMD_DELETE:
- commandTag = "DELETE";
+ /* OK */
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
errmsg("utility statements cannot be prepared")));
- commandTag = NULL; /* keep compiler quiet */
break;
}
@@ -168,7 +160,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
StorePreparedStatement(stmt->name,
stmt->query,
queryString,
- commandTag,
+ CreateCommandTag((Node *) query),
argtypes,
nargs,
0, /* default cursor options */
@@ -244,11 +236,12 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
errmsg("prepared statement is not a SELECT")));
pstmt = (PlannedStmt *) linitial(plan_list);
if (!IsA(pstmt, PlannedStmt) ||
- pstmt->commandType != CMD_SELECT)
+ pstmt->commandType != CMD_SELECT ||
+ pstmt->utilityStmt != NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
- pstmt->into = copyObject(stmt->into);
+ pstmt->intoClause = copyObject(stmt->into);
MemoryContextSwitchTo(oldContext);
@@ -689,7 +682,8 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
if (execstmt->into)
{
- if (pstmt->commandType != CMD_SELECT)
+ if (pstmt->commandType != CMD_SELECT ||
+ pstmt->utilityStmt != NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
@@ -697,7 +691,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
/* Copy the stmt so we can modify it */
pstmt = copyObject(pstmt);
- pstmt->into = execstmt->into;
+ pstmt->intoClause = execstmt->into;
}
/*