mirror of
https://github.com/postgres/postgres.git
synced 2025-10-18 04:29:09 +03:00
Allow WITH clauses to be attached to INSERT, UPDATE, DELETE statements.
This is not the hoped-for facility of using INSERT/UPDATE/DELETE inside a WITH, but rather the other way around. It seems useful in its own right anyway. Note: catversion bumped because, although the contents of stored rules might look compatible, there's actually a subtle semantic change. A single Query containing a WITH and INSERT...VALUES now represents writing the WITH before the INSERT, not before the VALUES. While it's not clear that that matters to anyone, it seems like a good idea to have it cited in the git history for catversion.h. Original patch by Marko Tiikkaja, with updating and cleanup by Hitoshi Harada.
This commit is contained in:
@@ -283,6 +283,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
|
||||
qry->commandType = CMD_DELETE;
|
||||
|
||||
/* process the WITH clause independently of all else */
|
||||
if (stmt->withClause)
|
||||
{
|
||||
qry->hasRecursive = stmt->withClause->recursive;
|
||||
qry->cteList = transformWithClause(pstate, stmt->withClause);
|
||||
}
|
||||
|
||||
/* set up range table with just the result rel */
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation,
|
||||
interpretInhOption(stmt->relation->inhOpt),
|
||||
@@ -340,9 +347,19 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
ListCell *attnos;
|
||||
ListCell *lc;
|
||||
|
||||
/* There can't be any outer WITH to worry about */
|
||||
Assert(pstate->p_ctenamespace == NIL);
|
||||
|
||||
qry->commandType = CMD_INSERT;
|
||||
pstate->p_is_insert = true;
|
||||
|
||||
/* process the WITH clause independently of all else */
|
||||
if (stmt->withClause)
|
||||
{
|
||||
qry->hasRecursive = stmt->withClause->recursive;
|
||||
qry->cteList = transformWithClause(pstate, stmt->withClause);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
|
||||
* VALUES list, or general SELECT input. We special-case VALUES, both for
|
||||
@@ -376,8 +393,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
pstate->p_relnamespace = NIL;
|
||||
sub_varnamespace = pstate->p_varnamespace;
|
||||
pstate->p_varnamespace = NIL;
|
||||
/* There can't be any outer WITH to worry about */
|
||||
Assert(pstate->p_ctenamespace == NIL);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -518,13 +533,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
List *exprsLists = NIL;
|
||||
int sublist_length = -1;
|
||||
|
||||
/* process the WITH clause */
|
||||
if (selectStmt->withClause)
|
||||
{
|
||||
qry->hasRecursive = selectStmt->withClause->recursive;
|
||||
qry->cteList = transformWithClause(pstate, selectStmt->withClause);
|
||||
}
|
||||
|
||||
foreach(lc, selectStmt->valuesLists)
|
||||
{
|
||||
List *sublist = (List *) lfirst(lc);
|
||||
@@ -618,13 +626,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
|
||||
Assert(list_length(valuesLists) == 1);
|
||||
|
||||
/* process the WITH clause */
|
||||
if (selectStmt->withClause)
|
||||
{
|
||||
qry->hasRecursive = selectStmt->withClause->recursive;
|
||||
qry->cteList = transformWithClause(pstate, selectStmt->withClause);
|
||||
}
|
||||
|
||||
/* Do basic expression transformation (same as a ROW() expr) */
|
||||
exprList = transformExpressionList(pstate,
|
||||
(List *) linitial(valuesLists));
|
||||
@@ -1794,6 +1795,13 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
qry->commandType = CMD_UPDATE;
|
||||
pstate->p_is_update = true;
|
||||
|
||||
/* process the WITH clause independently of all else */
|
||||
if (stmt->withClause)
|
||||
{
|
||||
qry->hasRecursive = stmt->withClause->recursive;
|
||||
qry->cteList = transformWithClause(pstate, stmt->withClause);
|
||||
}
|
||||
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation,
|
||||
interpretInhOption(stmt->relation->inhOpt),
|
||||
true,
|
||||
|
@@ -433,7 +433,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
|
||||
%type <boolean> xml_whitespace_option
|
||||
|
||||
%type <node> common_table_expr
|
||||
%type <with> with_clause
|
||||
%type <with> with_clause opt_with_clause
|
||||
%type <list> cte_list
|
||||
|
||||
%type <list> window_clause window_definition_list opt_partition_clause
|
||||
@@ -7269,11 +7269,12 @@ DeallocateStmt: DEALLOCATE name
|
||||
*****************************************************************************/
|
||||
|
||||
InsertStmt:
|
||||
INSERT INTO qualified_name insert_rest returning_clause
|
||||
opt_with_clause INSERT INTO qualified_name insert_rest returning_clause
|
||||
{
|
||||
$4->relation = $3;
|
||||
$4->returningList = $5;
|
||||
$$ = (Node *) $4;
|
||||
$5->relation = $4;
|
||||
$5->returningList = $6;
|
||||
$5->withClause = $1;
|
||||
$$ = (Node *) $5;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -7329,14 +7330,15 @@ returning_clause:
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DeleteStmt: DELETE_P FROM relation_expr_opt_alias
|
||||
DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias
|
||||
using_clause where_or_current_clause returning_clause
|
||||
{
|
||||
DeleteStmt *n = makeNode(DeleteStmt);
|
||||
n->relation = $3;
|
||||
n->usingClause = $4;
|
||||
n->whereClause = $5;
|
||||
n->returningList = $6;
|
||||
n->relation = $4;
|
||||
n->usingClause = $5;
|
||||
n->whereClause = $6;
|
||||
n->returningList = $7;
|
||||
n->withClause = $1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -7391,18 +7393,19 @@ opt_nowait: NOWAIT { $$ = TRUE; }
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
UpdateStmt: UPDATE relation_expr_opt_alias
|
||||
UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
|
||||
SET set_clause_list
|
||||
from_clause
|
||||
where_or_current_clause
|
||||
returning_clause
|
||||
{
|
||||
UpdateStmt *n = makeNode(UpdateStmt);
|
||||
n->relation = $2;
|
||||
n->targetList = $4;
|
||||
n->fromClause = $5;
|
||||
n->whereClause = $6;
|
||||
n->returningList = $7;
|
||||
n->relation = $3;
|
||||
n->targetList = $5;
|
||||
n->fromClause = $6;
|
||||
n->whereClause = $7;
|
||||
n->returningList = $8;
|
||||
n->withClause = $1;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -7744,6 +7747,11 @@ common_table_expr: name opt_name_list AS select_with_parens
|
||||
}
|
||||
;
|
||||
|
||||
opt_with_clause:
|
||||
with_clause { $$ = $1; }
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
;
|
||||
|
||||
into_clause:
|
||||
INTO OptTempTableName
|
||||
{
|
||||
|
@@ -1867,6 +1867,35 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* OLD/NEW are not allowed in WITH queries, because they would
|
||||
* amount to outer references for the WITH, which we disallow.
|
||||
* However, they were already in the outer rangetable when we
|
||||
* analyzed the query, so we have to check.
|
||||
*
|
||||
* Note that in the INSERT...SELECT case, we need to examine
|
||||
* the CTE lists of both top_subqry and sub_qry.
|
||||
*
|
||||
* Note that we aren't digging into the body of the query
|
||||
* looking for WITHs in nested sub-SELECTs. A WITH down there
|
||||
* can legitimately refer to OLD/NEW, because it'd be an
|
||||
* indirect-correlated outer reference.
|
||||
*/
|
||||
if (rangeTableEntry_used((Node *) top_subqry->cteList,
|
||||
PRS2_OLD_VARNO, 0) ||
|
||||
rangeTableEntry_used((Node *) sub_qry->cteList,
|
||||
PRS2_OLD_VARNO, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot refer to OLD within WITH query")));
|
||||
if (rangeTableEntry_used((Node *) top_subqry->cteList,
|
||||
PRS2_NEW_VARNO, 0) ||
|
||||
rangeTableEntry_used((Node *) sub_qry->cteList,
|
||||
PRS2_NEW_VARNO, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot refer to NEW within WITH query")));
|
||||
|
||||
/*
|
||||
* For efficiency's sake, add OLD to the rule action's jointree
|
||||
* only if it was actually referenced in the statement or qual.
|
||||
|
Reference in New Issue
Block a user