summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-04-27 22:05:49 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-04-27 22:05:49 +0000
commitbbbe825f5f46d7ead60502f43d3b414719a41aa5 (patch)
treeb4dda033d2ecee0e6ba083621763e058ae0b7ba4 /src
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')
-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
-rw-r--r--src/backend/executor/execMain.c10
-rw-r--r--src/backend/executor/functions.c20
-rw-r--r--src/backend/executor/spi.c8
-rw-r--r--src/backend/nodes/copyfuncs.c9
-rw-r--r--src/backend/nodes/equalfuncs.c6
-rw-r--r--src/backend/nodes/outfuncs.c9
-rw-r--r--src/backend/nodes/readfuncs.c4
-rw-r--r--src/backend/optimizer/plan/planagg.c5
-rw-r--r--src/backend/optimizer/plan/planner.c10
-rw-r--r--src/backend/optimizer/prep/prepjointree.c8
-rw-r--r--src/backend/optimizer/util/clauses.c5
-rw-r--r--src/backend/parser/analyze.c178
-rw-r--r--src/backend/parser/gram.y8
-rw-r--r--src/backend/parser/parse_clause.c7
-rw-r--r--src/backend/parser/parse_expr.c5
-rw-r--r--src/backend/parser/parse_type.c4
-rw-r--r--src/backend/rewrite/rewriteDefine.c6
-rw-r--r--src/backend/tcop/pquery.c19
-rw-r--r--src/backend/tcop/utility.c49
-rw-r--r--src/include/commands/portalcmds.h5
-rw-r--r--src/include/nodes/parsenodes.h20
-rw-r--r--src/include/nodes/plannodes.h6
26 files changed, 333 insertions, 246 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;
}
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index cacd7c6fe70..298d0c69c9d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.292 2007/03/29 00:15:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.293 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -420,7 +420,7 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
*
* XXX should we allow this if the destination is temp?
*/
- if (plannedstmt->into != NULL)
+ if (plannedstmt->intoClause != NULL)
goto fail;
/* Fail if write permissions are requested on any non-temp table */
@@ -522,10 +522,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* correct tuple descriptors. (Other SELECT INTO stuff comes later.)
*/
estate->es_select_into = false;
- if (operation == CMD_SELECT && plannedstmt->into != NULL)
+ if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
{
estate->es_select_into = true;
- estate->es_into_oids = interpretOidsOption(plannedstmt->into->options);
+ estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
}
/*
@@ -2395,7 +2395,7 @@ typedef struct
static void
OpenIntoRel(QueryDesc *queryDesc)
{
- IntoClause *into = queryDesc->plannedstmt->into;
+ IntoClause *into = queryDesc->plannedstmt->intoClause;
EState *estate = queryDesc->estate;
Relation intoRelationDesc;
char *intoName;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index b59228b0c9e..9ad13916123 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.115 2007/04/16 01:14:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.116 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -317,7 +317,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
/* We assume we don't need to set up ActiveSnapshot for ExecutorStart */
/* Utility commands don't need Executor. */
- if (es->qd->operation != CMD_UTILITY)
+ if (es->qd->utilitystmt == NULL)
{
/*
* Only set up to collect queued triggers if it's not a SELECT.
@@ -346,9 +346,12 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
{
ActiveSnapshot = es->qd->snapshot;
- if (es->qd->operation == CMD_UTILITY)
+ if (es->qd->utilitystmt)
{
- ProcessUtility(es->qd->utilitystmt,
+ /* ProcessUtility needs the PlannedStmt for DECLARE CURSOR */
+ ProcessUtility((es->qd->plannedstmt ?
+ (Node *) es->qd->plannedstmt :
+ es->qd->utilitystmt),
fcache->src,
es->qd->params,
false, /* not top level */
@@ -366,7 +369,8 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
*/
if (LAST_POSTQUEL_COMMAND(es) &&
es->qd->operation == CMD_SELECT &&
- es->qd->plannedstmt->into == NULL)
+ es->qd->plannedstmt->utilityStmt == NULL &&
+ es->qd->plannedstmt->intoClause == NULL)
count = 1L;
else
count = 0L;
@@ -396,7 +400,7 @@ postquel_end(execution_state *es)
es->status = F_EXEC_DONE;
/* Utility commands don't need Executor. */
- if (es->qd->operation != CMD_UTILITY)
+ if (es->qd->utilitystmt == NULL)
{
/* Make our snapshot the active one for any called functions */
saveActiveSnapshot = ActiveSnapshot;
@@ -894,7 +898,9 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
* Note: eventually replace this test with QueryReturnsTuples? We'd need
* a more general method of determining the output type, though.
*/
- if (!(parse->commandType == CMD_SELECT && parse->into == NULL))
+ if (!(parse->commandType == CMD_SELECT &&
+ parse->utilityStmt == NULL &&
+ parse->intoClause == NULL))
{
if (rettype != VOIDOID)
ereport(ERROR,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 0a5625a0c58..f97a86fe214 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.178 2007/04/16 18:21:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.179 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1581,7 +1581,8 @@ _SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
ActiveSnapshot->curcid = GetCurrentCommandId();
}
- if (IsA(stmt, PlannedStmt))
+ if (IsA(stmt, PlannedStmt) &&
+ ((PlannedStmt *) stmt)->utilityStmt == NULL)
{
qdesc = CreateQueryDesc((PlannedStmt *) stmt,
ActiveSnapshot,
@@ -1687,7 +1688,8 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
switch (operation)
{
case CMD_SELECT:
- if (queryDesc->plannedstmt->into) /* select into table? */
+ Assert(queryDesc->plannedstmt->utilityStmt == NULL);
+ if (queryDesc->plannedstmt->intoClause) /* select into table? */
res = SPI_OK_SELINTO;
else if (queryDesc->dest->mydest != DestSPI)
{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 988120a9634..62853461a89 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.374 2007/04/26 16:13:10 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.375 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,7 +77,8 @@ _copyPlannedStmt(PlannedStmt *from)
COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations);
- COPY_NODE_FIELD(into);
+ COPY_NODE_FIELD(utilityStmt);
+ COPY_NODE_FIELD(intoClause);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(returningLists);
@@ -1819,7 +1820,7 @@ _copyQuery(Query *from)
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
- COPY_NODE_FIELD(into);
+ COPY_NODE_FIELD(intoClause);
COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasSubLinks);
COPY_NODE_FIELD(rtable);
@@ -1884,7 +1885,7 @@ _copySelectStmt(SelectStmt *from)
SelectStmt *newnode = makeNode(SelectStmt);
COPY_NODE_FIELD(distinctClause);
- COPY_NODE_FIELD(into);
+ COPY_NODE_FIELD(intoClause);
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(fromClause);
COPY_NODE_FIELD(whereClause);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c1500948dca..99fb8f18b6f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.305 2007/04/26 16:13:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.306 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -722,7 +722,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_SCALAR_FIELD(canSetTag);
COMPARE_NODE_FIELD(utilityStmt);
COMPARE_SCALAR_FIELD(resultRelation);
- COMPARE_NODE_FIELD(into);
+ COMPARE_NODE_FIELD(intoClause);
COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasSubLinks);
COMPARE_NODE_FIELD(rtable);
@@ -779,7 +779,7 @@ static bool
_equalSelectStmt(SelectStmt *a, SelectStmt *b)
{
COMPARE_NODE_FIELD(distinctClause);
- COMPARE_NODE_FIELD(into);
+ COMPARE_NODE_FIELD(intoClause);
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(fromClause);
COMPARE_NODE_FIELD(whereClause);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 49202f8664a..58d8ee620bc 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.305 2007/03/27 23:21:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.306 2007/04/27 22:05:47 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -244,7 +244,8 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node)
WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations);
- WRITE_NODE_FIELD(into);
+ WRITE_NODE_FIELD(utilityStmt);
+ WRITE_NODE_FIELD(intoClause);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(returningLists);
@@ -1549,7 +1550,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
WRITE_NODE_TYPE("SELECT");
WRITE_NODE_FIELD(distinctClause);
- WRITE_NODE_FIELD(into);
+ WRITE_NODE_FIELD(intoClause);
WRITE_NODE_FIELD(targetList);
WRITE_NODE_FIELD(fromClause);
WRITE_NODE_FIELD(whereClause);
@@ -1694,7 +1695,7 @@ _outQuery(StringInfo str, Query *node)
appendStringInfo(str, " :utilityStmt <>");
WRITE_INT_FIELD(resultRelation);
- WRITE_NODE_FIELD(into);
+ WRITE_NODE_FIELD(intoClause);
WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasSubLinks);
WRITE_NODE_FIELD(rtable);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1f3b81e275f..be450e94c02 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.205 2007/03/27 23:21:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.206 2007/04/27 22:05:47 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -139,7 +139,7 @@ _readQuery(void)
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
- READ_NODE_FIELD(into);
+ READ_NODE_FIELD(intoClause);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasSubLinks);
READ_NODE_FIELD(rtable);
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 92ade2fccf5..16d71af183d 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.31 2007/04/21 21:01:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.32 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -454,7 +454,8 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
subparse->commandType = CMD_SELECT;
subparse->resultRelation = 0;
subparse->returningList = NIL;
- subparse->into = NULL;
+ subparse->utilityStmt = NULL;
+ subparse->intoClause = NULL;
subparse->hasAggs = false;
subparse->groupClause = NIL;
subparse->havingQual = NULL;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5c65a78d6f7..d5dc6f7c7e6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.217 2007/04/16 01:14:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.218 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -90,6 +90,11 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
ListCell *lp,
*lr;
+ /* Cursor options may come from caller or from DECLARE CURSOR stmt */
+ if (parse->utilityStmt &&
+ IsA(parse->utilityStmt, DeclareCursorStmt))
+ cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;
+
/*
* Set up global state for this planner invocation. This data is needed
* across all levels of sub-Query that might exist in the given command,
@@ -156,7 +161,8 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->planTree = top_plan;
result->rtable = glob->finalrtable;
result->resultRelations = root->resultRelations;
- result->into = parse->into;
+ result->utilityStmt = parse->utilityStmt;
+ result->intoClause = parse->intoClause;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->returningLists = root->returningLists;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1519c01e60e..9ba293839cc 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.47 2007/02/19 07:03:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.48 2007/04/27 22:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -627,7 +627,8 @@ is_simple_subquery(Query *subquery)
*/
if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT ||
- subquery->into != NULL)
+ subquery->utilityStmt != NULL ||
+ subquery->intoClause != NULL)
elog(ERROR, "subquery is bogus");
/*
@@ -698,7 +699,8 @@ is_simple_union_all(Query *subquery)
/* Let's just make sure it's a valid subselect ... */
if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT ||
- subquery->into != NULL)
+ subquery->utilityStmt != NULL ||
+ subquery->intoClause != NULL)
elog(ERROR, "subquery is bogus");
/* Is it a set-operation query at all? */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index d4cb3f06b05..8b646516de8 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.241 2007/04/02 03:49:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.242 2007/04/27 22:05:48 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -2945,7 +2945,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->into ||
+ querytree->utilityStmt ||
+ querytree->intoClause ||
querytree->hasAggs ||
querytree->hasSubLinks ||
querytree->rtable ||
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a4e4418b145..68475387be3 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.362 2007/03/13 00:33:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.363 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -118,6 +118,10 @@ static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static Query *transformDeclareCursorStmt(ParseState *pstate,
+ DeclareCursorStmt *stmt);
+static Query *transformExplainStmt(ParseState *pstate,
+ ExplainStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
List **extras_before, List **extras_after);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
@@ -313,20 +317,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
switch (nodeTag(parseTree))
{
/*
- * Non-optimizable statements
- */
- case T_CreateStmt:
- result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
- extras_before, extras_after);
- break;
-
- case T_AlterTableStmt:
- result = transformAlterTableStmt(pstate,
- (AlterTableStmt *) parseTree,
- extras_before, extras_after);
- break;
-
- /*
* Optimizable statements
*/
case T_InsertStmt:
@@ -355,6 +345,33 @@ transformStmt(ParseState *pstate, Node *parseTree,
}
break;
+ /*
+ * Non-optimizable statements
+ */
+ case T_CreateStmt:
+ result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
+ extras_before, extras_after);
+ break;
+
+ case T_AlterTableStmt:
+ result = transformAlterTableStmt(pstate,
+ (AlterTableStmt *) parseTree,
+ extras_before, extras_after);
+ break;
+
+ /*
+ * Special cases
+ */
+ case T_DeclareCursorStmt:
+ result = transformDeclareCursorStmt(pstate,
+ (DeclareCursorStmt *) parseTree);
+ break;
+
+ case T_ExplainStmt:
+ result = transformExplainStmt(pstate,
+ (ExplainStmt *) parseTree);
+ break;
+
default:
/*
@@ -546,9 +563,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
release_pstate_resources(sub_pstate);
pfree(sub_pstate);
+ /* The grammar should have produced a SELECT, but it might have INTO */
Assert(IsA(selectQuery, Query));
Assert(selectQuery->commandType == CMD_SELECT);
- if (selectQuery->into)
+ Assert(selectQuery->utilityStmt == NULL);
+ if (selectQuery->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INSERT ... SELECT cannot specify INTO")));
@@ -2029,6 +2048,8 @@ analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
/*
* transformSelectStmt -
* transforms a Select Statement
+ *
+ * Note: this is also used for DECLARE CURSOR statements.
*/
static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
@@ -2085,11 +2106,11 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
"LIMIT");
/* handle any SELECT INTO/CREATE TABLE AS spec */
- if (stmt->into)
+ if (stmt->intoClause)
{
- qry->into = stmt->into;
- if (stmt->into->colNames)
- applyColumnNames(qry->targetList, stmt->into->colNames);
+ qry->intoClause = stmt->intoClause;
+ if (stmt->intoClause->colNames)
+ applyColumnNames(qry->targetList, stmt->intoClause->colNames);
}
qry->rtable = pstate->p_rtable;
@@ -2254,11 +2275,11 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
/* handle any CREATE TABLE AS spec */
- if (stmt->into)
+ if (stmt->intoClause)
{
- qry->into = stmt->into;
- if (stmt->into->colNames)
- applyColumnNames(qry->targetList, stmt->into->colNames);
+ qry->intoClause = stmt->intoClause;
+ if (stmt->intoClause->colNames)
+ applyColumnNames(qry->targetList, stmt->intoClause->colNames);
}
/*
@@ -2345,14 +2366,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
leftmostSelect = leftmostSelect->larg;
Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
leftmostSelect->larg == NULL);
- if (leftmostSelect->into)
+ if (leftmostSelect->intoClause)
{
- qry->into = leftmostSelect->into;
- intoColNames = leftmostSelect->into->colNames;
+ qry->intoClause = leftmostSelect->intoClause;
+ intoColNames = leftmostSelect->intoClause->colNames;
}
/* clear this to prevent complaints in transformSetOperationTree() */
- leftmostSelect->into = NULL;
+ leftmostSelect->intoClause = NULL;
/*
* These are not one-time, exactly, but we want to process them here and
@@ -2533,7 +2554,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
/*
* Validity-check both leaf and internal SELECTs for disallowed ops.
*/
- if (stmt->into)
+ if (stmt->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
@@ -3113,6 +3134,105 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
}
+/*
+ * transformDeclareCursorStmt -
+ * transform a DECLARE CURSOR Statement
+ *
+ * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not
+ * significantly different from a SELECT) as far as parsing/rewriting/planning
+ * are concerned, but it's not passed to the executor and so in that sense is
+ * a utility statement. We transform it into a Query exactly as if it were
+ * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt
+ * field to carry the cursor name and options.
+ */
+static Query *
+transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
+{
+ Query *result;
+ List *extras_before = NIL,
+ *extras_after = NIL;
+
+ /*
+ * 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")));
+
+ result = transformStmt(pstate, stmt->query,
+ &extras_before, &extras_after);
+
+ /* Shouldn't get any extras, since grammar only allows SelectStmt */
+ if (extras_before || extras_after)
+ elog(ERROR, "unexpected extra stuff in cursor statement");
+ if (!IsA(result, Query) ||
+ result->commandType != CMD_SELECT ||
+ result->utilityStmt != NULL)
+ elog(ERROR, "unexpected non-SELECT command in cursor statement");
+
+ /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
+ if (result->intoClause)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+ errmsg("DECLARE CURSOR cannot specify INTO")));
+
+ /* Implementation restriction (might go away someday) */
+ if (result->rowMarks != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
+ errdetail("Cursors must be READ ONLY.")));
+
+ /* We won't need the raw querytree any more */
+ stmt->query = NULL;
+
+ result->utilityStmt = (Node *) stmt;
+
+ return result;
+}
+
+
+/*
+ * transformExplainStmt -
+ * transform an EXPLAIN Statement
+ *
+ * EXPLAIN is just like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node with no transformation of the raw parse tree.
+ * However, if p_variableparams is set, it could be that the client is
+ * expecting us to resolve parameter types in something like
+ * EXPLAIN SELECT * FROM tab WHERE col = $1
+ * To deal with such cases, we run parse analysis and throw away the result;
+ * this is a bit grotty but not worth contorting the rest of the system for.
+ * (The approach we use for DECLARE CURSOR won't work because the statement
+ * being explained isn't necessarily a SELECT, and in particular might rewrite
+ * to multiple parsetrees.)
+ */
+static Query *
+transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
+{
+ Query *result;
+
+ if (pstate->p_variableparams)
+ {
+ List *extras_before = NIL,
+ *extras_after = NIL;
+
+ /* Since parse analysis scribbles on its input, copy the tree first! */
+ (void) transformStmt(pstate, copyObject(stmt->query),
+ &extras_before, &extras_after);
+ }
+
+ /* Now return the untransformed command as a utility Query */
+ result = makeNode(Query);
+ result->commandType = CMD_UTILITY;
+ result->utilityStmt = (Node *) stmt;
+
+ return result;
+}
+
+
/* exported so planner can check again after rewriting, query pullup, etc */
void
CheckSelectLocking(Query *qry)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a69e6989a94..8884da22892 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.590 2007/04/26 16:13:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.591 2007/04/27 22:05:48 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -2355,12 +2355,12 @@ CreateAsStmt:
* be attached to that Select's target list.
*/
SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
- if (n->into != NULL)
+ if (n->intoClause != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("CREATE TABLE AS cannot specify INTO")));
$4->rel->istemp = $2;
- n->into = $4;
+ n->intoClause = $4;
$$ = $6;
}
;
@@ -5993,7 +5993,7 @@ simple_select:
SelectStmt *n = makeNode(SelectStmt);
n->distinctClause = $2;
n->targetList = $3;
- n->into = $4;
+ n->intoClause = $4;
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 0c718adc32a..4c1fb0cc4c1 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.164 2007/02/01 19:10:27 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.165 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -447,9 +447,10 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
if (query == NULL || !IsA(query, Query))
elog(ERROR, "unexpected parse analysis result for subquery in FROM");
- if (query->commandType != CMD_SELECT)
+ if (query->commandType != CMD_SELECT ||
+ query->utilityStmt != NULL)
elog(ERROR, "expected SELECT query from subquery in FROM");
- if (query->into != NULL)
+ if (query->intoClause != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery in FROM cannot have SELECT INTO")));
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d9a6af82962..a0f92f66a64 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.216 2007/04/02 03:49:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.217 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1117,7 +1117,8 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
elog(ERROR, "bad query in sub-select");
qtree = (Query *) linitial(qtrees);
if (qtree->commandType != CMD_SELECT ||
- qtree->into != NULL)
+ qtree->utilityStmt != NULL ||
+ qtree->intoClause != NULL)
elog(ERROR, "bad query in sub-select");
sublink->subselect = (Node *) qtree;
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index ebb86f8bba9..0263386d2a2 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.88 2007/04/02 03:49:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.89 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -519,7 +519,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL ||
- stmt->into != NULL ||
+ stmt->intoClause != NULL ||
stmt->fromClause != NIL ||
stmt->whereClause != NULL ||
stmt->groupClause != NIL ||
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 864b00f1e73..e57e2ab046c 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.119 2007/03/19 23:38:29 wieck Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.120 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -294,7 +294,9 @@ DefineQueryRewrite(char *rulename,
*/
query = (Query *) linitial(action);
if (!is_instead ||
- query->commandType != CMD_SELECT || query->into != NULL)
+ query->commandType != CMD_SELECT ||
+ query->utilityStmt != NULL ||
+ query->intoClause != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("rules on SELECT must have action INSTEAD SELECT")));
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index b54dea45dca..58ed351c59e 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.115 2007/03/13 00:33:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.116 2007/04/27 22:05:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -68,7 +68,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
qd->operation = plannedstmt->commandType; /* operation */
qd->plannedstmt = plannedstmt; /* plan */
- qd->utilitystmt = NULL;
+ qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
qd->snapshot = snapshot; /* snapshot */
qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */
qd->dest = dest; /* output dest */
@@ -257,7 +257,8 @@ ChoosePortalStrategy(List *stmts)
if (query->canSetTag)
{
if (query->commandType == CMD_SELECT &&
- query->into == NULL)
+ query->utilityStmt == NULL &&
+ query->intoClause == NULL)
return PORTAL_ONE_SELECT;
if (query->commandType == CMD_UTILITY &&
query->utilityStmt != NULL)
@@ -276,7 +277,8 @@ ChoosePortalStrategy(List *stmts)
if (pstmt->canSetTag)
{
if (pstmt->commandType == CMD_SELECT &&
- pstmt->into == NULL)
+ pstmt->utilityStmt == NULL &&
+ pstmt->intoClause == NULL)
return PORTAL_ONE_SELECT;
}
}
@@ -380,7 +382,8 @@ FetchStatementTargetList(Node *stmt)
else
{
if (query->commandType == CMD_SELECT &&
- query->into == NULL)
+ query->utilityStmt == NULL &&
+ query->intoClause == NULL)
return query->targetList;
if (query->returningList)
return query->returningList;
@@ -392,7 +395,8 @@ FetchStatementTargetList(Node *stmt)
PlannedStmt *pstmt = (PlannedStmt *) stmt;
if (pstmt->commandType == CMD_SELECT &&
- pstmt->into == NULL)
+ pstmt->utilityStmt == NULL &&
+ pstmt->intoClause == NULL)
return pstmt->planTree->targetlist;
if (pstmt->returningLists)
return (List *) linitial(pstmt->returningLists);
@@ -1222,7 +1226,8 @@ PortalRunMulti(Portal portal, bool isTopLevel,
*/
CHECK_FOR_INTERRUPTS();
- if (IsA(stmt, PlannedStmt))
+ if (IsA(stmt, PlannedStmt) &&
+ ((PlannedStmt *) stmt)->utilityStmt == NULL)
{
/*
* process a plannable query.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 9dd700ffe70..028cb47c7ac 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.278 2007/04/26 16:13:12 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.279 2007/04/27 22:05:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -268,7 +268,7 @@ CommandIsReadOnly(Node *parsetree)
switch (stmt->commandType)
{
case CMD_SELECT:
- if (stmt->into != NULL)
+ if (stmt->intoClause != NULL)
return false; /* SELECT INTO */
else if (stmt->rowMarks != NIL)
return false; /* SELECT FOR UPDATE/SHARE */
@@ -505,10 +505,20 @@ ProcessUtility(Node *parsetree,
/*
* Portal (cursor) manipulation
+ *
+ * Note: DECLARE CURSOR is processed mostly as a SELECT, and
+ * therefore what we will get here is a PlannedStmt not a bare
+ * DeclareCursorStmt.
*/
- case T_DeclareCursorStmt:
- PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
- queryString, isTopLevel);
+ case T_PlannedStmt:
+ {
+ PlannedStmt *stmt = (PlannedStmt *) parsetree;
+
+ if (stmt->utilityStmt == NULL ||
+ !IsA(stmt->utilityStmt, DeclareCursorStmt))
+ elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
+ PerformCursorOpen(stmt, params, queryString, isTopLevel);
+ }
break;
case T_ClosePortalStmt:
@@ -1272,8 +1282,9 @@ QueryReturnsTuples(Query *parsetree)
switch (parsetree->commandType)
{
case CMD_SELECT:
- /* returns tuples ... unless it's SELECT INTO */
- if (parsetree->into == NULL)
+ /* returns tuples ... unless it's DECLARE CURSOR or SELECT INTO */
+ if (parsetree->utilityStmt == NULL &&
+ parsetree->intoClause == NULL)
return true;
break;
case CMD_INSERT:
@@ -1899,7 +1910,12 @@ CreateCommandTag(Node *parsetree)
* will be useful for complaints about read-only
* statements
*/
- if (stmt->into != NULL)
+ if (stmt->utilityStmt != NULL)
+ {
+ Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
+ tag = "DECLARE CURSOR";
+ }
+ else if (stmt->intoClause != NULL)
tag = "SELECT INTO";
else if (stmt->rowMarks != NIL)
{
@@ -1942,7 +1958,12 @@ CreateCommandTag(Node *parsetree)
* will be useful for complaints about read-only
* statements
*/
- if (stmt->into != NULL)
+ if (stmt->utilityStmt != NULL)
+ {
+ Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
+ tag = "DECLARE CURSOR";
+ }
+ else if (stmt->intoClause != NULL)
tag = "SELECT INTO";
else if (stmt->rowMarks != NIL)
{
@@ -2009,7 +2030,7 @@ GetCommandLogLevel(Node *parsetree)
break;
case T_SelectStmt:
- if (((SelectStmt *) parsetree)->into)
+ if (((SelectStmt *) parsetree)->intoClause)
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
else
lev = LOGSTMT_ALL;
@@ -2330,10 +2351,10 @@ GetCommandLogLevel(Node *parsetree)
switch (stmt->commandType)
{
case CMD_SELECT:
- if (stmt->into != NULL)
+ if (stmt->intoClause != NULL)
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
else
- lev = LOGSTMT_ALL;
+ lev = LOGSTMT_ALL; /* SELECT or DECLARE CURSOR */
break;
case CMD_UPDATE:
@@ -2359,10 +2380,10 @@ GetCommandLogLevel(Node *parsetree)
switch (stmt->commandType)
{
case CMD_SELECT:
- if (stmt->into != NULL)
+ if (stmt->intoClause != NULL)
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
else
- lev = LOGSTMT_ALL;
+ lev = LOGSTMT_ALL; /* SELECT or DECLARE CURSOR */
break;
case CMD_UPDATE:
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 3d774046136..66a2effc509 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.22 2007/03/13 00:33:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.23 2007/04/27 22:05:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -15,10 +15,11 @@
#define PORTALCMDS_H
#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
#include "utils/portal.h"
-extern void PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
+extern void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
const char *queryString, bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 09cb4ff5031..179d7a765db 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.347 2007/04/26 16:13:14 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.348 2007/04/27 22:05:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -82,9 +82,11 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
*
* Utility statements (i.e. non-optimizable statements) have the
* utilityStmt field set, and the Query itself is mostly dummy.
+ * DECLARE CURSOR is a special case: it is represented like a SELECT,
+ * but the original DeclareCursorStmt is stored in utilityStmt.
*
* Planning converts a Query tree into a Plan tree headed by a PlannedStmt
- * noded --- the Query structure is not used by the executor.
+ * node --- the Query structure is not used by the executor.
*/
typedef struct Query
{
@@ -96,13 +98,13 @@ typedef struct Query
bool canSetTag; /* do I set the command result tag? */
- Node *utilityStmt; /* non-null if this is a non-optimizable
- * statement */
+ Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a
+ * non-optimizable statement */
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
- IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */
+ IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasSubLinks; /* has subquery SubLink */
@@ -732,7 +734,7 @@ typedef struct SelectStmt
*/
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
* lcons(NIL,NIL) for all (SELECT DISTINCT) */
- IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */
+ IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */
List *targetList; /* the target list (of ResTarget) */
List *fromClause; /* the FROM clause */
Node *whereClause; /* WHERE qualification */
@@ -1427,6 +1429,10 @@ typedef struct CommentStmt
/* ----------------------
* Declare Cursor Statement
+ *
+ * Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
+ * output. After parse analysis it's set to null, and the Query points to the
+ * DeclareCursorStmt, not vice versa.
* ----------------------
*/
#define CURSOR_OPT_BINARY 0x0001 /* BINARY */
@@ -1441,7 +1447,7 @@ typedef struct DeclareCursorStmt
NodeTag type;
char *portalname; /* name of the portal (cursor) */
int options; /* bitmask of options (see above) */
- Node *query; /* the SELECT query */
+ Node *query; /* the raw SELECT query */
} DeclareCursorStmt;
/* ----------------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index aecfb519399..9d336e4b889 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.93 2007/02/27 01:11:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.94 2007/04/27 22:05:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -46,7 +46,9 @@ typedef struct PlannedStmt
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */
- IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */
+ Node *utilityStmt; /* non-null if this is DECLARE CURSOR */
+
+ IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */
List *subplans; /* Plan trees for SubPlan expressions */