diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 8 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 235 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 5 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 40 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 2 |
7 files changed, 231 insertions, 64 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 4a817b75ad8..e892df98194 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -676,6 +676,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) sub_pstate->p_rtable = sub_rtable; sub_pstate->p_rteperminfos = sub_rteperminfos; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ + sub_pstate->p_nullingrels = NIL; sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -857,7 +858,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * Generate list of Vars referencing the RTE */ - exprList = expandNSItemVars(nsitem, 0, -1, NULL); + exprList = expandNSItemVars(pstate, nsitem, 0, -1, NULL); /* * Re-apply any indirection on the target column specs to the Vars diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index caecaaae504..4fbf80c2713 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -1167,7 +1167,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) * entries are RTE_JOIN kind. */ if (hasJoinRTEs) - groupClauses = (List *) flatten_join_alias_vars(qry, + groupClauses = (List *) flatten_join_alias_vars(NULL, qry, (Node *) groupClauses); /* @@ -1211,7 +1211,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) groupClauses, hasJoinRTEs, have_non_var_grouping); if (hasJoinRTEs) - clause = flatten_join_alias_vars(qry, clause); + clause = flatten_join_alias_vars(NULL, qry, clause); check_ungrouped_columns(clause, pstate, qry, groupClauses, groupClauseCommonVars, have_non_var_grouping, @@ -1222,7 +1222,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) groupClauses, hasJoinRTEs, have_non_var_grouping); if (hasJoinRTEs) - clause = flatten_join_alias_vars(qry, clause); + clause = flatten_join_alias_vars(NULL, qry, clause); check_ungrouped_columns(clause, pstate, qry, groupClauses, groupClauseCommonVars, have_non_var_grouping, @@ -1551,7 +1551,7 @@ finalize_grouping_exprs_walker(Node *node, Index ref = 0; if (context->hasJoinRTEs) - expr = flatten_join_alias_vars(context->qry, expr); + expr = flatten_join_alias_vars(NULL, context->qry, expr); /* * Each expression must match a grouping entry at the current diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index bafa5bb3813..f61f7947552 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -52,7 +52,8 @@ #include "utils/syscache.h" -static int extractRemainingColumns(ParseNamespaceColumn *src_nscolumns, +static int extractRemainingColumns(ParseState *pstate, + ParseNamespaceColumn *src_nscolumns, List *src_colnames, List **src_colnos, List **res_colnames, List **res_colvars, @@ -75,9 +76,11 @@ static ParseNamespaceItem *getNSItemForSpecialRelationTypes(ParseState *pstate, static Node *transformFromClauseItem(ParseState *pstate, Node *n, ParseNamespaceItem **top_nsitem, List **namespace); -static Var *buildVarFromNSColumn(ParseNamespaceColumn *nscol); +static Var *buildVarFromNSColumn(ParseState *pstate, + ParseNamespaceColumn *nscol); static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); +static void markRelsAsNulledBy(ParseState *pstate, Node *n, int jindex); static void setNamespaceColumnVisibility(List *namespace, bool cols_visible); static void setNamespaceLateralState(List *namespace, bool lateral_only, bool lateral_ok); @@ -251,7 +254,8 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * Returns the number of columns added. */ static int -extractRemainingColumns(ParseNamespaceColumn *src_nscolumns, +extractRemainingColumns(ParseState *pstate, + ParseNamespaceColumn *src_nscolumns, List *src_colnames, List **src_colnos, List **res_colnames, List **res_colvars, @@ -287,7 +291,8 @@ extractRemainingColumns(ParseNamespaceColumn *src_nscolumns, *src_colnos = lappend_int(*src_colnos, attnum); *res_colnames = lappend(*res_colnames, lfirst(lc)); *res_colvars = lappend(*res_colvars, - buildVarFromNSColumn(src_nscolumns + attnum - 1)); + buildVarFromNSColumn(pstate, + src_nscolumns + attnum - 1)); /* Copy the input relation's nscolumn data for this column */ res_nscolumns[colcount] = src_nscolumns[attnum - 1]; colcount++; @@ -1288,8 +1293,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, { /* * JOIN/USING (or NATURAL JOIN, as transformed above). Transform - * the list into an explicit ON-condition, and generate a list of - * merged result columns. + * the list into an explicit ON-condition. */ List *ucols = j->usingClause; List *l_usingvars = NIL; @@ -1307,8 +1311,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, int r_index = -1; Var *l_colvar, *r_colvar; - Node *u_colvar; - ParseNamespaceColumn *res_nscolumn; Assert(u_colname[0] != '\0'); @@ -1372,17 +1374,109 @@ transformFromClauseItem(ParseState *pstate, Node *n, u_colname))); r_colnos = lappend_int(r_colnos, r_index + 1); - l_colvar = buildVarFromNSColumn(l_nscolumns + l_index); + /* Build Vars to use in the generated JOIN ON clause */ + l_colvar = buildVarFromNSColumn(pstate, l_nscolumns + l_index); l_usingvars = lappend(l_usingvars, l_colvar); - r_colvar = buildVarFromNSColumn(r_nscolumns + r_index); + r_colvar = buildVarFromNSColumn(pstate, r_nscolumns + r_index); r_usingvars = lappend(r_usingvars, r_colvar); + /* + * While we're here, add column names to the res_colnames + * list. It's a bit ugly to do this here while the + * corresponding res_colvars entries are not made till later, + * but doing this later would require an additional traversal + * of the usingClause list. + */ res_colnames = lappend(res_colnames, lfirst(ucol)); + } + + /* Construct the generated JOIN ON clause */ + j->quals = transformJoinUsingClause(pstate, + l_usingvars, + r_usingvars); + } + else if (j->quals) + { + /* User-written ON-condition; transform it */ + j->quals = transformJoinOnClause(pstate, j, my_namespace); + } + else + { + /* CROSS JOIN: no quals */ + } + + /* + * If this is an outer join, now mark the appropriate child RTEs as + * being nulled by this join. We have finished processing the child + * join expressions as well as the current join's quals, which deal in + * non-nulled input columns. All future references to those RTEs will + * see possibly-nulled values, and we should mark generated Vars to + * account for that. In particular, the join alias Vars that we're + * about to build should reflect the nulling effects of this join. + * + * A difficulty with doing this is that we need the join's RT index, + * which we don't officially have yet. However, no other RTE can get + * made between here and the addRangeTableEntryForJoin call, so we can + * predict what the assignment will be. (Alternatively, we could call + * addRangeTableEntryForJoin before we have all the data computed, but + * this seems less ugly.) + */ + j->rtindex = list_length(pstate->p_rtable) + 1; + + switch (j->jointype) + { + case JOIN_INNER: + break; + case JOIN_LEFT: + markRelsAsNulledBy(pstate, j->rarg, j->rtindex); + break; + case JOIN_FULL: + markRelsAsNulledBy(pstate, j->larg, j->rtindex); + markRelsAsNulledBy(pstate, j->rarg, j->rtindex); + break; + case JOIN_RIGHT: + markRelsAsNulledBy(pstate, j->larg, j->rtindex); + break; + default: + /* shouldn't see any other types here */ + elog(ERROR, "unrecognized join type: %d", + (int) j->jointype); + break; + } + + /* + * Now we can construct join alias expressions for the USING columns. + */ + if (j->usingClause) + { + ListCell *lc1, + *lc2; + + /* Scan the colnos lists to recover info from the previous loop */ + forboth(lc1, l_colnos, lc2, r_colnos) + { + int l_index = lfirst_int(lc1) - 1; + int r_index = lfirst_int(lc2) - 1; + Var *l_colvar, + *r_colvar; + Node *u_colvar; + ParseNamespaceColumn *res_nscolumn; + + /* + * Note we re-build these Vars: they might have different + * varnullingrels than the ones made in the previous loop. + */ + l_colvar = buildVarFromNSColumn(pstate, l_nscolumns + l_index); + r_colvar = buildVarFromNSColumn(pstate, r_nscolumns + r_index); + + /* Construct the join alias Var for this column */ u_colvar = buildMergedJoinVar(pstate, j->jointype, l_colvar, r_colvar); res_colvars = lappend(res_colvars, u_colvar); + + /* Construct column's res_nscolumns[] entry */ res_nscolumn = res_nscolumns + res_colindex; res_colindex++; if (u_colvar == (Node *) l_colvar) @@ -1400,47 +1494,45 @@ transformFromClauseItem(ParseState *pstate, Node *n, /* * Merged column is not semantically equivalent to either * input, so it needs to be referenced as the join output - * column. We don't know the join's varno yet, so we'll - * replace these zeroes below. + * column. */ - res_nscolumn->p_varno = 0; + res_nscolumn->p_varno = j->rtindex; res_nscolumn->p_varattno = res_colindex; res_nscolumn->p_vartype = exprType(u_colvar); res_nscolumn->p_vartypmod = exprTypmod(u_colvar); res_nscolumn->p_varcollid = exprCollation(u_colvar); - res_nscolumn->p_varnosyn = 0; + res_nscolumn->p_varnosyn = j->rtindex; res_nscolumn->p_varattnosyn = res_colindex; } } - - j->quals = transformJoinUsingClause(pstate, - l_usingvars, - r_usingvars); - } - else if (j->quals) - { - /* User-written ON-condition; transform it */ - j->quals = transformJoinOnClause(pstate, j, my_namespace); - } - else - { - /* CROSS JOIN: no quals */ } /* Add remaining columns from each side to the output columns */ res_colindex += - extractRemainingColumns(l_nscolumns, l_colnames, &l_colnos, + extractRemainingColumns(pstate, + l_nscolumns, l_colnames, &l_colnos, &res_colnames, &res_colvars, res_nscolumns + res_colindex); res_colindex += - extractRemainingColumns(r_nscolumns, r_colnames, &r_colnos, + extractRemainingColumns(pstate, + r_nscolumns, r_colnames, &r_colnos, &res_colnames, &res_colvars, res_nscolumns + res_colindex); + /* If join has an alias, it syntactically hides all inputs */ + if (j->alias) + { + for (k = 0; k < res_colindex; k++) + { + ParseNamespaceColumn *nscol = res_nscolumns + k; + + nscol->p_varnosyn = j->rtindex; + nscol->p_varattnosyn = k + 1; + } + } + /* * Now build an RTE and nsitem for the result of the join. - * res_nscolumns isn't totally done yet, but that's OK because - * addRangeTableEntryForJoin doesn't examine it, only store a pointer. */ nsitem = addRangeTableEntryForJoin(pstate, res_colnames, @@ -1454,31 +1546,16 @@ transformFromClauseItem(ParseState *pstate, Node *n, j->alias, true); - j->rtindex = nsitem->p_rtindex; + /* Verify that we correctly predicted the join's RT index */ + Assert(j->rtindex == nsitem->p_rtindex); + /* Cross-check number of columns, too */ + Assert(res_colindex == list_length(nsitem->p_names->colnames)); /* - * Now that we know the join RTE's rangetable index, we can fix up the - * res_nscolumns data in places where it should contain that. + * Save a link to the JoinExpr in the proper element of p_joinexprs. + * Since we maintain that list lazily, it may be necessary to fill in + * empty entries before we can add the JoinExpr in the right place. */ - Assert(res_colindex == list_length(nsitem->p_names->colnames)); - for (k = 0; k < res_colindex; k++) - { - ParseNamespaceColumn *nscol = res_nscolumns + k; - - /* fill in join RTI for merged columns */ - if (nscol->p_varno == 0) - nscol->p_varno = j->rtindex; - if (nscol->p_varnosyn == 0) - nscol->p_varnosyn = j->rtindex; - /* if join has an alias, it syntactically hides all inputs */ - if (j->alias) - { - nscol->p_varnosyn = j->rtindex; - nscol->p_varattnosyn = k + 1; - } - } - - /* make a matching link to the JoinExpr for later use */ for (k = list_length(pstate->p_joinexprs) + 1; k < j->rtindex; k++) pstate->p_joinexprs = lappend(pstate->p_joinexprs, NULL); pstate->p_joinexprs = lappend(pstate->p_joinexprs, j); @@ -1547,10 +1624,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, * buildVarFromNSColumn - * build a Var node using ParseNamespaceColumn data * - * We assume varlevelsup should be 0, and no location is specified + * This is used to construct joinaliasvars entries. + * We can assume varlevelsup should be 0, and no location is specified. + * Note also that no column SELECT privilege is requested here; that would + * happen only if the column is actually referenced in the query. */ static Var * -buildVarFromNSColumn(ParseNamespaceColumn *nscol) +buildVarFromNSColumn(ParseState *pstate, ParseNamespaceColumn *nscol) { Var *var; @@ -1564,6 +1644,10 @@ buildVarFromNSColumn(ParseNamespaceColumn *nscol) /* makeVar doesn't offer parameters for these, so set by hand: */ var->varnosyn = nscol->p_varnosyn; var->varattnosyn = nscol->p_varattnosyn; + + /* ... and update varnullingrels */ + markNullableIfNeeded(pstate, var); + return var; } @@ -1676,6 +1760,47 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, } /* + * markRelsAsNulledBy - + * Mark the given jointree node and its children as nulled by join jindex + */ +static void +markRelsAsNulledBy(ParseState *pstate, Node *n, int jindex) +{ + int varno; + ListCell *lc; + + /* Note: we can't see FromExpr here */ + if (IsA(n, RangeTblRef)) + { + varno = ((RangeTblRef *) n)->rtindex; + } + else if (IsA(n, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) n; + + /* recurse to children */ + markRelsAsNulledBy(pstate, j->larg, jindex); + markRelsAsNulledBy(pstate, j->rarg, jindex); + varno = j->rtindex; + } + else + { + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n)); + varno = 0; /* keep compiler quiet */ + } + + /* + * Now add jindex to the p_nullingrels set for relation varno. Since we + * maintain the p_nullingrels list lazily, we might need to extend it to + * make the varno'th entry exist. + */ + while (list_length(pstate->p_nullingrels) < varno) + pstate->p_nullingrels = lappend(pstate->p_nullingrels, NULL); + lc = list_nth_cell(pstate->p_nullingrels, varno - 1); + lfirst(lc) = bms_add_member((Bitmapset *) lfirst(lc), jindex); +} + +/* * setNamespaceColumnVisibility - * Convenience subroutine to update cols_visible flags in a namespace list. */ diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 34757da0fa6..52787b67943 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1042,7 +1042,7 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ParseNamespaceItem *nsitem; nsitem = GetNSItemByRangeTablePosn(pstate, rtindex, sublevels_up); - args = expandNSItemVars(nsitem, sublevels_up, vlocation, NULL); + args = expandNSItemVars(pstate, nsitem, sublevels_up, vlocation, NULL); } else ereport(ERROR, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 53e904ca6d4..7ff41acb846 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2478,6 +2478,9 @@ transformWholeRowRef(ParseState *pstate, ParseNamespaceItem *nsitem, /* location is not filled in by makeWholeRowVar */ result->location = location; + /* mark Var if it's nulled by any outer joins */ + markNullableIfNeeded(pstate, result); + /* mark relation as requiring whole-row SELECT access */ markVarForSelectPriv(pstate, result); @@ -2505,6 +2508,8 @@ transformWholeRowRef(ParseState *pstate, ParseNamespaceItem *nsitem, rowexpr->colnames = copyObject(nsitem->p_names->colnames); rowexpr->location = location; + /* XXX we ought to mark the row as possibly nullable */ + return (Node *) rowexpr; } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index b490541f03d..52b4a6e89df 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -763,6 +763,9 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem, } var->location = location; + /* Mark Var if it's nulled by any outer joins */ + markNullableIfNeeded(pstate, var); + /* Require read access to the column */ markVarForSelectPriv(pstate, var); @@ -1024,6 +1027,35 @@ searchRangeTableForCol(ParseState *pstate, const char *alias, const char *colnam } /* + * markNullableIfNeeded + * If the RTE referenced by the Var is nullable by outer join(s) + * at this point in the query, set var->varnullingrels to show that. + */ +void +markNullableIfNeeded(ParseState *pstate, Var *var) +{ + int rtindex = var->varno; + Bitmapset *relids; + + /* Find the appropriate pstate */ + for (int lv = 0; lv < var->varlevelsup; lv++) + pstate = pstate->parentParseState; + + /* Find currently-relevant join relids for the Var's rel */ + if (rtindex > 0 && rtindex <= list_length(pstate->p_nullingrels)) + relids = (Bitmapset *) list_nth(pstate->p_nullingrels, rtindex - 1); + else + relids = NULL; + + /* + * Merge with any already-declared nulling rels. (Typically there won't + * be any, but let's get it right if there are.) + */ + if (relids != NULL) + var->varnullingrels = bms_union(var->varnullingrels, relids); +} + +/* * markRTEForSelectPriv * Mark the specified column of the RTE with index rtindex * as requiring SELECT privilege @@ -3087,7 +3119,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, * the list elements mustn't be modified. */ List * -expandNSItemVars(ParseNamespaceItem *nsitem, +expandNSItemVars(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location, List **colnames) { @@ -3123,6 +3155,10 @@ expandNSItemVars(ParseNamespaceItem *nsitem, var->varnosyn = nscol->p_varnosyn; var->varattnosyn = nscol->p_varattnosyn; var->location = location; + + /* ... and update varnullingrels */ + markNullableIfNeeded(pstate, var); + result = lappend(result, var); if (colnames) *colnames = lappend(*colnames, colnameval); @@ -3158,7 +3194,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, *var; List *te_list = NIL; - vars = expandNSItemVars(nsitem, sublevels_up, location, &names); + vars = expandNSItemVars(pstate, nsitem, sublevels_up, location, &names); /* * Require read access to the table. This is normally redundant with the diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 0ca6beccb8a..25781db5c1d 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1371,7 +1371,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, List *vars; ListCell *l; - vars = expandNSItemVars(nsitem, sublevels_up, location, NULL); + vars = expandNSItemVars(pstate, nsitem, sublevels_up, location, NULL); /* * Require read access to the table. This is normally redundant with |