diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index f0d4a8e23d1..5b41d6320f5 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -748,7 +748,7 @@ flatten_join_alias_vars_mutator(Node *node, newvar = (Node *) lfirst(l); attnum++; /* Ignore dropped columns */ - if (IsA(newvar, Const)) + if (newvar == NULL) continue; /* @@ -778,6 +778,7 @@ flatten_join_alias_vars_mutator(Node *node, /* Expand join alias reference */ Assert(var->varattno > 0); newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); + Assert(newvar != NULL); /* * If we are expanding an alias carried down from an upper query, must diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 5ae0036f34d..39b49875a3b 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -24,6 +24,7 @@ #include "funcapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "parser/parsetree.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" @@ -682,14 +683,15 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, * The aliasvar could be either a Var or a COALESCE expression, * but in the latter case we should already have marked the two * referent variables as being selected, due to their use in the - * JOIN clause. So we need only be concerned with the simple Var - * case. + * JOIN clause. So we need only be concerned with the Var case. + * But we do need to drill down through implicit coercions. */ Var *aliasvar; Assert(col > 0 && col <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1); - if (IsA(aliasvar, Var)) + aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar); + if (aliasvar && IsA(aliasvar, Var)) markVarForSelectPriv(pstate, aliasvar, NULL); } } @@ -1779,10 +1781,10 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, * deleted columns in the join; but we have to check since * this routine is also used by the rewriter, and joins * found in stored rules might have join columns for - * since-deleted columns. This will be signaled by a NULL - * Const in the alias-vars list. + * since-deleted columns. This will be signaled by a null + * pointer in the alias-vars list. */ - if (IsA(avar, Const)) + if (avar == NULL) { if (include_dropped) { @@ -1790,8 +1792,15 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, *colnames = lappend(*colnames, makeString(pstrdup(""))); if (colvars) + { + /* + * Can't use join's column type here (it might + * be dropped!); but it doesn't really matter + * what type the Const claims to be. + */ *colvars = lappend(*colvars, - copyObject(avar)); + makeNullConst(INT4OID, -1)); + } } continue; } @@ -2167,6 +2176,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1); + Assert(aliasvar != NULL); *vartype = exprType(aliasvar); *vartypmod = exprTypmod(aliasvar); } @@ -2228,7 +2238,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) * but one in a stored rule might contain columns that were * dropped from the underlying tables, if said columns are * nowhere explicitly referenced in the rule. This will be - * signaled to us by a NULL Const in the joinaliasvars list. + * signaled to us by a null pointer in the joinaliasvars list. */ Var *aliasvar; @@ -2237,7 +2247,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) elog(ERROR, "invalid varattno %d", attnum); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - result = IsA(aliasvar, Const); + result = (aliasvar == NULL); } break; case RTE_FUNCTION: diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index c9ec7c33e26..b56f3a095a4 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -288,6 +288,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); + /* We intentionally don't strip implicit coercions here */ markTargetListOrigin(pstate, tle, aliasvar, netlevelsup); } break; @@ -1216,6 +1217,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) /* Join RTE --- recursively inspect the alias variable */ Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); + Assert(expr != NULL); + /* We intentionally don't strip implicit coercions here */ if (IsA(expr, Var)) return expandRecordVariable(pstate, (Var *) expr, netlevelsup); /* else fall through to inspect the expression */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 89ef9a356d2..8d9805013bb 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -17,6 +17,7 @@ #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "parser/analyze.h" #include "parser/parse_coerce.h" #include "parser/parsetree.h" @@ -81,9 +82,8 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs); * such a list in a stored rule to include references to dropped columns. * (If the column is not explicitly referenced anywhere else in the query, * the dependency mechanism won't consider it used by the rule and so won't - * prevent the column drop.) To support get_rte_attribute_is_dropped(), - * we replace join alias vars that reference dropped columns with NULL Const - * nodes. + * prevent the column drop.) To support get_rte_attribute_is_dropped(), we + * replace join alias vars that reference dropped columns with null pointers. * * (In PostgreSQL 8.0, we did not do this processing but instead had * get_rte_attribute_is_dropped() recurse to detect dropped columns in joins. @@ -142,8 +142,8 @@ AcquireRewriteLocks(Query *parsetree) /* * Scan the join's alias var list to see if any columns have - * been dropped, and if so replace those Vars with NULL - * Consts. + * been dropped, and if so replace those Vars with null + * pointers. * * Since a join has only two inputs, we can expect to see * multiple references to the same input RTE; optimize away @@ -154,16 +154,20 @@ AcquireRewriteLocks(Query *parsetree) curinputrte = NULL; foreach(ll, rte->joinaliasvars) { - Var *aliasvar = (Var *) lfirst(ll); + Var *aliasitem = (Var *) lfirst(ll); + Var *aliasvar = aliasitem; + + /* Look through any implicit coercion */ + aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar); /* * If the list item isn't a simple Var, then it must * represent a merged column, ie a USING column, and so it * couldn't possibly be dropped, since it's referenced in - * the join clause. (Conceivably it could also be a NULL - * constant already? But that's OK too.) + * the join clause. (Conceivably it could also be a null + * pointer already? But that's OK too.) */ - if (IsA(aliasvar, Var)) + if (aliasvar && IsA(aliasvar, Var)) { /* * The elements of an alias list have to refer to @@ -187,15 +191,11 @@ AcquireRewriteLocks(Query *parsetree) if (get_rte_attribute_is_dropped(curinputrte, aliasvar->varattno)) { - /* - * can't use vartype here, since that might be a - * now-dropped type OID, but it doesn't really - * matter what type the Const claims to be. - */ - aliasvar = (Var *) makeNullConst(INT4OID, -1); + /* Replace the join alias item with a NULL */ + aliasitem = NULL; } } - newaliasvars = lappend(newaliasvars, aliasvar); + newaliasvars = lappend(newaliasvars, aliasitem); } rte->joinaliasvars = newaliasvars; break; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 281b57e6ce9..344984abd5d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3505,7 +3505,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) Var *aliasvar; aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - if (IsA(aliasvar, Var)) + /* we intentionally don't strip implicit coercions here */ + if (aliasvar && IsA(aliasvar, Var)) { return get_variable(aliasvar, var->varlevelsup + levelsup, istoplevel, context); @@ -3789,6 +3790,8 @@ get_name_for_var_field(Var *var, int fieldno, elog(ERROR, "cannot decompile join alias var in plan tree"); Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); + Assert(expr != NULL); + /* we intentionally don't strip implicit coercions here */ if (IsA(expr, Var)) return get_name_for_var_field((Var *) expr, fieldno, var->varlevelsup + levelsup, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7123a5a130f..f23c9c54ae5 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -597,7 +597,7 @@ typedef struct XmlSerialize * a stored rule might contain entries for columns dropped since the rule * was created. (This is only possible for columns not actually referenced * in the rule.) When loading a stored rule, we replace the joinaliasvars - * items for any such columns with NULL Consts. (We can't simply delete + * items for any such columns with null pointers. (We can't simply delete * them from the joinaliasvars list, because that would affect the attnums * of Vars referencing the rest of the list.) * @@ -667,13 +667,19 @@ typedef struct RangeTblEntry /* * Fields valid for a join RTE (else NULL/zero): * - * joinaliasvars is a list of Vars or COALESCE expressions corresponding - * to the columns of the join result. An alias Var referencing column K - * of the join result can be replaced by the K'th element of joinaliasvars - * --- but to simplify the task of reverse-listing aliases correctly, we - * do not do that until planning time. In a Query loaded from a stored - * rule, it is also possible for joinaliasvars items to be NULL Consts, - * denoting columns dropped since the rule was made. + * joinaliasvars is a list of (usually) Vars corresponding to the columns + * of the join result. An alias Var referencing column K of the join + * result can be replaced by the K'th element of joinaliasvars --- but to + * simplify the task of reverse-listing aliases correctly, we do not do + * that until planning time. In detail: an element of joinaliasvars can + * be a Var of one of the join's input relations, or such a Var with an + * implicit coercion to the join's output column type, or a COALESCE + * expression containing the two input column Vars (possibly coerced). + * Within a Query loaded from a stored rule, it is also possible for + * joinaliasvars items to be null pointers, which are placeholders for + * (necessarily unreferenced) columns dropped since the rule was made. + * Also, once planning begins, joinaliasvars items can be almost anything, + * as a result of subquery-flattening substitutions. */ JoinType jointype; /* type of join */ List *joinaliasvars; /* list of alias-var expansions */