Skip to content

Commit 5fbc313

Browse files
committed
Change post-rewriter representation of dropped columns in joinaliasvars.
It's possible to drop a column from an input table of a JOIN clause in a view, if that column is nowhere actually referenced in the view. But it will still be there in the JOIN clause's joinaliasvars list. We used to replace such entries with NULL Const nodes, which is handy for generation of RowExpr expansion of a whole-row reference to the view. The trouble with that is that it can't be distinguished from the situation after subquery pull-up of a constant subquery output expression below the JOIN. Instead, replace such joinaliasvars with null pointers (empty expression trees), which can't be confused with pulled-up expressions. expandRTE() still emits the old convention, though, for convenience of RowExpr generation and to reduce the risk of breaking extension code. In HEAD and 9.3, this patch also fixes a problem with some new code in ruleutils.c that was failing to cope with implicitly-casted joinaliasvars entries, as per recent report from Feike Steenbergen. That oversight was because of an inadequate description of the data structure in parsenodes.h, which I've now corrected. There were some pre-existing oversights of the same ilk elsewhere, which I believe are now all fixed.
1 parent c0977b4 commit 5fbc313

File tree

6 files changed

+59
-35
lines changed

6 files changed

+59
-35
lines changed

src/backend/optimizer/util/var.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ flatten_join_alias_vars_mutator(Node *node,
795795
newvar = (Node *) lfirst(lv);
796796
attnum++;
797797
/* Ignore dropped columns */
798-
if (IsA(newvar, Const))
798+
if (newvar == NULL)
799799
continue;
800800
newvar = copyObject(newvar);
801801

@@ -828,6 +828,7 @@ flatten_join_alias_vars_mutator(Node *node,
828828
/* Expand join alias reference */
829829
Assert(var->varattno > 0);
830830
newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1);
831+
Assert(newvar != NULL);
831832
newvar = copyObject(newvar);
832833

833834
/*

src/backend/parser/parse_relation.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "funcapi.h"
2424
#include "nodes/makefuncs.h"
2525
#include "nodes/nodeFuncs.h"
26+
#include "optimizer/clauses.h"
2627
#include "parser/parsetree.h"
2728
#include "parser/parse_relation.h"
2829
#include "parser/parse_type.h"
@@ -661,14 +662,15 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
661662
* The aliasvar could be either a Var or a COALESCE expression,
662663
* but in the latter case we should already have marked the two
663664
* referent variables as being selected, due to their use in the
664-
* JOIN clause. So we need only be concerned with the simple Var
665-
* case.
665+
* JOIN clause. So we need only be concerned with the Var case.
666+
* But we do need to drill down through implicit coercions.
666667
*/
667668
Var *aliasvar;
668669

669670
Assert(col > 0 && col <= list_length(rte->joinaliasvars));
670671
aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1);
671-
if (IsA(aliasvar, Var))
672+
aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar);
673+
if (aliasvar && IsA(aliasvar, Var))
672674
markVarForSelectPriv(pstate, aliasvar, NULL);
673675
}
674676
}
@@ -1762,19 +1764,27 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
17621764
* deleted columns in the join; but we have to check since
17631765
* this routine is also used by the rewriter, and joins
17641766
* found in stored rules might have join columns for
1765-
* since-deleted columns. This will be signaled by a NULL
1766-
* Const in the alias-vars list.
1767+
* since-deleted columns. This will be signaled by a null
1768+
* pointer in the alias-vars list.
17671769
*/
1768-
if (IsA(avar, Const))
1770+
if (avar == NULL)
17691771
{
17701772
if (include_dropped)
17711773
{
17721774
if (colnames)
17731775
*colnames = lappend(*colnames,
17741776
makeString(pstrdup("")));
17751777
if (colvars)
1778+
{
1779+
/*
1780+
* Can't use join's column type here (it might
1781+
* be dropped!); but it doesn't really matter
1782+
* what type the Const claims to be.
1783+
*/
17761784
*colvars = lappend(*colvars,
1777-
copyObject(avar));
1785+
makeNullConst(INT4OID, -1,
1786+
InvalidOid));
1787+
}
17781788
}
17791789
continue;
17801790
}
@@ -2163,6 +2173,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
21632173

