diff options
Diffstat (limited to 'src/backend/parser')
| -rw-r--r-- | src/backend/parser/analyze.c | 40 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 40 | ||||
| -rw-r--r-- | src/backend/parser/parse_utilcmd.c | 29 |
3 files changed, 77 insertions, 32 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 21342e8a9d..bb2bf04e17 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 3a74fa5082..609c472701 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 37ca331c21..a8aee204c7 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1868,6 +1868,35 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, } /* + * 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. * |
