mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Add INSERT/UPDATE/DELETE RETURNING, with basic docs and regression tests.
plpgsql support to come later. Along the way, convert execMain's SELECT INTO support into a DestReceiver, in order to eliminate some ugly special cases. Jonah Harris and Tom Lane
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.344 2006/08/10 02:36:29 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.345 2006/08/12 02:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -100,6 +100,7 @@ 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);
|
||||
@ -494,9 +495,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
*/
|
||||
transformFromClause(pstate, stmt->usingClause);
|
||||
|
||||
/* fix where clause */
|
||||
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
|
||||
|
||||
qry->returningList = transformReturningList(pstate, stmt->returningList);
|
||||
|
||||
/* done building the range table and jointree */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
@ -820,6 +822,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
attnos = lnext(attnos);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a RETURNING clause, we need to add the target relation
|
||||
* to the query namespace before processing it, so that Var references
|
||||
* in RETURNING will work. Also, remove any namespace entries added
|
||||
* in a sub-SELECT or VALUES list.
|
||||
*/
|
||||
if (stmt->returningList)
|
||||
{
|
||||
pstate->p_relnamespace = NIL;
|
||||
pstate->p_varnamespace = NIL;
|
||||
addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
|
||||
false, true, true);
|
||||
qry->returningList = transformReturningList(pstate,
|
||||
stmt->returningList);
|
||||
}
|
||||
|
||||
/* done building the range table and jointree */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
||||
@ -1297,7 +1315,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
||||
|
||||
if (including_indexes)
|
||||
elog(ERROR, "TODO");
|
||||
|
||||
|
||||
/*
|
||||
* Insert the inherited attributes into the cxt for the new table
|
||||
* definition.
|
||||
@ -1368,11 +1386,11 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
||||
def->cooked_default = pstrdup(this_default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (including_constraints && tupleDesc->constr) {
|
||||
int ccnum;
|
||||
AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
|
||||
|
||||
|
||||
for(ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) {
|
||||
char *ccname = tupleDesc->constr->check[ccnum].ccname;
|
||||
char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
|
||||
@ -1380,7 +1398,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
||||
Constraint *n = makeNode(Constraint);
|
||||
|
||||
change_varattnos_of_a_node(ccbin_node, attmap);
|
||||
|
||||
|
||||
n->contype = CONSTR_CHECK;
|
||||
n->name = pstrdup(ccname);
|
||||
n->raw_expr = ccbin_node;
|
||||
@ -2777,6 +2795,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
|
||||
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
|
||||
|
||||
qry->returningList = transformReturningList(pstate, stmt->returningList);
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
|
||||
@ -2851,7 +2871,62 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* tranformAlterTableStmt -
|
||||
* transformReturningList -
|
||||
* handle a RETURNING clause in INSERT/UPDATE/DELETE
|
||||
*/
|
||||
static List *
|
||||
transformReturningList(ParseState *pstate, List *returningList)
|
||||
{
|
||||
List *rlist;
|
||||
int save_next_resno;
|
||||
bool save_hasAggs;
|
||||
int length_rtable;
|
||||
|
||||
if (returningList == NIL)
|
||||
return NIL; /* nothing to do */
|
||||
|
||||
/*
|
||||
* We need to assign resnos starting at one in the RETURNING list.
|
||||
* Save and restore the main tlist's value of p_next_resno, just in
|
||||
* case someone looks at it later (probably won't happen).
|
||||
*/
|
||||
save_next_resno = pstate->p_next_resno;
|
||||
pstate->p_next_resno = 1;
|
||||
|
||||
/* save other state so that we can detect disallowed stuff */
|
||||
save_hasAggs = pstate->p_hasAggs;
|
||||
pstate->p_hasAggs = false;
|
||||
length_rtable = list_length(pstate->p_rtable);
|
||||
|
||||
/* transform RETURNING identically to a SELECT targetlist */
|
||||
rlist = transformTargetList(pstate, returningList);
|
||||
|
||||
/* check for disallowed stuff */
|
||||
|
||||
/* aggregates not allowed (but subselects are okay) */
|
||||
if (pstate->p_hasAggs)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in RETURNING")));
|
||||
|
||||
/* no new relation references please */
|
||||
if (list_length(pstate->p_rtable) != length_rtable)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("RETURNING may not contain references to other relations")));
|
||||
|
||||
/* mark column origins */
|
||||
markTargetListOrigins(pstate, rlist);
|
||||
|
||||
/* restore state */
|
||||
pstate->p_next_resno = save_next_resno;
|
||||
pstate->p_hasAggs = save_hasAggs;
|
||||
|
||||
return rlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformAlterTableStmt -
|
||||
* transform an Alter Table Statement
|
||||
*/
|
||||
static Query *
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.554 2006/08/02 01:59:46 joe Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.555 2006/08/12 02:52:05 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -244,7 +244,7 @@ static void doNegateFloat(Value *v);
|
||||
transaction_mode_list_or_empty
|
||||
TableFuncElementList
|
||||
prep_type_clause prep_type_list
|
||||
execute_param_clause using_clause
|
||||
execute_param_clause using_clause returning_clause
|
||||
|
||||
%type <range> into_clause OptTempTableName
|
||||
|
||||
@ -412,7 +412,7 @@ static void doNegateFloat(Value *v);
|
||||
QUOTE
|
||||
|
||||
READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
|
||||
REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
|
||||
REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
|
||||
ROLE ROLLBACK ROW ROWS RULE
|
||||
|
||||
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
|
||||
@ -5334,9 +5334,10 @@ DeallocateStmt: DEALLOCATE name
|
||||
*****************************************************************************/
|
||||
|
||||
InsertStmt:
|
||||
INSERT INTO qualified_name insert_rest
|
||||
INSERT INTO qualified_name insert_rest returning_clause
|
||||
{
|
||||
$4->relation = $3;
|
||||
$4->returningList = $5;
|
||||
$$ = (Node *) $4;
|
||||
}
|
||||
;
|
||||
@ -5380,6 +5381,11 @@ insert_column_item:
|
||||
}
|
||||
;
|
||||
|
||||
returning_clause:
|
||||
RETURNING target_list { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@ -5389,12 +5395,13 @@ insert_column_item:
|
||||
*****************************************************************************/
|
||||
|
||||
DeleteStmt: DELETE_P FROM relation_expr_opt_alias
|
||||
using_clause where_clause
|
||||
using_clause where_clause returning_clause
|
||||
{
|
||||
DeleteStmt *n = makeNode(DeleteStmt);
|
||||
n->relation = $3;
|
||||
n->usingClause = $4;
|
||||
n->whereClause = $5;
|
||||
n->returningList = $6;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@ -5445,12 +5452,14 @@ UpdateStmt: UPDATE relation_expr_opt_alias
|
||||
SET update_target_list
|
||||
from_clause
|
||||
where_clause
|
||||
returning_clause
|
||||
{
|
||||
UpdateStmt *n = makeNode(UpdateStmt);
|
||||
n->relation = $2;
|
||||
n->targetList = $4;
|
||||
n->fromClause = $5;
|
||||
n->whereClause = $6;
|
||||
n->returningList = $7;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@ -8809,6 +8818,7 @@ reserved_keyword:
|
||||
| PLACING
|
||||
| PRIMARY
|
||||
| REFERENCES
|
||||
| RETURNING
|
||||
| SELECT
|
||||
| SESSION_USER
|
||||
| SOME
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.174 2006/07/31 01:16:37 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.175 2006/08/12 02:52:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -284,6 +284,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"reset", RESET},
|
||||
{"restart", RESTART},
|
||||
{"restrict", RESTRICT},
|
||||
{"returning", RETURNING},
|
||||
{"returns", RETURNS},
|
||||
{"revoke", REVOKE},
|
||||
{"right", RIGHT},
|
||||
|
Reference in New Issue
Block a user