|
42 | 42 | #include"rewrite/rewriteManip.h"
|
43 | 43 |
|
44 | 44 |
|
| 45 | +typedefstructnullingrel_info |
| 46 | +{ |
| 47 | +/* |
| 48 | + * For each leaf RTE, nullingrels[rti] is the set of relids of outer joins |
| 49 | + * that potentially null that RTE. |
| 50 | + */ |
| 51 | +Relids*nullingrels; |
| 52 | +/* Length of range table (maximum index in nullingrels[]) */ |
| 53 | +intrtlength;/* used only for assertion checks */ |
| 54 | +}nullingrel_info; |
| 55 | + |
45 | 56 | typedefstructpullup_replace_vars_context
|
46 | 57 | {
|
47 | 58 | PlannerInfo*root;
|
48 | 59 | List*targetlist;/* tlist of subquery being pulled up */
|
49 | 60 | RangeTblEntry*target_rte;/* RTE of subquery */
|
50 | 61 | Relidsrelids;/* relids within subquery, as numbered after
|
51 | 62 | * pullup (set only if target_rte->lateral) */
|
| 63 | +nullingrel_info*nullinfo;/* per-RTE nullingrel info (set only if |
| 64 | + * target_rte->lateral) */ |
52 | 65 | bool*outer_hasSubLinks;/* -> outer query's hasSubLinks */
|
53 | 66 | intvarno;/* varno of subquery */
|
54 | 67 | boolwrap_non_vars;/* do we need all non-Var outputs to be PHVs? */
|
@@ -142,6 +155,9 @@ static void substitute_phv_relids(Node *node,
|
142 | 155 | staticvoidfix_append_rel_relids(PlannerInfo*root,intvarno,
|
143 | 156 | Relidssubrelids);
|
144 | 157 | staticNode*find_jointree_node_for_rel(Node*jtnode,intrelid);
|
| 158 | +staticnullingrel_info*get_nullingrels(Query*parse); |
| 159 | +staticvoidget_nullingrels_recurse(Node*jtnode,Relidsupper_nullingrels, |
| 160 | +nullingrel_info*info); |
145 | 161 |
|
146 | 162 |
|
147 | 163 | /*
|
@@ -1259,10 +1275,16 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
|
1259 | 1275 | rvcontext.targetlist=subquery->targetList;
|
1260 | 1276 | rvcontext.target_rte=rte;
|
1261 | 1277 | if (rte->lateral)
|
| 1278 | +{ |
1262 | 1279 | rvcontext.relids=get_relids_in_jointree((Node*)subquery->jointree,
|
1263 | 1280 | true, true);
|
1264 |
| -else/* won't need relids */ |
| 1281 | +rvcontext.nullinfo=get_nullingrels(parse); |
| 1282 | +} |
| 1283 | +else/* won't need these values */ |
| 1284 | +{ |
1265 | 1285 | rvcontext.relids=NULL;
|
| 1286 | +rvcontext.nullinfo=NULL; |
| 1287 | +} |
1266 | 1288 | rvcontext.outer_hasSubLinks=&parse->hasSubLinks;
|
1267 | 1289 | rvcontext.varno=varno;
|
1268 | 1290 | /* this flag will be set below, if needed */
|
@@ -1725,6 +1747,9 @@ is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte,
|
1725 | 1747 | * such refs to be wrapped in PlaceHolderVars, even when they're below
|
1726 | 1748 | * the nearest outer join?But it's a pretty hokey usage, so not
|
1727 | 1749 | * clear this is worth sweating over.)
|
| 1750 | + * |
| 1751 | + * If you change this, see also the comments about lateral references |
| 1752 | + * in pullup_replace_vars_callback(). |
1728 | 1753 | */
|
1729 | 1754 | if (lowest_outer_join!=NULL)
|
1730 | 1755 | {
|
@@ -1809,7 +1834,8 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
1809 | 1834 | rvcontext.root=root;
|
1810 | 1835 | rvcontext.targetlist=tlist;
|
1811 | 1836 | rvcontext.target_rte=rte;
|
1812 |
| -rvcontext.relids=NULL; |
| 1837 | +rvcontext.relids=NULL;/* can't be any lateral references here */ |
| 1838 | +rvcontext.nullinfo=NULL; |
1813 | 1839 | rvcontext.outer_hasSubLinks=&parse->hasSubLinks;
|
1814 | 1840 | rvcontext.varno=varno;
|
1815 | 1841 | rvcontext.wrap_non_vars= false;
|
@@ -1971,9 +1997,10 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode,
|
1971 | 1997 | /*
|
1972 | 1998 | * Since this function was reduced to a Const, it doesn't contain any
|
1973 | 1999 | * lateral references, even if it's marked as LATERAL. This means we
|
1974 |
| - * don't need to fill relids. |
| 2000 | + * don't need to fill relids or nullinfo. |
1975 | 2001 | */
|
1976 | 2002 | rvcontext.relids=NULL;
|
| 2003 | +rvcontext.nullinfo=NULL; |
1977 | 2004 |
|
1978 | 2005 | rvcontext.outer_hasSubLinks=&parse->hasSubLinks;
|
1979 | 2006 | rvcontext.varno= ((RangeTblRef*)jtnode)->rtindex;
|
@@ -2688,9 +2715,52 @@ pullup_replace_vars_callback(Var *var,
|
2688 | 2715 | {
|
2689 | 2716 | /*
|
2690 | 2717 | * There should be Vars/PHVs within the expression that we can
|
2691 |
| - * modify. Per above discussion, modify only Vars/PHVs of the |
2692 |
| - * subquery, not lateral references. |
| 2718 | + * modify. Vars/PHVs of the subquery should have the full |
| 2719 | + * var->varnullingrels added to them, but if there are lateral |
| 2720 | + * references within the expression, those must be marked with |
| 2721 | + * only the nullingrels that potentially apply to them. (This |
| 2722 | + * corresponds to the fact that the expression will now be |
| 2723 | + * evaluated at the join level of the Var that we are replacing: |
| 2724 | + * the lateral references may have bubbled up through fewer outer |
| 2725 | + * joins than the subquery's Vars have. Per the discussion above, |
| 2726 | + * we'll still get the right answers.) That relid set could be |
| 2727 | + * different for different lateral relations, so we have to do |
| 2728 | + * this work for each one. |
| 2729 | + * |
| 2730 | + * (Currently, the restrictions in is_simple_subquery() mean that |
| 2731 | + * at most we have to remove the lowest outer join's relid from |
| 2732 | + * the nullingrels of a lateral reference. However, we might |
| 2733 | + * relax those restrictions someday, so let's do this right.) |
2693 | 2734 | */
|
| 2735 | +if (rcon->target_rte->lateral) |
| 2736 | +{ |
| 2737 | +nullingrel_info*nullinfo=rcon->nullinfo; |
| 2738 | +Relidslvarnos; |
| 2739 | +intlvarno; |
| 2740 | + |
| 2741 | +/* |
| 2742 | + * Identify lateral varnos used within newnode. We must do |
| 2743 | + * this before injecting var->varnullingrels into the tree. |
| 2744 | + */ |
| 2745 | +lvarnos=pull_varnos(rcon->root,newnode); |
| 2746 | +lvarnos=bms_del_members(lvarnos,rcon->relids); |
| 2747 | +/* For each one, add relevant nullingrels if any */ |
| 2748 | +lvarno=-1; |
| 2749 | +while ((lvarno=bms_next_member(lvarnos,lvarno)) >=0) |
| 2750 | +{ |
| 2751 | +Relidslnullingrels; |
| 2752 | + |
| 2753 | +Assert(lvarno>0&&lvarno <=nullinfo->rtlength); |
| 2754 | +lnullingrels=bms_intersect(var->varnullingrels, |
| 2755 | +nullinfo->nullingrels[lvarno]); |
| 2756 | +if (!bms_is_empty(lnullingrels)) |
| 2757 | +newnode=add_nulling_relids(newnode, |
| 2758 | +bms_make_singleton(lvarno), |
| 2759 | +lnullingrels); |
| 2760 | +} |
| 2761 | +} |
| 2762 | + |
| 2763 | +/* Finally, deal with Vars/PHVs of the subquery itself */ |
2694 | 2764 | newnode=add_nulling_relids(newnode,
|
2695 | 2765 | rcon->relids,
|
2696 | 2766 | var->varnullingrels);
|
@@ -4120,3 +4190,94 @@ find_jointree_node_for_rel(Node *jtnode, int relid)
|
4120 | 4190 | (int)nodeTag(jtnode));
|
4121 | 4191 | returnNULL;
|
4122 | 4192 | }
|
| 4193 | + |
| 4194 | +/* |
| 4195 | + * get_nullingrels: collect info about which outer joins null which relations |
| 4196 | + * |
| 4197 | + * The result struct contains, for each leaf relation used in the query, |
| 4198 | + * the set of relids of outer joins that potentially null that rel. |
| 4199 | + */ |
| 4200 | +staticnullingrel_info* |
| 4201 | +get_nullingrels(Query*parse) |
| 4202 | +{ |
| 4203 | +nullingrel_info*result=palloc_object(nullingrel_info); |
| 4204 | + |
| 4205 | +result->rtlength=list_length(parse->rtable); |
| 4206 | +result->nullingrels=palloc0_array(Relids,result->rtlength+1); |
| 4207 | +get_nullingrels_recurse((Node*)parse->jointree,NULL,result); |
| 4208 | +returnresult; |
| 4209 | +} |
| 4210 | + |
| 4211 | +/* |
| 4212 | + * Recursive guts of get_nullingrels(). |
| 4213 | + * |
| 4214 | + * Note: at any recursion level, the passed-down upper_nullingrels must be |
| 4215 | + * treated as a constant, but it can be stored directly into *info |
| 4216 | + * if we're at leaf level. Upper recursion levels do not free their mutated |
| 4217 | + * copies of the nullingrels, because those are probably referenced by |
| 4218 | + * at least one leaf rel. |
| 4219 | + */ |
| 4220 | +staticvoid |
| 4221 | +get_nullingrels_recurse(Node*jtnode,Relidsupper_nullingrels, |
| 4222 | +nullingrel_info*info) |
| 4223 | +{ |
| 4224 | +if (jtnode==NULL) |
| 4225 | +return; |
| 4226 | +if (IsA(jtnode,RangeTblRef)) |
| 4227 | +{ |
| 4228 | +intvarno= ((RangeTblRef*)jtnode)->rtindex; |
| 4229 | + |
| 4230 | +Assert(varno>0&&varno <=info->rtlength); |
| 4231 | +info->nullingrels[varno]=upper_nullingrels; |
| 4232 | +} |
| 4233 | +elseif (IsA(jtnode,FromExpr)) |
| 4234 | +{ |
| 4235 | +FromExpr*f= (FromExpr*)jtnode; |
| 4236 | +ListCell*l; |
| 4237 | + |
| 4238 | +foreach(l,f->fromlist) |
| 4239 | +{ |
| 4240 | +get_nullingrels_recurse(lfirst(l),upper_nullingrels,info); |
| 4241 | +} |
| 4242 | +} |
| 4243 | +elseif (IsA(jtnode,JoinExpr)) |
| 4244 | +{ |
| 4245 | +JoinExpr*j= (JoinExpr*)jtnode; |
| 4246 | +Relidslocal_nullingrels; |
| 4247 | + |
| 4248 | +switch (j->jointype) |
| 4249 | +{ |
| 4250 | +caseJOIN_INNER: |
| 4251 | +get_nullingrels_recurse(j->larg,upper_nullingrels,info); |
| 4252 | +get_nullingrels_recurse(j->rarg,upper_nullingrels,info); |
| 4253 | +break; |
| 4254 | +caseJOIN_LEFT: |
| 4255 | +caseJOIN_SEMI: |
| 4256 | +caseJOIN_ANTI: |
| 4257 | +local_nullingrels=bms_add_member(bms_copy(upper_nullingrels), |
| 4258 | +j->rtindex); |
| 4259 | +get_nullingrels_recurse(j->larg,upper_nullingrels,info); |
| 4260 | +get_nullingrels_recurse(j->rarg,local_nullingrels,info); |
| 4261 | +break; |
| 4262 | +caseJOIN_FULL: |
| 4263 | +local_nullingrels=bms_add_member(bms_copy(upper_nullingrels), |
| 4264 | +j->rtindex); |
| 4265 | +get_nullingrels_recurse(j->larg,local_nullingrels,info); |
| 4266 | +get_nullingrels_recurse(j->rarg,local_nullingrels,info); |
| 4267 | +break; |
| 4268 | +caseJOIN_RIGHT: |
| 4269 | +local_nullingrels=bms_add_member(bms_copy(upper_nullingrels), |
| 4270 | +j->rtindex); |
| 4271 | +get_nullingrels_recurse(j->larg,local_nullingrels,info); |
| 4272 | +get_nullingrels_recurse(j->rarg,upper_nullingrels,info); |
| 4273 | +break; |
| 4274 | +default: |
| 4275 | +elog(ERROR,"unrecognized join type: %d", |
| 4276 | + (int)j->jointype); |
| 4277 | +break; |
| 4278 | +} |
| 4279 | +} |
| 4280 | +else |
| 4281 | +elog(ERROR,"unrecognized node type: %d", |
| 4282 | + (int)nodeTag(jtnode)); |
| 4283 | +} |