@@ -1010,7 +1010,7 @@ SetVarReturningType_walker(Node *node, SetVarReturningType_context *context)
1010
1010
return expression_tree_walker (node , SetVarReturningType_walker , context );
1011
1011
}
1012
1012
1013
- void
1013
+ static void
1014
1014
SetVarReturningType (Node * node , int result_relation , int sublevels_up ,
1015
1015
VarReturningType returning_type )
1016
1016
{
@@ -1797,6 +1797,11 @@ map_variable_attnos(Node *node,
1797
1797
* referencing result_relation, it is wrapped in a ReturningExpr node (causing
1798
1798
* the executor to return NULL if the OLD/NEW row doesn't exist).
1799
1799
*
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
+ *
1800
1805
* outer_hasSubLinks works the same as for replace_rte_variables().
1801
1806
*/
1802
1807
@@ -1814,6 +1819,30 @@ ReplaceVarsFromTargetList_callback(Var *var,
1814
1819
replace_rte_variables_context * context )
1815
1820
{
1816
1821
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
+ {
1817
1846
TargetEntry * tle ;
1818
1847
1819
1848
if (var -> varattno == InvalidAttrNumber )
@@ -1822,6 +1851,7 @@ ReplaceVarsFromTargetList_callback(Var *var,
1822
1851
RowExpr * rowexpr ;
1823
1852
List * colnames ;
1824
1853
List * fields ;
1854
+ ListCell * lc ;
1825
1855
1826
1856
/*
1827
1857
* If generating an expansion for a var of a named rowtype (ie, this
@@ -1830,29 +1860,46 @@ ReplaceVarsFromTargetList_callback(Var *var,
1830
1860
* omit dropped columns. In the latter case, attach column names to
1831
1861
* the RowExpr for use of the executor and ruleutils.c.
1832
1862
*
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
+ *
1833
1867
* The varreturningtype is copied onto each individual field Var, so
1834
1868
* that it is handled correctly when we recurse.
1835
1869
*/
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 ),
1839
1874
& colnames , & fields );
1840
- /* Adjust the generated per-field Vars... */
1841
- fields = (List * ) replace_rte_variables_mutator ((Node * ) fields ,
1842
- context );
1843
1875
rowexpr = makeNode (RowExpr );
1844
- rowexpr -> args = fields ;
1876
+ /* the fields will be set below */
1877
+ rowexpr -> args = NIL ;
1845
1878
rowexpr -> row_typeid = var -> vartype ;
1846
1879
rowexpr -> row_format = COERCE_IMPLICIT_CAST ;
1847
1880
rowexpr -> colnames = (var -> vartype == RECORDOID ) ? colnames : NIL ;
1848
1881
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
+ }
1849
1896
1850
1897
/* Wrap it in a ReturningExpr, if needed, per comments above */
1851
1898
if (var -> varreturningtype != VAR_RETURNING_DEFAULT )
1852
1899
{
1853
1900
ReturningExpr * rexpr = makeNode (ReturningExpr );
1854
1901
1855
- rexpr -> retlevelsup = var -> varlevelsup ;
1902
+ rexpr -> retlevelsup = 0 ;
1856
1903
rexpr -> retold = (var -> varreturningtype == VAR_RETURNING_OLD );
1857
1904
rexpr -> retexpr = (Expr * ) rowexpr ;
1858
1905
@@ -1863,20 +1910,21 @@ ReplaceVarsFromTargetList_callback(Var *var,
1863
1910
}
1864
1911
1865
1912
/* 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 );
1867
1914
1868
1915
if (tle == NULL || tle -> resjunk )
1869
1916
{
1870
1917
/* Failed to find column in targetlist */
1871
- switch (rcon -> nomatch_option )
1918
+ switch (nomatch_option )
1872
1919
{
1873
1920
case REPLACEVARS_REPORT_ERROR :
1874
1921
/* fall through, throw error below */
1875
1922
break ;
1876
1923
1877
1924
case REPLACEVARS_CHANGE_VARNO :
1878
1925
var = copyObject (var );
1879
- var -> varno = rcon -> nomatch_varno ;
1926
+ var -> varno = nomatch_varno ;
1927
+ var -> varlevelsup = 0 ;
1880
1928
/* we leave the syntactic referent alone */
1881
1929
return (Node * ) var ;
1882
1930
@@ -1906,10 +1954,6 @@ ReplaceVarsFromTargetList_callback(Var *var,
1906
1954
/* Make a copy of the tlist item to return */
1907
1955
Expr * newnode = copyObject (tle -> expr );
1908
1956
1909
- /* Must adjust varlevelsup if tlist item is from higher query */
1910
- if (var -> varlevelsup > 0 )
1911
- IncrementVarSublevelsUp ((Node * ) newnode , var -> varlevelsup , 0 );
1912
-
1913
1957
/*
1914
1958
* Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1915
1959
* and throw error if so. This case could only happen when expanding
@@ -1932,20 +1976,20 @@ ReplaceVarsFromTargetList_callback(Var *var,
1932
1976
* Copy varreturningtype onto any Vars in the tlist item that
1933
1977
* refer to result_relation (which had better be non-zero).
1934
1978
*/
1935
- if (rcon -> result_relation == 0 )
1979
+ if (result_relation == 0 )
1936
1980
elog (ERROR , "variable returning old/new found outside RETURNING list" );
1937
1981
1938
- SetVarReturningType ((Node * ) newnode , rcon -> result_relation ,
1939
- var -> varlevelsup , var -> varreturningtype );
1982
+ SetVarReturningType ((Node * ) newnode , result_relation ,
1983
+ 0 , var -> varreturningtype );
1940
1984
1941
1985
/* Wrap it in a ReturningExpr, if needed, per comments above */
1942
1986
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 )
1945
1989
{
1946
1990
ReturningExpr * rexpr = makeNode (ReturningExpr );
1947
1991
1948
- rexpr -> retlevelsup = var -> varlevelsup ;
1992
+ rexpr -> retlevelsup = 0 ;
1949
1993
rexpr -> retold = (var -> varreturningtype == VAR_RETURNING_OLD );
1950
1994
rexpr -> retexpr = newnode ;
1951
1995
0 commit comments