|
| 1 | +/* |
| 2 | + * This file and its contents are licensed under the Apache License 2.0. |
| 3 | + * Please see the included NOTICE for copyright information and |
| 4 | + * LICENSE-APACHE for a copy of the license. |
| 5 | + */ |
| 6 | + |
| 7 | +/* |
| 8 | + * This file contains source code that was copied and/or modified from |
| 9 | + * the PostgreSQL database, which is licensed under the open-source |
| 10 | + * PostgreSQL License. Please see the NOTICE at the top level |
| 11 | + * directory for a copy of the PostgreSQL License. |
| 12 | + */ |
| 13 | +#include <postgres.h> |
| 14 | +#include <nodes/bitmapset.h> |
| 15 | +#include <nodes/nodes.h> |
| 16 | +#include <nodes/parsenodes.h> |
| 17 | +#include <nodes/pathnodes.h> |
| 18 | +#include <nodes/primnodes.h> |
| 19 | + |
| 20 | +#include "prepjointree.h" |
| 21 | + |
| 22 | +/* |
| 23 | + * Copied verbatim from src/backend/optimizer/prep/prepjointree.c. |
| 24 | + */ |
| 25 | +typedef struct nullingrel_info |
| 26 | +{ |
| 27 | + /* |
| 28 | + * For each leaf RTE, nullingrels[rti] is the set of relids of outer joins |
| 29 | + * that potentially null that RTE. |
| 30 | + */ |
| 31 | + Relids *nullingrels; |
| 32 | + /* Length of range table (maximum index in nullingrels[]) */ |
| 33 | + int rtlength; /* used only for assertion checks */ |
| 34 | +} nullingrel_info; |
| 35 | + |
| 36 | +static void get_nullingrels_recurse(Node *jtnode, Relids upper_nullingrels, |
| 37 | + nullingrel_info *info); |
| 38 | + |
| 39 | +/* |
| 40 | + * get_nullingrels: collect info about which outer joins null which relations |
| 41 | + * |
| 42 | + * The result struct contains, for each leaf relation used in the query, |
| 43 | + * the set of relids of outer joins that potentially null that rel. |
| 44 | + */ |
| 45 | +static nullingrel_info * |
| 46 | +get_nullingrels(Query *parse) |
| 47 | +{ |
| 48 | + nullingrel_info *result = palloc_object(nullingrel_info); |
| 49 | + |
| 50 | + result->rtlength = list_length(parse->rtable); |
| 51 | + result->nullingrels = palloc0_array(Relids, result->rtlength + 1); |
| 52 | + get_nullingrels_recurse((Node *) parse->jointree, NULL, result); |
| 53 | + return result; |
| 54 | +} |
| 55 | + |
| 56 | +/* |
| 57 | + * Recursive guts of get_nullingrels(). |
| 58 | + * |
| 59 | + * Note: at any recursion level, the passed-down upper_nullingrels must be |
| 60 | + * treated as a constant, but it can be stored directly into *info |
| 61 | + * if we're at leaf level. Upper recursion levels do not free their mutated |
| 62 | + * copies of the nullingrels, because those are probably referenced by |
| 63 | + * at least one leaf rel. |
| 64 | + */ |
| 65 | +static void |
| 66 | +get_nullingrels_recurse(Node *jtnode, Relids upper_nullingrels, nullingrel_info *info) |
| 67 | +{ |
| 68 | + if (jtnode == NULL) |
| 69 | + return; |
| 70 | + if (IsA(jtnode, RangeTblRef)) |
| 71 | + { |
| 72 | + int varno = ((RangeTblRef *) jtnode)->rtindex; |
| 73 | + |
| 74 | + Assert(varno > 0 && varno <= info->rtlength); |
| 75 | + info->nullingrels[varno] = upper_nullingrels; |
| 76 | + } |
| 77 | + else if (IsA(jtnode, FromExpr)) |
| 78 | + { |
| 79 | + FromExpr *f = (FromExpr *) jtnode; |
| 80 | + ListCell *l; |
| 81 | + |
| 82 | + foreach (l, f->fromlist) |
| 83 | + { |
| 84 | + get_nullingrels_recurse(lfirst(l), upper_nullingrels, info); |
| 85 | + } |
| 86 | + } |
| 87 | + else if (IsA(jtnode, JoinExpr)) |
| 88 | + { |
| 89 | + JoinExpr *j = (JoinExpr *) jtnode; |
| 90 | + Relids local_nullingrels; |
| 91 | + |
| 92 | + switch (j->jointype) |
| 93 | + { |
| 94 | + case JOIN_INNER: |
| 95 | + get_nullingrels_recurse(j->larg, upper_nullingrels, info); |
| 96 | + get_nullingrels_recurse(j->rarg, upper_nullingrels, info); |
| 97 | + break; |
| 98 | + case JOIN_LEFT: |
| 99 | + case JOIN_SEMI: |
| 100 | + case JOIN_ANTI: |
| 101 | + local_nullingrels = bms_add_member(bms_copy(upper_nullingrels), j->rtindex); |
| 102 | + get_nullingrels_recurse(j->larg, upper_nullingrels, info); |
| 103 | + get_nullingrels_recurse(j->rarg, local_nullingrels, info); |
| 104 | + break; |
| 105 | + case JOIN_FULL: |
| 106 | + local_nullingrels = bms_add_member(bms_copy(upper_nullingrels), j->rtindex); |
| 107 | + get_nullingrels_recurse(j->larg, local_nullingrels, info); |
| 108 | + get_nullingrels_recurse(j->rarg, local_nullingrels, info); |
| 109 | + break; |
| 110 | + case JOIN_RIGHT: |
| 111 | + local_nullingrels = bms_add_member(bms_copy(upper_nullingrels), j->rtindex); |
| 112 | + get_nullingrels_recurse(j->larg, local_nullingrels, info); |
| 113 | + get_nullingrels_recurse(j->rarg, upper_nullingrels, info); |
| 114 | + break; |
| 115 | + default: |
| 116 | + elog(ERROR, "unrecognized join type: %d", (int) j->jointype); |
| 117 | + break; |
| 118 | + } |
| 119 | + } |
| 120 | + else |
| 121 | + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); |
| 122 | +} |
| 123 | + |
| 124 | +/* |
| 125 | + * Return the set of rtindexes of relations that can be NULL-extended by some |
| 126 | + * outer join in the query's jointree. |
| 127 | + */ |
| 128 | +Bitmapset * |
| 129 | +ts_get_nullable_rtis(Query *parse) |
| 130 | +{ |
| 131 | + nullingrel_info *info = get_nullingrels(parse); |
| 132 | + Bitmapset *nullable_rtis = NULL; |
| 133 | + |
| 134 | + for (int rti = 1; rti <= info->rtlength; rti++) |
| 135 | + { |
| 136 | + if (!bms_is_empty(info->nullingrels[rti])) |
| 137 | + nullable_rtis = bms_add_member(nullable_rtis, rti); |
| 138 | + } |
| 139 | + |
| 140 | + return nullable_rtis; |
| 141 | +} |
0 commit comments