21642174
Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
21652175
aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
2176+
Assert(aliasvar != NULL);
21662177
*vartype = exprType(aliasvar);
21672178
*vartypmod = exprTypmod(aliasvar);
21682179
*varcollid = exprCollation(aliasvar);
@@ -2225,7 +2236,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
22252236
* but one in a stored rule might contain columns that were
22262237
* dropped from the underlying tables, if said columns are
22272238
* nowhere explicitly referenced in the rule. This will be
2228-
* signaled to us by a NULL Const in the joinaliasvars list.
2239+
* signaled to us by a null pointer in the joinaliasvars list.
22292240
*/
22302241
Var *aliasvar;
22312242

@@ -2234,7 +2245,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
22342245
elog(ERROR, "invalid varattno %d", attnum);
22352246
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
22362247

2237-
result = IsA(aliasvar, Const);
2248+
result = (aliasvar == NULL);
22382249
}
22392250
break;
22402251
case RTE_FUNCTION:

src/backend/parser/parse_target.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
305305

306306
Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
307307
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
308+
/* We intentionally don't strip implicit coercions here */
308309
markTargetListOrigin(pstate, tle, aliasvar, netlevelsup);
309310
}
310311
break;
@@ -1420,6 +1421,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
14201421
/* Join RTE --- recursively inspect the alias variable */
14211422
Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
14221423
expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
1424+
Assert(expr != NULL);
1425+
/* We intentionally don't strip implicit coercions here */
14231426
if (IsA(expr, Var))
14241427
return expandRecordVariable(pstate, (Var *) expr, netlevelsup);
14251428
/* else fall through to inspect the expression */

src/backend/rewrite/rewriteHandler.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "commands/trigger.h"
1919
#include "nodes/makefuncs.h"
2020
#include "nodes/nodeFuncs.h"
21+
#include "optimizer/clauses.h"
2122
#include "parser/analyze.h"
2223
#include "parser/parse_coerce.h"
2324
#include "parser/parsetree.h"
@@ -89,9 +90,8 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
8990
* such a list in a stored rule to include references to dropped columns.
9091
* (If the column is not explicitly referenced anywhere else in the query,
9192
* the dependency mechanism won't consider it used by the rule and so won't
92-
* prevent the column drop.) To support get_rte_attribute_is_dropped(),
93-
* we replace join alias vars that reference dropped columns with NULL Const
94-
* nodes.
93+
* prevent the column drop.) To support get_rte_attribute_is_dropped(), we
94+
* replace join alias vars that reference dropped columns with null pointers.
9595
*
9696
* (In PostgreSQL 8.0, we did not do this processing but instead had
9797
* get_rte_attribute_is_dropped() recurse to detect dropped columns in joins.
@@ -158,8 +158,8 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
158158

159159
/*
160160
* Scan the join's alias var list to see if any columns have
161-
* been dropped, and if so replace those Vars with NULL
162-
* Consts.
161+
* been dropped, and if so replace those Vars with null
162+
* pointers.
163163
*
164164
* Since a join has only two inputs, we can expect to see
165165
* multiple references to the same input RTE; optimize away
@@ -170,16 +170,20 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
170170
curinputrte = NULL;
171171
foreach(ll, rte->joinaliasvars)
172172
{
173-
Var *aliasvar = (Var *) lfirst(ll);
173+
Var *aliasitem = (Var *) lfirst(ll);
174+
Var *aliasvar = aliasitem;
175+
176+
/* Look through any implicit coercion */
177+
aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar);
174178

