Skip to content

Commit 363a6e8

Browse files
author
Richard Guo
committed
Eliminate code duplication in replace_rte_variables callbacks
The callback functions ReplaceVarsFromTargetList_callback and pullup_replace_vars_callback are both used to replace Vars in an expression tree that reference a particular RTE with items from a targetlist, and they both need to expand whole-tuple references and deal with OLD/NEW RETURNING list Vars. As a result, currently there is significant code duplication between these two functions. This patch introduces a new function, ReplaceVarFromTargetList, to perform the replacement and calls it from both callback functions, thereby eliminating code duplication. Author: Dean Rasheed <dean.a.rasheed@gmail.com> Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: Jian He <jian.universality@gmail.com> Discussion: https://postgr.es/m/CAEZATCWhr=FM4X5kCPvVs-g2XEk+ceLsNtBK_zZMkqFn9vUjsw@mail.gmail.com
1 parent 1e4351a commit 363a6e8

File tree

3 files changed

+96
-142
lines changed

3 files changed

+96
-142
lines changed

src/backend/optimizer/prep/prepjointree.c

Lines changed: 24 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,131 +2666,38 @@ pullup_replace_vars_callback(Var *var,
26662666
/* Just copy the entry and fall through to adjust phlevelsup etc */
26672667
newnode = copyObject(rcon->rv_cache[varattno]);
26682668
}
2669-
else if (varattno == InvalidAttrNumber)
2669+
else
26702670
{
2671-
/* Must expand whole-tuple reference into RowExpr */
2672-
RowExpr *rowexpr;
2673-
List *colnames;
2674-
List *fields;
2675-
bool save_wrap_non_vars = rcon->wrap_non_vars;
2676-
int save_sublevelsup = context->sublevels_up;
2677-
26782671
/*
2679-
* If generating an expansion for a var of a named rowtype (ie, this
2680-
* is a plain relation RTE), then we must include dummy items for
2681-
* dropped columns. If the var is RECORD (ie, this is a JOIN), then
2682-
* omit dropped columns. In the latter case, attach column names to
2683-
* the RowExpr for use of the executor and ruleutils.c.
2684-
*
2685-
* In order to be able to cache the results, we always generate the
2686-
* expansion with varlevelsup = 0, and then adjust below if needed.
2672+
* Generate the replacement expression. This takes care of expanding
2673+
* wholerow references and dealing with non-default varreturningtype.
26872674
*/
2688-
expandRTE(rcon->target_rte,
2689-
var->varno, 0 /* not varlevelsup */ ,
2690-
var->varreturningtype, var->location,
2691-
(var->vartype != RECORDOID),
2692-
&colnames, &fields);
2693-
/* Expand the generated per-field Vars, but don't insert PHVs there */
2694-
rcon->wrap_non_vars = false;
2695-
context->sublevels_up = 0; /* to match the expandRTE output */
2696-
fields = (List *) replace_rte_variables_mutator((Node *) fields,
2697-
context);
2698-
rcon->wrap_non_vars = save_wrap_non_vars;
2699-
context->sublevels_up = save_sublevelsup;
2700-
2701-
rowexpr = makeNode(RowExpr);
2702-
rowexpr->args = fields;
2703-
rowexpr->row_typeid = var->vartype;
2704-
rowexpr->row_format = COERCE_IMPLICIT_CAST;
2705-
rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
2706-
rowexpr->location = var->location;
2707-
newnode = (Node *) rowexpr;
2708-
2709-
/* Handle any OLD/NEW RETURNING list Vars */
2710-
if (var->varreturningtype != VAR_RETURNING_DEFAULT)
2711-
{
2712-
/*
2713-
* Wrap the RowExpr in a ReturningExpr node, so that the executor
2714-
* returns NULL if the OLD/NEW row does not exist.
2715-
*/
2716-
ReturningExpr *rexpr = makeNode(ReturningExpr);
2717-
2718-
rexpr->retlevelsup = 0;
2719-
rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
2720-
rexpr->retexpr = (Expr *) newnode;
2721-
2722-
newnode = (Node *) rexpr;
2723-
}
2724-
2725-
/*
2726-
* Insert PlaceHolderVar if needed. Notice that we are wrapping one
2727-
* PlaceHolderVar around the whole RowExpr, rather than putting one
2728-
* around each element of the row. This is because we need the
2729-
* expression to yield NULL, not ROW(NULL,NULL,...) when it is forced
2730-
* to null by an outer join.
2731-
*/
2732-
if (need_phv)
2733-
{
2734-
newnode = (Node *)
2735-
make_placeholder_expr(rcon->root,
2736-
(Expr *) newnode,
2737-
bms_make_singleton(rcon->varno));
2738-
/* cache it with the PHV, and with phlevelsup etc not set yet */
2739-
rcon->rv_cache[InvalidAttrNumber] = copyObject(newnode);
2740-
}
2741-
}
2742-
else
2743-
{
2744-
/* Normal case referencing one targetlist element */
2745-
TargetEntry *tle = get_tle_by_resno(rcon->targetlist, varattno);
2746-
2747-
if (tle == NULL) /* shouldn't happen */
2748-
elog(ERROR, "could not find attribute %d in subquery targetlist",
2749-
varattno);
2750-
2751-
/* Make a copy of the tlist item to return */
2752-
newnode = (Node *) copyObject(tle->expr);
2753-
2754-
/* Handle any OLD/NEW RETURNING list Vars */
2755-
if (var->varreturningtype != VAR_RETURNING_DEFAULT)
2756-
{
2757-
/*
2758-
* Copy varreturningtype onto any Vars in the tlist item that
2759-
* refer to result_relation (which had better be non-zero).
2760-
*/
2761-
if (rcon->result_relation == 0)
2762-
elog(ERROR, "variable returning old/new found outside RETURNING list");
2763-
2764-
SetVarReturningType((Node *) newnode, rcon->result_relation,
2765-
0, var->varreturningtype);
2766-
2767-
/*
2768-
* If the replacement expression in the targetlist is not simply a
2769-
* Var referencing result_relation, wrap it in a ReturningExpr
2770-
* node, so that the executor returns NULL if the OLD/NEW row does
2771-
* not exist.
2772-
*/
2773-
if (!IsA(newnode, Var) ||
2774-
((Var *) newnode)->varno != rcon->result_relation ||
2775-
((Var *) newnode)->varlevelsup != 0)
2776-
{
2777-
ReturningExpr *rexpr = makeNode(ReturningExpr);
2778-
2779-
rexpr->retlevelsup = 0;
2780-
rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
2781-
rexpr->retexpr = (Expr *) newnode;
2782-
2783-
newnode = (Node *) rexpr;
2784-
}
2785-
}
2675+
newnode = ReplaceVarFromTargetList(var,
2676+
rcon->target_rte,
2677+
rcon->targetlist,
2678+
rcon->result_relation,
2679+
REPLACEVARS_REPORT_ERROR,
2680+
0);
27862681

27872682
/* Insert PlaceHolderVar if needed */
27882683
if (need_phv)
27892684
{
27902685
bool wrap;
27912686

2792-
if (newnode && IsA(newnode, Var) &&
2793-
((Var *) newnode)->varlevelsup == 0)
2687+
if (varattno == InvalidAttrNumber)
2688+
{
2689+
/*
2690+
* Insert PlaceHolderVar for whole-tuple reference. Notice
2691+
* that we are wrapping one PlaceHolderVar around the whole
2692+
* RowExpr, rather than putting one around each element of the
2693+
* row. This is because we need the expression to yield NULL,
2694+
* not ROW(NULL,NULL,...) when it is forced to null by an
2695+
* outer join.
2696+
*/
2697+
wrap = true;
2698+
}
2699+
else if (newnode && IsA(newnode, Var) &&
2700+
((Var *) newnode)->varlevelsup == 0)
27942701
{
27952702
/*
27962703
* Simple Vars always escape being wrapped, unless they are
@@ -2936,7 +2843,7 @@ pullup_replace_vars_callback(Var *var,
29362843
* Cache it if possible (ie, if the attno is in range, which
29372844
* it probably always should be).
29382845
*/
2939-
if (varattno > InvalidAttrNumber &&
2846+
if (varattno >= InvalidAttrNumber &&
29402847
varattno <= list_length(rcon->targetlist))
29412848
rcon->rv_cache[varattno] = copyObject(newnode);
29422849
}

src/backend/rewrite/rewriteManip.c

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ SetVarReturningType_walker(Node *node, SetVarReturningType_context *context)
10101010
return expression_tree_walker(node, SetVarReturningType_walker, context);
10111011
}
10121012

1013-
void
1013+
static void
10141014
SetVarReturningType(Node *node, int result_relation, int sublevels_up,
10151015
VarReturningType returning_type)
10161016
{
@@ -1797,6 +1797,11 @@ map_variable_attnos(Node *node,
17971797
* referencing result_relation, it is wrapped in a ReturningExpr node (causing
17981798
* the executor to return NULL if the OLD/NEW row doesn't exist).
17991799
*
1800+
* Note that ReplaceVarFromTargetList always generates the replacement
1801+
* expression with varlevelsup = 0. The caller is responsible for adjusting
1802+
* the varlevelsup if needed. This simplifies the caller's life if it wants to
1803+
* cache the replacement expressions.
1804+
*
18001805
* outer_hasSubLinks works the same as for replace_rte_variables().
18011806
*/
18021807

@@ -1814,6 +1819,30 @@ ReplaceVarsFromTargetList_callback(Var *var,
18141819
replace_rte_variables_context *context)
18151820
{
18161821
ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1822+
Node *newnode;
1823+
1824+
newnode = ReplaceVarFromTargetList(var,
1825+
rcon->target_rte,
1826+
rcon->targetlist,
1827+
rcon->result_relation,
1828+
rcon->nomatch_option,
1829+
rcon->nomatch_varno);
1830+
1831+
/* Must adjust varlevelsup if replaced Var is within a subquery */
1832+
if (var->varlevelsup > 0)
1833+
IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
1834+
1835+
return newnode;
1836+
}
1837+
1838+
Node *
1839+
ReplaceVarFromTargetList(Var *var,
1840+
RangeTblEntry *target_rte,
1841+
List *targetlist,
1842+
int result_relation,
1843+
ReplaceVarsNoMatchOption nomatch_option,
1844+
int nomatch_varno)
1845+
{
18171846
TargetEntry *tle;
18181847

18191848
if (var->varattno == InvalidAttrNumber)
@@ -1822,6 +1851,7 @@ ReplaceVarsFromTargetList_callback(Var *var,
18221851
RowExpr *rowexpr;
18231852
List *colnames;
18241853
List *fields;
1854+
ListCell *lc;
18251855

18261856
/*
18271857
* If generating an expansion for a var of a named rowtype (ie, this
@@ -1830,29 +1860,46 @@ ReplaceVarsFromTargetList_callback(Var *var,
18301860
* omit dropped columns. In the latter case, attach column names to
18311861
* the RowExpr for use of the executor and ruleutils.c.
18321862
*
1863+
* In order to be able to cache the results, we always generate the
1864+
* expansion with varlevelsup = 0. The caller is responsible for
1865+
* adjusting it if needed.
1866+
*
18331867
* The varreturningtype is copied onto each individual field Var, so
18341868
* that it is handled correctly when we recurse.
18351869
*/
1836-
expandRTE(rcon->target_rte,
1837-
var->varno, var->varlevelsup, var->varreturningtype,
1838-
var->location, (var->vartype != RECORDOID),
1870+
expandRTE(target_rte,
1871+
var->varno, 0 /* not varlevelsup */ ,
1872+
var->varreturningtype, var->location,
1873+
(var->vartype != RECORDOID),
18391874
&colnames, &fields);
1840-
/* Adjust the generated per-field Vars... */
1841-
fields = (List *) replace_rte_variables_mutator((Node *) fields,
1842-
context);
18431875
rowexpr = makeNode(RowExpr);
1844-
rowexpr->args = fields;
1876+
/* the fields will be set below */
1877+
rowexpr->args = NIL;
18451878
rowexpr->row_typeid = var->vartype;
18461879
rowexpr->row_format = COERCE_IMPLICIT_CAST;
18471880
rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
18481881
rowexpr->location = var->location;
1882+
/* Adjust the generated per-field Vars... */
1883+
foreach(lc, fields)
1884+
{
1885+
Node *field = lfirst(lc);
1886+
1887+
if (field && IsA(field, Var))
1888+
field = ReplaceVarFromTargetList((Var *) field,
1889+
target_rte,
1890+
targetlist,
1891+
result_relation,
1892+
nomatch_option,
1893+
nomatch_varno);
1894+
rowexpr->args = lappend(rowexpr->args, field);
1895+
}
18491896

18501897
/* Wrap it in a ReturningExpr, if needed, per comments above */
18511898
if (var->varreturningtype != VAR_RETURNING_DEFAULT)
18521899
{
18531900
ReturningExpr *rexpr = makeNode(ReturningExpr);
18541901

1855-
rexpr->retlevelsup = var->varlevelsup;
1902+
rexpr->retlevelsup = 0;
18561903
rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
18571904
rexpr->retexpr = (Expr *) rowexpr;
18581905

@@ -1863,20 +1910,21 @@ ReplaceVarsFromTargetList_callback(Var *var,
18631910
}
18641911

18651912
/* Normal case referencing one targetlist element */
1866-
tle = get_tle_by_resno(rcon->targetlist, var->varattno);
1913+
tle = get_tle_by_resno(targetlist, var->varattno);
18671914

18681915
if (tle == NULL || tle->resjunk)
18691916
{
18701917
/* Failed to find column in targetlist */
1871-
switch (rcon->nomatch_option)
1918+
switch (nomatch_option)
18721919
{
18731920
case REPLACEVARS_REPORT_ERROR:
18741921
/* fall through, throw error below */
18751922
break;
18761923

18771924
case REPLACEVARS_CHANGE_VARNO:
18781925
var = copyObject(var);
1879-
var->varno = rcon->nomatch_varno;
1926+
var->varno = nomatch_varno;
1927+
var->varlevelsup = 0;
18801928
/* we leave the syntactic referent alone */
18811929
return (Node *) var;
18821930

@@ -1906,10 +1954,6 @@ ReplaceVarsFromTargetList_callback(Var *var,
19061954
/* Make a copy of the tlist item to return */
19071955
Expr *newnode = copyObject(tle->expr);
19081956

1909-
/* Must adjust varlevelsup if tlist item is from higher query */
1910-
if (var->varlevelsup > 0)
1911-
IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
1912-
19131957
/*
19141958
* Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
19151959
* and throw error if so. This case could only happen when expanding
@@ -1932,20 +1976,20 @@ ReplaceVarsFromTargetList_callback(Var *var,
19321976
* Copy varreturningtype onto any Vars in the tlist item that
19331977
* refer to result_relation (which had better be non-zero).
19341978
*/
1935-
if (rcon->result_relation == 0)
1979+
if (result_relation == 0)
19361980
elog(ERROR, "variable returning old/new found outside RETURNING list");
19371981

1938-
SetVarReturningType((Node *) newnode, rcon->result_relation,
1939-
var->varlevelsup, var->varreturningtype);
1982+
SetVarReturningType((Node *) newnode, result_relation,
1983+
0, var->varreturningtype);
19401984

19411985
/* Wrap it in a ReturningExpr, if needed, per comments above */
19421986
if (!IsA(newnode, Var) ||
1943-
((Var *) newnode)->varno != rcon->result_relation ||
1944-
((Var *) newnode)->varlevelsup != var->varlevelsup)
1987+
((Var *) newnode)->varno != result_relation ||
1988+
((Var *) newnode)->varlevelsup != 0)
19451989
{
19461990
ReturningExpr *rexpr = makeNode(ReturningExpr);
19471991

1948-
rexpr->retlevelsup = var->varlevelsup;
1992+
rexpr->retlevelsup = 0;
19491993
rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
19501994
rexpr->retexpr = newnode;
19511995

src/include/rewrite/rewriteManip.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ extern void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
5555
extern void IncrementVarSublevelsUp_rtable(List *rtable,
5656
int delta_sublevels_up, int min_sublevels_up);
5757

58-
extern void SetVarReturningType(Node *node, int result_relation, int sublevels_up,
59-
VarReturningType returning_type);
60-
6158
extern bool rangeTableEntry_used(Node *node, int rt_index,
6259
int sublevels_up);
6360

@@ -92,6 +89,12 @@ extern Node *map_variable_attnos(Node *node,
9289
const struct AttrMap *attno_map,
9390
Oid to_rowtype, bool *found_whole_row);
9491

92+
extern Node *ReplaceVarFromTargetList(Var *var,
93+
RangeTblEntry *target_rte,
94+
List *targetlist,
95+
int result_relation,
96+
ReplaceVarsNoMatchOption nomatch_option,
97+
int nomatch_varno);
9598
extern Node *ReplaceVarsFromTargetList(Node *node,
9699
int target_varno, int sublevels_up,
97100
RangeTblEntry *target_rte,

0 commit comments

Comments
 (0)