1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +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:
Tom Lane
2006-08-12 02:52:06 +00:00
parent 5c9e9c0c42
commit 7a3e30e608
35 changed files with 1472 additions and 422 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.20 2006/07/27 19:52:05 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.21 2006/08/12 02:52:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -439,6 +439,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
subparse->commandType = CMD_SELECT;
subparse->resultRelation = 0;
subparse->resultRelations = NIL;
subparse->returningLists = NIL;
subparse->into = NULL;
subparse->hasAggs = false;
subparse->groupClause = NIL;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.207 2006/08/05 17:21:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.208 2006/08/12 02:52:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -280,6 +280,10 @@ subquery_planner(Query *parse, double tuple_fraction,
preprocess_expression(root, (Node *) parse->targetList,
EXPRKIND_TARGET);
parse->returningList = (List *)
preprocess_expression(root, (Node *) parse->returningList,
EXPRKIND_TARGET);
preprocess_qual_conditions(root, (Node *) parse->jointree);
parse->havingQual = preprocess_expression(root, parse->havingQual,
@@ -554,13 +558,13 @@ inheritance_planner(PlannerInfo *root)
Query *parse = root->parse;
int parentRTindex = parse->resultRelation;
List *subplans = NIL;
List *resultRelations = NIL;
List *returningLists = NIL;
List *rtable = NIL;
List *tlist = NIL;
PlannerInfo subroot;
ListCell *l;
parse->resultRelations = NIL;
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
@@ -605,10 +609,20 @@ inheritance_planner(PlannerInfo *root)
subplans = lappend(subplans, subplan);
/* Build target-relations list for the executor */
parse->resultRelations = lappend_int(parse->resultRelations,
appinfo->child_relid);
resultRelations = lappend_int(resultRelations, appinfo->child_relid);
/* Build list of per-relation RETURNING targetlists */
if (parse->returningList)
{
Assert(list_length(subroot.parse->returningLists) == 1);
returningLists = list_concat(returningLists,
subroot.parse->returningLists);
}
}
parse->resultRelations = resultRelations;
parse->returningLists = returningLists;
/* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL;
@@ -1082,6 +1096,21 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
count_est);
}
/*
* Deal with the RETURNING clause if any. It's convenient to pass the
* returningList through setrefs.c now rather than at top level (if
* we waited, handling inherited UPDATE/DELETE would be much harder).
*/
if (parse->returningList)
{
List *rlist;
rlist = set_returning_clause_references(parse->returningList,
result_plan,
parse->resultRelation);
parse->returningLists = list_make1(rlist);
}
/*
* Return the actual output ordering in query_pathkeys for possible use by
* an outer query level.

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.123 2006/08/02 01:59:46 joe Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.124 2006/08/12 02:52:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,7 +42,6 @@ typedef struct
typedef struct
{
List *rtable;
indexed_tlist *outer_itlist;
indexed_tlist *inner_itlist;
Index acceptable_rel;
@@ -61,9 +60,8 @@ static void adjust_expr_varnos(Node *node, int rtoffset);
static bool adjust_expr_varnos_walker(Node *node, int *context);
static void fix_expr_references(Plan *plan, Node *node);
static bool fix_expr_references_walker(Node *node, void *context);
static void set_join_references(Join *join, List *rtable);
static void set_join_references(Join *join);
static void set_inner_join_references(Plan *inner_plan,
List *rtable,
indexed_tlist *outer_itlist);
static void set_uppernode_references(Plan *plan, Index subvarno);
static indexed_tlist *build_tlist_index(List *tlist);
@@ -74,7 +72,6 @@ static Var *search_indexed_tlist_for_non_var(Node *node,
indexed_tlist *itlist,
Index newvarno);
static List *join_references(List *clauses,
List *rtable,
indexed_tlist *outer_itlist,
indexed_tlist *inner_itlist,
Index acceptable_rel);
@@ -199,13 +196,13 @@ set_plan_references(Plan *plan, List *rtable)
}
break;
case T_NestLoop:
set_join_references((Join *) plan, rtable);
set_join_references((Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
break;
case T_MergeJoin:
set_join_references((Join *) plan, rtable);
set_join_references((Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -213,7 +210,7 @@ set_plan_references(Plan *plan, List *rtable)
(Node *) ((MergeJoin *) plan)->mergeclauses);
break;
case T_HashJoin:
set_join_references((Join *) plan, rtable);
set_join_references((Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -718,10 +715,9 @@ fix_expr_references_walker(Node *node, void *context)
* quals of the child indexscan. set_inner_join_references does that.
*
* 'join' is a join plan node
* 'rtable' is the associated range table
*/
static void
set_join_references(Join *join, List *rtable)
set_join_references(Join *join)
{
Plan *outer_plan = join->plan.lefttree;
Plan *inner_plan = join->plan.righttree;
@@ -733,17 +729,14 @@ set_join_references(Join *join, List *rtable)
/* All join plans have tlist, qual, and joinqual */
join->plan.targetlist = join_references(join->plan.targetlist,
rtable,
outer_itlist,
inner_itlist,
(Index) 0);
join->plan.qual = join_references(join->plan.qual,
rtable,
outer_itlist,
inner_itlist,
(Index) 0);
join->joinqual = join_references(join->joinqual,
rtable,
outer_itlist,
inner_itlist,
(Index) 0);
@@ -753,7 +746,6 @@ set_join_references(Join *join, List *rtable)
{
/* This processing is split out to handle possible recursion */
set_inner_join_references(inner_plan,
rtable,
outer_itlist);
}
else if (IsA(join, MergeJoin))
@@ -761,7 +753,6 @@ set_join_references(Join *join, List *rtable)
MergeJoin *mj = (MergeJoin *) join;
mj->mergeclauses = join_references(mj->mergeclauses,
rtable,
outer_itlist,
inner_itlist,
(Index) 0);
@@ -771,7 +762,6 @@ set_join_references(Join *join, List *rtable)
HashJoin *hj = (HashJoin *) join;
hj->hashclauses = join_references(hj->hashclauses,
rtable,
outer_itlist,
inner_itlist,
(Index) 0);
@@ -791,9 +781,7 @@ set_join_references(Join *join, List *rtable)
* function so that it can recurse.
*/
static void
set_inner_join_references(Plan *inner_plan,
List *rtable,
indexed_tlist *outer_itlist)
set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
{
if (IsA(inner_plan, IndexScan))
{
@@ -813,12 +801,10 @@ set_inner_join_references(Plan *inner_plan,
/* only refs to outer vars get changed in the inner qual */
innerscan->indexqualorig = join_references(indexqualorig,
rtable,
outer_itlist,
NULL,
innerrel);
innerscan->indexqual = join_references(innerscan->indexqual,
rtable,
outer_itlist,
NULL,
innerrel);
@@ -830,7 +816,6 @@ set_inner_join_references(Plan *inner_plan,
*/
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
rtable,
outer_itlist,
NULL,
innerrel);
@@ -851,12 +836,10 @@ set_inner_join_references(Plan *inner_plan,
/* only refs to outer vars get changed in the inner qual */
innerscan->indexqualorig = join_references(indexqualorig,
rtable,
outer_itlist,
NULL,
innerrel);
innerscan->indexqual = join_references(innerscan->indexqual,
rtable,
outer_itlist,
NULL,
innerrel);
@@ -880,7 +863,6 @@ set_inner_join_references(Plan *inner_plan,
/* only refs to outer vars get changed in the inner qual */
if (NumRelids((Node *) bitmapqualorig) > 1)
innerscan->bitmapqualorig = join_references(bitmapqualorig,
rtable,
outer_itlist,
NULL,
innerrel);
@@ -892,14 +874,12 @@ set_inner_join_references(Plan *inner_plan,
*/
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
rtable,
outer_itlist,
NULL,
innerrel);
/* Now recurse */
set_inner_join_references(inner_plan->lefttree,
rtable,
outer_itlist);
}
else if (IsA(inner_plan, BitmapAnd))
@@ -911,7 +891,6 @@ set_inner_join_references(Plan *inner_plan,
foreach(l, innerscan->bitmapplans)
{
set_inner_join_references((Plan *) lfirst(l),
rtable,
outer_itlist);
}
}
@@ -924,7 +903,6 @@ set_inner_join_references(Plan *inner_plan,
foreach(l, innerscan->bitmapplans)
{
set_inner_join_references((Plan *) lfirst(l),
rtable,
outer_itlist);
}
}
@@ -940,7 +918,6 @@ set_inner_join_references(Plan *inner_plan,
foreach(l, appendplan->appendplans)
{
set_inner_join_references((Plan *) lfirst(l),
rtable,
outer_itlist);
}
}
@@ -950,7 +927,6 @@ set_inner_join_references(Plan *inner_plan,
Index innerrel = innerscan->scan.scanrelid;
innerscan->tidquals = join_references(innerscan->tidquals,
rtable,
outer_itlist,
NULL,
innerrel);
@@ -1061,6 +1037,52 @@ build_tlist_index(List *tlist)
return itlist;
}
/*
* build_tlist_index_other_vars --- build a restricted tlist index
*
* This is like build_tlist_index, but we only index tlist entries that
* are Vars and belong to some rel other than the one specified.
*/
static indexed_tlist *
build_tlist_index_other_vars(List *tlist, Index ignore_rel)
{
indexed_tlist *itlist;
tlist_vinfo *vinfo;
ListCell *l;
/* Create data structure with enough slots for all tlist entries */
itlist = (indexed_tlist *)
palloc(offsetof(indexed_tlist, vars) +
list_length(tlist) * sizeof(tlist_vinfo));
itlist->tlist = tlist;
itlist->has_non_vars = false;
/* Find the desired Vars and fill in the index array */
vinfo = itlist->vars;
foreach(l, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->expr && IsA(tle->expr, Var))
{
Var *var = (Var *) tle->expr;
if (var->varno != ignore_rel)
{
vinfo->varno = var->varno;
vinfo->varattno = var->varattno;
vinfo->resno = tle->resno;
vinfo++;
}
}
}
itlist->num_vars = (vinfo - itlist->vars);
return itlist;
}
/*
* search_indexed_tlist_for_var --- find a Var in an indexed tlist
*
@@ -1137,14 +1159,14 @@ search_indexed_tlist_for_non_var(Node *node,
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
* and an indexscan being used on the inner side of a nestloop join.
* In the latter case we want to replace the outer-relation Vars by OUTER
* references, but not touch the Vars of the inner relation.
* references, but not touch the Vars of the inner relation. (We also
* implement RETURNING clause fixup using this second scenario.)
*
* For a normal join, acceptable_rel should be zero so that any failure to
* match a Var will be reported as an error. For the indexscan case,
* pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation.
*
* 'clauses' is the targetlist or list of join clauses
* 'rtable' is the current range table
* 'outer_itlist' is the indexed target list of the outer join relation
* 'inner_itlist' is the indexed target list of the inner join relation,
* or NULL
@@ -1156,14 +1178,12 @@ search_indexed_tlist_for_non_var(Node *node,
*/
static List *
join_references(List *clauses,
List *rtable,
indexed_tlist *outer_itlist,
indexed_tlist *inner_itlist,
Index acceptable_rel)
{
join_references_context context;
context.rtable = rtable;
context.outer_itlist = outer_itlist;
context.inner_itlist = inner_itlist;
context.acceptable_rel = acceptable_rel;
@@ -1295,6 +1315,53 @@ replace_vars_with_subplan_refs_mutator(Node *node,
(void *) context);
}
/*
* set_returning_clause_references
* Perform setrefs.c's work on a RETURNING targetlist
*
* If the query involves more than just the result table, we have to
* adjust any Vars that refer to other tables to reference junk tlist
* entries in the top plan's targetlist. Vars referencing the result
* table should be left alone, however (the executor will evaluate them
* using the actual heap tuple, after firing triggers if any). In the
* adjusted RETURNING list, result-table Vars will still have their
* original varno, but Vars for other rels will have varno OUTER.
*
* We also must apply fix_expr_references to the list.
*
* 'rlist': the RETURNING targetlist to be fixed
* 'topplan': the top Plan node for the query (not yet passed through
* set_plan_references)
* 'resultRelation': RT index of the query's result relation
*/
List *
set_returning_clause_references(List *rlist,
Plan *topplan,
Index resultRelation)
{
indexed_tlist *itlist;
/*
* We can perform the desired Var fixup by abusing the join_references
* machinery that normally handles inner indexscan fixup. We search
* the top plan's targetlist for Vars of non-result relations, and use
* join_references to convert RETURNING Vars into references to those
* tlist entries, while leaving result-rel Vars as-is.
*/
itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
rlist = join_references(rlist,
itlist,
NULL,
resultRelation);
fix_expr_references(topplan, (Node *) rlist);
pfree(itlist);
return rlist;
}
/*****************************************************************************
* OPERATOR REGPROC LOOKUP
*****************************************************************************/

View File

@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.40 2006/08/10 02:36:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.41 2006/08/12 02:52:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -372,6 +372,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
ResolveNew((Node *) parse->targetList,
varno, 0, rte,
subtlist, CMD_SELECT, 0);
parse->returningList = (List *)
ResolveNew((Node *) parse->returningList,
varno, 0, rte,
subtlist, CMD_SELECT, 0);
resolvenew_in_jointree((Node *) parse->jointree, varno,
rte, subtlist);
Assert(parse->setOperations == NULL);

View File

@@ -9,13 +9,14 @@
* relation in the correct order. For both UPDATE and DELETE queries,
* we need a junk targetlist entry holding the CTID attribute --- the
* executor relies on this to find the tuple to be replaced/deleted.
* We may also need junk tlist entries for Vars used in the RETURNING list.
*
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.82 2006/04/30 18:30:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.83 2006/08/12 02:52:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,6 +28,8 @@
#include "nodes/makefuncs.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
@@ -151,6 +154,40 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
}
}
/*
* If the query has a RETURNING list, add resjunk entries for any Vars
* used in RETURNING that belong to other relations. We need to do this
* to make these Vars available for the RETURNING calculation. Vars
* that belong to the result rel don't need to be added, because they
* will be made to refer to the actual heap tuple.
*/
if (parse->returningList && list_length(parse->rtable) > 1)
{
List *vars;
ListCell *l;
vars = pull_var_clause((Node *) parse->returningList, false);
foreach(l, vars)
{
Var *var = (Var *) lfirst(l);
TargetEntry *tle;
if (var->varno == result_relation)
continue; /* don't need it */
if (tlist_member((Node *) var, tlist))
continue; /* already got it */
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
NULL,
true);
tlist = lappend(tlist, tle);
}
list_free(vars);
}
return tlist;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.217 2006/08/04 14:09:51 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.218 2006/08/12 02:52:05 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -3351,6 +3351,8 @@ query_tree_walker(Query *query,
if (walker((Node *) query->targetList, context))
return true;
if (walker((Node *) query->returningList, context))
return true;
if (walker((Node *) query->jointree, context))
return true;
if (walker(query->setOperations, context))
@@ -3913,6 +3915,7 @@ query_tree_mutator(Query *query,
}
MUTATE(query->targetList, query->targetList, List *);
MUTATE(query->returningList, query->returningList, List *);
MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *);