mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
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.
This commit is contained in:
parent
a264671116
commit
bbbe825f5f
@ -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, ¶m_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, ¶m_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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user