Remove the Query structure from the executor's API. This allows us to stop
storing mostly-redundant Query trees in prepared statements, portals, etc. To replace Query, a new node type called PlannedStmt is inserted by the planner at the top of a completed plan tree; this carries just the fields of Query that are still needed at runtime. The statement lists kept in portals etc. now consist of intermixed PlannedStmt and bare utility-statement nodes --- no Query. This incidentally allows us to remove some fields from Query and Plan nodes that shouldn't have been there in the first place. Still to do: simplify the execution-time range table; at the moment the range table passed to the executor still contains Query trees for subqueries. initdb forced due to change of stored rules.
This commit is contained in:
parent
71b0cf2f6b
commit
9cbd0c155d
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.275 2007/01/25 02:17:26 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.276 2007/02/20 17:32:13 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -986,10 +986,11 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
{
|
{
|
||||||
Query *query = stmt->query;
|
Query *query = stmt->query;
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
Plan *plan;
|
PlannedStmt *plan;
|
||||||
DestReceiver *dest;
|
DestReceiver *dest;
|
||||||
|
|
||||||
Assert(query);
|
Assert(query);
|
||||||
|
Assert(query->commandType == CMD_SELECT);
|
||||||
Assert(!is_from);
|
Assert(!is_from);
|
||||||
cstate->rel = NULL;
|
cstate->rel = NULL;
|
||||||
|
|
||||||
@ -999,6 +1000,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("COPY (SELECT) WITH OIDS is not supported")));
|
errmsg("COPY (SELECT) WITH OIDS is not supported")));
|
||||||
|
|
||||||
|
/* Query mustn't use INTO, either */
|
||||||
if (query->into)
|
if (query->into)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
@ -1016,7 +1018,6 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
* shouldn't modify its input ... FIXME someday.
|
* shouldn't modify its input ... FIXME someday.
|
||||||
*/
|
*/
|
||||||
query = copyObject(query);
|
query = copyObject(query);
|
||||||
Assert(query->commandType == CMD_SELECT);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must acquire locks in case we didn't come fresh from the parser.
|
* Must acquire locks in case we didn't come fresh from the parser.
|
||||||
@ -1051,7 +1052,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
((DR_copy *) dest)->cstate = cstate;
|
((DR_copy *) dest)->cstate = cstate;
|
||||||
|
|
||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
cstate->queryDesc = CreateQueryDesc(query, plan,
|
cstate->queryDesc = CreateQueryDesc(plan,
|
||||||
ActiveSnapshot, InvalidSnapshot,
|
ActiveSnapshot, InvalidSnapshot,
|
||||||
dest, NULL, false);
|
dest, NULL, false);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.155 2007/02/19 02:23:11 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -149,7 +149,7 @@ static void
|
|||||||
ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
|
ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
|
||||||
TupOutputState *tstate)
|
TupOutputState *tstate)
|
||||||
{
|
{
|
||||||
Plan *plan;
|
PlannedStmt *plan;
|
||||||
QueryDesc *queryDesc;
|
QueryDesc *queryDesc;
|
||||||
bool isCursor = false;
|
bool isCursor = false;
|
||||||
int cursorOptions = 0;
|
int cursorOptions = 0;
|
||||||
@ -203,7 +203,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
|
|||||||
ActiveSnapshot->curcid = GetCurrentCommandId();
|
ActiveSnapshot->curcid = GetCurrentCommandId();
|
||||||
|
|
||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
queryDesc = CreateQueryDesc(query, plan,
|
queryDesc = CreateQueryDesc(plan,
|
||||||
ActiveSnapshot, InvalidSnapshot,
|
ActiveSnapshot, InvalidSnapshot,
|
||||||
None_Receiver, params,
|
None_Receiver, params,
|
||||||
stmt->analyze);
|
stmt->analyze);
|
||||||
@ -260,14 +260,14 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
|||||||
|
|
||||||
es->printNodes = stmt->verbose;
|
es->printNodes = stmt->verbose;
|
||||||
es->printAnalyze = stmt->analyze;
|
es->printAnalyze = stmt->analyze;
|
||||||
es->rtable = queryDesc->parsetree->rtable;
|
es->rtable = queryDesc->plannedstmt->rtable;
|
||||||
|
|
||||||
if (es->printNodes)
|
if (es->printNodes)
|
||||||
{
|
{
|
||||||
char *s;
|
char *s;
|
||||||
char *f;
|
char *f;
|
||||||
|
|
||||||
s = nodeToString(queryDesc->plantree);
|
s = nodeToString(queryDesc->plannedstmt->planTree);
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
if (Explain_pretty_print)
|
if (Explain_pretty_print)
|
||||||
@ -282,7 +282,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
explain_outNode(&buf, queryDesc->plantree, queryDesc->planstate,
|
explain_outNode(&buf,
|
||||||
|
queryDesc->plannedstmt->planTree, queryDesc->planstate,
|
||||||
NULL, 0, es);
|
NULL, 0, es);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.60 2007/02/06 22:49:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.61 2007/02/20 17:32:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -42,7 +42,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
|||||||
{
|
{
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
Query *query;
|
Query *query;
|
||||||
Plan *plan;
|
PlannedStmt *plan;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
|
||||||
@ -98,13 +98,12 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
|||||||
plan = planner(query, true, stmt->options, params);
|
plan = planner(query, true, stmt->options, params);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a portal and copy the query and plan into its memory context.
|
* Create a portal and copy the plan into its memory context.
|
||||||
*/
|
*/
|
||||||
portal = CreatePortal(stmt->portalname, false, false);
|
portal = CreatePortal(stmt->portalname, false, false);
|
||||||
|
|
||||||
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
|
|
||||||
query = copyObject(query);
|
|
||||||
plan = copyObject(plan);
|
plan = copyObject(plan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -115,7 +114,6 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
|||||||
NULL,
|
NULL,
|
||||||
debug_query_string ? pstrdup(debug_query_string) : NULL,
|
debug_query_string ? pstrdup(debug_query_string) : NULL,
|
||||||
"SELECT", /* cursor's query is always a SELECT */
|
"SELECT", /* cursor's query is always a SELECT */
|
||||||
list_make1(query),
|
|
||||||
list_make1(plan),
|
list_make1(plan),
|
||||||
PortalGetHeapMemory(portal));
|
PortalGetHeapMemory(portal));
|
||||||
|
|
||||||
@ -140,7 +138,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
|||||||
portal->cursorOptions = stmt->options;
|
portal->cursorOptions = stmt->options;
|
||||||
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
|
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
|
||||||
{
|
{
|
||||||
if (ExecSupportsBackwardScan(plan))
|
if (ExecSupportsBackwardScan(plan->planTree))
|
||||||
portal->cursorOptions |= CURSOR_OPT_SCROLL;
|
portal->cursorOptions |= CURSOR_OPT_SCROLL;
|
||||||
else
|
else
|
||||||
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
|
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.68 2007/01/28 19:05:35 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.69 2007/02/20 17:32:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -114,9 +114,9 @@ PrepareQuery(PrepareStmt *stmt)
|
|||||||
StorePreparedStatement(stmt->name,
|
StorePreparedStatement(stmt->name,
|
||||||
debug_query_string,
|
debug_query_string,
|
||||||
commandTag,
|
commandTag,
|
||||||
query_list,
|
|
||||||
plan_list,
|
plan_list,
|
||||||
stmt->argtype_oids,
|
stmt->argtype_oids,
|
||||||
|
true,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +129,7 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
|||||||
{
|
{
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
char *query_string;
|
char *query_string;
|
||||||
List *query_list,
|
List *plan_list;
|
||||||
*plan_list;
|
|
||||||
MemoryContext qcontext;
|
MemoryContext qcontext;
|
||||||
ParamListInfo paramLI = NULL;
|
ParamListInfo paramLI = NULL;
|
||||||
EState *estate = NULL;
|
EState *estate = NULL;
|
||||||
@ -139,12 +138,17 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
|||||||
/* Look it up in the hash table */
|
/* Look it up in the hash table */
|
||||||
entry = FetchPreparedStatement(stmt->name, true);
|
entry = FetchPreparedStatement(stmt->name, true);
|
||||||
|
|
||||||
query_string = entry->query_string;
|
/*
|
||||||
query_list = entry->query_list;
|
* Punt if not fully planned. (Currently, that only happens for the
|
||||||
plan_list = entry->plan_list;
|
* protocol-level unnamed statement, which can't be accessed from SQL;
|
||||||
qcontext = entry->context;
|
* so there's no point in doing more than a quick check here.)
|
||||||
|
*/
|
||||||
|
if (!entry->fully_planned)
|
||||||
|
elog(ERROR, "EXECUTE does not support unplanned prepared statements");
|
||||||
|
|
||||||
Assert(list_length(query_list) == list_length(plan_list));
|
query_string = entry->query_string;
|
||||||
|
plan_list = entry->stmt_list;
|
||||||
|
qcontext = entry->context;
|
||||||
|
|
||||||
/* Evaluate parameters, if any */
|
/* Evaluate parameters, if any */
|
||||||
if (entry->argtype_list != NIL)
|
if (entry->argtype_list != NIL)
|
||||||
@ -172,30 +176,26 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
|||||||
if (stmt->into)
|
if (stmt->into)
|
||||||
{
|
{
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
Query *query;
|
PlannedStmt *pstmt;
|
||||||
|
|
||||||
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
qcontext = PortalGetHeapMemory(portal);
|
||||||
|
oldContext = MemoryContextSwitchTo(qcontext);
|
||||||
|
|
||||||
if (query_string)
|
if (query_string)
|
||||||
query_string = pstrdup(query_string);
|
query_string = pstrdup(query_string);
|
||||||
query_list = copyObject(query_list);
|
|
||||||
plan_list = copyObject(plan_list);
|
plan_list = copyObject(plan_list);
|
||||||
qcontext = PortalGetHeapMemory(portal);
|
|
||||||
|
|
||||||
if (list_length(query_list) != 1)
|
if (list_length(plan_list) != 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("prepared statement is not a SELECT")));
|
errmsg("prepared statement is not a SELECT")));
|
||||||
query = (Query *) linitial(query_list);
|
pstmt = (PlannedStmt *) linitial(plan_list);
|
||||||
if (query->commandType != CMD_SELECT)
|
if (!IsA(pstmt, PlannedStmt) ||
|
||||||
|
pstmt->commandType != CMD_SELECT)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("prepared statement is not a SELECT")));
|
errmsg("prepared statement is not a SELECT")));
|
||||||
query->into = copyObject(stmt->into);
|
pstmt->into = copyObject(stmt->into);
|
||||||
query->intoOptions = copyObject(stmt->intoOptions);
|
|
||||||
query->intoOnCommit = stmt->into_on_commit;
|
|
||||||
if (stmt->into_tbl_space)
|
|
||||||
query->intoTableSpaceName = pstrdup(stmt->into_tbl_space);
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
}
|
}
|
||||||
@ -204,7 +204,6 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
|||||||
NULL,
|
NULL,
|
||||||
query_string,
|
query_string,
|
||||||
entry->commandTag,
|
entry->commandTag,
|
||||||
query_list,
|
|
||||||
plan_list,
|
plan_list,
|
||||||
qcontext);
|
qcontext);
|
||||||
|
|
||||||
@ -305,9 +304,9 @@ void
|
|||||||
StorePreparedStatement(const char *stmt_name,
|
StorePreparedStatement(const char *stmt_name,
|
||||||
const char *query_string,
|
const char *query_string,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *query_list,
|
List *stmt_list,
|
||||||
List *plan_list,
|
|
||||||
List *argtype_list,
|
List *argtype_list,
|
||||||
|
bool fully_planned,
|
||||||
bool from_sql)
|
bool from_sql)
|
||||||
{
|
{
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
@ -345,8 +344,7 @@ StorePreparedStatement(const char *stmt_name,
|
|||||||
* incomplete (ie corrupt) hashtable entry.
|
* incomplete (ie corrupt) hashtable entry.
|
||||||
*/
|
*/
|
||||||
qstring = query_string ? pstrdup(query_string) : NULL;
|
qstring = query_string ? pstrdup(query_string) : NULL;
|
||||||
query_list = (List *) copyObject(query_list);
|
stmt_list = (List *) copyObject(stmt_list);
|
||||||
plan_list = (List *) copyObject(plan_list);
|
|
||||||
argtype_list = list_copy(argtype_list);
|
argtype_list = list_copy(argtype_list);
|
||||||
|
|
||||||
/* Now we can add entry to hash table */
|
/* Now we can add entry to hash table */
|
||||||
@ -363,12 +361,12 @@ StorePreparedStatement(const char *stmt_name,
|
|||||||
/* Fill in the hash table entry with copied data */
|
/* Fill in the hash table entry with copied data */
|
||||||
entry->query_string = qstring;
|
entry->query_string = qstring;
|
||||||
entry->commandTag = commandTag;
|
entry->commandTag = commandTag;
|
||||||
entry->query_list = query_list;
|
entry->stmt_list = stmt_list;
|
||||||
entry->plan_list = plan_list;
|
|
||||||
entry->argtype_list = argtype_list;
|
entry->argtype_list = argtype_list;
|
||||||
|
entry->fully_planned = fully_planned;
|
||||||
|
entry->from_sql = from_sql;
|
||||||
entry->context = entrycxt;
|
entry->context = entrycxt;
|
||||||
entry->prepare_time = GetCurrentStatementStartTimestamp();
|
entry->prepare_time = GetCurrentStatementStartTimestamp();
|
||||||
entry->from_sql = from_sql;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
MemoryContextSwitchTo(oldcxt);
|
||||||
}
|
}
|
||||||
@ -426,21 +424,54 @@ FetchPreparedStatementParams(const char *stmt_name)
|
|||||||
TupleDesc
|
TupleDesc
|
||||||
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
||||||
{
|
{
|
||||||
|
Node *node;
|
||||||
Query *query;
|
Query *query;
|
||||||
|
PlannedStmt *pstmt;
|
||||||
|
|
||||||
switch (ChoosePortalStrategy(stmt->query_list))
|
switch (ChoosePortalStrategy(stmt->stmt_list))
|
||||||
{
|
{
|
||||||
case PORTAL_ONE_SELECT:
|
case PORTAL_ONE_SELECT:
|
||||||
query = (Query *) linitial(stmt->query_list);
|
node = (Node *) linitial(stmt->stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
return ExecCleanTypeFromTL(query->targetList, false);
|
return ExecCleanTypeFromTL(query->targetList, false);
|
||||||
|
}
|
||||||
|
if (IsA(node, PlannedStmt))
|
||||||
|
{
|
||||||
|
pstmt = (PlannedStmt *) node;
|
||||||
|
return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
|
||||||
|
}
|
||||||
|
/* other cases shouldn't happen, but return NULL */
|
||||||
|
break;
|
||||||
|
|
||||||
case PORTAL_ONE_RETURNING:
|
case PORTAL_ONE_RETURNING:
|
||||||
query = PortalListGetPrimaryQuery(stmt->query_list);
|
node = PortalListGetPrimaryStmt(stmt->stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
|
Assert(query->returningList);
|
||||||
return ExecCleanTypeFromTL(query->returningList, false);
|
return ExecCleanTypeFromTL(query->returningList, false);
|
||||||
|
}
|
||||||
|
if (IsA(node, PlannedStmt))
|
||||||
|
{
|
||||||
|
pstmt = (PlannedStmt *) node;
|
||||||
|
Assert(pstmt->returningLists);
|
||||||
|
return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
|
||||||
|
}
|
||||||
|
/* other cases shouldn't happen, but return NULL */
|
||||||
|
break;
|
||||||
|
|
||||||
case PORTAL_UTIL_SELECT:
|
case PORTAL_UTIL_SELECT:
|
||||||
query = (Query *) linitial(stmt->query_list);
|
node = (Node *) linitial(stmt->stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
|
Assert(query->utilityStmt);
|
||||||
return UtilityTupleDescriptor(query->utilityStmt);
|
return UtilityTupleDescriptor(query->utilityStmt);
|
||||||
|
}
|
||||||
|
/* else it's a bare utility statement */
|
||||||
|
return UtilityTupleDescriptor(node);
|
||||||
|
|
||||||
case PORTAL_MULTI_QUERY:
|
case PORTAL_MULTI_QUERY:
|
||||||
/* will not return tuples */
|
/* will not return tuples */
|
||||||
@ -460,7 +491,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
|||||||
bool
|
bool
|
||||||
PreparedStatementReturnsTuples(PreparedStatement *stmt)
|
PreparedStatementReturnsTuples(PreparedStatement *stmt)
|
||||||
{
|
{
|
||||||
switch (ChoosePortalStrategy(stmt->query_list))
|
switch (ChoosePortalStrategy(stmt->stmt_list))
|
||||||
{
|
{
|
||||||
case PORTAL_ONE_SELECT:
|
case PORTAL_ONE_SELECT:
|
||||||
case PORTAL_ONE_RETURNING:
|
case PORTAL_ONE_RETURNING:
|
||||||
@ -480,52 +511,15 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt)
|
|||||||
* targetlist.
|
* targetlist.
|
||||||
*
|
*
|
||||||
* Note: do not modify the result.
|
* Note: do not modify the result.
|
||||||
*
|
|
||||||
* XXX be careful to keep this in sync with FetchPortalTargetList,
|
|
||||||
* and with UtilityReturnsTuples.
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
FetchPreparedStatementTargetList(PreparedStatement *stmt)
|
FetchPreparedStatementTargetList(PreparedStatement *stmt)
|
||||||
{
|
{
|
||||||
PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list);
|
/* no point in looking if it doesn't return tuples */
|
||||||
|
if (ChoosePortalStrategy(stmt->stmt_list) == PORTAL_MULTI_QUERY)
|
||||||
if (strategy == PORTAL_ONE_SELECT)
|
|
||||||
return ((Query *) linitial(stmt->query_list))->targetList;
|
|
||||||
if (strategy == PORTAL_ONE_RETURNING)
|
|
||||||
return (PortalListGetPrimaryQuery(stmt->query_list))->returningList;
|
|
||||||
if (strategy == PORTAL_UTIL_SELECT)
|
|
||||||
{
|
|
||||||
Node *utilityStmt;
|
|
||||||
|
|
||||||
utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
|
|
||||||
switch (nodeTag(utilityStmt))
|
|
||||||
{
|
|
||||||
case T_FetchStmt:
|
|
||||||
{
|
|
||||||
FetchStmt *substmt = (FetchStmt *) utilityStmt;
|
|
||||||
Portal subportal;
|
|
||||||
|
|
||||||
Assert(!substmt->ismove);
|
|
||||||
subportal = GetPortalByName(substmt->portalname);
|
|
||||||
Assert(PortalIsValid(subportal));
|
|
||||||
return FetchPortalTargetList(subportal);
|
|
||||||
}
|
|
||||||
|
|
||||||
case T_ExecuteStmt:
|
|
||||||
{
|
|
||||||
ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
|
|
||||||
PreparedStatement *entry;
|
|
||||||
|
|
||||||
Assert(!substmt->into);
|
|
||||||
entry = FetchPreparedStatement(substmt->name, true);
|
|
||||||
return FetchPreparedStatementTargetList(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NIL;
|
return NIL;
|
||||||
|
/* get the primary statement and find out what it returns */
|
||||||
|
return FetchStatementTargetList(PortalListGetPrimaryStmt(stmt->stmt_list));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -574,10 +568,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
{
|
{
|
||||||
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
|
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
ListCell *q,
|
List *plan_list;
|
||||||
*p;
|
ListCell *p;
|
||||||
List *query_list,
|
|
||||||
*plan_list;
|
|
||||||
ParamListInfo paramLI = NULL;
|
ParamListInfo paramLI = NULL;
|
||||||
EState *estate = NULL;
|
EState *estate = NULL;
|
||||||
|
|
||||||
@ -587,10 +579,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
/* Look it up in the hash table */
|
/* Look it up in the hash table */
|
||||||
entry = FetchPreparedStatement(execstmt->name, true);
|
entry = FetchPreparedStatement(execstmt->name, true);
|
||||||
|
|
||||||
query_list = entry->query_list;
|
/*
|
||||||
plan_list = entry->plan_list;
|
* Punt if not fully planned. (Currently, that only happens for the
|
||||||
|
* protocol-level unnamed statement, which can't be accessed from SQL;
|
||||||
|
* so there's no point in doing more than a quick check here.)
|
||||||
|
*/
|
||||||
|
if (!entry->fully_planned)
|
||||||
|
elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
|
||||||
|
|
||||||
Assert(list_length(query_list) == list_length(plan_list));
|
plan_list = entry->stmt_list;
|
||||||
|
|
||||||
/* Evaluate parameters, if any */
|
/* Evaluate parameters, if any */
|
||||||
if (entry->argtype_list != NIL)
|
if (entry->argtype_list != NIL)
|
||||||
@ -606,17 +603,16 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Explain each query */
|
/* Explain each query */
|
||||||
forboth(q, query_list, p, plan_list)
|
foreach(p, plan_list)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(q);
|
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
|
||||||
Plan *plan = (Plan *) lfirst(p);
|
|
||||||
bool is_last_query;
|
bool is_last_query;
|
||||||
|
|
||||||
is_last_query = (lnext(p) == NULL);
|
is_last_query = (lnext(p) == NULL);
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (!IsA(pstmt, PlannedStmt))
|
||||||
{
|
{
|
||||||
if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
|
if (IsA(pstmt, NotifyStmt))
|
||||||
do_text_output_oneline(tstate, "NOTIFY");
|
do_text_output_oneline(tstate, "NOTIFY");
|
||||||
else
|
else
|
||||||
do_text_output_oneline(tstate, "UTILITY");
|
do_text_output_oneline(tstate, "UTILITY");
|
||||||
@ -627,15 +623,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
|
|
||||||
if (execstmt->into)
|
if (execstmt->into)
|
||||||
{
|
{
|
||||||
if (query->commandType != CMD_SELECT)
|
if (pstmt->commandType != CMD_SELECT)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("prepared statement is not a SELECT")));
|
errmsg("prepared statement is not a SELECT")));
|
||||||
|
|
||||||
/* Copy the query so we can modify it */
|
/* Copy the stmt so we can modify it */
|
||||||
query = copyObject(query);
|
pstmt = copyObject(pstmt);
|
||||||
|
|
||||||
query->into = execstmt->into;
|
pstmt->into = execstmt->into;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -648,7 +644,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
ActiveSnapshot->curcid = GetCurrentCommandId();
|
ActiveSnapshot->curcid = GetCurrentCommandId();
|
||||||
|
|
||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
qdesc = CreateQueryDesc(query, plan,
|
qdesc = CreateQueryDesc(pstmt,
|
||||||
ActiveSnapshot, InvalidSnapshot,
|
ActiveSnapshot, InvalidSnapshot,
|
||||||
None_Receiver,
|
None_Receiver,
|
||||||
paramLI, stmt->analyze);
|
paramLI, stmt->analyze);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.286 2007/02/02 00:07:02 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -93,7 +93,8 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning,
|
|||||||
static TupleTableSlot *EvalPlanQualNext(EState *estate);
|
static TupleTableSlot *EvalPlanQualNext(EState *estate);
|
||||||
static void EndEvalPlanQual(EState *estate);
|
static void EndEvalPlanQual(EState *estate);
|
||||||
static void ExecCheckRTEPerms(RangeTblEntry *rte);
|
static void ExecCheckRTEPerms(RangeTblEntry *rte);
|
||||||
static void ExecCheckXactReadOnly(Query *parsetree);
|
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
|
||||||
|
static void ExecCheckRangeTblReadOnly(List *rtable);
|
||||||
static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
|
static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
|
||||||
evalPlanQual *priorepq);
|
evalPlanQual *priorepq);
|
||||||
static void EvalPlanQualStop(evalPlanQual *epq);
|
static void EvalPlanQualStop(evalPlanQual *epq);
|
||||||
@ -139,7 +140,7 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
|
|||||||
* planned to non-temporary tables. EXPLAIN is considered read-only.
|
* planned to non-temporary tables. EXPLAIN is considered read-only.
|
||||||
*/
|
*/
|
||||||
if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
|
if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
|
||||||
ExecCheckXactReadOnly(queryDesc->parsetree);
|
ExecCheckXactReadOnly(queryDesc->plannedstmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build EState, switch into per-query memory context for startup.
|
* Build EState, switch into per-query memory context for startup.
|
||||||
@ -154,9 +155,9 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
|
|||||||
*/
|
*/
|
||||||
estate->es_param_list_info = queryDesc->params;
|
estate->es_param_list_info = queryDesc->params;
|
||||||
|
|
||||||
if (queryDesc->plantree->nParamExec > 0)
|
if (queryDesc->plannedstmt->nParamExec > 0)
|
||||||
estate->es_param_exec_vals = (ParamExecData *)
|
estate->es_param_exec_vals = (ParamExecData *)
|
||||||
palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
|
palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy other important information into the EState
|
* Copy other important information into the EState
|
||||||
@ -227,7 +228,7 @@ ExecutorRun(QueryDesc *queryDesc,
|
|||||||
estate->es_lastoid = InvalidOid;
|
estate->es_lastoid = InvalidOid;
|
||||||
|
|
||||||
sendTuples = (operation == CMD_SELECT ||
|
sendTuples = (operation == CMD_SELECT ||
|
||||||
queryDesc->parsetree->returningList);
|
queryDesc->plannedstmt->returningLists);
|
||||||
|
|
||||||
if (sendTuples)
|
if (sendTuples)
|
||||||
(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
|
(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
|
||||||
@ -414,26 +415,41 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
|
|||||||
* Check that the query does not imply any writes to non-temp tables.
|
* Check that the query does not imply any writes to non-temp tables.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ExecCheckXactReadOnly(Query *parsetree)
|
ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
|
||||||
{
|
{
|
||||||
ListCell *l;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CREATE TABLE AS or SELECT INTO?
|
* CREATE TABLE AS or SELECT INTO?
|
||||||
*
|
*
|
||||||
* XXX should we allow this if the destination is temp?
|
* XXX should we allow this if the destination is temp?
|
||||||
*/
|
*/
|
||||||
if (parsetree->into != NULL)
|
if (plannedstmt->into != NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* Fail if write permissions are requested on any non-temp table */
|
/* Fail if write permissions are requested on any non-temp table */
|
||||||
foreach(l, parsetree->rtable)
|
ExecCheckRangeTblReadOnly(plannedstmt->rtable);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
|
||||||
|
errmsg("transaction is read-only")));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ExecCheckRangeTblReadOnly(List *rtable)
|
||||||
|
{
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
/* Fail if write permissions are requested on any non-temp table */
|
||||||
|
foreach(l, rtable)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte = lfirst(l);
|
RangeTblEntry *rte = lfirst(l);
|
||||||
|
|
||||||
if (rte->rtekind == RTE_SUBQUERY)
|
if (rte->rtekind == RTE_SUBQUERY)
|
||||||
{
|
{
|
||||||
ExecCheckXactReadOnly(rte->subquery);
|
Assert(!rte->subquery->into);
|
||||||
|
ExecCheckRangeTblReadOnly(rte->subquery->rtable);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,11 +485,11 @@ static void
|
|||||||
InitPlan(QueryDesc *queryDesc, int eflags)
|
InitPlan(QueryDesc *queryDesc, int eflags)
|
||||||
{
|
{
|
||||||
CmdType operation = queryDesc->operation;
|
CmdType operation = queryDesc->operation;
|
||||||
Query *parseTree = queryDesc->parsetree;
|
PlannedStmt *plannedstmt = queryDesc->plannedstmt;
|
||||||
Plan *plan = queryDesc->plantree;
|
Plan *plan = plannedstmt->planTree;
|
||||||
|
List *rangeTable = plannedstmt->rtable;
|
||||||
EState *estate = queryDesc->estate;
|
EState *estate = queryDesc->estate;
|
||||||
PlanState *planstate;
|
PlanState *planstate;
|
||||||
List *rangeTable;
|
|
||||||
TupleDesc tupType;
|
TupleDesc tupType;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
@ -482,12 +498,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* rangetable here --- subplan RTEs will be checked during
|
* rangetable here --- subplan RTEs will be checked during
|
||||||
* ExecInitSubPlan().
|
* ExecInitSubPlan().
|
||||||
*/
|
*/
|
||||||
ExecCheckRTPerms(parseTree->rtable);
|
ExecCheckRTPerms(rangeTable);
|
||||||
|
|
||||||
/*
|
|
||||||
* get information from query descriptor
|
|
||||||
*/
|
|
||||||
rangeTable = parseTree->rtable;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize the node's execution state
|
* initialize the node's execution state
|
||||||
@ -495,23 +506,15 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
estate->es_range_table = rangeTable;
|
estate->es_range_table = rangeTable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if there is a result relation, initialize result relation stuff
|
* initialize result relation stuff
|
||||||
*/
|
*/
|
||||||
if (parseTree->resultRelation)
|
if (plannedstmt->resultRelations)
|
||||||
{
|
{
|
||||||
List *resultRelations = parseTree->resultRelations;
|
List *resultRelations = plannedstmt->resultRelations;
|
||||||
int numResultRelations;
|
int numResultRelations = list_length(resultRelations);
|
||||||
ResultRelInfo *resultRelInfos;
|
ResultRelInfo *resultRelInfos;
|
||||||
|
|
||||||
if (resultRelations != NIL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Multiple result relations (due to inheritance)
|
|
||||||
* parseTree->resultRelations identifies them all
|
|
||||||
*/
|
|
||||||
ResultRelInfo *resultRelInfo;
|
ResultRelInfo *resultRelInfo;
|
||||||
|
|
||||||
numResultRelations = list_length(resultRelations);
|
|
||||||
resultRelInfos = (ResultRelInfo *)
|
resultRelInfos = (ResultRelInfo *)
|
||||||
palloc(numResultRelations * sizeof(ResultRelInfo));
|
palloc(numResultRelations * sizeof(ResultRelInfo));
|
||||||
resultRelInfo = resultRelInfos;
|
resultRelInfo = resultRelInfos;
|
||||||
@ -524,21 +527,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
estate->es_instrument);
|
estate->es_instrument);
|
||||||
resultRelInfo++;
|
resultRelInfo++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Single result relation identified by parseTree->resultRelation
|
|
||||||
*/
|
|
||||||
numResultRelations = 1;
|
|
||||||
resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
|
|
||||||
initResultRelInfo(resultRelInfos,
|
|
||||||
parseTree->resultRelation,
|
|
||||||
rangeTable,
|
|
||||||
operation,
|
|
||||||
estate->es_instrument);
|
|
||||||
}
|
|
||||||
|
|
||||||
estate->es_result_relations = resultRelInfos;
|
estate->es_result_relations = resultRelInfos;
|
||||||
estate->es_num_result_relations = numResultRelations;
|
estate->es_num_result_relations = numResultRelations;
|
||||||
/* Initialize to first or only result rel */
|
/* Initialize to first or only result rel */
|
||||||
@ -560,10 +548,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* correct tuple descriptors. (Other SELECT INTO stuff comes later.)
|
* correct tuple descriptors. (Other SELECT INTO stuff comes later.)
|
||||||
*/
|
*/
|
||||||
estate->es_select_into = false;
|
estate->es_select_into = false;
|
||||||
if (operation == CMD_SELECT && parseTree->into != NULL)
|
if (operation == CMD_SELECT && plannedstmt->into != NULL)
|
||||||
{
|
{
|
||||||
estate->es_select_into = true;
|
estate->es_select_into = true;
|
||||||
estate->es_into_oids = interpretOidsOption(parseTree->intoOptions);
|
estate->es_into_oids = interpretOidsOption(plannedstmt->into->options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -572,7 +560,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* While we are at it, build the ExecRowMark list.
|
* While we are at it, build the ExecRowMark list.
|
||||||
*/
|
*/
|
||||||
estate->es_rowMarks = NIL;
|
estate->es_rowMarks = NIL;
|
||||||
foreach(l, parseTree->rowMarks)
|
foreach(l, plannedstmt->rowMarks)
|
||||||
{
|
{
|
||||||
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
||||||
Oid relid = getrelid(rc->rti, rangeTable);
|
Oid relid = getrelid(rc->rti, rangeTable);
|
||||||
@ -600,13 +588,13 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
{
|
{
|
||||||
int nSlots = ExecCountSlotsNode(plan);
|
int nSlots = ExecCountSlotsNode(plan);
|
||||||
|
|
||||||
if (parseTree->resultRelations != NIL)
|
if (plannedstmt->resultRelations != NIL)
|
||||||
nSlots += list_length(parseTree->resultRelations);
|
nSlots += list_length(plannedstmt->resultRelations);
|
||||||
else
|
else
|
||||||
nSlots += 1;
|
nSlots += 1;
|
||||||
if (operation != CMD_SELECT)
|
if (operation != CMD_SELECT)
|
||||||
nSlots++; /* for es_trig_tuple_slot */
|
nSlots++; /* for es_trig_tuple_slot */
|
||||||
if (parseTree->returningLists)
|
if (plannedstmt->returningLists)
|
||||||
nSlots++; /* for RETURNING projection */
|
nSlots++; /* for RETURNING projection */
|
||||||
|
|
||||||
estate->es_tupleTable = ExecCreateTupleTable(nSlots);
|
estate->es_tupleTable = ExecCreateTupleTable(nSlots);
|
||||||
@ -617,7 +605,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* mark EvalPlanQual not active */
|
/* mark EvalPlanQual not active */
|
||||||
estate->es_topPlan = plan;
|
estate->es_plannedstmt = plannedstmt;
|
||||||
estate->es_evalPlanQual = NULL;
|
estate->es_evalPlanQual = NULL;
|
||||||
estate->es_evTupleNull = NULL;
|
estate->es_evTupleNull = NULL;
|
||||||
estate->es_evTuple = NULL;
|
estate->es_evTuple = NULL;
|
||||||
@ -683,7 +671,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* junk filter. Note this is only possible for UPDATE/DELETE, so
|
* junk filter. Note this is only possible for UPDATE/DELETE, so
|
||||||
* we can't be fooled by some needing a filter and some not.
|
* we can't be fooled by some needing a filter and some not.
|
||||||
*/
|
*/
|
||||||
if (parseTree->resultRelations != NIL)
|
if (list_length(plannedstmt->resultRelations) > 1)
|
||||||
{
|
{
|
||||||
PlanState **appendplans;
|
PlanState **appendplans;
|
||||||
int as_nplans;
|
int as_nplans;
|
||||||
@ -772,7 +760,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
/*
|
/*
|
||||||
* Initialize RETURNING projections if needed.
|
* Initialize RETURNING projections if needed.
|
||||||
*/
|
*/
|
||||||
if (parseTree->returningLists)
|
if (plannedstmt->returningLists)
|
||||||
{
|
{
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
@ -782,7 +770,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* We set QueryDesc.tupDesc to be the RETURNING rowtype in this case.
|
* We set QueryDesc.tupDesc to be the RETURNING rowtype in this case.
|
||||||
* We assume all the sublists will generate the same output tupdesc.
|
* We assume all the sublists will generate the same output tupdesc.
|
||||||
*/
|
*/
|
||||||
tupType = ExecTypeFromTL((List *) linitial(parseTree->returningLists),
|
tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
/* Set up a slot for the output of the RETURNING projection(s) */
|
/* Set up a slot for the output of the RETURNING projection(s) */
|
||||||
@ -795,9 +783,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
* Build a projection for each result rel. Note that any SubPlans in
|
* Build a projection for each result rel. Note that any SubPlans in
|
||||||
* the RETURNING lists get attached to the topmost plan node.
|
* the RETURNING lists get attached to the topmost plan node.
|
||||||
*/
|
*/
|
||||||
Assert(list_length(parseTree->returningLists) == estate->es_num_result_relations);
|
Assert(list_length(plannedstmt->returningLists) == estate->es_num_result_relations);
|
||||||
resultRelInfo = estate->es_result_relations;
|
resultRelInfo = estate->es_result_relations;
|
||||||
foreach(l, parseTree->returningLists)
|
foreach(l, plannedstmt->returningLists)
|
||||||
{
|
{
|
||||||
List *rlist = (List *) lfirst(l);
|
List *rlist = (List *) lfirst(l);
|
||||||
List *rliststate;
|
List *rliststate;
|
||||||
@ -2273,14 +2261,14 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
|
|||||||
epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor;
|
epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor;
|
||||||
epqstate->es_into_relation_use_wal = estate->es_into_relation_use_wal;
|
epqstate->es_into_relation_use_wal = estate->es_into_relation_use_wal;
|
||||||
epqstate->es_param_list_info = estate->es_param_list_info;
|
epqstate->es_param_list_info = estate->es_param_list_info;
|
||||||
if (estate->es_topPlan->nParamExec > 0)
|
if (estate->es_plannedstmt->nParamExec > 0)
|
||||||
epqstate->es_param_exec_vals = (ParamExecData *)
|
epqstate->es_param_exec_vals = (ParamExecData *)
|
||||||
palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
|
palloc0(estate->es_plannedstmt->nParamExec * sizeof(ParamExecData));
|
||||||
epqstate->es_rowMarks = estate->es_rowMarks;
|
epqstate->es_rowMarks = estate->es_rowMarks;
|
||||||
epqstate->es_instrument = estate->es_instrument;
|
epqstate->es_instrument = estate->es_instrument;
|
||||||
epqstate->es_select_into = estate->es_select_into;
|
epqstate->es_select_into = estate->es_select_into;
|
||||||
epqstate->es_into_oids = estate->es_into_oids;
|
epqstate->es_into_oids = estate->es_into_oids;
|
||||||
epqstate->es_topPlan = estate->es_topPlan;
|
epqstate->es_plannedstmt = estate->es_plannedstmt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each epqstate must have its own es_evTupleNull state, but all the stack
|
* Each epqstate must have its own es_evTupleNull state, but all the stack
|
||||||
@ -2299,7 +2287,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
|
|||||||
epqstate->es_tupleTable =
|
epqstate->es_tupleTable =
|
||||||
ExecCreateTupleTable(estate->es_tupleTable->size);
|
ExecCreateTupleTable(estate->es_tupleTable->size);
|
||||||
|
|
||||||
epq->planstate = ExecInitNode(estate->es_topPlan, epqstate, 0);
|
epq->planstate = ExecInitNode(estate->es_plannedstmt->planTree, epqstate, 0);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
@ -2365,7 +2353,7 @@ typedef struct
|
|||||||
static void
|
static void
|
||||||
OpenIntoRel(QueryDesc *queryDesc)
|
OpenIntoRel(QueryDesc *queryDesc)
|
||||||
{
|
{
|
||||||
Query *parseTree = queryDesc->parsetree;
|
IntoClause *into = queryDesc->plannedstmt->into;
|
||||||
EState *estate = queryDesc->estate;
|
EState *estate = queryDesc->estate;
|
||||||
Relation intoRelationDesc;
|
Relation intoRelationDesc;
|
||||||
char *intoName;
|
char *intoName;
|
||||||
@ -2377,10 +2365,12 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
DR_intorel *myState;
|
DR_intorel *myState;
|
||||||
|
|
||||||
|
Assert(into);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check consistency of arguments
|
* Check consistency of arguments
|
||||||
*/
|
*/
|
||||||
if (parseTree->intoOnCommit != ONCOMMIT_NOOP && !parseTree->into->istemp)
|
if (into->onCommit != ONCOMMIT_NOOP && !into->rel->istemp)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||||
errmsg("ON COMMIT can only be used on temporary tables")));
|
errmsg("ON COMMIT can only be used on temporary tables")));
|
||||||
@ -2388,8 +2378,8 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|||||||
/*
|
/*
|
||||||
* Find namespace to create in, check its permissions
|
* Find namespace to create in, check its permissions
|
||||||
*/
|
*/
|
||||||
intoName = parseTree->into->relname;
|
intoName = into->rel->relname;
|
||||||
namespaceId = RangeVarGetCreationNamespace(parseTree->into);
|
namespaceId = RangeVarGetCreationNamespace(into->rel);
|
||||||
|
|
||||||
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
|
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
|
||||||
ACL_CREATE);
|
ACL_CREATE);
|
||||||
@ -2401,16 +2391,16 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|||||||
* Select tablespace to use. If not specified, use default_tablespace
|
* Select tablespace to use. If not specified, use default_tablespace
|
||||||
* (which may in turn default to database's default).
|
* (which may in turn default to database's default).
|
||||||
*/
|
*/
|
||||||
if (parseTree->intoTableSpaceName)
|
if (into->tableSpaceName)
|
||||||
{
|
{
|
||||||
tablespaceId = get_tablespace_oid(parseTree->intoTableSpaceName);
|
tablespaceId = get_tablespace_oid(into->tableSpaceName);
|
||||||
if (!OidIsValid(tablespaceId))
|
if (!OidIsValid(tablespaceId))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
errmsg("tablespace \"%s\" does not exist",
|
errmsg("tablespace \"%s\" does not exist",
|
||||||
parseTree->intoTableSpaceName)));
|
into->tableSpaceName)));
|
||||||
}
|
}
|
||||||
else if (parseTree->into->istemp)
|
else if (into->rel->istemp)
|
||||||
{
|
{
|
||||||
tablespaceId = GetTempTablespace();
|
tablespaceId = GetTempTablespace();
|
||||||
}
|
}
|
||||||
@ -2435,7 +2425,7 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|||||||
|
|
||||||
/* Parse and validate any reloptions */
|
/* Parse and validate any reloptions */
|
||||||
reloptions = transformRelOptions((Datum) 0,
|
reloptions = transformRelOptions((Datum) 0,
|
||||||
parseTree->intoOptions,
|
into->options,
|
||||||
true,
|
true,
|
||||||
false);
|
false);
|
||||||
(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
|
(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
|
||||||
@ -2454,7 +2444,7 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
0,
|
0,
|
||||||
parseTree->intoOnCommit,
|
into->onCommit,
|
||||||
reloptions,
|
reloptions,
|
||||||
allowSystemTableMods);
|
allowSystemTableMods);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.144 2007/02/06 17:35:20 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -242,7 +242,7 @@ InternalCreateExecutorState(MemoryContext qcontext, bool is_subquery)
|
|||||||
|
|
||||||
estate->es_per_tuple_exprcontext = NULL;
|
estate->es_per_tuple_exprcontext = NULL;
|
||||||
|
|
||||||
estate->es_topPlan = NULL;
|
estate->es_plannedstmt = NULL;
|
||||||
estate->es_evalPlanQual = NULL;
|
estate->es_evalPlanQual = NULL;
|
||||||
estate->es_evTupleNull = NULL;
|
estate->es_evTupleNull = NULL;
|
||||||
estate->es_evTuple = NULL;
|
estate->es_evTuple = NULL;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.110 2007/02/02 00:02:55 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.111 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -33,8 +33,8 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We have an execution_state record for each query in a function. Each
|
* We have an execution_state record for each query in a function. Each
|
||||||
* record contains a querytree and plantree for its query. If the query
|
* record contains a plantree for its query. If the query is currently in
|
||||||
* is currently in F_EXEC_RUN state then there's a QueryDesc too.
|
* F_EXEC_RUN state then there's a QueryDesc too.
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -45,8 +45,7 @@ typedef struct local_es
|
|||||||
{
|
{
|
||||||
struct local_es *next;
|
struct local_es *next;
|
||||||
ExecStatus status;
|
ExecStatus status;
|
||||||
Query *query;
|
Node *stmt; /* PlannedStmt or utility statement */
|
||||||
Plan *plan;
|
|
||||||
QueryDesc *qd; /* null unless status == RUN */
|
QueryDesc *qd; /* null unless status == RUN */
|
||||||
} execution_state;
|
} execution_state;
|
||||||
|
|
||||||
@ -105,26 +104,30 @@ init_execution_state(List *queryTree_list, bool readonly_func)
|
|||||||
foreach(qtl_item, queryTree_list)
|
foreach(qtl_item, queryTree_list)
|
||||||
{
|
{
|
||||||
Query *queryTree = lfirst(qtl_item);
|
Query *queryTree = lfirst(qtl_item);
|
||||||
Plan *planTree;
|
Node *stmt;
|
||||||
execution_state *newes;
|
execution_state *newes;
|
||||||
|
|
||||||
|
Assert(IsA(queryTree, Query));
|
||||||
|
|
||||||
|
if (queryTree->commandType == CMD_UTILITY)
|
||||||
|
stmt = queryTree->utilityStmt;
|
||||||
|
else
|
||||||
|
stmt = (Node *) pg_plan_query(queryTree, NULL);
|
||||||
|
|
||||||
/* Precheck all commands for validity in a function */
|
/* Precheck all commands for validity in a function */
|
||||||
if (queryTree->commandType == CMD_UTILITY &&
|
if (IsA(stmt, TransactionStmt))
|
||||||
IsA(queryTree->utilityStmt, TransactionStmt))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
/* translator: %s is a SQL statement name */
|
/* translator: %s is a SQL statement name */
|
||||||
errmsg("%s is not allowed in a SQL function",
|
errmsg("%s is not allowed in a SQL function",
|
||||||
CreateQueryTag(queryTree))));
|
CreateCommandTag(stmt))));
|
||||||
|
|
||||||
if (readonly_func && !QueryIsReadOnly(queryTree))
|
if (readonly_func && !CommandIsReadOnly(stmt))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
/* translator: %s is a SQL statement name */
|
/* translator: %s is a SQL statement name */
|
||||||
errmsg("%s is not allowed in a non-volatile function",
|
errmsg("%s is not allowed in a non-volatile function",
|
||||||
CreateQueryTag(queryTree))));
|
CreateCommandTag(stmt))));
|
||||||
|
|
||||||
planTree = pg_plan_query(queryTree, NULL);
|
|
||||||
|
|
||||||
newes = (execution_state *) palloc(sizeof(execution_state));
|
newes = (execution_state *) palloc(sizeof(execution_state));
|
||||||
if (preves)
|
if (preves)
|
||||||
@ -134,8 +137,7 @@ init_execution_state(List *queryTree_list, bool readonly_func)
|
|||||||
|
|
||||||
newes->next = NULL;
|
newes->next = NULL;
|
||||||
newes->status = F_EXEC_START;
|
newes->status = F_EXEC_START;
|
||||||
newes->query = queryTree;
|
newes->stmt = stmt;
|
||||||
newes->plan = planTree;
|
|
||||||
newes->qd = NULL;
|
newes->qd = NULL;
|
||||||
|
|
||||||
preves = newes;
|
preves = newes;
|
||||||
@ -298,10 +300,16 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
|
|||||||
snapshot = CopySnapshot(GetTransactionSnapshot());
|
snapshot = CopySnapshot(GetTransactionSnapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
es->qd = CreateQueryDesc(es->query, es->plan,
|
if (IsA(es->stmt, PlannedStmt))
|
||||||
|
es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
|
||||||
snapshot, InvalidSnapshot,
|
snapshot, InvalidSnapshot,
|
||||||
None_Receiver,
|
None_Receiver,
|
||||||
fcache->paramLI, false);
|
fcache->paramLI, false);
|
||||||
|
else
|
||||||
|
es->qd = CreateUtilityQueryDesc(es->stmt,
|
||||||
|
snapshot,
|
||||||
|
None_Receiver,
|
||||||
|
fcache->paramLI);
|
||||||
|
|
||||||
/* We assume we don't need to set up ActiveSnapshot for ExecutorStart */
|
/* We assume we don't need to set up ActiveSnapshot for ExecutorStart */
|
||||||
|
|
||||||
@ -337,7 +345,7 @@ postquel_getnext(execution_state *es)
|
|||||||
|
|
||||||
if (es->qd->operation == CMD_UTILITY)
|
if (es->qd->operation == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->params,
|
ProcessUtility(es->qd->utilitystmt, es->qd->params,
|
||||||
es->qd->dest, NULL);
|
es->qd->dest, NULL);
|
||||||
result = NULL;
|
result = NULL;
|
||||||
}
|
}
|
||||||
@ -351,7 +359,7 @@ postquel_getnext(execution_state *es)
|
|||||||
*/
|
*/
|
||||||
if (LAST_POSTQUEL_COMMAND(es) &&
|
if (LAST_POSTQUEL_COMMAND(es) &&
|
||||||
es->qd->operation == CMD_SELECT &&
|
es->qd->operation == CMD_SELECT &&
|
||||||
es->qd->parsetree->into == NULL)
|
es->qd->plannedstmt->into == NULL)
|
||||||
count = 1L;
|
count = 1L;
|
||||||
else
|
else
|
||||||
count = 0L;
|
count = 0L;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.169 2007/01/09 22:00:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.170 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -831,8 +831,7 @@ SPI_cursor_open(const char *name, void *plan,
|
|||||||
bool read_only)
|
bool read_only)
|
||||||
{
|
{
|
||||||
_SPI_plan *spiplan = (_SPI_plan *) plan;
|
_SPI_plan *spiplan = (_SPI_plan *) plan;
|
||||||
List *qtlist;
|
List *stmt_list;
|
||||||
List *ptlist;
|
|
||||||
ParamListInfo paramLI;
|
ParamListInfo paramLI;
|
||||||
Snapshot snapshot;
|
Snapshot snapshot;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
@ -846,29 +845,22 @@ SPI_cursor_open(const char *name, void *plan,
|
|||||||
if (!SPI_is_cursor_plan(spiplan))
|
if (!SPI_is_cursor_plan(spiplan))
|
||||||
{
|
{
|
||||||
/* try to give a good error message */
|
/* try to give a good error message */
|
||||||
Query *queryTree;
|
Node *stmt;
|
||||||
|
|
||||||
if (list_length(spiplan->qtlist) != 1)
|
if (list_length(spiplan->stmt_list_list) != 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||||
errmsg("cannot open multi-query plan as cursor")));
|
errmsg("cannot open multi-query plan as cursor")));
|
||||||
queryTree = PortalListGetPrimaryQuery((List *) linitial(spiplan->qtlist));
|
stmt = PortalListGetPrimaryStmt((List *) linitial(spiplan->stmt_list_list));
|
||||||
if (queryTree == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
|
||||||
errmsg("cannot open empty query as cursor")));
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||||
/* translator: %s is name of a SQL command, eg INSERT */
|
/* translator: %s is name of a SQL command, eg INSERT */
|
||||||
errmsg("cannot open %s query as cursor",
|
errmsg("cannot open %s query as cursor",
|
||||||
CreateQueryTag(queryTree))));
|
CreateCommandTag(stmt))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(list_length(spiplan->qtlist) == 1);
|
Assert(list_length(spiplan->stmt_list_list) == 1);
|
||||||
qtlist = (List *) linitial(spiplan->qtlist);
|
stmt_list = (List *) linitial(spiplan->stmt_list_list);
|
||||||
ptlist = spiplan->ptlist;
|
|
||||||
if (list_length(qtlist) != list_length(ptlist))
|
|
||||||
elog(ERROR, "corrupted SPI plan lists");
|
|
||||||
|
|
||||||
/* Reset SPI result (note we deliberately don't touch lastoid) */
|
/* Reset SPI result (note we deliberately don't touch lastoid) */
|
||||||
SPI_processed = 0;
|
SPI_processed = 0;
|
||||||
@ -888,10 +880,9 @@ SPI_cursor_open(const char *name, void *plan,
|
|||||||
portal = CreatePortal(name, false, false);
|
portal = CreatePortal(name, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to portal's memory and copy the parsetrees and plans to there */
|
/* Switch to portal's memory and copy the plans to there */
|
||||||
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
qtlist = copyObject(qtlist);
|
stmt_list = copyObject(stmt_list);
|
||||||
ptlist = copyObject(ptlist);
|
|
||||||
|
|
||||||
/* If the plan has parameters, set them up */
|
/* If the plan has parameters, set them up */
|
||||||
if (spiplan->nargs > 0)
|
if (spiplan->nargs > 0)
|
||||||
@ -934,9 +925,8 @@ SPI_cursor_open(const char *name, void *plan,
|
|||||||
PortalDefineQuery(portal,
|
PortalDefineQuery(portal,
|
||||||
NULL, /* no statement name */
|
NULL, /* no statement name */
|
||||||
spiplan->query,
|
spiplan->query,
|
||||||
CreateQueryTag(PortalListGetPrimaryQuery(qtlist)),
|
CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
|
||||||
qtlist,
|
stmt_list,
|
||||||
ptlist,
|
|
||||||
PortalGetHeapMemory(portal));
|
PortalGetHeapMemory(portal));
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
@ -945,8 +935,9 @@ SPI_cursor_open(const char *name, void *plan,
|
|||||||
* Set up options for portal.
|
* Set up options for portal.
|
||||||
*/
|
*/
|
||||||
portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
|
portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
|
||||||
if (list_length(ptlist) == 1 &&
|
if (list_length(stmt_list) == 1 &&
|
||||||
ExecSupportsBackwardScan((Plan *) linitial(ptlist)))
|
IsA((Node *) linitial(stmt_list), PlannedStmt) &&
|
||||||
|
ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
|
||||||
portal->cursorOptions |= CURSOR_OPT_SCROLL;
|
portal->cursorOptions |= CURSOR_OPT_SCROLL;
|
||||||
else
|
else
|
||||||
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
|
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
|
||||||
@ -1076,10 +1067,10 @@ SPI_is_cursor_plan(void *plan)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_length(spiplan->qtlist) != 1)
|
if (list_length(spiplan->stmt_list_list) != 1)
|
||||||
return false; /* not exactly 1 pre-rewrite command */
|
return false; /* not exactly 1 pre-rewrite command */
|
||||||
|
|
||||||
switch (ChoosePortalStrategy((List *) linitial(spiplan->qtlist)))
|
switch (ChoosePortalStrategy((List *) linitial(spiplan->stmt_list_list)))
|
||||||
{
|
{
|
||||||
case PORTAL_ONE_SELECT:
|
case PORTAL_ONE_SELECT:
|
||||||
case PORTAL_ONE_RETURNING:
|
case PORTAL_ONE_RETURNING:
|
||||||
@ -1257,14 +1248,13 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
|||||||
*
|
*
|
||||||
* At entry, plan->argtypes and plan->nargs must be valid.
|
* At entry, plan->argtypes and plan->nargs must be valid.
|
||||||
*
|
*
|
||||||
* Query and plan lists are stored into *plan.
|
* Result lists are stored into *plan.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
_SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
_SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
||||||
{
|
{
|
||||||
List *raw_parsetree_list;
|
List *raw_parsetree_list;
|
||||||
List *query_list_list;
|
List *stmt_list_list;
|
||||||
List *plan_list;
|
|
||||||
ListCell *list_item;
|
ListCell *list_item;
|
||||||
ErrorContextCallback spierrcontext;
|
ErrorContextCallback spierrcontext;
|
||||||
Oid *argtypes = plan->argtypes;
|
Oid *argtypes = plan->argtypes;
|
||||||
@ -1294,12 +1284,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
|||||||
/*
|
/*
|
||||||
* Do parse analysis and rule rewrite for each raw parsetree.
|
* Do parse analysis and rule rewrite for each raw parsetree.
|
||||||
*
|
*
|
||||||
* We save the querytrees from each raw parsetree as a separate sublist.
|
* We save the results from each raw parsetree as a separate sublist.
|
||||||
* This allows _SPI_execute_plan() to know where the boundaries between
|
* This allows _SPI_execute_plan() to know where the boundaries between
|
||||||
* original queries fall.
|
* original queries fall.
|
||||||
*/
|
*/
|
||||||
query_list_list = NIL;
|
stmt_list_list = NIL;
|
||||||
plan_list = NIL;
|
|
||||||
|
|
||||||
foreach(list_item, raw_parsetree_list)
|
foreach(list_item, raw_parsetree_list)
|
||||||
{
|
{
|
||||||
@ -1308,14 +1297,11 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
|||||||
|
|
||||||
query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
|
query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
|
||||||
|
|
||||||
query_list_list = lappend(query_list_list, query_list);
|
stmt_list_list = lappend(stmt_list_list,
|
||||||
|
|
||||||
plan_list = list_concat(plan_list,
|
|
||||||
pg_plan_queries(query_list, NULL, false));
|
pg_plan_queries(query_list, NULL, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
plan->qtlist = query_list_list;
|
plan->stmt_list_list = stmt_list_list;
|
||||||
plan->ptlist = plan_list;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop the error context stack
|
* Pop the error context stack
|
||||||
@ -1348,9 +1334,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
saveActiveSnapshot = ActiveSnapshot;
|
saveActiveSnapshot = ActiveSnapshot;
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
List *query_list_list = plan->qtlist;
|
ListCell *stmt_list_list_item;
|
||||||
ListCell *plan_list_item = list_head(plan->ptlist);
|
|
||||||
ListCell *query_list_list_item;
|
|
||||||
ErrorContextCallback spierrcontext;
|
ErrorContextCallback spierrcontext;
|
||||||
int nargs = plan->nargs;
|
int nargs = plan->nargs;
|
||||||
ParamListInfo paramLI;
|
ParamListInfo paramLI;
|
||||||
@ -1386,57 +1370,61 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
spierrcontext.previous = error_context_stack;
|
spierrcontext.previous = error_context_stack;
|
||||||
error_context_stack = &spierrcontext;
|
error_context_stack = &spierrcontext;
|
||||||
|
|
||||||
foreach(query_list_list_item, query_list_list)
|
foreach(stmt_list_list_item, plan->stmt_list_list)
|
||||||
{
|
{
|
||||||
List *query_list = lfirst(query_list_list_item);
|
List *stmt_list = (List *) lfirst(stmt_list_list_item);
|
||||||
ListCell *query_list_item;
|
ListCell *stmt_list_item;
|
||||||
|
|
||||||
foreach(query_list_item, query_list)
|
foreach(stmt_list_item, stmt_list)
|
||||||
{
|
{
|
||||||
Query *queryTree = (Query *) lfirst(query_list_item);
|
Node *stmt = (Node *) lfirst(stmt_list_item);
|
||||||
Plan *planTree;
|
bool canSetTag;
|
||||||
QueryDesc *qdesc;
|
QueryDesc *qdesc;
|
||||||
DestReceiver *dest;
|
DestReceiver *dest;
|
||||||
|
|
||||||
planTree = lfirst(plan_list_item);
|
|
||||||
plan_list_item = lnext(plan_list_item);
|
|
||||||
|
|
||||||
_SPI_current->processed = 0;
|
_SPI_current->processed = 0;
|
||||||
_SPI_current->lastoid = InvalidOid;
|
_SPI_current->lastoid = InvalidOid;
|
||||||
_SPI_current->tuptable = NULL;
|
_SPI_current->tuptable = NULL;
|
||||||
|
|
||||||
if (queryTree->commandType == CMD_UTILITY)
|
if (IsA(stmt, PlannedStmt))
|
||||||
{
|
{
|
||||||
if (IsA(queryTree->utilityStmt, CopyStmt))
|
canSetTag = ((PlannedStmt *) stmt)->canSetTag;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
|
/* utilities are canSetTag if only thing in list */
|
||||||
|
canSetTag = (list_length(stmt_list) == 1);
|
||||||
|
|
||||||
if (stmt->filename == NULL)
|
if (IsA(stmt, CopyStmt))
|
||||||
|
{
|
||||||
|
CopyStmt *cstmt = (CopyStmt *) stmt;
|
||||||
|
|
||||||
|
if (cstmt->filename == NULL)
|
||||||
{
|
{
|
||||||
my_res = SPI_ERROR_COPY;
|
my_res = SPI_ERROR_COPY;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
|
else if (IsA(stmt, DeclareCursorStmt) ||
|
||||||
IsA(queryTree->utilityStmt, ClosePortalStmt) ||
|
IsA(stmt, ClosePortalStmt) ||
|
||||||
IsA(queryTree->utilityStmt, FetchStmt))
|
IsA(stmt, FetchStmt))
|
||||||
{
|
{
|
||||||
my_res = SPI_ERROR_CURSOR;
|
my_res = SPI_ERROR_CURSOR;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
else if (IsA(queryTree->utilityStmt, TransactionStmt))
|
else if (IsA(stmt, TransactionStmt))
|
||||||
{
|
{
|
||||||
my_res = SPI_ERROR_TRANSACTION;
|
my_res = SPI_ERROR_TRANSACTION;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_only && !QueryIsReadOnly(queryTree))
|
if (read_only && !CommandIsReadOnly(stmt))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
/* translator: %s is a SQL statement name */
|
/* translator: %s is a SQL statement name */
|
||||||
errmsg("%s is not allowed in a non-volatile function",
|
errmsg("%s is not allowed in a non-volatile function",
|
||||||
CreateQueryTag(queryTree))));
|
CreateCommandTag(stmt))));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If not read-only mode, advance the command counter before
|
* If not read-only mode, advance the command counter before
|
||||||
@ -1445,7 +1433,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
if (!read_only)
|
if (!read_only)
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone,
|
dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (snapshot == InvalidSnapshot)
|
if (snapshot == InvalidSnapshot)
|
||||||
@ -1471,26 +1459,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
ActiveSnapshot->curcid = GetCurrentCommandId();
|
ActiveSnapshot->curcid = GetCurrentCommandId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryTree->commandType == CMD_UTILITY)
|
if (IsA(stmt, PlannedStmt))
|
||||||
{
|
{
|
||||||
ProcessUtility(queryTree->utilityStmt, paramLI,
|
qdesc = CreateQueryDesc((PlannedStmt *) stmt,
|
||||||
dest, NULL);
|
|
||||||
/* Update "processed" if stmt returned tuples */
|
|
||||||
if (_SPI_current->tuptable)
|
|
||||||
_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
|
|
||||||
res = SPI_OK_UTILITY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qdesc = CreateQueryDesc(queryTree, planTree,
|
|
||||||
ActiveSnapshot,
|
ActiveSnapshot,
|
||||||
crosscheck_snapshot,
|
crosscheck_snapshot,
|
||||||
dest,
|
dest,
|
||||||
paramLI, false);
|
paramLI, false);
|
||||||
res = _SPI_pquery(qdesc,
|
res = _SPI_pquery(qdesc, canSetTag ? tcount : 0);
|
||||||
queryTree->canSetTag ? tcount : 0);
|
|
||||||
FreeQueryDesc(qdesc);
|
FreeQueryDesc(qdesc);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessUtility(stmt, paramLI, dest, NULL);
|
||||||
|
/* Update "processed" if stmt returned tuples */
|
||||||
|
if (_SPI_current->tuptable)
|
||||||
|
_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
|
||||||
|
res = SPI_OK_UTILITY;
|
||||||
|
}
|
||||||
FreeSnapshot(ActiveSnapshot);
|
FreeSnapshot(ActiveSnapshot);
|
||||||
ActiveSnapshot = NULL;
|
ActiveSnapshot = NULL;
|
||||||
|
|
||||||
@ -1499,7 +1485,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
* the caller. Be careful to free any tuptables not returned,
|
* the caller. Be careful to free any tuptables not returned,
|
||||||
* to avoid intratransaction memory leak.
|
* to avoid intratransaction memory leak.
|
||||||
*/
|
*/
|
||||||
if (queryTree->canSetTag)
|
if (canSetTag)
|
||||||
{
|
{
|
||||||
my_processed = _SPI_current->processed;
|
my_processed = _SPI_current->processed;
|
||||||
my_lastoid = _SPI_current->lastoid;
|
my_lastoid = _SPI_current->lastoid;
|
||||||
@ -1565,7 +1551,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
|
|||||||
switch (operation)
|
switch (operation)
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
if (queryDesc->parsetree->into) /* select into table? */
|
if (queryDesc->plannedstmt->into) /* select into table? */
|
||||||
res = SPI_OK_SELINTO;
|
res = SPI_OK_SELINTO;
|
||||||
else if (queryDesc->dest->mydest != DestSPI)
|
else if (queryDesc->dest->mydest != DestSPI)
|
||||||
{
|
{
|
||||||
@ -1576,19 +1562,19 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
|
|||||||
res = SPI_OK_SELECT;
|
res = SPI_OK_SELECT;
|
||||||
break;
|
break;
|
||||||
case CMD_INSERT:
|
case CMD_INSERT:
|
||||||
if (queryDesc->parsetree->returningList)
|
if (queryDesc->plannedstmt->returningLists)
|
||||||
res = SPI_OK_INSERT_RETURNING;
|
res = SPI_OK_INSERT_RETURNING;
|
||||||
else
|
else
|
||||||
res = SPI_OK_INSERT;
|
res = SPI_OK_INSERT;
|
||||||
break;
|
break;
|
||||||
case CMD_DELETE:
|
case CMD_DELETE:
|
||||||
if (queryDesc->parsetree->returningList)
|
if (queryDesc->plannedstmt->returningLists)
|
||||||
res = SPI_OK_DELETE_RETURNING;
|
res = SPI_OK_DELETE_RETURNING;
|
||||||
else
|
else
|
||||||
res = SPI_OK_DELETE;
|
res = SPI_OK_DELETE;
|
||||||
break;
|
break;
|
||||||
case CMD_UPDATE:
|
case CMD_UPDATE:
|
||||||
if (queryDesc->parsetree->returningList)
|
if (queryDesc->plannedstmt->returningLists)
|
||||||
res = SPI_OK_UPDATE_RETURNING;
|
res = SPI_OK_UPDATE_RETURNING;
|
||||||
else
|
else
|
||||||
res = SPI_OK_UPDATE;
|
res = SPI_OK_UPDATE;
|
||||||
@ -1611,7 +1597,7 @@ _SPI_pquery(QueryDesc *queryDesc, long tcount)
|
|||||||
_SPI_current->processed = queryDesc->estate->es_processed;
|
_SPI_current->processed = queryDesc->estate->es_processed;
|
||||||
_SPI_current->lastoid = queryDesc->estate->es_lastoid;
|
_SPI_current->lastoid = queryDesc->estate->es_lastoid;
|
||||||
|
|
||||||
if ((res == SPI_OK_SELECT || queryDesc->parsetree->returningList) &&
|
if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->returningLists) &&
|
||||||
queryDesc->dest->mydest == DestSPI)
|
queryDesc->dest->mydest == DestSPI)
|
||||||
{
|
{
|
||||||
if (_SPI_checktuples())
|
if (_SPI_checktuples())
|
||||||
@ -1813,8 +1799,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
|
|||||||
newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
|
newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
|
||||||
newplan->plancxt = plancxt;
|
newplan->plancxt = plancxt;
|
||||||
newplan->query = pstrdup(plan->query);
|
newplan->query = pstrdup(plan->query);
|
||||||
newplan->qtlist = (List *) copyObject(plan->qtlist);
|
newplan->stmt_list_list = (List *) copyObject(plan->stmt_list_list);
|
||||||
newplan->ptlist = (List *) copyObject(plan->ptlist);
|
|
||||||
newplan->nargs = plan->nargs;
|
newplan->nargs = plan->nargs;
|
||||||
if (plan->nargs > 0)
|
if (plan->nargs > 0)
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.366 2007/02/19 02:23:11 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -64,6 +64,27 @@
|
|||||||
* ****************************************************************
|
* ****************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _copyPlannedStmt
|
||||||
|
*/
|
||||||
|
static PlannedStmt *
|
||||||
|
_copyPlannedStmt(PlannedStmt *from)
|
||||||
|
{
|
||||||
|
PlannedStmt *newnode = makeNode(PlannedStmt);
|
||||||
|
|
||||||
|
COPY_SCALAR_FIELD(commandType);
|
||||||
|
COPY_SCALAR_FIELD(canSetTag);
|
||||||
|
COPY_NODE_FIELD(planTree);
|
||||||
|
COPY_NODE_FIELD(rtable);
|
||||||
|
COPY_NODE_FIELD(resultRelations);
|
||||||
|
COPY_NODE_FIELD(into);
|
||||||
|
COPY_NODE_FIELD(returningLists);
|
||||||
|
COPY_NODE_FIELD(rowMarks);
|
||||||
|
COPY_SCALAR_FIELD(nParamExec);
|
||||||
|
|
||||||
|
return newnode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CopyPlanFields
|
* CopyPlanFields
|
||||||
*
|
*
|
||||||
@ -84,7 +105,6 @@ CopyPlanFields(Plan *from, Plan *newnode)
|
|||||||
COPY_NODE_FIELD(initPlan);
|
COPY_NODE_FIELD(initPlan);
|
||||||
COPY_BITMAPSET_FIELD(extParam);
|
COPY_BITMAPSET_FIELD(extParam);
|
||||||
COPY_BITMAPSET_FIELD(allParam);
|
COPY_BITMAPSET_FIELD(allParam);
|
||||||
COPY_SCALAR_FIELD(nParamExec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -698,6 +718,23 @@ _copyRangeVar(RangeVar *from)
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _copyIntoClause
|
||||||
|
*/
|
||||||
|
static IntoClause *
|
||||||
|
_copyIntoClause(IntoClause *from)
|
||||||
|
{
|
||||||
|
IntoClause *newnode = makeNode(IntoClause);
|
||||||
|
|
||||||
|
COPY_NODE_FIELD(rel);
|
||||||
|
COPY_NODE_FIELD(colNames);
|
||||||
|
COPY_NODE_FIELD(options);
|
||||||
|
COPY_SCALAR_FIELD(onCommit);
|
||||||
|
COPY_STRING_FIELD(tableSpaceName);
|
||||||
|
|
||||||
|
return newnode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need a _copyExpr because Expr is an abstract supertype which
|
* We don't need a _copyExpr because Expr is an abstract supertype which
|
||||||
* should never actually get instantiated. Also, since it has no common
|
* should never actually get instantiated. Also, since it has no common
|
||||||
@ -1762,9 +1799,6 @@ _copyQuery(Query *from)
|
|||||||
COPY_NODE_FIELD(utilityStmt);
|
COPY_NODE_FIELD(utilityStmt);
|
||||||
COPY_SCALAR_FIELD(resultRelation);
|
COPY_SCALAR_FIELD(resultRelation);
|
||||||
COPY_NODE_FIELD(into);
|
COPY_NODE_FIELD(into);
|
||||||
COPY_NODE_FIELD(intoOptions);
|
|
||||||
COPY_SCALAR_FIELD(intoOnCommit);
|
|
||||||
COPY_STRING_FIELD(intoTableSpaceName);
|
|
||||||
COPY_SCALAR_FIELD(hasAggs);
|
COPY_SCALAR_FIELD(hasAggs);
|
||||||
COPY_SCALAR_FIELD(hasSubLinks);
|
COPY_SCALAR_FIELD(hasSubLinks);
|
||||||
COPY_NODE_FIELD(rtable);
|
COPY_NODE_FIELD(rtable);
|
||||||
@ -1779,8 +1813,6 @@ _copyQuery(Query *from)
|
|||||||
COPY_NODE_FIELD(limitCount);
|
COPY_NODE_FIELD(limitCount);
|
||||||
COPY_NODE_FIELD(rowMarks);
|
COPY_NODE_FIELD(rowMarks);
|
||||||
COPY_NODE_FIELD(setOperations);
|
COPY_NODE_FIELD(setOperations);
|
||||||
COPY_NODE_FIELD(resultRelations);
|
|
||||||
COPY_NODE_FIELD(returningLists);
|
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -1832,10 +1864,6 @@ _copySelectStmt(SelectStmt *from)
|
|||||||
|
|
||||||
COPY_NODE_FIELD(distinctClause);
|
COPY_NODE_FIELD(distinctClause);
|
||||||
COPY_NODE_FIELD(into);
|
COPY_NODE_FIELD(into);
|
||||||
COPY_NODE_FIELD(intoColNames);
|
|
||||||
COPY_NODE_FIELD(intoOptions);
|
|
||||||
COPY_SCALAR_FIELD(intoOnCommit);
|
|
||||||
COPY_STRING_FIELD(intoTableSpaceName);
|
|
||||||
COPY_NODE_FIELD(targetList);
|
COPY_NODE_FIELD(targetList);
|
||||||
COPY_NODE_FIELD(fromClause);
|
COPY_NODE_FIELD(fromClause);
|
||||||
COPY_NODE_FIELD(whereClause);
|
COPY_NODE_FIELD(whereClause);
|
||||||
@ -2768,9 +2796,6 @@ _copyExecuteStmt(ExecuteStmt *from)
|
|||||||
|
|
||||||
COPY_STRING_FIELD(name);
|
COPY_STRING_FIELD(name);
|
||||||
COPY_NODE_FIELD(into);
|
COPY_NODE_FIELD(into);
|
||||||
COPY_NODE_FIELD(intoOptions);
|
|
||||||
COPY_SCALAR_FIELD(into_on_commit);
|
|
||||||
COPY_STRING_FIELD(into_tbl_space);
|
|
||||||
COPY_NODE_FIELD(params);
|
COPY_NODE_FIELD(params);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
@ -2902,6 +2927,9 @@ copyObject(void *from)
|
|||||||
/*
|
/*
|
||||||
* PLAN NODES
|
* PLAN NODES
|
||||||
*/
|
*/
|
||||||
|
case T_PlannedStmt:
|
||||||
|
retval = _copyPlannedStmt(from);
|
||||||
|
break;
|
||||||
case T_Plan:
|
case T_Plan:
|
||||||
retval = _copyPlan(from);
|
retval = _copyPlan(from);
|
||||||
break;
|
break;
|
||||||
@ -2990,6 +3018,9 @@ copyObject(void *from)
|
|||||||
case T_RangeVar:
|
case T_RangeVar:
|
||||||
retval = _copyRangeVar(from);
|
retval = _copyRangeVar(from);
|
||||||
break;
|
break;
|
||||||
|
case T_IntoClause:
|
||||||
|
retval = _copyIntoClause(from);
|
||||||
|
break;
|
||||||
case T_Var:
|
case T_Var:
|
||||||
retval = _copyVar(from);
|
retval = _copyVar(from);
|
||||||
break;
|
break;
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.298 2007/02/03 14:06:54 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -102,6 +102,18 @@ _equalRangeVar(RangeVar *a, RangeVar *b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
_equalIntoClause(IntoClause *a, IntoClause *b)
|
||||||
|
{
|
||||||
|
COMPARE_NODE_FIELD(rel);
|
||||||
|
COMPARE_NODE_FIELD(colNames);
|
||||||
|
COMPARE_NODE_FIELD(options);
|
||||||
|
COMPARE_SCALAR_FIELD(onCommit);
|
||||||
|
COMPARE_STRING_FIELD(tableSpaceName);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need an _equalExpr because Expr is an abstract supertype which
|
* We don't need an _equalExpr because Expr is an abstract supertype which
|
||||||
* should never actually get instantiated. Also, since it has no common
|
* should never actually get instantiated. Also, since it has no common
|
||||||
@ -690,9 +702,6 @@ _equalQuery(Query *a, Query *b)
|
|||||||
COMPARE_NODE_FIELD(utilityStmt);
|
COMPARE_NODE_FIELD(utilityStmt);
|
||||||
COMPARE_SCALAR_FIELD(resultRelation);
|
COMPARE_SCALAR_FIELD(resultRelation);
|
||||||
COMPARE_NODE_FIELD(into);
|
COMPARE_NODE_FIELD(into);
|
||||||
COMPARE_NODE_FIELD(intoOptions);
|
|
||||||
COMPARE_SCALAR_FIELD(intoOnCommit);
|
|
||||||
COMPARE_STRING_FIELD(intoTableSpaceName);
|
|
||||||
COMPARE_SCALAR_FIELD(hasAggs);
|
COMPARE_SCALAR_FIELD(hasAggs);
|
||||||
COMPARE_SCALAR_FIELD(hasSubLinks);
|
COMPARE_SCALAR_FIELD(hasSubLinks);
|
||||||
COMPARE_NODE_FIELD(rtable);
|
COMPARE_NODE_FIELD(rtable);
|
||||||
@ -707,8 +716,6 @@ _equalQuery(Query *a, Query *b)
|
|||||||
COMPARE_NODE_FIELD(limitCount);
|
COMPARE_NODE_FIELD(limitCount);
|
||||||
COMPARE_NODE_FIELD(rowMarks);
|
COMPARE_NODE_FIELD(rowMarks);
|
||||||
COMPARE_NODE_FIELD(setOperations);
|
COMPARE_NODE_FIELD(setOperations);
|
||||||
COMPARE_NODE_FIELD(resultRelations);
|
|
||||||
COMPARE_NODE_FIELD(returningLists);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -752,10 +759,6 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
|
|||||||
{
|
{
|
||||||
COMPARE_NODE_FIELD(distinctClause);
|
COMPARE_NODE_FIELD(distinctClause);
|
||||||
COMPARE_NODE_FIELD(into);
|
COMPARE_NODE_FIELD(into);
|
||||||
COMPARE_NODE_FIELD(intoColNames);
|
|
||||||
COMPARE_NODE_FIELD(intoOptions);
|
|
||||||
COMPARE_SCALAR_FIELD(intoOnCommit);
|
|
||||||
COMPARE_STRING_FIELD(intoTableSpaceName);
|
|
||||||
COMPARE_NODE_FIELD(targetList);
|
COMPARE_NODE_FIELD(targetList);
|
||||||
COMPARE_NODE_FIELD(fromClause);
|
COMPARE_NODE_FIELD(fromClause);
|
||||||
COMPARE_NODE_FIELD(whereClause);
|
COMPARE_NODE_FIELD(whereClause);
|
||||||
@ -1545,9 +1548,6 @@ _equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b)
|
|||||||
{
|
{
|
||||||
COMPARE_STRING_FIELD(name);
|
COMPARE_STRING_FIELD(name);
|
||||||
COMPARE_NODE_FIELD(into);
|
COMPARE_NODE_FIELD(into);
|
||||||
COMPARE_NODE_FIELD(intoOptions);
|
|
||||||
COMPARE_SCALAR_FIELD(into_on_commit);
|
|
||||||
COMPARE_STRING_FIELD(into_tbl_space);
|
|
||||||
COMPARE_NODE_FIELD(params);
|
COMPARE_NODE_FIELD(params);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1967,6 +1967,9 @@ equal(void *a, void *b)
|
|||||||
case T_RangeVar:
|
case T_RangeVar:
|
||||||
retval = _equalRangeVar(a, b);
|
retval = _equalRangeVar(a, b);
|
||||||
break;
|
break;
|
||||||
|
case T_IntoClause:
|
||||||
|
retval = _equalIntoClause(a, b);
|
||||||
|
break;
|
||||||
case T_Var:
|
case T_Var:
|
||||||
retval = _equalVar(a, b);
|
retval = _equalVar(a, b);
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.299 2007/02/19 07:03:27 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -234,6 +234,22 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
|
|||||||
* Stuff from plannodes.h
|
* Stuff from plannodes.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outPlannedStmt(StringInfo str, PlannedStmt *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("PLANNEDSTMT");
|
||||||
|
|
||||||
|
WRITE_ENUM_FIELD(commandType, CmdType);
|
||||||
|
WRITE_BOOL_FIELD(canSetTag);
|
||||||
|
WRITE_NODE_FIELD(planTree);
|
||||||
|
WRITE_NODE_FIELD(rtable);
|
||||||
|
WRITE_NODE_FIELD(resultRelations);
|
||||||
|
WRITE_NODE_FIELD(into);
|
||||||
|
WRITE_NODE_FIELD(returningLists);
|
||||||
|
WRITE_NODE_FIELD(rowMarks);
|
||||||
|
WRITE_INT_FIELD(nParamExec);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* print the basic stuff of all nodes that inherit from Plan
|
* print the basic stuff of all nodes that inherit from Plan
|
||||||
*/
|
*/
|
||||||
@ -251,7 +267,6 @@ _outPlanInfo(StringInfo str, Plan *node)
|
|||||||
WRITE_NODE_FIELD(initPlan);
|
WRITE_NODE_FIELD(initPlan);
|
||||||
WRITE_BITMAPSET_FIELD(extParam);
|
WRITE_BITMAPSET_FIELD(extParam);
|
||||||
WRITE_BITMAPSET_FIELD(allParam);
|
WRITE_BITMAPSET_FIELD(allParam);
|
||||||
WRITE_INT_FIELD(nParamExec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -635,6 +650,18 @@ _outRangeVar(StringInfo str, RangeVar *node)
|
|||||||
WRITE_NODE_FIELD(alias);
|
WRITE_NODE_FIELD(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outIntoClause(StringInfo str, IntoClause *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("INTOCLAUSE");
|
||||||
|
|
||||||
|
WRITE_NODE_FIELD(rel);
|
||||||
|
WRITE_NODE_FIELD(colNames);
|
||||||
|
WRITE_NODE_FIELD(options);
|
||||||
|
WRITE_ENUM_FIELD(onCommit, OnCommitAction);
|
||||||
|
WRITE_STRING_FIELD(tableSpaceName);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outVar(StringInfo str, Var *node)
|
_outVar(StringInfo str, Var *node)
|
||||||
{
|
{
|
||||||
@ -1245,6 +1272,8 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
|
|||||||
WRITE_NODE_FIELD(glob);
|
WRITE_NODE_FIELD(glob);
|
||||||
WRITE_UINT_FIELD(query_level);
|
WRITE_UINT_FIELD(query_level);
|
||||||
WRITE_NODE_FIELD(join_rel_list);
|
WRITE_NODE_FIELD(join_rel_list);
|
||||||
|
WRITE_NODE_FIELD(resultRelations);
|
||||||
|
WRITE_NODE_FIELD(returningLists);
|
||||||
WRITE_NODE_FIELD(init_plans);
|
WRITE_NODE_FIELD(init_plans);
|
||||||
WRITE_NODE_FIELD(eq_classes);
|
WRITE_NODE_FIELD(eq_classes);
|
||||||
WRITE_NODE_FIELD(canon_pathkeys);
|
WRITE_NODE_FIELD(canon_pathkeys);
|
||||||
@ -1502,10 +1531,6 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
|
|||||||
|
|
||||||
WRITE_NODE_FIELD(distinctClause);
|
WRITE_NODE_FIELD(distinctClause);
|
||||||
WRITE_NODE_FIELD(into);
|
WRITE_NODE_FIELD(into);
|
||||||
WRITE_NODE_FIELD(intoColNames);
|
|
||||||
WRITE_NODE_FIELD(intoOptions);
|
|
||||||
WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction);
|
|
||||||
WRITE_STRING_FIELD(intoTableSpaceName);
|
|
||||||
WRITE_NODE_FIELD(targetList);
|
WRITE_NODE_FIELD(targetList);
|
||||||
WRITE_NODE_FIELD(fromClause);
|
WRITE_NODE_FIELD(fromClause);
|
||||||
WRITE_NODE_FIELD(whereClause);
|
WRITE_NODE_FIELD(whereClause);
|
||||||
@ -1651,9 +1676,6 @@ _outQuery(StringInfo str, Query *node)
|
|||||||
|
|
||||||
WRITE_INT_FIELD(resultRelation);
|
WRITE_INT_FIELD(resultRelation);
|
||||||
WRITE_NODE_FIELD(into);
|
WRITE_NODE_FIELD(into);
|
||||||
WRITE_NODE_FIELD(intoOptions);
|
|
||||||
WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction);
|
|
||||||
WRITE_STRING_FIELD(intoTableSpaceName);
|
|
||||||
WRITE_BOOL_FIELD(hasAggs);
|
WRITE_BOOL_FIELD(hasAggs);
|
||||||
WRITE_BOOL_FIELD(hasSubLinks);
|
WRITE_BOOL_FIELD(hasSubLinks);
|
||||||
WRITE_NODE_FIELD(rtable);
|
WRITE_NODE_FIELD(rtable);
|
||||||
@ -1668,8 +1690,6 @@ _outQuery(StringInfo str, Query *node)
|
|||||||
WRITE_NODE_FIELD(limitCount);
|
WRITE_NODE_FIELD(limitCount);
|
||||||
WRITE_NODE_FIELD(rowMarks);
|
WRITE_NODE_FIELD(rowMarks);
|
||||||
WRITE_NODE_FIELD(setOperations);
|
WRITE_NODE_FIELD(setOperations);
|
||||||
WRITE_NODE_FIELD(resultRelations);
|
|
||||||
WRITE_NODE_FIELD(returningLists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1988,6 +2008,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
appendStringInfoChar(str, '{');
|
appendStringInfoChar(str, '{');
|
||||||
switch (nodeTag(obj))
|
switch (nodeTag(obj))
|
||||||
{
|
{
|
||||||
|
case T_PlannedStmt:
|
||||||
|
_outPlannedStmt(str, obj);
|
||||||
|
break;
|
||||||
case T_Plan:
|
case T_Plan:
|
||||||
_outPlan(str, obj);
|
_outPlan(str, obj);
|
||||||
break;
|
break;
|
||||||
@ -2072,6 +2095,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_RangeVar:
|
case T_RangeVar:
|
||||||
_outRangeVar(str, obj);
|
_outRangeVar(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_IntoClause:
|
||||||
|
_outIntoClause(str, obj);
|
||||||
|
break;
|
||||||
case T_Var:
|
case T_Var:
|
||||||
_outVar(str, obj);
|
_outVar(str, obj);
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.202 2007/02/03 14:06:54 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.203 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Path and Plan nodes do not have any readfuncs support, because we
|
* Path and Plan nodes do not have any readfuncs support, because we
|
||||||
@ -140,9 +140,6 @@ _readQuery(void)
|
|||||||
READ_NODE_FIELD(utilityStmt);
|
READ_NODE_FIELD(utilityStmt);
|
||||||
READ_INT_FIELD(resultRelation);
|
READ_INT_FIELD(resultRelation);
|
||||||
READ_NODE_FIELD(into);
|
READ_NODE_FIELD(into);
|
||||||
READ_NODE_FIELD(intoOptions);
|
|
||||||
READ_ENUM_FIELD(intoOnCommit, OnCommitAction);
|
|
||||||
READ_STRING_FIELD(intoTableSpaceName);
|
|
||||||
READ_BOOL_FIELD(hasAggs);
|
READ_BOOL_FIELD(hasAggs);
|
||||||
READ_BOOL_FIELD(hasSubLinks);
|
READ_BOOL_FIELD(hasSubLinks);
|
||||||
READ_NODE_FIELD(rtable);
|
READ_NODE_FIELD(rtable);
|
||||||
@ -157,8 +154,6 @@ _readQuery(void)
|
|||||||
READ_NODE_FIELD(limitCount);
|
READ_NODE_FIELD(limitCount);
|
||||||
READ_NODE_FIELD(rowMarks);
|
READ_NODE_FIELD(rowMarks);
|
||||||
READ_NODE_FIELD(setOperations);
|
READ_NODE_FIELD(setOperations);
|
||||||
READ_NODE_FIELD(resultRelations);
|
|
||||||
READ_NODE_FIELD(returningLists);
|
|
||||||
|
|
||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
@ -287,6 +282,20 @@ _readRangeVar(void)
|
|||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IntoClause *
|
||||||
|
_readIntoClause(void)
|
||||||
|
{
|
||||||
|
READ_LOCALS(IntoClause);
|
||||||
|
|
||||||
|
READ_NODE_FIELD(rel);
|
||||||
|
READ_NODE_FIELD(colNames);
|
||||||
|
READ_NODE_FIELD(options);
|
||||||
|
READ_ENUM_FIELD(onCommit, OnCommitAction);
|
||||||
|
READ_STRING_FIELD(tableSpaceName);
|
||||||
|
|
||||||
|
READ_DONE();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _readVar
|
* _readVar
|
||||||
*/
|
*/
|
||||||
@ -984,6 +993,8 @@ parseNodeString(void)
|
|||||||
return_value = _readAlias();
|
return_value = _readAlias();
|
||||||
else if (MATCH("RANGEVAR", 8))
|
else if (MATCH("RANGEVAR", 8))
|
||||||
return_value = _readRangeVar();
|
return_value = _readRangeVar();
|
||||||
|
else if (MATCH("INTOCLAUSE", 10))
|
||||||
|
return_value = _readIntoClause();
|
||||||
else if (MATCH("VAR", 3))
|
else if (MATCH("VAR", 3))
|
||||||
return_value = _readVar();
|
return_value = _readVar();
|
||||||
else if (MATCH("CONST", 5))
|
else if (MATCH("CONST", 5))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.159 2007/02/19 07:03:28 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -444,8 +444,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
Query *subquery = rte->subquery;
|
Query *subquery = rte->subquery;
|
||||||
bool *differentTypes;
|
bool *differentTypes;
|
||||||
double tuple_fraction;
|
double tuple_fraction;
|
||||||
|
PlannerInfo *subroot;
|
||||||
List *pathkeys;
|
List *pathkeys;
|
||||||
List *subquery_pathkeys;
|
|
||||||
|
|
||||||
/* We need a workspace for keeping track of set-op type coercions */
|
/* We need a workspace for keeping track of set-op type coercions */
|
||||||
differentTypes = (bool *)
|
differentTypes = (bool *)
|
||||||
@ -520,7 +520,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
rel->subplan = subquery_planner(root->glob, subquery,
|
rel->subplan = subquery_planner(root->glob, subquery,
|
||||||
root->query_level + 1,
|
root->query_level + 1,
|
||||||
tuple_fraction,
|
tuple_fraction,
|
||||||
&subquery_pathkeys);
|
&subroot);
|
||||||
|
|
||||||
/* Copy number of output rows from subplan */
|
/* Copy number of output rows from subplan */
|
||||||
rel->tuples = rel->subplan->plan_rows;
|
rel->tuples = rel->subplan->plan_rows;
|
||||||
@ -529,7 +529,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
set_baserel_size_estimates(root, rel);
|
set_baserel_size_estimates(root, rel);
|
||||||
|
|
||||||
/* Convert subquery pathkeys to outer representation */
|
/* Convert subquery pathkeys to outer representation */
|
||||||
pathkeys = convert_subquery_pathkeys(root, rel, subquery_pathkeys);
|
pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
|
||||||
|
|
||||||
/* Generate appropriate path */
|
/* Generate appropriate path */
|
||||||
add_path(rel, create_subqueryscan_path(rel, pathkeys));
|
add_path(rel, create_subqueryscan_path(rel, pathkeys));
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.27 2007/02/19 07:03:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -453,8 +453,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
|
|||||||
subroot.init_plans = NIL;
|
subroot.init_plans = NIL;
|
||||||
subparse->commandType = CMD_SELECT;
|
subparse->commandType = CMD_SELECT;
|
||||||
subparse->resultRelation = 0;
|
subparse->resultRelation = 0;
|
||||||
subparse->resultRelations = NIL;
|
subparse->returningList = NIL;
|
||||||
subparse->returningLists = NIL;
|
|
||||||
subparse->into = NULL;
|
subparse->into = NULL;
|
||||||
subparse->hasAggs = false;
|
subparse->hasAggs = false;
|
||||||
subparse->groupClause = NIL;
|
subparse->groupClause = NIL;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.213 2007/02/19 07:03:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -79,13 +79,15 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
|
|||||||
* Query optimizer entry point
|
* Query optimizer entry point
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
Plan *
|
PlannedStmt *
|
||||||
planner(Query *parse, bool isCursor, int cursorOptions,
|
planner(Query *parse, bool isCursor, int cursorOptions,
|
||||||
ParamListInfo boundParams)
|
ParamListInfo boundParams)
|
||||||
{
|
{
|
||||||
|
PlannedStmt *result;
|
||||||
PlannerGlobal *glob;
|
PlannerGlobal *glob;
|
||||||
double tuple_fraction;
|
double tuple_fraction;
|
||||||
Plan *result_plan;
|
PlannerInfo *root;
|
||||||
|
Plan *top_plan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up global state for this planner invocation. This data is needed
|
* Set up global state for this planner invocation. This data is needed
|
||||||
@ -117,7 +119,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* primary planning entry point (may recurse for subqueries) */
|
/* primary planning entry point (may recurse for subqueries) */
|
||||||
result_plan = subquery_planner(glob, parse, 1, tuple_fraction, NULL);
|
top_plan = subquery_planner(glob, parse, 1, tuple_fraction, &root);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If creating a plan for a scrollable cursor, make sure it can run
|
* If creating a plan for a scrollable cursor, make sure it can run
|
||||||
@ -125,17 +127,27 @@ planner(Query *parse, bool isCursor, int cursorOptions,
|
|||||||
*/
|
*/
|
||||||
if (isCursor && (cursorOptions & CURSOR_OPT_SCROLL))
|
if (isCursor && (cursorOptions & CURSOR_OPT_SCROLL))
|
||||||
{
|
{
|
||||||
if (!ExecSupportsBackwardScan(result_plan))
|
if (!ExecSupportsBackwardScan(top_plan))
|
||||||
result_plan = materialize_finished_plan(result_plan);
|
top_plan = materialize_finished_plan(top_plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* final cleanup of the plan */
|
/* final cleanup of the plan */
|
||||||
result_plan = set_plan_references(result_plan, parse->rtable);
|
top_plan = set_plan_references(top_plan, parse->rtable);
|
||||||
|
|
||||||
/* executor wants to know total number of Params used overall */
|
/* build the PlannedStmt result */
|
||||||
result_plan->nParamExec = list_length(glob->paramlist);
|
result = makeNode(PlannedStmt);
|
||||||
|
|
||||||
return result_plan;
|
result->commandType = parse->commandType;
|
||||||
|
result->canSetTag = parse->canSetTag;
|
||||||
|
result->planTree = top_plan;
|
||||||
|
result->rtable = parse->rtable;
|
||||||
|
result->resultRelations = root->resultRelations;
|
||||||
|
result->into = parse->into;
|
||||||
|
result->returningLists = root->returningLists;
|
||||||
|
result->rowMarks = parse->rowMarks;
|
||||||
|
result->nParamExec = list_length(glob->paramlist);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -150,8 +162,8 @@ planner(Query *parse, bool isCursor, int cursorOptions,
|
|||||||
* tuple_fraction is the fraction of tuples we expect will be retrieved.
|
* tuple_fraction is the fraction of tuples we expect will be retrieved.
|
||||||
* tuple_fraction is interpreted as explained for grouping_planner, below.
|
* tuple_fraction is interpreted as explained for grouping_planner, below.
|
||||||
*
|
*
|
||||||
* If subquery_pathkeys isn't NULL, it receives a list of pathkeys indicating
|
* If subroot isn't NULL, we pass back the query's final PlannerInfo struct;
|
||||||
* the output sort ordering of the completed plan.
|
* among other things this tells the output sort ordering of the plan.
|
||||||
*
|
*
|
||||||
* Basically, this routine does the stuff that should only be done once
|
* Basically, this routine does the stuff that should only be done once
|
||||||
* per Query object. It then calls grouping_planner. At one time,
|
* per Query object. It then calls grouping_planner. At one time,
|
||||||
@ -168,7 +180,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
|
|||||||
Plan *
|
Plan *
|
||||||
subquery_planner(PlannerGlobal *glob, Query *parse,
|
subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||||
Index level, double tuple_fraction,
|
Index level, double tuple_fraction,
|
||||||
List **subquery_pathkeys)
|
PlannerInfo **subroot)
|
||||||
{
|
{
|
||||||
int saved_plan_id = glob->next_plan_id;
|
int saved_plan_id = glob->next_plan_id;
|
||||||
PlannerInfo *root;
|
PlannerInfo *root;
|
||||||
@ -375,9 +387,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
|||||||
if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
|
if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
|
||||||
SS_finalize_plan(root, plan);
|
SS_finalize_plan(root, plan);
|
||||||
|
|
||||||
/* Return sort ordering info if caller wants it */
|
/* Return internal info if caller wants it */
|
||||||
if (subquery_pathkeys)
|
if (subroot)
|
||||||
*subquery_pathkeys = root->query_pathkeys;
|
*subroot = root;
|
||||||
|
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
@ -593,14 +605,14 @@ inheritance_planner(PlannerInfo *root)
|
|||||||
/* Build list of per-relation RETURNING targetlists */
|
/* Build list of per-relation RETURNING targetlists */
|
||||||
if (parse->returningList)
|
if (parse->returningList)
|
||||||
{
|
{
|
||||||
Assert(list_length(subroot.parse->returningLists) == 1);
|
Assert(list_length(subroot.returningLists) == 1);
|
||||||
returningLists = list_concat(returningLists,
|
returningLists = list_concat(returningLists,
|
||||||
subroot.parse->returningLists);
|
subroot.returningLists);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse->resultRelations = resultRelations;
|
root->resultRelations = resultRelations;
|
||||||
parse->returningLists = returningLists;
|
root->returningLists = returningLists;
|
||||||
|
|
||||||
/* Mark result as unordered (probably unnecessary) */
|
/* Mark result as unordered (probably unnecessary) */
|
||||||
root->query_pathkeys = NIL;
|
root->query_pathkeys = NIL;
|
||||||
@ -1101,8 +1113,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
rlist = set_returning_clause_references(parse->returningList,
|
rlist = set_returning_clause_references(parse->returningList,
|
||||||
result_plan,
|
result_plan,
|
||||||
parse->resultRelation);
|
parse->resultRelation);
|
||||||
parse->returningLists = list_make1(rlist);
|
root->returningLists = list_make1(rlist);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
root->returningLists = NIL;
|
||||||
|
|
||||||
|
/* Compute result-relations list if needed */
|
||||||
|
if (parse->resultRelation)
|
||||||
|
root->resultRelations = list_make1_int(parse->resultRelation);
|
||||||
|
else
|
||||||
|
root->resultRelations = NIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the actual output ordering in query_pathkeys for possible use by
|
* Return the actual output ordering in query_pathkeys for possible use by
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.360 2007/02/01 19:10:27 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.361 2007/02/20 17:32:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2143,11 +2143,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
if (stmt->into)
|
if (stmt->into)
|
||||||
{
|
{
|
||||||
qry->into = stmt->into;
|
qry->into = stmt->into;
|
||||||
if (stmt->intoColNames)
|
if (stmt->into->colNames)
|
||||||
applyColumnNames(qry->targetList, stmt->intoColNames);
|
applyColumnNames(qry->targetList, stmt->into->colNames);
|
||||||
qry->intoOptions = copyObject(stmt->intoOptions);
|
|
||||||
qry->intoOnCommit = stmt->intoOnCommit;
|
|
||||||
qry->intoTableSpaceName = stmt->intoTableSpaceName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qry->rtable = pstate->p_rtable;
|
qry->rtable = pstate->p_rtable;
|
||||||
@ -2315,11 +2312,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
|||||||
if (stmt->into)
|
if (stmt->into)
|
||||||
{
|
{
|
||||||
qry->into = stmt->into;
|
qry->into = stmt->into;
|
||||||
if (stmt->intoColNames)
|
if (stmt->into->colNames)
|
||||||
applyColumnNames(qry->targetList, stmt->intoColNames);
|
applyColumnNames(qry->targetList, stmt->into->colNames);
|
||||||
qry->intoOptions = copyObject(stmt->intoOptions);
|
|
||||||
qry->intoOnCommit = stmt->intoOnCommit;
|
|
||||||
qry->intoTableSpaceName = stmt->intoTableSpaceName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2399,9 +2393,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Find leftmost leaf SelectStmt; extract the one-time-only items from it
|
* Find leftmost leaf SelectStmt; extract the one-time-only items from it
|
||||||
* and from the top-level node. (Most of the INTO options can be
|
* and from the top-level node.
|
||||||
* transferred to the Query immediately, but intoColNames has to be saved
|
|
||||||
* to apply below.)
|
|
||||||
*/
|
*/
|
||||||
leftmostSelect = stmt->larg;
|
leftmostSelect = stmt->larg;
|
||||||
while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
|
while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
|
||||||
@ -2411,10 +2403,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
if (leftmostSelect->into)
|
if (leftmostSelect->into)
|
||||||
{
|
{
|
||||||
qry->into = leftmostSelect->into;
|
qry->into = leftmostSelect->into;
|
||||||
intoColNames = leftmostSelect->intoColNames;
|
intoColNames = leftmostSelect->into->colNames;
|
||||||
qry->intoOptions = copyObject(leftmostSelect->intoOptions);
|
|
||||||
qry->intoOnCommit = leftmostSelect->intoOnCommit;
|
|
||||||
qry->intoTableSpaceName = leftmostSelect->intoTableSpaceName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear this to prevent complaints in transformSetOperationTree() */
|
/* clear this to prevent complaints in transformSetOperationTree() */
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.579 2007/02/03 14:06:54 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.580 2007/02/20 17:32:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -139,6 +139,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
|
|||||||
IndexElem *ielem;
|
IndexElem *ielem;
|
||||||
Alias *alias;
|
Alias *alias;
|
||||||
RangeVar *range;
|
RangeVar *range;
|
||||||
|
IntoClause *into;
|
||||||
A_Indices *aind;
|
A_Indices *aind;
|
||||||
ResTarget *target;
|
ResTarget *target;
|
||||||
PrivTarget *privtarget;
|
PrivTarget *privtarget;
|
||||||
@ -248,7 +249,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
|
|||||||
prep_type_clause
|
prep_type_clause
|
||||||
execute_param_clause using_clause returning_clause
|
execute_param_clause using_clause returning_clause
|
||||||
|
|
||||||
%type <range> into_clause OptTempTableName
|
%type <range> OptTempTableName
|
||||||
|
%type <into> into_clause create_as_target
|
||||||
|
|
||||||
%type <defelt> createfunc_opt_item common_func_opt_item
|
%type <defelt> createfunc_opt_item common_func_opt_item
|
||||||
%type <fun_param> func_arg
|
%type <fun_param> func_arg
|
||||||
@ -2253,8 +2255,7 @@ OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; }
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
CreateAsStmt:
|
CreateAsStmt:
|
||||||
CREATE OptTemp TABLE qualified_name OptCreateAs
|
CREATE OptTemp TABLE create_as_target AS SelectStmt
|
||||||
OptWith OnCommitOption OptTableSpace AS SelectStmt
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* When the SelectStmt is a set-operation tree, we must
|
* When the SelectStmt is a set-operation tree, we must
|
||||||
@ -2263,18 +2264,26 @@ CreateAsStmt:
|
|||||||
* to find it. Similarly, the output column names must
|
* to find it. Similarly, the output column names must
|
||||||
* be attached to that Select's target list.
|
* be attached to that Select's target list.
|
||||||
*/
|
*/
|
||||||
SelectStmt *n = findLeftmostSelect((SelectStmt *) $10);
|
SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
|
||||||
if (n->into != NULL)
|
if (n->into != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("CREATE TABLE AS cannot specify INTO")));
|
errmsg("CREATE TABLE AS cannot specify INTO")));
|
||||||
$4->istemp = $2;
|
$4->rel->istemp = $2;
|
||||||
n->into = $4;
|
n->into = $4;
|
||||||
n->intoColNames = $5;
|
$$ = $6;
|
||||||
n->intoOptions = $6;
|
}
|
||||||
n->intoOnCommit = $7;
|
;
|
||||||
n->intoTableSpaceName = $8;
|
|
||||||
$$ = $10;
|
create_as_target:
|
||||||
|
qualified_name OptCreateAs OptWith OnCommitOption OptTableSpace
|
||||||
|
{
|
||||||
|
$$ = makeNode(IntoClause);
|
||||||
|
$$->rel = $1;
|
||||||
|
$$->colNames = $2;
|
||||||
|
$$->options = $3;
|
||||||
|
$$->onCommit = $4;
|
||||||
|
$$->tableSpaceName = $5;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -5459,19 +5468,15 @@ ExecuteStmt: EXECUTE name execute_param_clause
|
|||||||
n->into = NULL;
|
n->into = NULL;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| CREATE OptTemp TABLE qualified_name OptCreateAs
|
| CREATE OptTemp TABLE create_as_target AS
|
||||||
OptWith OnCommitOption OptTableSpace AS
|
|
||||||
EXECUTE name execute_param_clause
|
EXECUTE name execute_param_clause
|
||||||
{
|
{
|
||||||
ExecuteStmt *n = makeNode(ExecuteStmt);
|
ExecuteStmt *n = makeNode(ExecuteStmt);
|
||||||
n->name = $11;
|
n->name = $7;
|
||||||
n->params = $12;
|
n->params = $8;
|
||||||
$4->istemp = $2;
|
$4->rel->istemp = $2;
|
||||||
n->into = $4;
|
n->into = $4;
|
||||||
n->intoOptions = $6;
|
if ($4->colNames)
|
||||||
n->into_on_commit = $7;
|
|
||||||
n->into_tbl_space = $8;
|
|
||||||
if ($5)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE")));
|
errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE")));
|
||||||
@ -5854,7 +5859,6 @@ simple_select:
|
|||||||
n->distinctClause = $2;
|
n->distinctClause = $2;
|
||||||
n->targetList = $3;
|
n->targetList = $3;
|
||||||
n->into = $4;
|
n->into = $4;
|
||||||
n->intoColNames = NIL;
|
|
||||||
n->fromClause = $5;
|
n->fromClause = $5;
|
||||||
n->whereClause = $6;
|
n->whereClause = $6;
|
||||||
n->groupClause = $7;
|
n->groupClause = $7;
|
||||||
@ -5877,8 +5881,17 @@ simple_select:
|
|||||||
;
|
;
|
||||||
|
|
||||||
into_clause:
|
into_clause:
|
||||||
INTO OptTempTableName { $$ = $2; }
|
INTO OptTempTableName
|
||||||
| /*EMPTY*/ { $$ = NULL; }
|
{
|
||||||
|
$$ = makeNode(IntoClause);
|
||||||
|
$$->rel = $2;
|
||||||
|
$$->colNames = NIL;
|
||||||
|
$$->options = NIL;
|
||||||
|
$$->onCommit = ONCOMMIT_NOOP;
|
||||||
|
$$->tableSpaceName = NULL;
|
||||||
|
}
|
||||||
|
| /*EMPTY*/
|
||||||
|
{ $$ = NULL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.524 2007/02/17 19:33:32 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.525 2007/02/20 17:32:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -165,8 +165,7 @@ static int InteractiveBackend(StringInfo inBuf);
|
|||||||
static int SocketBackend(StringInfo inBuf);
|
static int SocketBackend(StringInfo inBuf);
|
||||||
static int ReadCommand(StringInfo inBuf);
|
static int ReadCommand(StringInfo inBuf);
|
||||||
static List *pg_rewrite_queries(List *querytree_list);
|
static List *pg_rewrite_queries(List *querytree_list);
|
||||||
static bool check_log_statement_raw(List *raw_parsetree_list);
|
static bool check_log_statement(List *stmt_list);
|
||||||
static bool check_log_statement_cooked(List *parsetree_list);
|
|
||||||
static int errdetail_execute(List *raw_parsetree_list);
|
static int errdetail_execute(List *raw_parsetree_list);
|
||||||
static int errdetail_params(ParamListInfo params);
|
static int errdetail_params(ParamListInfo params);
|
||||||
static void start_xact_command(void);
|
static void start_xact_command(void);
|
||||||
@ -659,10 +658,10 @@ pg_rewrite_queries(List *querytree_list)
|
|||||||
|
|
||||||
|
|
||||||
/* Generate a plan for a single already-rewritten query. */
|
/* Generate a plan for a single already-rewritten query. */
|
||||||
Plan *
|
PlannedStmt *
|
||||||
pg_plan_query(Query *querytree, ParamListInfo boundParams)
|
pg_plan_query(Query *querytree, ParamListInfo boundParams)
|
||||||
{
|
{
|
||||||
Plan *plan;
|
PlannedStmt *plan;
|
||||||
|
|
||||||
/* Utility commands have no plans. */
|
/* Utility commands have no plans. */
|
||||||
if (querytree->commandType == CMD_UTILITY)
|
if (querytree->commandType == CMD_UTILITY)
|
||||||
@ -680,7 +679,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
|
|||||||
#ifdef COPY_PARSE_PLAN_TREES
|
#ifdef COPY_PARSE_PLAN_TREES
|
||||||
/* Optional debugging check: pass plan output through copyObject() */
|
/* Optional debugging check: pass plan output through copyObject() */
|
||||||
{
|
{
|
||||||
Plan *new_plan = (Plan *) copyObject(plan);
|
PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* equal() currently does not have routines to compare Plan nodes, so
|
* equal() currently does not have routines to compare Plan nodes, so
|
||||||
@ -715,23 +714,26 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
|
|||||||
* utility statements depend on not having frozen the snapshot yet.
|
* utility statements depend on not having frozen the snapshot yet.
|
||||||
* (We assume that such statements cannot appear together with plannable
|
* (We assume that such statements cannot appear together with plannable
|
||||||
* statements in the rewriter's output.)
|
* statements in the rewriter's output.)
|
||||||
|
*
|
||||||
|
* Normal optimizable statements generate PlannedStmt entries in the result
|
||||||
|
* list. Utility statements are simply represented by their statement nodes.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
pg_plan_queries(List *querytrees, ParamListInfo boundParams,
|
pg_plan_queries(List *querytrees, ParamListInfo boundParams,
|
||||||
bool needSnapshot)
|
bool needSnapshot)
|
||||||
{
|
{
|
||||||
List *plan_list = NIL;
|
List *stmt_list = NIL;
|
||||||
ListCell *query_list;
|
ListCell *query_list;
|
||||||
|
|
||||||
foreach(query_list, querytrees)
|
foreach(query_list, querytrees)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(query_list);
|
Query *query = (Query *) lfirst(query_list);
|
||||||
Plan *plan;
|
Node *stmt;
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (query->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
/* Utility commands have no plans. */
|
/* Utility commands have no plans. */
|
||||||
plan = NULL;
|
stmt = query->utilityStmt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -740,13 +742,13 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams,
|
|||||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||||
needSnapshot = false;
|
needSnapshot = false;
|
||||||
}
|
}
|
||||||
plan = pg_plan_query(query, boundParams);
|
stmt = (Node *) pg_plan_query(query, boundParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
plan_list = lappend(plan_list, plan);
|
stmt_list = lappend(stmt_list, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return plan_list;
|
return stmt_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -817,7 +819,7 @@ exec_simple_query(const char *query_string)
|
|||||||
parsetree_list = pg_parse_query(query_string);
|
parsetree_list = pg_parse_query(query_string);
|
||||||
|
|
||||||
/* Log immediately if dictated by log_statement */
|
/* Log immediately if dictated by log_statement */
|
||||||
if (check_log_statement_raw(parsetree_list))
|
if (check_log_statement(parsetree_list))
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("statement: %s", query_string),
|
(errmsg("statement: %s", query_string),
|
||||||
@ -905,7 +907,6 @@ exec_simple_query(const char *query_string)
|
|||||||
NULL,
|
NULL,
|
||||||
query_string,
|
query_string,
|
||||||
commandTag,
|
commandTag,
|
||||||
querytree_list,
|
|
||||||
plantree_list,
|
plantree_list,
|
||||||
MessageContext);
|
MessageContext);
|
||||||
|
|
||||||
@ -1050,9 +1051,10 @@ exec_parse_message(const char *query_string, /* string to execute */
|
|||||||
List *parsetree_list;
|
List *parsetree_list;
|
||||||
const char *commandTag;
|
const char *commandTag;
|
||||||
List *querytree_list,
|
List *querytree_list,
|
||||||
*plantree_list,
|
*stmt_list,
|
||||||
*param_list;
|
*param_list;
|
||||||
bool is_named;
|
bool is_named;
|
||||||
|
bool fully_planned;
|
||||||
bool save_log_statement_stats = log_statement_stats;
|
bool save_log_statement_stats = log_statement_stats;
|
||||||
char msec_str[32];
|
char msec_str[32];
|
||||||
|
|
||||||
@ -1202,17 +1204,23 @@ exec_parse_message(const char *query_string, /* string to execute */
|
|||||||
* planning until Bind. Otherwise do it now.
|
* planning until Bind. Otherwise do it now.
|
||||||
*/
|
*/
|
||||||
if (!is_named && numParams > 0)
|
if (!is_named && numParams > 0)
|
||||||
plantree_list = NIL;
|
{
|
||||||
|
stmt_list = querytree_list;
|
||||||
|
fully_planned = false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
plantree_list = pg_plan_queries(querytree_list, NULL, true);
|
{
|
||||||
|
stmt_list = pg_plan_queries(querytree_list, NULL, true);
|
||||||
|
fully_planned = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Empty input string. This is legal. */
|
/* Empty input string. This is legal. */
|
||||||
commandTag = NULL;
|
commandTag = NULL;
|
||||||
querytree_list = NIL;
|
stmt_list = NIL;
|
||||||
plantree_list = NIL;
|
|
||||||
param_list = NIL;
|
param_list = NIL;
|
||||||
|
fully_planned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we got a cancel signal in analysis or planning, quit */
|
/* If we got a cancel signal in analysis or planning, quit */
|
||||||
@ -1226,9 +1234,9 @@ exec_parse_message(const char *query_string, /* string to execute */
|
|||||||
StorePreparedStatement(stmt_name,
|
StorePreparedStatement(stmt_name,
|
||||||
query_string,
|
query_string,
|
||||||
commandTag,
|
commandTag,
|
||||||
querytree_list,
|
stmt_list,
|
||||||
plantree_list,
|
|
||||||
param_list,
|
param_list,
|
||||||
|
fully_planned,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1240,9 +1248,9 @@ exec_parse_message(const char *query_string, /* string to execute */
|
|||||||
pstmt->query_string = pstrdup(query_string);
|
pstmt->query_string = pstrdup(query_string);
|
||||||
/* the rest is there already */
|
/* the rest is there already */
|
||||||
pstmt->commandTag = commandTag;
|
pstmt->commandTag = commandTag;
|
||||||
pstmt->query_list = querytree_list;
|
pstmt->stmt_list = stmt_list;
|
||||||
pstmt->plan_list = plantree_list;
|
|
||||||
pstmt->argtype_list = param_list;
|
pstmt->argtype_list = param_list;
|
||||||
|
pstmt->fully_planned = fully_planned;
|
||||||
pstmt->from_sql = false;
|
pstmt->from_sql = false;
|
||||||
pstmt->context = unnamed_stmt_context;
|
pstmt->context = unnamed_stmt_context;
|
||||||
/* Now the unnamed statement is complete and valid */
|
/* Now the unnamed statement is complete and valid */
|
||||||
@ -1393,7 +1401,7 @@ exec_bind_message(StringInfo input_message)
|
|||||||
* functions.
|
* functions.
|
||||||
*/
|
*/
|
||||||
if (IsAbortedTransactionBlockState() &&
|
if (IsAbortedTransactionBlockState() &&
|
||||||
(!IsTransactionExitStmtList(pstmt->query_list) ||
|
(!IsTransactionExitStmtList(pstmt->stmt_list) ||
|
||||||
numParams != 0))
|
numParams != 0))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||||
@ -1581,22 +1589,21 @@ exec_bind_message(StringInfo input_message)
|
|||||||
* portal's queryContext becomes its own heap context rather than the
|
* portal's queryContext becomes its own heap context rather than the
|
||||||
* prepared statement's context. FIXME someday
|
* prepared statement's context. FIXME someday
|
||||||
*/
|
*/
|
||||||
if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
|
if (pstmt->fully_planned)
|
||||||
|
{
|
||||||
|
plan_list = pstmt->stmt_list;
|
||||||
|
qContext = pstmt->context;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
|
||||||
qContext = PortalGetHeapMemory(portal);
|
qContext = PortalGetHeapMemory(portal);
|
||||||
oldContext = MemoryContextSwitchTo(qContext);
|
oldContext = MemoryContextSwitchTo(qContext);
|
||||||
query_list = copyObject(pstmt->query_list);
|
query_list = copyObject(pstmt->stmt_list);
|
||||||
plan_list = pg_plan_queries(query_list, params, true);
|
plan_list = pg_plan_queries(query_list, params, true);
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
query_list = pstmt->query_list;
|
|
||||||
plan_list = pstmt->plan_list;
|
|
||||||
qContext = pstmt->context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define portal and start execution.
|
* Define portal and start execution.
|
||||||
@ -1605,7 +1612,6 @@ exec_bind_message(StringInfo input_message)
|
|||||||
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
|
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
|
||||||
pstmt->query_string,
|
pstmt->query_string,
|
||||||
pstmt->commandTag,
|
pstmt->commandTag,
|
||||||
query_list,
|
|
||||||
plan_list,
|
plan_list,
|
||||||
qContext);
|
qContext);
|
||||||
|
|
||||||
@ -1688,13 +1694,13 @@ exec_execute_message(const char *portal_name, long max_rows)
|
|||||||
*/
|
*/
|
||||||
if (portal->commandTag == NULL)
|
if (portal->commandTag == NULL)
|
||||||
{
|
{
|
||||||
Assert(portal->parseTrees == NIL);
|
Assert(portal->stmts == NIL);
|
||||||
NullCommand(dest);
|
NullCommand(dest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Does the portal contain a transaction command? */
|
/* Does the portal contain a transaction command? */
|
||||||
is_xact_command = IsTransactionStmtList(portal->parseTrees);
|
is_xact_command = IsTransactionStmtList(portal->stmts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must copy the sourceText and prepStmtName into MessageContext in
|
* We must copy the sourceText and prepStmtName into MessageContext in
|
||||||
@ -1760,7 +1766,7 @@ exec_execute_message(const char *portal_name, long max_rows)
|
|||||||
execute_is_fetch = !portal->atStart;
|
execute_is_fetch = !portal->atStart;
|
||||||
|
|
||||||
/* Log immediately if dictated by log_statement */
|
/* Log immediately if dictated by log_statement */
|
||||||
if (check_log_statement_cooked(portal->parseTrees))
|
if (check_log_statement(portal->stmts))
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("%s %s%s%s%s%s",
|
(errmsg("%s %s%s%s%s%s",
|
||||||
@ -1781,7 +1787,7 @@ exec_execute_message(const char *portal_name, long max_rows)
|
|||||||
* actually run are those containing COMMIT or ROLLBACK commands.
|
* actually run are those containing COMMIT or ROLLBACK commands.
|
||||||
*/
|
*/
|
||||||
if (IsAbortedTransactionBlockState() &&
|
if (IsAbortedTransactionBlockState() &&
|
||||||
!IsTransactionExitStmtList(portal->parseTrees))
|
!IsTransactionExitStmtList(portal->stmts))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||||
errmsg("current transaction is aborted, "
|
errmsg("current transaction is aborted, "
|
||||||
@ -1865,15 +1871,16 @@ exec_execute_message(const char *portal_name, long max_rows)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check_log_statement_raw
|
* check_log_statement
|
||||||
* Determine whether command should be logged because of log_statement
|
* Determine whether command should be logged because of log_statement
|
||||||
*
|
*
|
||||||
* raw_parsetree_list is the raw grammar output
|
* parsetree_list can be either raw grammar output or a list of planned
|
||||||
|
* statements
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
check_log_statement_raw(List *raw_parsetree_list)
|
check_log_statement(List *stmt_list)
|
||||||
{
|
{
|
||||||
ListCell *parsetree_item;
|
ListCell *stmt_item;
|
||||||
|
|
||||||
if (log_statement == LOGSTMT_NONE)
|
if (log_statement == LOGSTMT_NONE)
|
||||||
return false;
|
return false;
|
||||||
@ -1881,37 +1888,11 @@ check_log_statement_raw(List *raw_parsetree_list)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Else we have to inspect the statement(s) to see whether to log */
|
/* Else we have to inspect the statement(s) to see whether to log */
|
||||||
foreach(parsetree_item, raw_parsetree_list)
|
foreach(stmt_item, stmt_list)
|
||||||
{
|
{
|
||||||
Node *parsetree = (Node *) lfirst(parsetree_item);
|
Node *stmt = (Node *) lfirst(stmt_item);
|
||||||
|
|
||||||
if (GetCommandLogLevel(parsetree) <= log_statement)
|
if (GetCommandLogLevel(stmt) <= log_statement)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check_log_statement_cooked
|
|
||||||
* As above, but work from already-analyzed querytrees
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
check_log_statement_cooked(List *parsetree_list)
|
|
||||||
{
|
|
||||||
ListCell *parsetree_item;
|
|
||||||
|
|
||||||
if (log_statement == LOGSTMT_NONE)
|
|
||||||
return false;
|
|
||||||
if (log_statement == LOGSTMT_ALL)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* Else we have to inspect the statement(s) to see whether to log */
|
|
||||||
foreach(parsetree_item, parsetree_list)
|
|
||||||
{
|
|
||||||
Query *parsetree = (Query *) lfirst(parsetree_item);
|
|
||||||
|
|
||||||
if (GetQueryLogLevel(parsetree) <= log_statement)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2259,6 +2240,7 @@ finish_xact_command(void)
|
|||||||
* ones that we allow in transaction-aborted state.
|
* ones that we allow in transaction-aborted state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Test a bare parsetree */
|
||||||
static bool
|
static bool
|
||||||
IsTransactionExitStmt(Node *parsetree)
|
IsTransactionExitStmt(Node *parsetree)
|
||||||
{
|
{
|
||||||
@ -2275,29 +2257,45 @@ IsTransactionExitStmt(Node *parsetree)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test a list that might contain Query nodes or bare parsetrees */
|
||||||
static bool
|
static bool
|
||||||
IsTransactionExitStmtList(List *parseTrees)
|
IsTransactionExitStmtList(List *parseTrees)
|
||||||
{
|
{
|
||||||
if (list_length(parseTrees) == 1)
|
if (list_length(parseTrees) == 1)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) linitial(parseTrees);
|
Node *stmt = (Node *) linitial(parseTrees);
|
||||||
|
|
||||||
|
if (IsA(stmt, Query))
|
||||||
|
{
|
||||||
|
Query *query = (Query *) stmt;
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY &&
|
if (query->commandType == CMD_UTILITY &&
|
||||||
IsTransactionExitStmt(query->utilityStmt))
|
IsTransactionExitStmt(query->utilityStmt))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (IsTransactionExitStmt(stmt))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test a list that might contain Query nodes or bare parsetrees */
|
||||||
static bool
|
static bool
|
||||||
IsTransactionStmtList(List *parseTrees)
|
IsTransactionStmtList(List *parseTrees)
|
||||||
{
|
{
|
||||||
if (list_length(parseTrees) == 1)
|
if (list_length(parseTrees) == 1)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) linitial(parseTrees);
|
Node *stmt = (Node *) linitial(parseTrees);
|
||||||
|
|
||||||
|
if (IsA(stmt, Query))
|
||||||
|
{
|
||||||
|
Query *query = (Query *) stmt;
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY &&
|
if (query->commandType == CMD_UTILITY &&
|
||||||
query->utilityStmt && IsA(query->utilityStmt, TransactionStmt))
|
IsA(query->utilityStmt, TransactionStmt))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (IsA(stmt, TransactionStmt))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.113 2007/02/18 19:49:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -32,8 +32,7 @@
|
|||||||
Portal ActivePortal = NULL;
|
Portal ActivePortal = NULL;
|
||||||
|
|
||||||
|
|
||||||
static void ProcessQuery(Query *parsetree,
|
static void ProcessQuery(PlannedStmt *plan,
|
||||||
Plan *plan,
|
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
@ -42,7 +41,7 @@ static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
|
|||||||
DestReceiver *dest);
|
DestReceiver *dest);
|
||||||
static long PortalRunSelect(Portal portal, bool forward, long count,
|
static long PortalRunSelect(Portal portal, bool forward, long count,
|
||||||
DestReceiver *dest);
|
DestReceiver *dest);
|
||||||
static void PortalRunUtility(Portal portal, Query *query,
|
static void PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
static void PortalRunMulti(Portal portal,
|
static void PortalRunMulti(Portal portal,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
@ -58,8 +57,7 @@ static void DoPortalRewind(Portal portal);
|
|||||||
* CreateQueryDesc
|
* CreateQueryDesc
|
||||||
*/
|
*/
|
||||||
QueryDesc *
|
QueryDesc *
|
||||||
CreateQueryDesc(Query *parsetree,
|
CreateQueryDesc(PlannedStmt *plannedstmt,
|
||||||
Plan *plantree,
|
|
||||||
Snapshot snapshot,
|
Snapshot snapshot,
|
||||||
Snapshot crosscheck_snapshot,
|
Snapshot crosscheck_snapshot,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
@ -68,9 +66,9 @@ CreateQueryDesc(Query *parsetree,
|
|||||||
{
|
{
|
||||||
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
|
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
|
||||||
|
|
||||||
qd->operation = parsetree->commandType; /* operation */
|
qd->operation = plannedstmt->commandType; /* operation */
|
||||||
qd->parsetree = parsetree; /* parse tree */
|
qd->plannedstmt = plannedstmt; /* plan */
|
||||||
qd->plantree = plantree; /* plan */
|
qd->utilitystmt = NULL;
|
||||||
qd->snapshot = snapshot; /* snapshot */
|
qd->snapshot = snapshot; /* snapshot */
|
||||||
qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */
|
qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */
|
||||||
qd->dest = dest; /* output dest */
|
qd->dest = dest; /* output dest */
|
||||||
@ -85,6 +83,34 @@ CreateQueryDesc(Query *parsetree,
|
|||||||
return qd;
|
return qd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreateUtilityQueryDesc
|
||||||
|
*/
|
||||||
|
QueryDesc *
|
||||||
|
CreateUtilityQueryDesc(Node *utilitystmt,
|
||||||
|
Snapshot snapshot,
|
||||||
|
DestReceiver *dest,
|
||||||
|
ParamListInfo params)
|
||||||
|
{
|
||||||
|
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
|
||||||
|
|
||||||
|
qd->operation = CMD_UTILITY; /* operation */
|
||||||
|
qd->plannedstmt = NULL;
|
||||||
|
qd->utilitystmt = utilitystmt; /* utility command */
|
||||||
|
qd->snapshot = snapshot; /* snapshot */
|
||||||
|
qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
|
||||||
|
qd->dest = dest; /* output dest */
|
||||||
|
qd->params = params; /* parameter values passed into query */
|
||||||
|
qd->doInstrument = false; /* uninteresting for utilities */
|
||||||
|
|
||||||
|
/* null these fields until set by ExecutorStart */
|
||||||
|
qd->tupDesc = NULL;
|
||||||
|
qd->estate = NULL;
|
||||||
|
qd->planstate = NULL;
|
||||||
|
|
||||||
|
return qd;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FreeQueryDesc
|
* FreeQueryDesc
|
||||||
*/
|
*/
|
||||||
@ -103,7 +129,6 @@ FreeQueryDesc(QueryDesc *qdesc)
|
|||||||
* Execute a single plannable query within a PORTAL_MULTI_QUERY
|
* Execute a single plannable query within a PORTAL_MULTI_QUERY
|
||||||
* or PORTAL_ONE_RETURNING portal
|
* or PORTAL_ONE_RETURNING portal
|
||||||
*
|
*
|
||||||
* parsetree: the query tree
|
|
||||||
* plan: the plan tree for the query
|
* plan: the plan tree for the query
|
||||||
* params: any parameters needed
|
* params: any parameters needed
|
||||||
* dest: where to send results
|
* dest: where to send results
|
||||||
@ -116,13 +141,11 @@ FreeQueryDesc(QueryDesc *qdesc)
|
|||||||
* error; otherwise the executor's memory usage will be leaked.
|
* error; otherwise the executor's memory usage will be leaked.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ProcessQuery(Query *parsetree,
|
ProcessQuery(PlannedStmt *plan,
|
||||||
Plan *plan,
|
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
int operation = parsetree->commandType;
|
|
||||||
QueryDesc *queryDesc;
|
QueryDesc *queryDesc;
|
||||||
|
|
||||||
ereport(DEBUG3,
|
ereport(DEBUG3,
|
||||||
@ -137,7 +160,7 @@ ProcessQuery(Query *parsetree,
|
|||||||
/*
|
/*
|
||||||
* Create the QueryDesc object
|
* Create the QueryDesc object
|
||||||
*/
|
*/
|
||||||
queryDesc = CreateQueryDesc(parsetree, plan,
|
queryDesc = CreateQueryDesc(plan,
|
||||||
ActiveSnapshot, InvalidSnapshot,
|
ActiveSnapshot, InvalidSnapshot,
|
||||||
dest, params, false);
|
dest, params, false);
|
||||||
|
|
||||||
@ -163,7 +186,7 @@ ProcessQuery(Query *parsetree,
|
|||||||
{
|
{
|
||||||
Oid lastOid;
|
Oid lastOid;
|
||||||
|
|
||||||
switch (operation)
|
switch (queryDesc->operation)
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
strcpy(completionTag, "SELECT");
|
strcpy(completionTag, "SELECT");
|
||||||
@ -206,26 +229,33 @@ ProcessQuery(Query *parsetree,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ChoosePortalStrategy
|
* ChoosePortalStrategy
|
||||||
* Select portal execution strategy given the intended query list.
|
* Select portal execution strategy given the intended statement list.
|
||||||
|
*
|
||||||
|
* The list elements can be Querys, PlannedStmts, or utility statements.
|
||||||
|
* That's more general than portals need, but we use this for prepared
|
||||||
|
* statements as well.
|
||||||
*
|
*
|
||||||
* See the comments in portal.h.
|
* See the comments in portal.h.
|
||||||
*/
|
*/
|
||||||
PortalStrategy
|
PortalStrategy
|
||||||
ChoosePortalStrategy(List *parseTrees)
|
ChoosePortalStrategy(List *stmts)
|
||||||
{
|
{
|
||||||
int nSetTag;
|
int nSetTag;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
|
* PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
|
||||||
* single-Query-struct case, since there are no rewrite rules that can add
|
* single-statement case, since there are no rewrite rules that can add
|
||||||
* auxiliary queries to a SELECT or a utility command.
|
* auxiliary queries to a SELECT or a utility command.
|
||||||
*/
|
*/
|
||||||
if (list_length(parseTrees) == 1)
|
if (list_length(stmts) == 1)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) linitial(parseTrees);
|
Node *stmt = (Node *) linitial(stmts);
|
||||||
|
|
||||||
|
if (IsA(stmt, Query))
|
||||||
|
{
|
||||||
|
Query *query = (Query *) stmt;
|
||||||
|
|
||||||
Assert(IsA(query, Query));
|
|
||||||
if (query->canSetTag)
|
if (query->canSetTag)
|
||||||
{
|
{
|
||||||
if (query->commandType == CMD_SELECT &&
|
if (query->commandType == CMD_SELECT &&
|
||||||
@ -241,6 +271,26 @@ ChoosePortalStrategy(List *parseTrees)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (IsA(stmt, PlannedStmt))
|
||||||
|
{
|
||||||
|
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||||
|
|
||||||
|
if (pstmt->canSetTag)
|
||||||
|
{
|
||||||
|
if (pstmt->commandType == CMD_SELECT &&
|
||||||
|
pstmt->into == NULL)
|
||||||
|
return PORTAL_ONE_SELECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* must be a utility command; assume it's canSetTag */
|
||||||
|
if (UtilityReturnsTuples(stmt))
|
||||||
|
return PORTAL_UTIL_SELECT;
|
||||||
|
/* it can't be ONE_RETURNING, so give up */
|
||||||
|
return PORTAL_MULTI_QUERY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
|
* PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
|
||||||
@ -248,11 +298,14 @@ ChoosePortalStrategy(List *parseTrees)
|
|||||||
* it has a RETURNING list.
|
* it has a RETURNING list.
|
||||||
*/
|
*/
|
||||||
nSetTag = 0;
|
nSetTag = 0;
|
||||||
foreach(lc, parseTrees)
|
foreach(lc, stmts)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(lc);
|
Node *stmt = (Node *) lfirst(lc);
|
||||||
|
|
||||||
|
if (IsA(stmt, Query))
|
||||||
|
{
|
||||||
|
Query *query = (Query *) stmt;
|
||||||
|
|
||||||
Assert(IsA(query, Query));
|
|
||||||
if (query->canSetTag)
|
if (query->canSetTag)
|
||||||
{
|
{
|
||||||
if (++nSetTag > 1)
|
if (++nSetTag > 1)
|
||||||
@ -261,6 +314,20 @@ ChoosePortalStrategy(List *parseTrees)
|
|||||||
return PORTAL_MULTI_QUERY; /* no need to look further */
|
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (IsA(stmt, PlannedStmt))
|
||||||
|
{
|
||||||
|
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||||
|
|
||||||
|
if (pstmt->canSetTag)
|
||||||
|
{
|
||||||
|
if (++nSetTag > 1)
|
||||||
|
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||||
|
if (pstmt->returningLists == NIL)
|
||||||
|
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* otherwise, utility command, assumed not canSetTag */
|
||||||
|
}
|
||||||
if (nSetTag == 1)
|
if (nSetTag == 1)
|
||||||
return PORTAL_ONE_RETURNING;
|
return PORTAL_ONE_RETURNING;
|
||||||
|
|
||||||
@ -274,49 +341,85 @@ ChoosePortalStrategy(List *parseTrees)
|
|||||||
* Returns NIL if the portal doesn't have a determinable targetlist.
|
* Returns NIL if the portal doesn't have a determinable targetlist.
|
||||||
*
|
*
|
||||||
* Note: do not modify the result.
|
* Note: do not modify the result.
|
||||||
*
|
|
||||||
* XXX be careful to keep this in sync with FetchPreparedStatementTargetList,
|
|
||||||
* and with UtilityReturnsTuples.
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
FetchPortalTargetList(Portal portal)
|
FetchPortalTargetList(Portal portal)
|
||||||
{
|
{
|
||||||
if (portal->strategy == PORTAL_ONE_SELECT)
|
/* no point in looking if we determined it doesn't return tuples */
|
||||||
return ((Query *) linitial(portal->parseTrees))->targetList;
|
if (portal->strategy == PORTAL_MULTI_QUERY)
|
||||||
if (portal->strategy == PORTAL_ONE_RETURNING)
|
return NIL;
|
||||||
return (PortalGetPrimaryQuery(portal))->returningList;
|
/* get the primary statement and find out what it returns */
|
||||||
if (portal->strategy == PORTAL_UTIL_SELECT)
|
return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
|
||||||
{
|
}
|
||||||
Node *utilityStmt;
|
|
||||||
|
|
||||||
utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt;
|
/*
|
||||||
switch (nodeTag(utilityStmt))
|
* FetchStatementTargetList
|
||||||
|
* Given a statement that returns tuples, extract the query targetlist.
|
||||||
|
* Returns NIL if the statement doesn't have a determinable targetlist.
|
||||||
|
*
|
||||||
|
* This can be applied to a Query, a PlannedStmt, or a utility statement.
|
||||||
|
* That's more general than portals need, but we use this for prepared
|
||||||
|
* statements as well.
|
||||||
|
*
|
||||||
|
* Note: do not modify the result.
|
||||||
|
*
|
||||||
|
* XXX be careful to keep this in sync with UtilityReturnsTuples.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
FetchStatementTargetList(Node *stmt)
|
||||||
{
|
{
|
||||||
case T_FetchStmt:
|
if (stmt == NULL)
|
||||||
|
return NIL;
|
||||||
|
if (IsA(stmt, Query))
|
||||||
{
|
{
|
||||||
FetchStmt *substmt = (FetchStmt *) utilityStmt;
|
Query *query = (Query *) stmt;
|
||||||
|
|
||||||
|
if (query->commandType == CMD_UTILITY &&
|
||||||
|
query->utilityStmt != NULL)
|
||||||
|
{
|
||||||
|
/* transfer attention to utility statement */
|
||||||
|
stmt = query->utilityStmt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (query->commandType == CMD_SELECT &&
|
||||||
|
query->into == NULL)
|
||||||
|
return query->targetList;
|
||||||
|
if (query->returningList)
|
||||||
|
return query->returningList;
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsA(stmt, PlannedStmt))
|
||||||
|
{
|
||||||
|
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||||
|
|
||||||
|
if (pstmt->commandType == CMD_SELECT &&
|
||||||
|
pstmt->into == NULL)
|
||||||
|
return pstmt->planTree->targetlist;
|
||||||
|
if (pstmt->returningLists)
|
||||||
|
return (List *) linitial(pstmt->returningLists);
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
if (IsA(stmt, FetchStmt))
|
||||||
|
{
|
||||||
|
FetchStmt *fstmt = (FetchStmt *) stmt;
|
||||||
Portal subportal;
|
Portal subportal;
|
||||||
|
|
||||||
Assert(!substmt->ismove);
|
Assert(!fstmt->ismove);
|
||||||
subportal = GetPortalByName(substmt->portalname);
|
subportal = GetPortalByName(fstmt->portalname);
|
||||||
Assert(PortalIsValid(subportal));
|
Assert(PortalIsValid(subportal));
|
||||||
return FetchPortalTargetList(subportal);
|
return FetchPortalTargetList(subportal);
|
||||||
}
|
}
|
||||||
|
if (IsA(stmt, ExecuteStmt))
|
||||||
case T_ExecuteStmt:
|
|
||||||
{
|
{
|
||||||
ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
|
ExecuteStmt *estmt = (ExecuteStmt *) stmt;
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
|
|
||||||
Assert(!substmt->into);
|
Assert(!estmt->into);
|
||||||
entry = FetchPreparedStatement(substmt->name, true);
|
entry = FetchPreparedStatement(estmt->name, true);
|
||||||
return FetchPreparedStatementTargetList(entry);
|
return FetchPreparedStatementTargetList(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +477,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
|||||||
/*
|
/*
|
||||||
* Determine the portal execution strategy
|
* Determine the portal execution strategy
|
||||||
*/
|
*/
|
||||||
portal->strategy = ChoosePortalStrategy(portal->parseTrees);
|
portal->strategy = ChoosePortalStrategy(portal->stmts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fire her up according to the strategy
|
* Fire her up according to the strategy
|
||||||
@ -396,8 +499,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
|||||||
* Create QueryDesc in portal's context; for the moment, set
|
* Create QueryDesc in portal's context; for the moment, set
|
||||||
* the destination to DestNone.
|
* the destination to DestNone.
|
||||||
*/
|
*/
|
||||||
queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
|
queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
|
||||||
(Plan *) linitial(portal->planTrees),
|
|
||||||
ActiveSnapshot,
|
ActiveSnapshot,
|
||||||
InvalidSnapshot,
|
InvalidSnapshot,
|
||||||
None_Receiver,
|
None_Receiver,
|
||||||
@ -450,8 +552,16 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
|||||||
* We don't start the executor until we are told to run the
|
* We don't start the executor until we are told to run the
|
||||||
* portal. We do need to set up the result tupdesc.
|
* portal. We do need to set up the result tupdesc.
|
||||||
*/
|
*/
|
||||||
|
{
|
||||||
|
PlannedStmt *pstmt;
|
||||||
|
|
||||||
|
pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
|
||||||
|
Assert(IsA(pstmt, PlannedStmt));
|
||||||
|
Assert(pstmt->returningLists);
|
||||||
portal->tupDesc =
|
portal->tupDesc =
|
||||||
ExecCleanTypeFromTL((PortalGetPrimaryQuery(portal))->returningList, false);
|
ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset cursor position data to "start of query"
|
* Reset cursor position data to "start of query"
|
||||||
@ -468,8 +578,12 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
|||||||
* We don't set snapshot here, because PortalRunUtility will
|
* We don't set snapshot here, because PortalRunUtility will
|
||||||
* take care of it if needed.
|
* take care of it if needed.
|
||||||
*/
|
*/
|
||||||
portal->tupDesc =
|
{
|
||||||
UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
|
Node *ustmt = PortalGetPrimaryStmt(portal);
|
||||||
|
|
||||||
|
Assert(!IsA(ustmt, PlannedStmt));
|
||||||
|
portal->tupDesc = UtilityTupleDescriptor(ustmt);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset cursor position data to "start of query"
|
* Reset cursor position data to "start of query"
|
||||||
@ -934,7 +1048,7 @@ FillPortalStore(Portal portal)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PORTAL_UTIL_SELECT:
|
case PORTAL_UTIL_SELECT:
|
||||||
PortalRunUtility(portal, linitial(portal->parseTrees),
|
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
|
||||||
treceiver, completionTag);
|
treceiver, completionTag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1023,11 +1137,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
|
|||||||
* Execute a utility statement inside a portal.
|
* Execute a utility statement inside a portal.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PortalRunUtility(Portal portal, Query *query,
|
PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||||
DestReceiver *dest, char *completionTag)
|
DestReceiver *dest, char *completionTag)
|
||||||
{
|
{
|
||||||
Node *utilityStmt = query->utilityStmt;
|
|
||||||
|
|
||||||
ereport(DEBUG3,
|
ereport(DEBUG3,
|
||||||
(errmsg_internal("ProcessUtility")));
|
(errmsg_internal("ProcessUtility")));
|
||||||
|
|
||||||
@ -1061,18 +1173,7 @@ PortalRunUtility(Portal portal, Query *query,
|
|||||||
else
|
else
|
||||||
ActiveSnapshot = NULL;
|
ActiveSnapshot = NULL;
|
||||||
|
|
||||||
if (query->canSetTag)
|
|
||||||
{
|
|
||||||
/* utility statement can override default tag string */
|
|
||||||
ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
|
ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
|
||||||
if (completionTag && completionTag[0] == '\0' && portal->commandTag)
|
|
||||||
strcpy(completionTag, portal->commandTag); /* use the default */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* utility added by rewrite cannot set tag */
|
|
||||||
ProcessUtility(utilityStmt, portal->portalParams, dest, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some utility statements may change context on us */
|
/* Some utility statements may change context on us */
|
||||||
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
@ -1092,8 +1193,7 @@ PortalRunMulti(Portal portal,
|
|||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
ListCell *querylist_item;
|
ListCell *stmtlist_item;
|
||||||
ListCell *planlist_item;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the destination is DestRemoteExecute, change to DestNone. The
|
* If the destination is DestRemoteExecute, change to DestNone. The
|
||||||
@ -1114,47 +1214,36 @@ PortalRunMulti(Portal portal,
|
|||||||
* Loop to handle the individual queries generated from a single parsetree
|
* Loop to handle the individual queries generated from a single parsetree
|
||||||
* by analysis and rewrite.
|
* by analysis and rewrite.
|
||||||
*/
|
*/
|
||||||
forboth(querylist_item, portal->parseTrees,
|
foreach(stmtlist_item, portal->stmts)
|
||||||
planlist_item, portal->planTrees)
|
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(querylist_item);
|
Node *stmt = (Node *) lfirst(stmtlist_item);
|
||||||
Plan *plan = (Plan *) lfirst(planlist_item);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we got a cancel signal in prior command, quit
|
* If we got a cancel signal in prior command, quit
|
||||||
*/
|
*/
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (IsA(stmt, PlannedStmt))
|
||||||
{
|
|
||||||
/*
|
|
||||||
* process utility functions (create, destroy, etc..)
|
|
||||||
*/
|
|
||||||
Assert(plan == NULL);
|
|
||||||
|
|
||||||
PortalRunUtility(portal, query,
|
|
||||||
query->canSetTag ? dest : altdest,
|
|
||||||
completionTag);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* process a plannable query.
|
* process a plannable query.
|
||||||
*/
|
*/
|
||||||
|
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||||
|
|
||||||
if (log_executor_stats)
|
if (log_executor_stats)
|
||||||
ResetUsage();
|
ResetUsage();
|
||||||
|
|
||||||
if (query->canSetTag)
|
if (pstmt->canSetTag)
|
||||||
{
|
{
|
||||||
/* statement can set tag string */
|
/* statement can set tag string */
|
||||||
ProcessQuery(query, plan,
|
ProcessQuery(pstmt,
|
||||||
portal->portalParams,
|
portal->portalParams,
|
||||||
dest, completionTag);
|
dest, completionTag);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* stmt added by rewrite cannot set tag */
|
/* stmt added by rewrite cannot set tag */
|
||||||
ProcessQuery(query, plan,
|
ProcessQuery(pstmt,
|
||||||
portal->portalParams,
|
portal->portalParams,
|
||||||
altdest, NULL);
|
altdest, NULL);
|
||||||
}
|
}
|
||||||
@ -1162,12 +1251,25 @@ PortalRunMulti(Portal portal,
|
|||||||
if (log_executor_stats)
|
if (log_executor_stats)
|
||||||
ShowUsage("EXECUTOR STATISTICS");
|
ShowUsage("EXECUTOR STATISTICS");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* process utility functions (create, destroy, etc..)
|
||||||
|
*
|
||||||
|
* These are assumed canSetTag if they're the only stmt in the
|
||||||
|
* portal.
|
||||||
|
*/
|
||||||
|
if (list_length(portal->stmts) == 1)
|
||||||
|
PortalRunUtility(portal, stmt, dest, completionTag);
|
||||||
|
else
|
||||||
|
PortalRunUtility(portal, stmt, altdest, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Increment command counter between queries, but not after the last
|
* Increment command counter between queries, but not after the last
|
||||||
* one.
|
* one.
|
||||||
*/
|
*/
|
||||||
if (lnext(planlist_item) != NULL)
|
if (lnext(stmtlist_item) != NULL)
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -248,21 +248,27 @@ CheckRelationOwnership(RangeVar *rel, bool noCatalogs)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QueryIsReadOnly: is an analyzed/rewritten query read-only?
|
* CommandIsReadOnly: is an executable query read-only?
|
||||||
*
|
*
|
||||||
* This is a much stricter test than we apply for XactReadOnly mode;
|
* This is a much stricter test than we apply for XactReadOnly mode;
|
||||||
* the query must be *in truth* read-only, because the caller wishes
|
* the query must be *in truth* read-only, because the caller wishes
|
||||||
* not to do CommandCounterIncrement for it.
|
* not to do CommandCounterIncrement for it.
|
||||||
|
*
|
||||||
|
* Note: currently no need to support Query nodes here
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
QueryIsReadOnly(Query *parsetree)
|
CommandIsReadOnly(Node *parsetree)
|
||||||
{
|
{
|
||||||
switch (parsetree->commandType)
|
if (IsA(parsetree, PlannedStmt))
|
||||||
|
{
|
||||||
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
||||||
|
|
||||||
|
switch (stmt->commandType)
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
if (parsetree->into != NULL)
|
if (stmt->into != NULL)
|
||||||
return false; /* SELECT INTO */
|
return false; /* SELECT INTO */
|
||||||
else if (parsetree->rowMarks != NIL)
|
else if (stmt->rowMarks != NIL)
|
||||||
return false; /* SELECT FOR UPDATE/SHARE */
|
return false; /* SELECT FOR UPDATE/SHARE */
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
@ -270,14 +276,13 @@ QueryIsReadOnly(Query *parsetree)
|
|||||||
case CMD_INSERT:
|
case CMD_INSERT:
|
||||||
case CMD_DELETE:
|
case CMD_DELETE:
|
||||||
return false;
|
return false;
|
||||||
case CMD_UTILITY:
|
|
||||||
/* For now, treat all utility commands as read/write */
|
|
||||||
return false;
|
|
||||||
default:
|
default:
|
||||||
elog(WARNING, "unrecognized commandType: %d",
|
elog(WARNING, "unrecognized commandType: %d",
|
||||||
(int) parsetree->commandType);
|
(int) stmt->commandType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* For now, treat all utility commands as read/write */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1161,7 +1166,7 @@ UtilityReturnsTuples(Node *parsetree)
|
|||||||
entry = FetchPreparedStatement(stmt->name, false);
|
entry = FetchPreparedStatement(stmt->name, false);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return false; /* not our business to raise error */
|
return false; /* not our business to raise error */
|
||||||
switch (ChoosePortalStrategy(entry->query_list))
|
switch (ChoosePortalStrategy(entry->stmt_list))
|
||||||
{
|
{
|
||||||
case PORTAL_ONE_SELECT:
|
case PORTAL_ONE_SELECT:
|
||||||
case PORTAL_ONE_RETURNING:
|
case PORTAL_ONE_RETURNING:
|
||||||
@ -1244,6 +1249,7 @@ UtilityTupleDescriptor(Node *parsetree)
|
|||||||
* QueryReturnsTuples
|
* QueryReturnsTuples
|
||||||
* Return "true" if this Query will send output to the destination.
|
* Return "true" if this Query will send output to the destination.
|
||||||
*/
|
*/
|
||||||
|
#ifdef NOT_USED
|
||||||
bool
|
bool
|
||||||
QueryReturnsTuples(Query *parsetree)
|
QueryReturnsTuples(Query *parsetree)
|
||||||
{
|
{
|
||||||
@ -1270,14 +1276,15 @@ QueryReturnsTuples(Query *parsetree)
|
|||||||
}
|
}
|
||||||
return false; /* default */
|
return false; /* default */
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CreateCommandTag
|
* CreateCommandTag
|
||||||
* utility to get a string representation of the
|
* utility to get a string representation of the command operation,
|
||||||
* command operation, given a raw (un-analyzed) parsetree.
|
* given either a raw (un-analyzed) parsetree or a planned query.
|
||||||
*
|
*
|
||||||
* This must handle all raw command types, but since the vast majority
|
* This must handle all command types, but since the vast majority
|
||||||
* of 'em are utility commands, it seems sensible to keep it here.
|
* of 'em are utility commands, it seems sensible to keep it here.
|
||||||
*
|
*
|
||||||
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
|
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
|
||||||
@ -1290,6 +1297,7 @@ CreateCommandTag(Node *parsetree)
|
|||||||
|
|
||||||
switch (nodeTag(parsetree))
|
switch (nodeTag(parsetree))
|
||||||
{
|
{
|
||||||
|
/* raw plannable queries */
|
||||||
case T_InsertStmt:
|
case T_InsertStmt:
|
||||||
tag = "INSERT";
|
tag = "INSERT";
|
||||||
break;
|
break;
|
||||||
@ -1306,6 +1314,7 @@ CreateCommandTag(Node *parsetree)
|
|||||||
tag = "SELECT";
|
tag = "SELECT";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* utility statements --- same whether raw or cooked */
|
||||||
case T_TransactionStmt:
|
case T_TransactionStmt:
|
||||||
{
|
{
|
||||||
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
||||||
@ -1826,43 +1835,67 @@ CreateCommandTag(Node *parsetree)
|
|||||||
tag = "DEALLOCATE";
|
tag = "DEALLOCATE";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* already-planned queries */
|
||||||
|
case T_PlannedStmt:
|
||||||
|
{
|
||||||
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
||||||
|
|
||||||
|
switch (stmt->commandType)
|
||||||
|
{
|
||||||
|
case CMD_SELECT:
|
||||||
|
/*
|
||||||
|
* We take a little extra care here so that the result
|
||||||
|
* will be useful for complaints about read-only
|
||||||
|
* statements
|
||||||
|
*/
|
||||||
|
if (stmt->into != NULL)
|
||||||
|
tag = "SELECT INTO";
|
||||||
|
else if (stmt->rowMarks != NIL)
|
||||||
|
{
|
||||||
|
if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
|
||||||
|
tag = "SELECT FOR UPDATE";
|
||||||
|
else
|
||||||
|
tag = "SELECT FOR SHARE";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tag = "SELECT";
|
||||||
|
break;
|
||||||
|
case CMD_UPDATE:
|
||||||
|
tag = "UPDATE";
|
||||||
|
break;
|
||||||
|
case CMD_INSERT:
|
||||||
|
tag = "INSERT";
|
||||||
|
break;
|
||||||
|
case CMD_DELETE:
|
||||||
|
tag = "DELETE";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
elog(WARNING, "unrecognized node type: %d",
|
elog(WARNING, "unrecognized commandType: %d",
|
||||||
(int) nodeTag(parsetree));
|
(int) stmt->commandType);
|
||||||
tag = "???";
|
tag = "???";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/* parsed-and-rewritten-but-not-planned queries */
|
||||||
* CreateQueryTag
|
case T_Query:
|
||||||
* utility to get a string representation of a Query operation.
|
|
||||||
*
|
|
||||||
* This is exactly like CreateCommandTag, except it works on a Query
|
|
||||||
* that has already been through parse analysis (and possibly further).
|
|
||||||
*/
|
|
||||||
const char *
|
|
||||||
CreateQueryTag(Query *parsetree)
|
|
||||||
{
|
{
|
||||||
const char *tag;
|
Query *stmt = (Query *) parsetree;
|
||||||
|
|
||||||
Assert(IsA(parsetree, Query));
|
switch (stmt->commandType)
|
||||||
|
|
||||||
switch (parsetree->commandType)
|
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We take a little extra care here so that the result will be
|
* We take a little extra care here so that the result
|
||||||
* useful for complaints about read-only statements
|
* will be useful for complaints about read-only
|
||||||
|
* statements
|
||||||
*/
|
*/
|
||||||
if (parsetree->into != NULL)
|
if (stmt->into != NULL)
|
||||||
tag = "SELECT INTO";
|
tag = "SELECT INTO";
|
||||||
else if (parsetree->rowMarks != NIL)
|
else if (stmt->rowMarks != NIL)
|
||||||
{
|
{
|
||||||
if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate)
|
if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
|
||||||
tag = "SELECT FOR UPDATE";
|
tag = "SELECT FOR UPDATE";
|
||||||
else
|
else
|
||||||
tag = "SELECT FOR SHARE";
|
tag = "SELECT FOR SHARE";
|
||||||
@ -1880,11 +1913,20 @@ CreateQueryTag(Query *parsetree)
|
|||||||
tag = "DELETE";
|
tag = "DELETE";
|
||||||
break;
|
break;
|
||||||
case CMD_UTILITY:
|
case CMD_UTILITY:
|
||||||
tag = CreateCommandTag(parsetree->utilityStmt);
|
tag = CreateCommandTag(stmt->utilityStmt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(WARNING, "unrecognized commandType: %d",
|
elog(WARNING, "unrecognized commandType: %d",
|
||||||
(int) parsetree->commandType);
|
(int) stmt->commandType);
|
||||||
|
tag = "???";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(WARNING, "unrecognized node type: %d",
|
||||||
|
(int) nodeTag(parsetree));
|
||||||
tag = "???";
|
tag = "???";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1896,9 +1938,9 @@ CreateQueryTag(Query *parsetree)
|
|||||||
/*
|
/*
|
||||||
* GetCommandLogLevel
|
* GetCommandLogLevel
|
||||||
* utility to get the minimum log_statement level for a command,
|
* utility to get the minimum log_statement level for a command,
|
||||||
* given a raw (un-analyzed) parsetree.
|
* given either a raw (un-analyzed) parsetree or a planned query.
|
||||||
*
|
*
|
||||||
* This must handle all raw command types, but since the vast majority
|
* This must handle all command types, but since the vast majority
|
||||||
* of 'em are utility commands, it seems sensible to keep it here.
|
* of 'em are utility commands, it seems sensible to keep it here.
|
||||||
*/
|
*/
|
||||||
LogStmtLevel
|
LogStmtLevel
|
||||||
@ -1908,6 +1950,7 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
|
|
||||||
switch (nodeTag(parsetree))
|
switch (nodeTag(parsetree))
|
||||||
{
|
{
|
||||||
|
/* raw plannable queries */
|
||||||
case T_InsertStmt:
|
case T_InsertStmt:
|
||||||
case T_DeleteStmt:
|
case T_DeleteStmt:
|
||||||
case T_UpdateStmt:
|
case T_UpdateStmt:
|
||||||
@ -1921,6 +1964,7 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* utility statements --- same whether raw or cooked */
|
||||||
case T_TransactionStmt:
|
case T_TransactionStmt:
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
break;
|
break;
|
||||||
@ -2216,12 +2260,12 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
pstmt = FetchPreparedStatement(stmt->name, false);
|
||||||
if (pstmt)
|
if (pstmt)
|
||||||
{
|
{
|
||||||
foreach(l, pstmt->query_list)
|
foreach(l, pstmt->stmt_list)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(l);
|
Node *substmt = (Node *) lfirst(l);
|
||||||
LogStmtLevel stmt_lev;
|
LogStmtLevel stmt_lev;
|
||||||
|
|
||||||
stmt_lev = GetQueryLogLevel(query);
|
stmt_lev = GetCommandLogLevel(substmt);
|
||||||
lev = Min(lev, stmt_lev);
|
lev = Min(lev, stmt_lev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2232,44 +2276,44 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_Query:
|
/* already-planned queries */
|
||||||
|
case T_PlannedStmt:
|
||||||
|
{
|
||||||
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
||||||
|
|
||||||
/*
|
switch (stmt->commandType)
|
||||||
* In complicated situations (eg, EXPLAIN ANALYZE in an extended
|
{
|
||||||
* Query protocol), we might find an already-analyzed query within
|
case CMD_SELECT:
|
||||||
* a utility statement. Cope.
|
if (stmt->into != NULL)
|
||||||
*/
|
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
||||||
lev = GetQueryLogLevel((Query *) parsetree);
|
else
|
||||||
|
lev = LOGSTMT_ALL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_UPDATE:
|
||||||
|
case CMD_INSERT:
|
||||||
|
case CMD_DELETE:
|
||||||
|
lev = LOGSTMT_MOD;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(WARNING, "unrecognized node type: %d",
|
elog(WARNING, "unrecognized commandType: %d",
|
||||||
(int) nodeTag(parsetree));
|
(int) stmt->commandType);
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lev;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/* parsed-and-rewritten-but-not-planned queries */
|
||||||
* GetQueryLogLevel
|
case T_Query:
|
||||||
* utility to get the minimum log_statement level for a Query operation.
|
|
||||||
*
|
|
||||||
* This is exactly like GetCommandLogLevel, except it works on a Query
|
|
||||||
* that has already been through parse analysis (and possibly further).
|
|
||||||
*/
|
|
||||||
LogStmtLevel
|
|
||||||
GetQueryLogLevel(Query *parsetree)
|
|
||||||
{
|
{
|
||||||
LogStmtLevel lev;
|
Query *stmt = (Query *) parsetree;
|
||||||
|
|
||||||
Assert(IsA(parsetree, Query));
|
switch (stmt->commandType)
|
||||||
|
|
||||||
switch (parsetree->commandType)
|
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
if (parsetree->into != NULL)
|
if (stmt->into != NULL)
|
||||||
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
||||||
else
|
else
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
@ -2282,12 +2326,22 @@ GetQueryLogLevel(Query *parsetree)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_UTILITY:
|
case CMD_UTILITY:
|
||||||
lev = GetCommandLogLevel(parsetree->utilityStmt);
|
lev = GetCommandLogLevel(stmt->utilityStmt);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(WARNING, "unrecognized commandType: %d",
|
elog(WARNING, "unrecognized commandType: %d",
|
||||||
(int) parsetree->commandType);
|
(int) stmt->commandType);
|
||||||
|
lev = LOGSTMT_ALL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(WARNING, "unrecognized node type: %d",
|
||||||
|
(int) nodeTag(parsetree));
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.98 2007/01/05 22:19:47 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.99 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -141,29 +141,45 @@ GetPortalByName(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PortalListGetPrimaryQuery
|
* PortalListGetPrimaryStmt
|
||||||
* Get the "primary" Query within a portal, ie, the one marked canSetTag.
|
* Get the "primary" stmt within a portal, ie, the one marked canSetTag.
|
||||||
*
|
*
|
||||||
* Returns NULL if no such Query. If multiple Query structs within the
|
* Returns NULL if no such stmt. If multiple PlannedStmt structs within the
|
||||||
* portal are marked canSetTag, returns the first one. Neither of these
|
* portal are marked canSetTag, returns the first one. Neither of these
|
||||||
* cases should occur in present usages of this function.
|
* cases should occur in present usages of this function.
|
||||||
*
|
*
|
||||||
|
* Copes if given a list of Querys --- can't happen in a portal, but this
|
||||||
|
* code also supports prepared statements, which need both cases.
|
||||||
|
*
|
||||||
* Note: the reason this is just handed a List is so that prepared statements
|
* Note: the reason this is just handed a List is so that prepared statements
|
||||||
* can share the code. For use with a portal, use PortalGetPrimaryQuery
|
* can share the code. For use with a portal, use PortalGetPrimaryStmt
|
||||||
* rather than calling this directly.
|
* rather than calling this directly.
|
||||||
*/
|
*/
|
||||||
Query *
|
Node *
|
||||||
PortalListGetPrimaryQuery(List *parseTrees)
|
PortalListGetPrimaryStmt(List *stmts)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
foreach(lc, parseTrees)
|
foreach(lc, stmts)
|
||||||
{
|
{
|
||||||
Query *query = (Query *) lfirst(lc);
|
Node *stmt = (Node *) lfirst(lc);
|
||||||
|
|
||||||
Assert(IsA(query, Query));
|
if (IsA(stmt, PlannedStmt))
|
||||||
if (query->canSetTag)
|
{
|
||||||
return query;
|
if (((PlannedStmt *) stmt)->canSetTag)
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
else if (IsA(stmt, Query))
|
||||||
|
{
|
||||||
|
if (((Query *) stmt)->canSetTag)
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Utility stmts are assumed canSetTag if they're the only stmt */
|
||||||
|
if (list_length(stmts) == 1)
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -261,30 +277,25 @@ CreateNewPortal(void)
|
|||||||
* (before rewriting) was an empty string. Also, the passed commandTag must
|
* (before rewriting) was an empty string. Also, the passed commandTag must
|
||||||
* be a pointer to a constant string, since it is not copied. The caller is
|
* be a pointer to a constant string, since it is not copied. The caller is
|
||||||
* responsible for ensuring that the passed prepStmtName (if any), sourceText
|
* responsible for ensuring that the passed prepStmtName (if any), sourceText
|
||||||
* (if any), parse and plan trees have adequate lifetime. Also, queryContext
|
* (if any), and plan trees have adequate lifetime.
|
||||||
* must accurately describe the location of the parse trees.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PortalDefineQuery(Portal portal,
|
PortalDefineQuery(Portal portal,
|
||||||
const char *prepStmtName,
|
const char *prepStmtName,
|
||||||
const char *sourceText,
|
const char *sourceText,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *parseTrees,
|
List *stmts,
|
||||||
List *planTrees,
|
|
||||||
MemoryContext queryContext)
|
MemoryContext queryContext)
|
||||||
{
|
{
|
||||||
AssertArg(PortalIsValid(portal));
|
AssertArg(PortalIsValid(portal));
|
||||||
AssertState(portal->queryContext == NULL); /* else defined already */
|
AssertState(portal->queryContext == NULL); /* else defined already */
|
||||||
|
|
||||||
Assert(list_length(parseTrees) == list_length(planTrees));
|
Assert(commandTag != NULL || stmts == NIL);
|
||||||
|
|
||||||
Assert(commandTag != NULL || parseTrees == NIL);
|
|
||||||
|
|
||||||
portal->prepStmtName = prepStmtName;
|
portal->prepStmtName = prepStmtName;
|
||||||
portal->sourceText = sourceText;
|
portal->sourceText = sourceText;
|
||||||
portal->commandTag = commandTag;
|
portal->commandTag = commandTag;
|
||||||
portal->parseTrees = parseTrees;
|
portal->stmts = stmts;
|
||||||
portal->planTrees = planTrees;
|
|
||||||
portal->queryContext = queryContext;
|
portal->queryContext = queryContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.387 2007/02/20 10:00:25 petere Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.388 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200702201
|
#define CATALOG_VERSION_NO 200702202
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,13 +7,14 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.20 2007/01/05 22:19:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.21 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef PORTALCMDS_H
|
#ifndef PORTALCMDS_H
|
||||||
#define PORTALCMDS_H
|
#define PORTALCMDS_H
|
||||||
|
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
#include "utils/portal.h"
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.23 2007/01/05 22:19:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.24 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -19,6 +19,10 @@
|
|||||||
/*
|
/*
|
||||||
* The data structure representing a prepared statement
|
* The data structure representing a prepared statement
|
||||||
*
|
*
|
||||||
|
* A prepared statement might be fully planned, or only parsed-and-rewritten.
|
||||||
|
* If fully planned, stmt_list contains PlannedStmts and/or utility statements;
|
||||||
|
* if not, it contains Query nodes.
|
||||||
|
*
|
||||||
* Note: all subsidiary storage lives in the context denoted by the context
|
* Note: all subsidiary storage lives in the context denoted by the context
|
||||||
* field. However, the string referenced by commandTag is not subsidiary
|
* field. However, the string referenced by commandTag is not subsidiary
|
||||||
* storage; it is assumed to be a compile-time-constant string. As with
|
* storage; it is assumed to be a compile-time-constant string. As with
|
||||||
@ -31,11 +35,11 @@ typedef struct
|
|||||||
char stmt_name[NAMEDATALEN];
|
char stmt_name[NAMEDATALEN];
|
||||||
char *query_string; /* text of query, or NULL */
|
char *query_string; /* text of query, or NULL */
|
||||||
const char *commandTag; /* command tag (a constant!), or NULL */
|
const char *commandTag; /* command tag (a constant!), or NULL */
|
||||||
List *query_list; /* list of queries, rewritten */
|
List *stmt_list; /* list of statement or Query nodes */
|
||||||
List *plan_list; /* list of plans */
|
|
||||||
List *argtype_list; /* list of parameter type OIDs */
|
List *argtype_list; /* list of parameter type OIDs */
|
||||||
|
bool fully_planned; /* what is in stmt_list, exactly? */
|
||||||
|
bool from_sql; /* prepared via SQL, not FE/BE protocol? */
|
||||||
TimestampTz prepare_time; /* the time when the stmt was prepared */
|
TimestampTz prepare_time; /* the time when the stmt was prepared */
|
||||||
bool from_sql; /* stmt prepared via SQL, not FE/BE protocol? */
|
|
||||||
MemoryContext context; /* context containing this query */
|
MemoryContext context; /* context containing this query */
|
||||||
} PreparedStatement;
|
} PreparedStatement;
|
||||||
|
|
||||||
@ -52,9 +56,9 @@ extern void ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
|||||||
extern void StorePreparedStatement(const char *stmt_name,
|
extern void StorePreparedStatement(const char *stmt_name,
|
||||||
const char *query_string,
|
const char *query_string,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *query_list,
|
List *stmt_list,
|
||||||
List *plan_list,
|
|
||||||
List *argtype_list,
|
List *argtype_list,
|
||||||
|
bool fully_planned,
|
||||||
bool from_sql);
|
bool from_sql);
|
||||||
extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
|
extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
|
||||||
bool throwError);
|
bool throwError);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.33 2007/01/05 22:19:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/execdesc.h,v 1.34 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,7 +16,7 @@
|
|||||||
#define EXECDESC_H
|
#define EXECDESC_H
|
||||||
|
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/plannodes.h"
|
||||||
#include "tcop/dest.h"
|
#include "tcop/dest.h"
|
||||||
|
|
||||||
|
|
||||||
@ -24,15 +24,19 @@
|
|||||||
* query descriptor:
|
* query descriptor:
|
||||||
*
|
*
|
||||||
* a QueryDesc encapsulates everything that the executor
|
* a QueryDesc encapsulates everything that the executor
|
||||||
* needs to execute the query
|
* needs to execute the query.
|
||||||
|
*
|
||||||
|
* For the convenience of SQL-language functions, we also support QueryDescs
|
||||||
|
* containing utility statements; these must not be passed to the executor
|
||||||
|
* however.
|
||||||
* ---------------------
|
* ---------------------
|
||||||
*/
|
*/
|
||||||
typedef struct QueryDesc
|
typedef struct QueryDesc
|
||||||
{
|
{
|
||||||
/* These fields are provided by CreateQueryDesc */
|
/* These fields are provided by CreateQueryDesc */
|
||||||
CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
|
CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
|
||||||
Query *parsetree; /* rewritten parsetree */
|
PlannedStmt *plannedstmt; /* planner's output, or null if utility */
|
||||||
Plan *plantree; /* planner's output */
|
Node *utilitystmt; /* utility statement, or null */
|
||||||
Snapshot snapshot; /* snapshot to use for query */
|
Snapshot snapshot; /* snapshot to use for query */
|
||||||
Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */
|
Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */
|
||||||
DestReceiver *dest; /* the destination for tuple output */
|
DestReceiver *dest; /* the destination for tuple output */
|
||||||
@ -46,13 +50,18 @@ typedef struct QueryDesc
|
|||||||
} QueryDesc;
|
} QueryDesc;
|
||||||
|
|
||||||
/* in pquery.c */
|
/* in pquery.c */
|
||||||
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
|
extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
|
||||||
Snapshot snapshot,
|
Snapshot snapshot,
|
||||||
Snapshot crosscheck_snapshot,
|
Snapshot crosscheck_snapshot,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
bool doInstrument);
|
bool doInstrument);
|
||||||
|
|
||||||
|
extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
|
||||||
|
Snapshot snapshot,
|
||||||
|
DestReceiver *dest,
|
||||||
|
ParamListInfo params);
|
||||||
|
|
||||||
extern void FreeQueryDesc(QueryDesc *qdesc);
|
extern void FreeQueryDesc(QueryDesc *qdesc);
|
||||||
|
|
||||||
#endif /* EXECDESC_H */
|
#endif /* EXECDESC_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.136 2007/02/06 02:59:13 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#define EXECUTOR_H
|
#define EXECUTOR_H
|
||||||
|
|
||||||
#include "executor/execdesc.h"
|
#include "executor/execdesc.h"
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.26 2007/01/05 22:19:55 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.27 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -35,10 +35,11 @@ typedef struct
|
|||||||
MemoryContext plancxt;
|
MemoryContext plancxt;
|
||||||
/* Original query string (used for error reporting) */
|
/* Original query string (used for error reporting) */
|
||||||
const char *query;
|
const char *query;
|
||||||
/* List of List of querytrees; one sublist per original parsetree */
|
/*
|
||||||
List *qtlist;
|
* List of List of PlannedStmts and utility stmts; one sublist per
|
||||||
/* List of plan trees --- length == # of querytrees, but flat list */
|
* original parsetree
|
||||||
List *ptlist;
|
*/
|
||||||
|
List *stmt_list_list;
|
||||||
/* Argument types, if a prepared plan */
|
/* Argument types, if a prepared plan */
|
||||||
int nargs;
|
int nargs;
|
||||||
Oid *argtypes;
|
Oid *argtypes;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.167 2007/02/06 02:59:13 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -344,7 +344,7 @@ typedef struct EState
|
|||||||
ExprContext *es_per_tuple_exprcontext;
|
ExprContext *es_per_tuple_exprcontext;
|
||||||
|
|
||||||
/* Below is to re-evaluate plan qual in READ COMMITTED mode */
|
/* Below is to re-evaluate plan qual in READ COMMITTED mode */
|
||||||
Plan *es_topPlan; /* link to top of plan tree */
|
PlannedStmt *es_plannedstmt; /* link to top of plan tree */
|
||||||
struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */
|
struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */
|
||||||
bool *es_evTupleNull; /* local array of EPQ status */
|
bool *es_evTupleNull; /* local array of EPQ status */
|
||||||
HeapTuple *es_evTuple; /* shared array of EPQ substitute tuples */
|
HeapTuple *es_evTuple; /* shared array of EPQ substitute tuples */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.195 2007/02/19 07:03:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.196 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -141,6 +141,7 @@ typedef enum NodeTag
|
|||||||
T_RangeTblRef,
|
T_RangeTblRef,
|
||||||
T_JoinExpr,
|
T_JoinExpr,
|
||||||
T_FromExpr,
|
T_FromExpr,
|
||||||
|
T_IntoClause,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
|
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
|
||||||
@ -225,9 +226,10 @@ typedef enum NodeTag
|
|||||||
T_OidList,
|
T_OidList,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
* TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
|
||||||
*/
|
*/
|
||||||
T_Query = 700,
|
T_Query = 700,
|
||||||
|
T_PlannedStmt,
|
||||||
T_InsertStmt,
|
T_InsertStmt,
|
||||||
T_DeleteStmt,
|
T_DeleteStmt,
|
||||||
T_UpdateStmt,
|
T_UpdateStmt,
|
||||||
@ -302,8 +304,12 @@ typedef enum NodeTag
|
|||||||
T_AlterOwnerStmt,
|
T_AlterOwnerStmt,
|
||||||
T_DropOwnedStmt,
|
T_DropOwnedStmt,
|
||||||
T_ReassignOwnedStmt,
|
T_ReassignOwnedStmt,
|
||||||
|
T_CompositeTypeStmt,
|
||||||
|
|
||||||
T_A_Expr = 800,
|
/*
|
||||||
|
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
||||||
|
*/
|
||||||
|
T_A_Expr = 900,
|
||||||
T_ColumnRef,
|
T_ColumnRef,
|
||||||
T_ParamRef,
|
T_ParamRef,
|
||||||
T_A_Const,
|
T_A_Const,
|
||||||
@ -328,7 +334,6 @@ typedef enum NodeTag
|
|||||||
T_FuncWithArgs,
|
T_FuncWithArgs,
|
||||||
T_PrivTarget,
|
T_PrivTarget,
|
||||||
T_CreateOpClassItem,
|
T_CreateOpClassItem,
|
||||||
T_CompositeTypeStmt,
|
|
||||||
T_InhRelation,
|
T_InhRelation,
|
||||||
T_FunctionParameter,
|
T_FunctionParameter,
|
||||||
T_LockingClause,
|
T_LockingClause,
|
||||||
@ -343,7 +348,7 @@ typedef enum NodeTag
|
|||||||
* purposes (usually because they are involved in APIs where we want to
|
* purposes (usually because they are involved in APIs where we want to
|
||||||
* pass multiple object types through the same pointer).
|
* pass multiple object types through the same pointer).
|
||||||
*/
|
*/
|
||||||
T_TriggerData = 900, /* in commands/trigger.h */
|
T_TriggerData = 950, /* in commands/trigger.h */
|
||||||
T_ReturnSetInfo, /* in nodes/execnodes.h */
|
T_ReturnSetInfo, /* in nodes/execnodes.h */
|
||||||
T_TIDBitmap /* in nodes/tidbitmap.h */
|
T_TIDBitmap /* in nodes/tidbitmap.h */
|
||||||
} NodeTag;
|
} NodeTag;
|
||||||
@ -430,10 +435,9 @@ typedef double Cost; /* execution cost (in page-access units) */
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* CmdType -
|
* CmdType -
|
||||||
* enums for type of operation represented by a Query
|
* enums for type of operation represented by a Query or PlannedStmt
|
||||||
*
|
*
|
||||||
* ??? could have put this in parsenodes.h but many files not in the
|
* This is needed in both parsenodes.h and plannodes.h, so put it here...
|
||||||
* optimizer also need this...
|
|
||||||
*/
|
*/
|
||||||
typedef enum CmdType
|
typedef enum CmdType
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.340 2007/02/03 14:06:55 petere Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.341 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -27,15 +27,6 @@ typedef enum QuerySource
|
|||||||
QSRC_NON_INSTEAD_RULE /* added by non-INSTEAD rule */
|
QSRC_NON_INSTEAD_RULE /* added by non-INSTEAD rule */
|
||||||
} QuerySource;
|
} QuerySource;
|
||||||
|
|
||||||
/* What to do at commit time for temporary relations */
|
|
||||||
typedef enum OnCommitAction
|
|
||||||
{
|
|
||||||
ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */
|
|
||||||
ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */
|
|
||||||
ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */
|
|
||||||
ONCOMMIT_DROP /* ON COMMIT DROP */
|
|
||||||
} OnCommitAction;
|
|
||||||
|
|
||||||
/* Sort ordering options for ORDER BY and CREATE INDEX */
|
/* Sort ordering options for ORDER BY and CREATE INDEX */
|
||||||
typedef enum SortByDir
|
typedef enum SortByDir
|
||||||
{
|
{
|
||||||
@ -86,11 +77,14 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Query -
|
* Query -
|
||||||
* all statements are turned into a Query tree (via transformStmt)
|
* Parse analysis turns all statements into a Query tree (via transformStmt)
|
||||||
* for further processing by the optimizer
|
* for further processing by the rewriter and planner.
|
||||||
*
|
*
|
||||||
* utility statements (i.e. non-optimizable statements) have the
|
* Utility statements (i.e. non-optimizable statements) have the
|
||||||
* utilityStmt field set, and the Query itself is mostly dummy.
|
* utilityStmt field set, and the Query itself is mostly dummy.
|
||||||
|
*
|
||||||
|
* Planning converts a Query tree into a Plan tree headed by a PlannedStmt
|
||||||
|
* noded --- the Query structure is not used by the executor.
|
||||||
*/
|
*/
|
||||||
typedef struct Query
|
typedef struct Query
|
||||||
{
|
{
|
||||||
@ -108,10 +102,7 @@ typedef struct Query
|
|||||||
int resultRelation; /* rtable index of target relation for
|
int resultRelation; /* rtable index of target relation for
|
||||||
* INSERT/UPDATE/DELETE; 0 for SELECT */
|
* INSERT/UPDATE/DELETE; 0 for SELECT */
|
||||||
|
|
||||||
RangeVar *into; /* target relation for SELECT INTO */
|
IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */
|
||||||
List *intoOptions; /* options from WITH clause */
|
|
||||||
OnCommitAction intoOnCommit; /* what do we do at COMMIT? */
|
|
||||||
char *intoTableSpaceName; /* table space to use, or NULL */
|
|
||||||
|
|
||||||
bool hasAggs; /* has aggregates in tlist or havingQual */
|
bool hasAggs; /* has aggregates in tlist or havingQual */
|
||||||
bool hasSubLinks; /* has subquery SubLink */
|
bool hasSubLinks; /* has subquery SubLink */
|
||||||
@ -138,29 +129,6 @@ typedef struct Query
|
|||||||
|
|
||||||
Node *setOperations; /* set-operation tree if this is top level of
|
Node *setOperations; /* set-operation tree if this is top level of
|
||||||
* a UNION/INTERSECT/EXCEPT query */
|
* a UNION/INTERSECT/EXCEPT query */
|
||||||
|
|
||||||
/*
|
|
||||||
* If the resultRelation turns out to be the parent of an inheritance
|
|
||||||
* tree, the planner will add all the child tables to the rtable and store
|
|
||||||
* a list of the rtindexes of all the result relations here. This is done
|
|
||||||
* at plan time, not parse time, since we don't want to commit to the
|
|
||||||
* exact set of child tables at parse time. XXX This field ought to go in
|
|
||||||
* some sort of TopPlan plan node, not in the Query.
|
|
||||||
*/
|
|
||||||
List *resultRelations; /* integer list of RT indexes, or NIL */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the query has a returningList then the planner will store a list of
|
|
||||||
* processed targetlists (one per result relation) here. We must have a
|
|
||||||
* separate RETURNING targetlist for each result rel because column
|
|
||||||
* numbers may vary within an inheritance tree. In the targetlists, Vars
|
|
||||||
* referencing the result relation will have their original varno and
|
|
||||||
* varattno, while Vars referencing other rels will be converted to have
|
|
||||||
* varno OUTER and varattno referencing a resjunk entry in the top plan
|
|
||||||
* node's targetlist. XXX This field ought to go in some sort of TopPlan
|
|
||||||
* plan node, not in the Query.
|
|
||||||
*/
|
|
||||||
List *returningLists; /* list of lists of TargetEntry, or NIL */
|
|
||||||
} Query;
|
} Query;
|
||||||
|
|
||||||
|
|
||||||
@ -761,17 +729,10 @@ typedef struct SelectStmt
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* These fields are used only in "leaf" SelectStmts.
|
* These fields are used only in "leaf" SelectStmts.
|
||||||
*
|
|
||||||
* into, intoColNames, intoOptions, intoOnCommit, and intoTableSpaceName
|
|
||||||
* are a kluge; they belong somewhere else...
|
|
||||||
*/
|
*/
|
||||||
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
|
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
|
||||||
* lcons(NIL,NIL) for all (SELECT DISTINCT) */
|
* lcons(NIL,NIL) for all (SELECT DISTINCT) */
|
||||||
RangeVar *into; /* target table (for select into table) */
|
IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */
|
||||||
List *intoColNames; /* column names for into table */
|
|
||||||
List *intoOptions; /* options from WITH clause */
|
|
||||||
OnCommitAction intoOnCommit; /* what do we do at COMMIT? */
|
|
||||||
char *intoTableSpaceName; /* table space to use, or NULL */
|
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
List *fromClause; /* the FROM clause */
|
List *fromClause; /* the FROM clause */
|
||||||
Node *whereClause; /* WHERE qualification */
|
Node *whereClause; /* WHERE qualification */
|
||||||
@ -1994,10 +1955,7 @@ typedef struct ExecuteStmt
|
|||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *name; /* The name of the plan to execute */
|
char *name; /* The name of the plan to execute */
|
||||||
RangeVar *into; /* Optional table to store results in */
|
IntoClause *into; /* Optional table to store results in */
|
||||||
List *intoOptions; /* Options from WITH clause */
|
|
||||||
OnCommitAction into_on_commit; /* What do we do at COMMIT? */
|
|
||||||
char *into_tbl_space; /* Tablespace to use, or NULL */
|
|
||||||
List *params; /* Values to assign to parameters */
|
List *params; /* Values to assign to parameters */
|
||||||
} ExecuteStmt;
|
} ExecuteStmt;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.90 2007/02/19 02:23:12 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -24,6 +24,48 @@
|
|||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* PlannedStmt node
|
||||||
|
*
|
||||||
|
* The output of the planner is a Plan tree headed by a PlannedStmt node.
|
||||||
|
* PlannedStmt holds the "one time" information needed by the executor.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef struct PlannedStmt
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
|
||||||
|
CmdType commandType; /* select|insert|update|delete */
|
||||||
|
|
||||||
|
bool canSetTag; /* do I set the command result tag? */
|
||||||
|
|
||||||
|
struct Plan *planTree; /* tree of Plan nodes */
|
||||||
|
|
||||||
|
List *rtable; /* list of RangeTblEntry nodes */
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the query has a returningList then the planner will store a list of
|
||||||
|
* processed targetlists (one per result relation) here. We must have a
|
||||||
|
* separate RETURNING targetlist for each result rel because column
|
||||||
|
* numbers may vary within an inheritance tree. In the targetlists, Vars
|
||||||
|
* referencing the result relation will have their original varno and
|
||||||
|
* varattno, while Vars referencing other rels will be converted to have
|
||||||
|
* varno OUTER and varattno referencing a resjunk entry in the top plan
|
||||||
|
* node's targetlist.
|
||||||
|
*/
|
||||||
|
List *returningLists; /* list of lists of TargetEntry, or NIL */
|
||||||
|
|
||||||
|
List *rowMarks; /* a list of RowMarkClause's */
|
||||||
|
|
||||||
|
int nParamExec; /* number of PARAM_EXEC Params used */
|
||||||
|
} PlannedStmt;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Plan node
|
* Plan node
|
||||||
*
|
*
|
||||||
@ -75,15 +117,6 @@ typedef struct Plan
|
|||||||
*/
|
*/
|
||||||
Bitmapset *extParam;
|
Bitmapset *extParam;
|
||||||
Bitmapset *allParam;
|
Bitmapset *allParam;
|
||||||
|
|
||||||
/*
|
|
||||||
* We really need in some TopPlan node to store range table and
|
|
||||||
* resultRelation from Query there and get rid of Query itself from
|
|
||||||
* Executor. Some other stuff like below could be put there, too.
|
|
||||||
*/
|
|
||||||
int nParamExec; /* Number of them in entire query. This is to
|
|
||||||
* get Executor know about how many PARAM_EXEC
|
|
||||||
* there are in query plan. */
|
|
||||||
} Plan;
|
} Plan;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.125 2007/02/19 07:03:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -49,6 +49,15 @@ typedef enum InhOption
|
|||||||
INH_DEFAULT /* Use current SQL_inheritance option */
|
INH_DEFAULT /* Use current SQL_inheritance option */
|
||||||
} InhOption;
|
} InhOption;
|
||||||
|
|
||||||
|
/* What to do at commit time for temporary relations */
|
||||||
|
typedef enum OnCommitAction
|
||||||
|
{
|
||||||
|
ONCOMMIT_NOOP, /* No ON COMMIT clause (do nothing) */
|
||||||
|
ONCOMMIT_PRESERVE_ROWS, /* ON COMMIT PRESERVE ROWS (do nothing) */
|
||||||
|
ONCOMMIT_DELETE_ROWS, /* ON COMMIT DELETE ROWS */
|
||||||
|
ONCOMMIT_DROP /* ON COMMIT DROP */
|
||||||
|
} OnCommitAction;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RangeVar - range variable, used in FROM clauses
|
* RangeVar - range variable, used in FROM clauses
|
||||||
*
|
*
|
||||||
@ -69,6 +78,20 @@ typedef struct RangeVar
|
|||||||
Alias *alias; /* table alias & optional column aliases */
|
Alias *alias; /* table alias & optional column aliases */
|
||||||
} RangeVar;
|
} RangeVar;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IntoClause - target information for SELECT INTO and CREATE TABLE AS
|
||||||
|
*/
|
||||||
|
typedef struct IntoClause
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
|
||||||
|
RangeVar *rel; /* target relation name */
|
||||||
|
List *colNames; /* column names to assign, or NIL */
|
||||||
|
List *options; /* options from WITH clause */
|
||||||
|
OnCommitAction onCommit; /* what do we do at COMMIT? */
|
||||||
|
char *tableSpaceName; /* table space to use, or NULL */
|
||||||
|
} IntoClause;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* node types for executable expressions
|
* node types for executable expressions
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.136 2007/02/19 07:03:33 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -110,6 +110,10 @@ typedef struct PlannerInfo
|
|||||||
List *join_rel_list; /* list of join-relation RelOptInfos */
|
List *join_rel_list; /* list of join-relation RelOptInfos */
|
||||||
struct HTAB *join_rel_hash; /* optional hashtable for join relations */
|
struct HTAB *join_rel_hash; /* optional hashtable for join relations */
|
||||||
|
|
||||||
|
List *resultRelations; /* integer list of RT indexes, or NIL */
|
||||||
|
|
||||||
|
List *returningLists; /* list of lists of TargetEntry, or NIL */
|
||||||
|
|
||||||
List *init_plans; /* init subplans for query */
|
List *init_plans; /* init subplans for query */
|
||||||
|
|
||||||
List *eq_classes; /* list of active EquivalenceClasses */
|
List *eq_classes; /* list of active EquivalenceClasses */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.37 2007/02/19 07:03:34 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.38 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -18,10 +18,10 @@
|
|||||||
#include "nodes/relation.h"
|
#include "nodes/relation.h"
|
||||||
|
|
||||||
|
|
||||||
extern Plan *planner(Query *parse, bool isCursor, int cursorOptions,
|
extern PlannedStmt *planner(Query *parse, bool isCursor, int cursorOptions,
|
||||||
ParamListInfo boundParams);
|
ParamListInfo boundParams);
|
||||||
extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
|
extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||||
Index level, double tuple_fraction,
|
Index level, double tuple_fraction,
|
||||||
List **subquery_pathkeys);
|
PlannerInfo **subroot);
|
||||||
|
|
||||||
#endif /* PLANNER_H */
|
#endif /* PLANNER_H */
|
||||||
|
@ -7,23 +7,26 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.40 2007/01/05 22:19:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.41 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef PQUERY_H
|
#ifndef PQUERY_H
|
||||||
#define PQUERY_H
|
#define PQUERY_H
|
||||||
|
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
#include "utils/portal.h"
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
extern DLLIMPORT Portal ActivePortal;
|
extern DLLIMPORT Portal ActivePortal;
|
||||||
|
|
||||||
|
|
||||||
extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
|
extern PortalStrategy ChoosePortalStrategy(List *stmts);
|
||||||
|
|
||||||
extern List *FetchPortalTargetList(Portal portal);
|
extern List *FetchPortalTargetList(Portal portal);
|
||||||
|
|
||||||
|
extern List *FetchStatementTargetList(Node *stmt);
|
||||||
|
|
||||||
extern void PortalStart(Portal portal, ParamListInfo params,
|
extern void PortalStart(Portal portal, ParamListInfo params,
|
||||||
Snapshot snapshot);
|
Snapshot snapshot);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.86 2007/01/05 22:19:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.87 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
* OLD COMMENTS
|
* OLD COMMENTS
|
||||||
* This file was created so that other c files could get the two
|
* This file was created so that other c files could get the two
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#define TCOPPROT_H
|
#define TCOPPROT_H
|
||||||
|
|
||||||
#include "executor/execdesc.h"
|
#include "executor/execdesc.h"
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
|
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ extern List *pg_parse_and_rewrite(const char *query_string,
|
|||||||
extern List *pg_parse_query(const char *query_string);
|
extern List *pg_parse_query(const char *query_string);
|
||||||
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
||||||
Oid *paramTypes, int numParams);
|
Oid *paramTypes, int numParams);
|
||||||
extern Plan *pg_plan_query(Query *querytree, ParamListInfo boundParams);
|
extern PlannedStmt *pg_plan_query(Query *querytree, ParamListInfo boundParams);
|
||||||
extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
|
extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
|
||||||
bool needSnapshot);
|
bool needSnapshot);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.30 2007/01/05 22:19:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.31 2007/02/20 17:32:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,15 +26,9 @@ extern TupleDesc UtilityTupleDescriptor(Node *parsetree);
|
|||||||
|
|
||||||
extern const char *CreateCommandTag(Node *parsetree);
|
extern const char *CreateCommandTag(Node *parsetree);
|
||||||
|
|
||||||
extern const char *CreateQueryTag(Query *parsetree);
|
|
||||||
|
|
||||||
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
|
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
|
||||||
|
|
||||||
extern LogStmtLevel GetQueryLogLevel(Query *parsetree);
|
extern bool CommandIsReadOnly(Node *parsetree);
|
||||||
|
|
||||||
extern bool QueryReturnsTuples(Query *parsetree);
|
|
||||||
|
|
||||||
extern bool QueryIsReadOnly(Query *parsetree);
|
|
||||||
|
|
||||||
extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs);
|
extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* to let the client suspend an update-type query partway through! Because
|
* to let the client suspend an update-type query partway through! Because
|
||||||
* the query rewriter does not allow arbitrary ON SELECT rewrite rules,
|
* the query rewriter does not allow arbitrary ON SELECT rewrite rules,
|
||||||
* only queries that were originally update-type could produce multiple
|
* only queries that were originally update-type could produce multiple
|
||||||
* parse/plan trees; so the restriction to a single query is not a problem
|
* plan trees; so the restriction to a single query is not a problem
|
||||||
* in practice.
|
* in practice.
|
||||||
*
|
*
|
||||||
* For SQL cursors, we support three kinds of scroll behavior:
|
* For SQL cursors, we support three kinds of scroll behavior:
|
||||||
@ -39,7 +39,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.72 2007/01/05 22:19:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.73 2007/02/20 17:32:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -124,9 +124,8 @@ typedef struct PortalData
|
|||||||
/* The query or queries the portal will execute */
|
/* The query or queries the portal will execute */
|
||||||
const char *sourceText; /* text of query, if known (may be NULL) */
|
const char *sourceText; /* text of query, if known (may be NULL) */
|
||||||
const char *commandTag; /* command tag for original query */
|
const char *commandTag; /* command tag for original query */
|
||||||
List *parseTrees; /* parse tree(s) */
|
List *stmts; /* PlannedStmts and/or utility statements */
|
||||||
List *planTrees; /* plan tree(s) */
|
MemoryContext queryContext; /* where the plan trees live */
|
||||||
MemoryContext queryContext; /* where the parse trees live */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: queryContext effectively identifies which prepared statement the
|
* Note: queryContext effectively identifies which prepared statement the
|
||||||
@ -191,7 +190,7 @@ typedef struct PortalData
|
|||||||
*/
|
*/
|
||||||
#define PortalGetQueryDesc(portal) ((portal)->queryDesc)
|
#define PortalGetQueryDesc(portal) ((portal)->queryDesc)
|
||||||
#define PortalGetHeapMemory(portal) ((portal)->heap)
|
#define PortalGetHeapMemory(portal) ((portal)->heap)
|
||||||
#define PortalGetPrimaryQuery(portal) PortalListGetPrimaryQuery((portal)->parseTrees)
|
#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts)
|
||||||
|
|
||||||
|
|
||||||
/* Prototypes for functions in utils/mmgr/portalmem.c */
|
/* Prototypes for functions in utils/mmgr/portalmem.c */
|
||||||
@ -217,10 +216,9 @@ extern void PortalDefineQuery(Portal portal,
|
|||||||
const char *prepStmtName,
|
const char *prepStmtName,
|
||||||
const char *sourceText,
|
const char *sourceText,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *parseTrees,
|
List *stmts,
|
||||||
List *planTrees,
|
|
||||||
MemoryContext queryContext);
|
MemoryContext queryContext);
|
||||||
extern Query *PortalListGetPrimaryQuery(List *parseTrees);
|
extern Node *PortalListGetPrimaryStmt(List *stmts);
|
||||||
extern void PortalCreateHoldStore(Portal portal);
|
extern void PortalCreateHoldStore(Portal portal);
|
||||||
|
|
||||||
#endif /* PORTAL_H */
|
#endif /* PORTAL_H */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.188 2007/02/08 18:37:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.189 2007/02/20 17:32:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2380,20 +2380,20 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
|||||||
exec_prepare_plan(estate, expr);
|
exec_prepare_plan(estate, expr);
|
||||||
stmt->mod_stmt = false;
|
stmt->mod_stmt = false;
|
||||||
spi_plan = (_SPI_plan *) expr->plan;
|
spi_plan = (_SPI_plan *) expr->plan;
|
||||||
foreach(l, spi_plan->qtlist)
|
foreach(l, spi_plan->stmt_list_list)
|
||||||
{
|
{
|
||||||
ListCell *l2;
|
ListCell *l2;
|
||||||
|
|
||||||
foreach(l2, (List *) lfirst(l))
|
foreach(l2, (List *) lfirst(l))
|
||||||
{
|
{
|
||||||
Query *q = (Query *) lfirst(l2);
|
PlannedStmt *p = (PlannedStmt *) lfirst(l2);
|
||||||
|
|
||||||
Assert(IsA(q, Query));
|
if (IsA(p, PlannedStmt) &&
|
||||||
if (q->canSetTag)
|
p->canSetTag)
|
||||||
{
|
{
|
||||||
if (q->commandType == CMD_INSERT ||
|
if (p->commandType == CMD_INSERT ||
|
||||||
q->commandType == CMD_UPDATE ||
|
p->commandType == CMD_UPDATE ||
|
||||||
q->commandType == CMD_DELETE)
|
p->commandType == CMD_DELETE)
|
||||||
stmt->mod_stmt = true;
|
stmt->mod_stmt = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4674,6 +4674,8 @@ static void
|
|||||||
exec_simple_check_plan(PLpgSQL_expr *expr)
|
exec_simple_check_plan(PLpgSQL_expr *expr)
|
||||||
{
|
{
|
||||||
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
|
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
|
||||||
|
List *sublist;
|
||||||
|
PlannedStmt *stmt;
|
||||||
Plan *plan;
|
Plan *plan;
|
||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
|
|
||||||
@ -4683,17 +4685,20 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
|
|||||||
* 1. We can only evaluate queries that resulted in one single execution
|
* 1. We can only evaluate queries that resulted in one single execution
|
||||||
* plan
|
* plan
|
||||||
*/
|
*/
|
||||||
if (list_length(spi_plan->ptlist) != 1)
|
if (list_length(spi_plan->stmt_list_list) != 1)
|
||||||
|
return;
|
||||||
|
sublist = (List *) linitial(spi_plan->stmt_list_list);
|
||||||
|
if (list_length(sublist) != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plan = (Plan *) linitial(spi_plan->ptlist);
|
stmt = (PlannedStmt *) linitial(sublist);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2. It must be a RESULT plan --> no scan's required
|
* 2. It must be a RESULT plan --> no scan's required
|
||||||
*/
|
*/
|
||||||
if (plan == NULL) /* utility statement produces this */
|
if (!IsA(stmt, PlannedStmt))
|
||||||
return;
|
return;
|
||||||
|
plan = stmt->planTree;
|
||||||
if (!IsA(plan, Result))
|
if (!IsA(plan, Result))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user