175179
/*
176180
* If the list item isn't a simple Var, then it must
177181
* represent a merged column, ie a USING column, and so it
178182
* couldn't possibly be dropped, since it's referenced in
179-
* the join clause. (Conceivably it could also be a NULL
180-
* constant already? But that's OK too.)
183+
* the join clause. (Conceivably it could also be a null
184+
* pointer already? But that's OK too.)
181185
*/
182-
if (IsA(aliasvar, Var))
186+
if (aliasvar && IsA(aliasvar, Var))
183187
{
184188
/*
185189
* The elements of an alias list have to refer to
@@ -203,15 +207,11 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
203207
if (get_rte_attribute_is_dropped(curinputrte,
204208
aliasvar->varattno))
205209
{
206-
/*
207-
* can't use vartype here, since that might be a
208-
* now-dropped type OID, but it doesn't really
209-
* matter what type the Const claims to be.
210-
*/
211-
aliasvar = (Var *) makeNullConst(INT4OID, -1, InvalidOid);
210+
/* Replace the join alias item with a NULL */
211+
aliasitem = NULL;
212212
}
213213
}
214-
newaliasvars = lappend(newaliasvars, aliasvar);
214+
newaliasvars = lappend(newaliasvars, aliasitem);
215215
}
216216
rte->joinaliasvars = newaliasvars;
217217
break;

src/backend/utils/adt/ruleutils.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4015,7 +4015,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
40154015
Var *aliasvar;
40164016

40174017
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
4018-
if (IsA(aliasvar, Var))
4018+
/* we intentionally don't strip implicit coercions here */
4019+
if (aliasvar && IsA(aliasvar, Var))
40194020
{
40204021
return get_variable(aliasvar, var->varlevelsup + levelsup,
40214022
istoplevel, context);
@@ -4322,6 +4323,8 @@ get_name_for_var_field(Var *var, int fieldno,
43224323
elog(ERROR, "cannot decompile join alias var in plan tree");
43234324
Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
43244325
expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
4326+
Assert(expr != NULL);
4327+
/* we intentionally don't strip implicit coercions here */
43254328
if (IsA(expr, Var))
43264329
return get_name_for_var_field((Var *) expr, fieldno,
43274330
var->varlevelsup + levelsup,

src/include/nodes/parsenodes.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ typedef struct XmlSerialize
640640
* a stored rule might contain entries for columns dropped since the rule
641641
* was created. (This is only possible for columns not actually referenced
642642
* in the rule.) When loading a stored rule, we replace the joinaliasvars
643-
* items for any such columns with NULL Consts. (We can't simply delete
643+
* items for any such columns with null pointers. (We can't simply delete
644644
* them from the joinaliasvars list, because that would affect the attnums
645645
* of Vars referencing the rest of the list.)
646646
*
@@ -711,13 +711,19 @@ typedef struct RangeTblEntry
711711
/*
712712
* Fields valid for a join RTE (else NULL/zero):
713713
*
714-
* joinaliasvars is a list of Vars or COALESCE expressions corresponding
715-
* to the columns of the join result. An alias Var referencing column K
716-
* of the join result can be replaced by the K'th element of joinaliasvars
717-
* --- but to simplify the task of reverse-listing aliases correctly, we
718-
* do not do that until planning time. In a Query loaded from a stored
719-
* rule, it is also possible for joinaliasvars items to be NULL Consts,
720-
* denoting columns dropped since the rule was made.
714+
* joinaliasvars is a list of (usually) Vars corresponding to the columns
715+
* of the join result. An alias Var referencing column K of the join
716+
* result can be replaced by the K'th element of joinaliasvars --- but to
717+
* simplify the task of reverse-listing aliases correctly, we do not do
718+
* that until planning time. In detail: an element of joinaliasvars can
719+
* be a Var of one of the join's input relations, or such a Var with an
720+
* implicit coercion to the join's output column type, or a COALESCE
721+
* expression containing the two input column Vars (possibly coerced).
722+
* Within a Query loaded from a stored rule, it is also possible for
723+
* joinaliasvars items to be null pointers, which are placeholders for
724+
* (necessarily unreferenced) columns dropped since the rule was made.
725+
* Also, once planning begins, joinaliasvars items can be almost anything,
726+
* as a result of subquery-flattening substitutions.
721727
*/
722728
JoinType jointype; /* type of join */
723729
List *joinaliasvars; /* list of alias-var expansions */

0 commit comments

Comments
 (0)