Commit bbbe825f authored by Tom Lane's avatar Tom Lane

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.
parent a2646711
......@@ -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")));
......
......@@ -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
*
......
......@@ -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;
......
......@@ -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;
}
/*
......
......@@ -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;
......
......@@ -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,
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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? */
......
......@@ -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 ||
......
......@@ -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,
......@@ -312,20 +316,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
*/
......@@ -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)
......
......@@ -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;
......
......@@ -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")));
......
......@@ -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;
......
......@@ -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 ||
......
......@@ -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")));
......
......@@ -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.
......
......@@ -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:
......
......@@ -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,
......
......@@ -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;
/* ----------------------
......
......@@ -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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment