@@ -5363,7 +5363,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
5363
5363
* Handle a simple Var for examine_variable
5364
5364
*
5365
5365
* This is split out as a subroutine so that we can recurse to deal with
5366
- * Vars referencing subqueries.
5366
+ * Vars referencing subqueries (either sub-SELECT-in-FROM or CTE style) .
5367
5367
*
5368
5368
* We already filled in all the fields of *vardata except for the stats tuple.
5369
5369
*/
@@ -5497,13 +5497,19 @@ examine_simple_variable(PlannerInfo *root, Var *var,
5497
5497
vardata -> acl_ok = true;
5498
5498
}
5499
5499
}
5500
- else if (rte -> rtekind == RTE_SUBQUERY && !rte -> inh )
5500
+ else if ((rte -> rtekind == RTE_SUBQUERY && !rte -> inh ) ||
5501
+ (rte -> rtekind == RTE_CTE && !rte -> self_reference ))
5501
5502
{
5502
5503
/*
5503
- * Plain subquery (not one that was converted to an appendrel).
5504
+ * Plain subquery (not one that was converted to an appendrel) or
5505
+ * non-recursive CTE. In either case, we can try to find out what the
5506
+ * Var refers to within the subquery. We skip this for appendrel and
5507
+ * recursive-CTE cases because any column stats we did find would
5508
+ * likely not be very relevant.
5504
5509
*/
5505
- Query * subquery = rte -> subquery ;
5506
- RelOptInfo * rel ;
5510
+ PlannerInfo * subroot ;
5511
+ Query * subquery ;
5512
+ List * subtlist ;
5507
5513
TargetEntry * ste ;
5508
5514
5509
5515
/*
@@ -5512,6 +5518,85 @@ examine_simple_variable(PlannerInfo *root, Var *var,
5512
5518
if (var -> varattno == InvalidAttrNumber )
5513
5519
return ;
5514
5520
5521
+ /*
5522
+ * Otherwise, find the subquery's planner subroot.
5523
+ */
5524
+ if (rte -> rtekind == RTE_SUBQUERY )
5525
+ {
5526
+ RelOptInfo * rel ;
5527
+
5528
+ /*
5529
+ * Fetch RelOptInfo for subquery. Note that we don't change the
5530
+ * rel returned in vardata, since caller expects it to be a rel of
5531
+ * the caller's query level. Because we might already be
5532
+ * recursing, we can't use that rel pointer either, but have to
5533
+ * look up the Var's rel afresh.
5534
+ */
5535
+ rel = find_base_rel (root , var -> varno );
5536
+
5537
+ subroot = rel -> subroot ;
5538
+ }
5539
+ else
5540
+ {
5541
+ /* CTE case is more difficult */
5542
+ PlannerInfo * cteroot ;
5543
+ Index levelsup ;
5544
+ int ndx ;
5545
+ int plan_id ;
5546
+ ListCell * lc ;
5547
+
5548
+ /*
5549
+ * Find the referenced CTE, and locate the subroot previously made
5550
+ * for it.
5551
+ */
5552
+ levelsup = rte -> ctelevelsup ;
5553
+ cteroot = root ;
5554
+ while (levelsup -- > 0 )
5555
+ {
5556
+ cteroot = cteroot -> parent_root ;
5557
+ if (!cteroot ) /* shouldn't happen */
5558
+ elog (ERROR , "bad levelsup for CTE \"%s\"" , rte -> ctename );
5559
+ }
5560
+
5561
+ /*
5562
+ * Note: cte_plan_ids can be shorter than cteList, if we are still
5563
+ * working on planning the CTEs (ie, this is a side-reference from
5564
+ * another CTE). So we mustn't use forboth here.
5565
+ */
5566
+ ndx = 0 ;
5567
+ foreach (lc , cteroot -> parse -> cteList )
5568
+ {
5569
+ CommonTableExpr * cte = (CommonTableExpr * ) lfirst (lc );
5570
+
5571
+ if (strcmp (cte -> ctename , rte -> ctename ) == 0 )
5572
+ break ;
5573
+ ndx ++ ;
5574
+ }
5575
+ if (lc == NULL ) /* shouldn't happen */
5576
+ elog (ERROR , "could not find CTE \"%s\"" , rte -> ctename );
5577
+ if (ndx >= list_length (cteroot -> cte_plan_ids ))
5578
+ elog (ERROR , "could not find plan for CTE \"%s\"" , rte -> ctename );
5579
+ plan_id = list_nth_int (cteroot -> cte_plan_ids , ndx );
5580
+ if (plan_id <= 0 )
5581
+ elog (ERROR , "no plan was made for CTE \"%s\"" , rte -> ctename );
5582
+ subroot = list_nth (root -> glob -> subroots , plan_id - 1 );
5583
+ }
5584
+
5585
+ /* If the subquery hasn't been planned yet, we have to punt */
5586
+ if (subroot == NULL )
5587
+ return ;
5588
+ Assert (IsA (subroot , PlannerInfo ));
5589
+
5590
+ /*
5591
+ * We must use the subquery parsetree as mangled by the planner, not
5592
+ * the raw version from the RTE, because we need a Var that will refer
5593
+ * to the subroot's live RelOptInfos. For instance, if any subquery
5594
+ * pullup happened during planning, Vars in the targetlist might have
5595
+ * gotten replaced, and we need to see the replacement expressions.
5596
+ */
5597
+ subquery = subroot -> parse ;
5598
+ Assert (IsA (subquery , Query ));
5599
+
5515
5600
/*
5516
5601
* Punt if subquery uses set operations or GROUP BY, as these will
5517
5602
* mash underlying columns' stats beyond recognition. (Set ops are
@@ -5525,33 +5610,12 @@ examine_simple_variable(PlannerInfo *root, Var *var,
5525
5610
subquery -> groupingSets )
5526
5611
return ;
5527
5612
5528
- /*
5529
- * OK, fetch RelOptInfo for subquery. Note that we don't change the
5530
- * rel returned in vardata, since caller expects it to be a rel of the
5531
- * caller's query level. Because we might already be recursing, we
5532
- * can't use that rel pointer either, but have to look up the Var's
5533
- * rel afresh.
5534
- */
5535
- rel = find_base_rel (root , var -> varno );
5536
-
5537
- /* If the subquery hasn't been planned yet, we have to punt */
5538
- if (rel -> subroot == NULL )
5539
- return ;
5540
- Assert (IsA (rel -> subroot , PlannerInfo ));
5541
-
5542
- /*
5543
- * Switch our attention to the subquery as mangled by the planner. It
5544
- * was okay to look at the pre-planning version for the tests above,
5545
- * but now we need a Var that will refer to the subroot's live
5546
- * RelOptInfos. For instance, if any subquery pullup happened during
5547
- * planning, Vars in the targetlist might have gotten replaced, and we
5548
- * need to see the replacement expressions.
5549
- */
5550
- subquery = rel -> subroot -> parse ;
5551
- Assert (IsA (subquery , Query ));
5552
-
5553
5613
/* Get the subquery output expression referenced by the upper Var */
5554
- ste = get_tle_by_resno (subquery -> targetList , var -> varattno );
5614
+ if (subquery -> returningList )
5615
+ subtlist = subquery -> returningList ;
5616
+ else
5617
+ subtlist = subquery -> targetList ;
5618
+ ste = get_tle_by_resno (subtlist , var -> varattno );
5555
5619
if (ste == NULL || ste -> resjunk )
5556
5620
elog (ERROR , "subquery %s does not have attribute %d" ,
5557
5621
rte -> eref -> aliasname , var -> varattno );
@@ -5599,16 +5663,16 @@ examine_simple_variable(PlannerInfo *root, Var *var,
5599
5663
* if the underlying column is unique, the subquery may have
5600
5664
* joined to other tables in a way that creates duplicates.
5601
5665
*/
5602
- examine_simple_variable (rel -> subroot , var , vardata );
5666
+ examine_simple_variable (subroot , var , vardata );
5603
5667
}
5604
5668
}
5605
5669
else
5606
5670
{
5607
5671
/*
5608
- * Otherwise, the Var comes from a FUNCTION, VALUES, or CTE RTE. (We
5609
- * won't see RTE_JOIN here because join alias Vars have already been
5672
+ * Otherwise, the Var comes from a FUNCTION or VALUES RTE. (We won't
5673
+ * see RTE_JOIN here because join alias Vars have already been
5610
5674
* flattened.) There's not much we can do with function outputs, but
5611
- * maybe someday try to be smarter about VALUES and/or CTEs .
5675
+ * maybe someday try to be smarter about VALUES.
5612
5676
*/
5613
5677
}
5614
5678
}
0 commit comments