mirror of
https://github.com/postgres/postgres.git
synced 2025-07-14 08:21:07 +03:00
First phase of plan-invalidation project: create a plan cache management
module and teach PREPARE and protocol-level prepared statements to use it. In service of this, rearrange utility-statement processing so that parse analysis does not assume table schemas can't change before execution for utility statements (necessary because we don't attempt to re-acquire locks for utility statements when reusing a stored plan). This requires some refactoring of the ProcessUtility API, but it ends up cleaner anyway, for instance we can get rid of the QueryContext global. Still to do: fix up SPI and related code to use the plan cache; I'm tempted to try to make SQL functions use it too. Also, there are at least some aspects of system state that we want to ensure remain the same during a replan as in the original processing; search_path certainly ought to behave that way for instance, and perhaps there are others.
This commit is contained in:
@ -1,12 +1,26 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* analyze.c
|
||||
* transform the parse tree into a query tree
|
||||
* transform the raw parse tree into a query tree
|
||||
*
|
||||
* For optimizable statements, we are careful to obtain a suitable lock on
|
||||
* each referenced table, and other modules of the backend preserve or
|
||||
* re-obtain these locks before depending on the results. It is therefore
|
||||
* okay to do significant semantic analysis of these statements. For
|
||||
* utility commands, no locks are obtained here (and if they were, we could
|
||||
* not be sure we'd still have them at execution). Hence the general rule
|
||||
* for utility commands is to just dump them into a Query node untransformed.
|
||||
* parse_analyze does do some purely syntactic transformations on CREATE TABLE
|
||||
* and ALTER TABLE, but that's about it. In cases where this module contains
|
||||
* mechanisms that are useful for utility statements, we provide separate
|
||||
* subroutines that should be called at the beginning of utility execution;
|
||||
* an example is analyzeIndexStmt.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.361 2007/02/20 17:32:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.362 2007/03/13 00:33:41 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -93,26 +107,17 @@ typedef struct
|
||||
static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
|
||||
static Query *transformStmt(ParseState *pstate, Node *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static List *transformInsertRow(ParseState *pstate, List *exprlist,
|
||||
List *stmtcols, List *icolumns, List *attrnos);
|
||||
static List *transformReturningList(ParseState *pstate, List *returningList);
|
||||
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||
static Query *transformDeclareCursorStmt(ParseState *pstate,
|
||||
DeclareCursorStmt *stmt);
|
||||
static Query *transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt);
|
||||
static Query *transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt);
|
||||
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
@ -155,7 +160,7 @@ static bool check_parameter_resolution_walker(Node *node,
|
||||
*
|
||||
* The result is a List of Query nodes (we need a list since some commands
|
||||
* produce multiple Queries). Optimizable statements require considerable
|
||||
* transformation, while many utility-type statements are simply hung off
|
||||
* transformation, while most utility-type statements are simply hung off
|
||||
* a dummy CMD_UTILITY Query node.
|
||||
*/
|
||||
List *
|
||||
@ -315,59 +320,12 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||
extras_before, extras_after);
|
||||
break;
|
||||
|
||||
case T_IndexStmt:
|
||||
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
|
||||
break;
|
||||
|
||||
case T_RuleStmt:
|
||||
result = transformRuleStmt(pstate, (RuleStmt *) parseTree,
|
||||
extras_before, extras_after);
|
||||
break;
|
||||
|
||||
case T_ViewStmt:
|
||||
result = transformViewStmt(pstate, (ViewStmt *) parseTree,
|
||||
extras_before, extras_after);
|
||||
break;
|
||||
|
||||
case T_ExplainStmt:
|
||||
{
|
||||
ExplainStmt *n = (ExplainStmt *) parseTree;
|
||||
|
||||
result = makeNode(Query);
|
||||
result->commandType = CMD_UTILITY;
|
||||
n->query = transformStmt(pstate, (Node *) n->query,
|
||||
extras_before, extras_after);
|
||||
result->utilityStmt = (Node *) parseTree;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CopyStmt:
|
||||
{
|
||||
CopyStmt *n = (CopyStmt *) parseTree;
|
||||
|
||||
result = makeNode(Query);
|
||||
result->commandType = CMD_UTILITY;
|
||||
if (n->query)
|
||||
n->query = transformStmt(pstate, (Node *) n->query,
|
||||
extras_before, extras_after);
|
||||
result->utilityStmt = (Node *) parseTree;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_AlterTableStmt:
|
||||
result = transformAlterTableStmt(pstate,
|
||||
(AlterTableStmt *) parseTree,
|
||||
extras_before, extras_after);
|
||||
break;
|
||||
|
||||
case T_PrepareStmt:
|
||||
result = transformPrepareStmt(pstate, (PrepareStmt *) parseTree);
|
||||
break;
|
||||
|
||||
case T_ExecuteStmt:
|
||||
result = transformExecuteStmt(pstate, (ExecuteStmt *) parseTree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Optimizable statements
|
||||
*/
|
||||
@ -397,16 +355,11 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||
}
|
||||
break;
|
||||
|
||||
case T_DeclareCursorStmt:
|
||||
result = transformDeclareCursorStmt(pstate,
|
||||
(DeclareCursorStmt *) parseTree);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/*
|
||||
* other statements don't require any transformation-- just return
|
||||
* the original parsetree, yea!
|
||||
* other statements don't require any transformation; just return
|
||||
* the original parsetree with a Query node plastered on top.
|
||||
*/
|
||||
result = makeNode(Query);
|
||||
result->commandType = CMD_UTILITY;
|
||||
@ -432,54 +385,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||
return result;
|
||||
}
|
||||
|
||||
static Query *
|
||||
transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
||||
List **extras_before, List **extras_after)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
stmt->query = transformStmt(pstate, (Node *) stmt->query,
|
||||
extras_before, extras_after);
|
||||
|
||||
/*
|
||||
* If a list of column names was given, run through and insert these into
|
||||
* the actual query tree. - thomas 2000-03-08
|
||||
*
|
||||
* Outer loop is over targetlist to make it easier to skip junk targetlist
|
||||
* entries.
|
||||
*/
|
||||
if (stmt->aliases != NIL)
|
||||
{
|
||||
ListCell *alist_item = list_head(stmt->aliases);
|
||||
ListCell *targetList;
|
||||
|
||||
foreach(targetList, stmt->query->targetList)
|
||||
{
|
||||
TargetEntry *te = (TargetEntry *) lfirst(targetList);
|
||||
|
||||
Assert(IsA(te, TargetEntry));
|
||||
/* junk columns don't get aliases */
|
||||
if (te->resjunk)
|
||||
continue;
|
||||
te->resname = pstrdup(strVal(lfirst(alist_item)));
|
||||
alist_item = lnext(alist_item);
|
||||
if (alist_item == NULL)
|
||||
break; /* done assigning aliases */
|
||||
}
|
||||
|
||||
if (alist_item != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("CREATE VIEW specifies more column "
|
||||
"names than columns")));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformDeleteStmt -
|
||||
* transforms a Delete Statement
|
||||
@ -1278,8 +1183,13 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
|
||||
/*
|
||||
* transformInhRelation
|
||||
*
|
||||
* Change the LIKE <subtable> portion of a CREATE TABLE statement into the
|
||||
* column definitions which recreate the user defined column portions of <subtable>.
|
||||
* Change the LIKE <subtable> portion of a CREATE TABLE statement into
|
||||
* column definitions which recreate the user defined column portions of
|
||||
* <subtable>.
|
||||
*
|
||||
* Note: because we do this at parse analysis time, any change in the
|
||||
* referenced table between parse analysis and execution won't be reflected
|
||||
* into the new table. Is this OK?
|
||||
*/
|
||||
static void
|
||||
transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
||||
@ -1644,7 +1554,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
* that strikes me as too anal-retentive. - tgl 2001-02-14
|
||||
*
|
||||
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
|
||||
* pre-existing indexes, too.
|
||||
* pre-existing indexes, too. However, that seems to risk race
|
||||
* conditions since we can't be sure the command will be executed
|
||||
* immediately.
|
||||
*/
|
||||
Assert(cxt->alist == NIL);
|
||||
if (cxt->pkey != NULL)
|
||||
@ -1746,37 +1658,55 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
|
||||
}
|
||||
|
||||
/*
|
||||
* transformIndexStmt -
|
||||
* transforms the qualification of the index statement
|
||||
* analyzeIndexStmt - perform parse analysis for CREATE INDEX
|
||||
*
|
||||
* Note that this has to be performed during execution not parse analysis, so
|
||||
* it's called by ProcessUtility. (Most other callers don't need to bother,
|
||||
* because this is a no-op for an index not using either index expressions or
|
||||
* a predicate expression.)
|
||||
*/
|
||||
static Query *
|
||||
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
IndexStmt *
|
||||
analyzeIndexStmt(IndexStmt *stmt, const char *queryString)
|
||||
{
|
||||
Query *qry;
|
||||
RangeTblEntry *rte = NULL;
|
||||
Relation rel;
|
||||
ParseState *pstate;
|
||||
RangeTblEntry *rte;
|
||||
ListCell *l;
|
||||
|
||||
qry = makeNode(Query);
|
||||
qry->commandType = CMD_UTILITY;
|
||||
/*
|
||||
* We must not scribble on the passed-in IndexStmt, so copy it. (This
|
||||
* is overkill, but easy.)
|
||||
*/
|
||||
stmt = (IndexStmt *) copyObject(stmt);
|
||||
|
||||
/*
|
||||
* Open the parent table with appropriate locking. We must do this
|
||||
* because addRangeTableEntry() would acquire only AccessShareLock,
|
||||
* leaving DefineIndex() needing to do a lock upgrade with consequent
|
||||
* risk of deadlock. Make sure this stays in sync with the type of
|
||||
* lock DefineIndex() wants.
|
||||
*/
|
||||
rel = heap_openrv(stmt->relation,
|
||||
(stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
|
||||
|
||||
/* Set up pstate */
|
||||
pstate = make_parsestate(NULL);
|
||||
pstate->p_sourcetext = queryString;
|
||||
|
||||
/*
|
||||
* Put the parent table into the rtable so that the expressions can
|
||||
* refer to its fields without qualification.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
|
||||
|
||||
/* no to join list, yes to namespaces */
|
||||
addRTEtoQuery(pstate, rte, false, true, true);
|
||||
|
||||
/* take care of the where clause */
|
||||
if (stmt->whereClause)
|
||||
{
|
||||
/*
|
||||
* Put the parent table into the rtable so that the WHERE clause can
|
||||
* refer to its fields without qualification. Note that this only
|
||||
* works if the parent table already exists --- so we can't easily
|
||||
* support predicates on indexes created implicitly by CREATE TABLE.
|
||||
* Fortunately, that's not necessary.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
|
||||
|
||||
/* no to join list, yes to namespaces */
|
||||
addRTEtoQuery(pstate, rte, false, true, true);
|
||||
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
|
||||
stmt->whereClause = transformWhereClause(pstate,
|
||||
stmt->whereClause,
|
||||
"WHERE");
|
||||
}
|
||||
|
||||
/* take care of any index expressions */
|
||||
foreach(l, stmt->indexParams)
|
||||
@ -1785,14 +1715,6 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
|
||||
if (ielem->expr)
|
||||
{
|
||||
/* Set up rtable as for predicate, see notes above */
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, stmt->relation, NULL,
|
||||
false, true);
|
||||
/* no to join list, yes to namespaces */
|
||||
addRTEtoQuery(pstate, rte, false, true, true);
|
||||
}
|
||||
ielem->expr = transformExpr(pstate, ielem->expr);
|
||||
|
||||
/*
|
||||
@ -1807,32 +1729,44 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
}
|
||||
}
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
stmt->rangetable = pstate->p_rtable;
|
||||
/*
|
||||
* Check that only the base rel is mentioned.
|
||||
*/
|
||||
if (list_length(pstate->p_rtable) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||
errmsg("index expressions and predicates can refer only to the table being indexed")));
|
||||
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
release_pstate_resources(pstate);
|
||||
pfree(pstate);
|
||||
|
||||
return qry;
|
||||
/* Close relation, but keep the lock */
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transformRuleStmt -
|
||||
* transform a Create Rule Statement. The actions is a list of parse
|
||||
* trees which is transformed into a list of query trees.
|
||||
* analyzeRuleStmt -
|
||||
* transform a Create Rule Statement. The action is a list of parse
|
||||
* trees which is transformed into a list of query trees, and we also
|
||||
* transform the WHERE clause if any.
|
||||
*
|
||||
* Note that this has to be performed during execution not parse analysis,
|
||||
* so it's called by DefineRule. Also note that we must not scribble on
|
||||
* the passed-in RuleStmt, so we do copyObject() on the actions and WHERE
|
||||
* clause.
|
||||
*/
|
||||
static Query *
|
||||
transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
List **extras_before, List **extras_after)
|
||||
void
|
||||
analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||
List **actions, Node **whereClause)
|
||||
{
|
||||
Query *qry;
|
||||
Relation rel;
|
||||
ParseState *pstate;
|
||||
RangeTblEntry *oldrte;
|
||||
RangeTblEntry *newrte;
|
||||
|
||||
qry = makeNode(Query);
|
||||
qry->commandType = CMD_UTILITY;
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
|
||||
/*
|
||||
* To avoid deadlock, make sure the first thing we do is grab
|
||||
* AccessExclusiveLock on the target relation. This will be needed by
|
||||
@ -1841,12 +1775,15 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
*/
|
||||
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
||||
|
||||
/* Set up pstate */
|
||||
pstate = make_parsestate(NULL);
|
||||
pstate->p_sourcetext = queryString;
|
||||
|
||||
/*
|
||||
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
|
||||
* Set up their RTEs in the main pstate for use in parsing the rule
|
||||
* qualification.
|
||||
*/
|
||||
Assert(pstate->p_rtable == NIL);
|
||||
oldrte = addRangeTableEntryForRelation(pstate, rel,
|
||||
makeAlias("*OLD*", NIL),
|
||||
false, false);
|
||||
@ -1886,8 +1823,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
}
|
||||
|
||||
/* take care of the where clause */
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
|
||||
"WHERE");
|
||||
*whereClause = transformWhereClause(pstate,
|
||||
(Node *) copyObject(stmt->whereClause),
|
||||
"WHERE");
|
||||
|
||||
if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
|
||||
ereport(ERROR,
|
||||
@ -1900,9 +1838,6 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in rule WHERE condition")));
|
||||
|
||||
/* save info about sublinks in where clause */
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
/*
|
||||
* 'instead nothing' rules with a qualification need a query rangetable so
|
||||
* the rewrite handler can add the negated rule qualification to the
|
||||
@ -1917,7 +1852,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
nothing_qry->rtable = pstate->p_rtable;
|
||||
nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
|
||||
|
||||
stmt->actions = list_make1(nothing_qry);
|
||||
*actions = list_make1(nothing_qry);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1930,12 +1865,20 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
foreach(l, stmt->actions)
|
||||
{
|
||||
Node *action = (Node *) lfirst(l);
|
||||
ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
|
||||
ParseState *sub_pstate = make_parsestate(NULL);
|
||||
Query *sub_qry,
|
||||
*top_subqry;
|
||||
List *extras_before = NIL;
|
||||
List *extras_after = NIL;
|
||||
bool has_old,
|
||||
has_new;
|
||||
|
||||
/*
|
||||
* Since outer ParseState isn't parent of inner, have to pass
|
||||
* down the query text by hand.
|
||||
*/
|
||||
sub_pstate->p_sourcetext = queryString;
|
||||
|
||||
/*
|
||||
* Set up OLD/NEW in the rtable for this statement. The entries
|
||||
* are added only to relnamespace, not varnamespace, because we
|
||||
@ -1955,8 +1898,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
addRTEtoQuery(sub_pstate, newrte, false, true, false);
|
||||
|
||||
/* Transform the rule action statement */
|
||||
top_subqry = transformStmt(sub_pstate, action,
|
||||
extras_before, extras_after);
|
||||
top_subqry = transformStmt(sub_pstate,
|
||||
(Node *) copyObject(action),
|
||||
&extras_before, &extras_after);
|
||||
|
||||
/*
|
||||
* We cannot support utility-statement actions (eg NOTIFY) with
|
||||
@ -1964,7 +1908,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
* the utility action execute conditionally.
|
||||
*/
|
||||
if (top_subqry->commandType == CMD_UTILITY &&
|
||||
stmt->whereClause != NULL)
|
||||
*whereClause != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
|
||||
@ -1982,7 +1926,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
* perhaps be relaxed someday, but for now, we may as well reject
|
||||
* such a rule immediately.
|
||||
*/
|
||||
if (sub_qry->setOperations != NULL && stmt->whereClause != NULL)
|
||||
if (sub_qry->setOperations != NULL && *whereClause != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
|
||||
@ -1992,10 +1936,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
*/
|
||||
has_old =
|
||||
rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
|
||||
rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
|
||||
rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
|
||||
has_new =
|
||||
rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
|
||||
rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
|
||||
rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
|
||||
|
||||
switch (stmt->event)
|
||||
{
|
||||
@ -2063,27 +2007,28 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
|
||||
}
|
||||
|
||||
newactions = list_concat(newactions, extras_before);
|
||||
newactions = lappend(newactions, top_subqry);
|
||||
newactions = list_concat(newactions, extras_after);
|
||||
|
||||
release_pstate_resources(sub_pstate);
|
||||
pfree(sub_pstate);
|
||||
}
|
||||
|
||||
stmt->actions = newactions;
|
||||
*actions = newactions;
|
||||
}
|
||||
|
||||
release_pstate_resources(pstate);
|
||||
pfree(pstate);
|
||||
|
||||
/* Close relation, but keep the exclusive lock */
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
return qry;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transformSelectStmt -
|
||||
* transforms a Select Statement
|
||||
*
|
||||
* Note: this is also used for DECLARE CURSOR statements.
|
||||
*/
|
||||
static Query *
|
||||
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
@ -2991,6 +2936,11 @@ transformReturningList(ParseState *pstate, List *returningList)
|
||||
/*
|
||||
* transformAlterTableStmt -
|
||||
* transform an Alter Table Statement
|
||||
*
|
||||
* CAUTION: resist the temptation to do any work here that depends on the
|
||||
* current state of the table. Actual execution of the command might not
|
||||
* occur till some future transaction. Hence, we do only purely syntactic
|
||||
* transformations here, comparable to the processing of CREATE TABLE.
|
||||
*/
|
||||
static Query *
|
||||
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
@ -3162,184 +3112,6 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
return qry;
|
||||
}
|
||||
|
||||
static Query *
|
||||
transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
List *extras_before = NIL,
|
||||
*extras_after = NIL;
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
/*
|
||||
* Don't allow both SCROLL and NO SCROLL to be specified
|
||||
*/
|
||||
if ((stmt->options & CURSOR_OPT_SCROLL) &&
|
||||
(stmt->options & CURSOR_OPT_NO_SCROLL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||
errmsg("cannot specify both SCROLL and NO SCROLL")));
|
||||
|
||||
stmt->query = (Node *) transformStmt(pstate, stmt->query,
|
||||
&extras_before, &extras_after);
|
||||
|
||||
/* Shouldn't get any extras, since grammar only allows SelectStmt */
|
||||
if (extras_before || extras_after)
|
||||
elog(ERROR, "unexpected extra stuff in cursor statement");
|
||||
if (!IsA(stmt->query, Query) ||
|
||||
((Query *) stmt->query)->commandType != CMD_SELECT)
|
||||
elog(ERROR, "unexpected non-SELECT command in cursor statement");
|
||||
|
||||
/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
|
||||
if (((Query *) stmt->query)->into)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||
errmsg("DECLARE CURSOR cannot specify INTO")));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static Query *
|
||||
transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
List *argtype_oids; /* argtype OIDs in a list */
|
||||
Oid *argtoids = NULL; /* and as an array */
|
||||
int nargs;
|
||||
List *queries;
|
||||
int i;
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
/* Transform list of TypeNames to list (and array) of type OIDs */
|
||||
nargs = list_length(stmt->argtypes);
|
||||
|
||||
if (nargs)
|
||||
{
|
||||
ListCell *l;
|
||||
|
||||
argtoids = (Oid *) palloc(nargs * sizeof(Oid));
|
||||
i = 0;
|
||||
|
||||
foreach(l, stmt->argtypes)
|
||||
{
|
||||
TypeName *tn = lfirst(l);
|
||||
Oid toid = typenameTypeId(pstate, tn);
|
||||
|
||||
argtoids[i++] = toid;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Analyze the statement using these parameter types (any parameters
|
||||
* passed in from above us will not be visible to it), allowing
|
||||
* information about unknown parameters to be deduced from context.
|
||||
*/
|
||||
queries = parse_analyze_varparams((Node *) stmt->query,
|
||||
pstate->p_sourcetext,
|
||||
&argtoids, &nargs);
|
||||
|
||||
/*
|
||||
* Shouldn't get any extra statements, since grammar only allows
|
||||
* OptimizableStmt
|
||||
*/
|
||||
if (list_length(queries) != 1)
|
||||
elog(ERROR, "unexpected extra stuff in prepared statement");
|
||||
|
||||
/*
|
||||
* Check that all parameter types were determined, and convert the array
|
||||
* of OIDs into a list for storage.
|
||||
*/
|
||||
argtype_oids = NIL;
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
Oid argtype = argtoids[i];
|
||||
|
||||
if (argtype == InvalidOid || argtype == UNKNOWNOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
||||
errmsg("could not determine data type of parameter $%d",
|
||||
i + 1)));
|
||||
|
||||
argtype_oids = lappend_oid(argtype_oids, argtype);
|
||||
}
|
||||
|
||||
stmt->argtype_oids = argtype_oids;
|
||||
stmt->query = linitial(queries);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Query *
|
||||
transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
List *paramtypes;
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
paramtypes = FetchPreparedStatementParams(stmt->name);
|
||||
|
||||
if (stmt->params || paramtypes)
|
||||
{
|
||||
int nparams = list_length(stmt->params);
|
||||
int nexpected = list_length(paramtypes);
|
||||
ListCell *l,
|
||||
*l2;
|
||||
int i = 1;
|
||||
|
||||
if (nparams != nexpected)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("wrong number of parameters for prepared statement \"%s\"",
|
||||
stmt->name),
|
||||
errdetail("Expected %d parameters but got %d.",
|
||||
nexpected, nparams)));
|
||||
|
||||
forboth(l, stmt->params, l2, paramtypes)
|
||||
{
|
||||
Node *expr = lfirst(l);
|
||||
Oid expected_type_id = lfirst_oid(l2);
|
||||
Oid given_type_id;
|
||||
|
||||
expr = transformExpr(pstate, expr);
|
||||
|
||||
/* Cannot contain subselects or aggregates */
|
||||
if (pstate->p_hasSubLinks)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot use subquery in EXECUTE parameter")));
|
||||
if (pstate->p_hasAggs)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in EXECUTE parameter")));
|
||||
|
||||
given_type_id = exprType(expr);
|
||||
|
||||
expr = coerce_to_target_type(pstate, expr, given_type_id,
|
||||
expected_type_id, -1,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
|
||||
if (expr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
|
||||
i,
|
||||
format_type_be(given_type_id),
|
||||
format_type_be(expected_type_id)),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
|
||||
lfirst(l) = expr;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* exported so planner can check again after rewriting, query pullup, etc */
|
||||
void
|
||||
|
Reference in New Issue
Block a user