| Line | Hits | Source | Commit |
| 1331 |
- |
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, |
- |
| 1332 |
- |
JoinExpr *lowest_outer_join, |
- |
| 1333 |
- |
AppendRelInfo *containing_appendrel) |
- |
| 1334 |
- |
{ |
- |
| 1335 |
- |
Query *parse = root->parse; |
- |
| 1336 |
- |
int varno = ((RangeTblRef *) jtnode)->rtindex; |
- |
| 1337 |
- |
Query *subquery; |
- |
| 1338 |
- |
PlannerInfo *subroot; |
- |
| 1339 |
- |
int rtoffset; |
- |
| 1340 |
- |
pullup_replace_vars_context rvcontext; |
- |
| 1341 |
- |
ListCell *lc; |
- |
| 1342 |
- |
|
- |
| 1343 |
- |
/* |
- |
| 1344 |
- |
* Make a modifiable copy of the subquery to hack on, so that the RTE will |
- |
| 1345 |
- |
* be left unchanged in case we decide below that we can't pull it up |
- |
| 1346 |
- |
* after all. |
- |
| 1347 |
- |
*/ |
- |
| 1348 |
- |
subquery = copyObject(rte->subquery); |
- |
| 1349 |
- |
|
- |
| 1350 |
- |
/* |
- |
| 1351 |
- |
* Create a PlannerInfo data structure for this subquery. |
- |
| 1352 |
- |
* |
- |
| 1353 |
- |
* NOTE: the next few steps should match the first processing in |
- |
| 1354 |
- |
* subquery_planner(). Can we refactor to avoid code duplication, or |
- |
| 1355 |
- |
* would that just make things uglier? |
- |
| 1356 |
- |
*/ |
- |
| 1357 |
- |
subroot = makeNode(PlannerInfo); |
- |
| 1358 |
- |
subroot->parse = subquery; |
- |
| 1359 |
- |
subroot->glob = root->glob; |
- |
| 1360 |
- |
subroot->query_level = root->query_level; |
- |
| 1361 |
- |
subroot->plan_name = root->plan_name; |
- |
| 1362 |
- |
subroot->parent_root = root->parent_root; |
- |
| 1363 |
- |
subroot->plan_params = NIL; |
- |
| 1364 |
- |
subroot->outer_params = NULL; |
- |
| 1365 |
- |
subroot->planner_cxt = CurrentMemoryContext; |
- |
| 1366 |
- |
subroot->init_plans = NIL; |
- |
| 1367 |
- |
subroot->cte_plan_ids = NIL; |
- |
| 1368 |
- |
subroot->multiexpr_params = NIL; |
- |
| 1369 |
- |
subroot->join_domains = NIL; |
- |
| 1370 |
- |
subroot->eq_classes = NIL; |
- |
| 1371 |
- |
subroot->ec_merging_done = false; |
- |
| 1372 |
- |
subroot->last_rinfo_serial = 0; |
- |
| 1373 |
- |
subroot->all_result_relids = NULL; |
- |
| 1374 |
- |
subroot->leaf_result_relids = NULL; |
- |
| 1375 |
- |
subroot->append_rel_list = NIL; |
- |
| 1376 |
- |
subroot->row_identity_vars = NIL; |
- |
| 1377 |
- |
subroot->rowMarks = NIL; |
- |
| 1378 |
- |
memset(subroot->upper_rels, 0, sizeof(subroot->upper_rels)); |
- |
| 1379 |
- |
memset(subroot->upper_targets, 0, sizeof(subroot->upper_targets)); |
- |
| 1380 |
- |
subroot->processed_groupClause = NIL; |
- |
| 1381 |
- |
subroot->processed_distinctClause = NIL; |
- |
| 1382 |
- |
subroot->processed_tlist = NIL; |
- |
| 1383 |
- |
subroot->update_colnos = NIL; |
- |
| 1384 |
- |
subroot->grouping_map = NULL; |
- |
| 1385 |
- |
subroot->minmax_aggs = NIL; |
- |
| 1386 |
- |
subroot->qual_security_level = 0; |
- |
| 1387 |
- |
subroot->placeholdersFrozen = false; |
- |
| 1388 |
- |
subroot->hasRecursion = false; |
- |
| 1389 |
- |
subroot->assumeReplanning = false; |
- |
| 1390 |
- |
subroot->wt_param_id = -1; |
- |
| 1391 |
- |
subroot->non_recursive_path = NULL; |
- |
| 1392 |
- |
/* We don't currently need a top JoinDomain for the subroot */ |
- |
| 1393 |
- |
|
- |
| 1394 |
- |
/* No CTEs to worry about */ |
- |
| 1395 |
- |
Assert(subquery->cteList == NIL); |
- |
| 1396 |
- |
|
- |
| 1397 |
- |
/* |
- |
| 1398 |
- |
* Scan the rangetable for relation RTEs and retrieve the necessary |
- |
| 1399 |
- |
* catalog information for each relation. Using this information, clear |
- |
| 1400 |
- |
* the inh flag for any relation that has no children, collect not-null |
- |
| 1401 |
- |
* attribute numbers for any relation that has column not-null |
- |
| 1402 |
- |
* constraints, and expand virtual generated columns for any relation that |
- |
| 1403 |
- |
* contains them. |
- |
| 1404 |
- |
*/ |
- |
| 1405 |
- |
subquery = subroot->parse = preprocess_relation_rtes(subroot); |
- |
| 1406 |
- |
|
- |
| 1407 |
- |
/* |
- |
| 1408 |
- |
* If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so |
- |
| 1409 |
- |
* that we don't need so many special cases to deal with that situation. |
- |
| 1410 |
- |
*/ |
- |
| 1411 |
- |
replace_empty_jointree(subquery); |
- |
| 1412 |
- |
|
- |
| 1413 |
- |
/* |
- |
| 1414 |
- |
* Pull up any SubLinks within the subquery's quals, so that we don't |
- |
| 1415 |
- |
* leave unoptimized SubLinks behind. |
- |
| 1416 |
- |
*/ |
- |
| 1417 |
- |
if (subquery->hasSubLinks) |
- |
| 1418 |
- |
pull_up_sublinks(subroot); |
- |
| 1419 |
- |
|
- |
| 1420 |
- |
/* |
- |
| 1421 |
- |
* Similarly, preprocess its function RTEs to inline any set-returning |
- |
| 1422 |
- |
* functions in its rangetable. |
- |
| 1423 |
- |
*/ |
- |
| 1424 |
- |
preprocess_function_rtes(subroot); |
- |
| 1425 |
- |
|
- |
| 1426 |
- |
/* |
- |
| 1427 |
- |
* Recursively pull up the subquery's subqueries, so that |
- |
| 1428 |
- |
* pull_up_subqueries' processing is complete for its jointree and |
- |
| 1429 |
- |
* rangetable. |
- |
| 1430 |
- |
* |
- |
| 1431 |
- |
* Note: it's okay that the subquery's recursion starts with NULL for |
- |
| 1432 |
- |
* containing-join info, even if we are within an outer join in the upper |
- |
| 1433 |
- |
* query; the lower query starts with a clean slate for outer-join |
- |
| 1434 |
- |
* semantics. Likewise, we needn't pass down appendrel state. |
- |
| 1435 |
- |
*/ |
- |
| 1436 |
- |
pull_up_subqueries(subroot); |
- |
| 1437 |
- |
|
- |
| 1438 |
- |
/* |
- |
| 1439 |
- |
* Now we must recheck whether the subquery is still simple enough to pull |
- |
| 1440 |
- |
* up. If not, abandon processing it. |
- |
| 1441 |
- |
* |
- |
| 1442 |
- |
* We don't really need to recheck all the conditions involved, but it's |
- |
| 1443 |
- |
* easier just to keep this "if" looking the same as the one in |
- |
| 1444 |
- |
* pull_up_subqueries_recurse. |
- |
| 1445 |
- |
*/ |
- |
| 1446 |
- |
if (is_simple_subquery(root, subquery, rte, lowest_outer_join) && |
- |
| 1447 |
- |
(containing_appendrel == NULL || is_safe_append_member(subquery))) |
- |
| 1448 |
- |
{ |
- |
| 1449 |
- |
/* good to go */ |
- |
| 1450 |
- |
} |
- |
| 1451 |
- |
else |
- |
| 1452 |
- |
{ |
- |
| 1453 |
- |
/* |
- |
| 1454 |
- |
* Give up, return unmodified RangeTblRef. |
- |
| 1455 |
- |
* |
- |
| 1456 |
- |
* Note: The work we just did will be redone when the subquery gets |
- |
| 1457 |
- |
* planned on its own. Perhaps we could avoid that by storing the |
- |
| 1458 |
- |
* modified subquery back into the rangetable, but I'm not gonna risk |
- |
| 1459 |
- |
* it now. |
- |
| 1460 |
- |
*/ |
- |
| 1461 |
- |
return jtnode; |
- |
| 1462 |
- |
} |
- |
| 1463 |
- |
|
- |
| 1464 |
- |
/* |
- |
| 1465 |
- |
* We must flatten any join alias Vars in the subquery's targetlist, |
- |
| 1466 |
- |
* because pulling up the subquery's subqueries might have changed their |
- |
| 1467 |
- |
* expansions into arbitrary expressions, which could affect |
- |
| 1468 |
- |
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers |
- |
| 1469 |
- |
* are needed for tlist entries. (Likely it'd be better to do |
- |
| 1470 |
- |
* flatten_join_alias_vars on the whole query tree at some earlier stage, |
- |
| 1471 |
- |
* maybe even in the rewriter; but for now let's just fix this case here.) |
- |
| 1472 |
- |
*/ |
- |
| 1473 |
- |
subquery->targetList = (List *) |
- |
| 1474 |
- |
flatten_join_alias_vars(subroot, subroot->parse, |
- |
| 1475 |
- |
(Node *) subquery->targetList); |
- |
| 1476 |
- |
|
- |
| 1477 |
- |
/* |
- |
| 1478 |
- |
* Adjust level-0 varnos in subquery so that we can append its rangetable |
- |
| 1479 |
- |
* to upper query's. We have to fix the subquery's append_rel_list as |
- |
| 1480 |
- |
* well. |
- |
| 1481 |
- |
*/ |
- |
| 1482 |
- |
rtoffset = list_length(parse->rtable); |
- |
| 1483 |
- |
OffsetVarNodes((Node *) subquery, rtoffset, 0); |
- |
| 1484 |
- |
OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0); |
- |
| 1485 |
- |
|
- |
| 1486 |
- |
/* |
- |
| 1487 |
- |
* Upper-level vars in subquery are now one level closer to their parent |
- |
| 1488 |
- |
* than before. |
- |
| 1489 |
- |
*/ |
- |
| 1490 |
- |
IncrementVarSublevelsUp((Node *) subquery, -1, 1); |
- |
| 1491 |
- |
IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1); |
- |
| 1492 |
- |
|
- |
| 1493 |
- |
/* |
- |
| 1494 |
- |
* The subquery's targetlist items are now in the appropriate form to |
- |
| 1495 |
- |
* insert into the top query, except that we may need to wrap them in |
- |
| 1496 |
- |
* PlaceHolderVars. Set up required context data for pullup_replace_vars. |
- |
| 1497 |
- |
* (Note that we should include the subquery's inner joins in relids, |
- |
| 1498 |
- |
* since it may include join alias vars referencing them.) |
- |
| 1499 |
- |
*/ |
- |
| 1500 |
- |
rvcontext.root = root; |
- |
| 1501 |
- |
rvcontext.targetlist = subquery->targetList; |
- |
| 1502 |
- |
rvcontext.target_rte = rte; |
- |
| 1503 |
- |
rvcontext.result_relation = 0; |
- |
| 1504 |
- |
if (rte->lateral) |
- |
| 1505 |
- |
{ |
- |
| 1506 |
- |
rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree, |
- |
| 1507 |
- |
true, true); |
- |
| 1508 |
- |
rvcontext.nullinfo = get_nullingrels(parse); |
- |
| 1509 |
- |
} |
- |
| 1510 |
- |
else /* won't need these values */ |
- |
| 1511 |
- |
{ |
- |
| 1512 |
- |
rvcontext.relids = NULL; |
- |
| 1513 |
- |
rvcontext.nullinfo = NULL; |
- |
| 1514 |
- |
} |
- |
| 1515 |
- |
rvcontext.outer_hasSubLinks = &parse->hasSubLinks; |
- |
| 1516 |
- |
rvcontext.varno = varno; |
- |
| 1517 |
- |
/* this flag will be set below, if needed */ |
- |
| 1518 |
- |
rvcontext.wrap_option = REPLACE_WRAP_NONE; |
- |
| 1519 |
- |
/* initialize cache array with indexes 0 .. length(tlist) */ |
- |
| 1520 |
- |
rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) * |
- |
| 1521 |
- |
sizeof(Node *)); |
- |
| 1522 |
- |
|
- |
| 1523 |
- |
/* |
- |
| 1524 |
- |
* If the parent query uses grouping sets, we need a PlaceHolderVar for |
- |
| 1525 |
- |
* each expression of the subquery's targetlist items. This ensures that |
- |
| 1526 |
- |
* expressions retain their separate identity so that they will match |
- |
| 1527 |
- |
* grouping set columns when appropriate. (It'd be sufficient to wrap |
- |
| 1528 |
- |
* values used in grouping set columns, and do so only in non-aggregated |
- |
| 1529 |
- |
* portions of the tlist and havingQual, but that would require a lot of |
- |
| 1530 |
- |
* infrastructure that pullup_replace_vars hasn't currently got.) |
- |
| 1531 |
- |
*/ |
- |
| 1532 |
- |
if (parse->groupingSets) |
- |
| 1533 |
- |
rvcontext.wrap_option = REPLACE_WRAP_ALL; |
- |
| 1534 |
- |
|
- |
| 1535 |
- |
/* |
- |
| 1536 |
- |
* Replace all of the top query's references to the subquery's outputs |
- |
| 1537 |
- |
* with copies of the adjusted subtlist items, being careful not to |
- |
| 1538 |
- |
* replace any of the jointree structure. |
- |
| 1539 |
- |
*/ |
- |
| 1540 |
- |
perform_pullup_replace_vars(root, &rvcontext, |
- |
| 1541 |
- |
containing_appendrel); |
- |
| 1542 |
- |
|
- |
| 1543 |
- |
/* |
- |
| 1544 |
- |
* If the subquery had a LATERAL marker, propagate that to any of its |
- |
| 1545 |
- |
* child RTEs that could possibly now contain lateral cross-references. |
- |
| 1546 |
- |
* The children might or might not contain any actual lateral |
- |
| 1547 |
- |
* cross-references, but we have to mark the pulled-up child RTEs so that |
- |
| 1548 |
- |
* later planner stages will check for such. |
- |
| 1549 |
- |
*/ |
- |
| 1550 |
- |
if (rte->lateral) |
- |
| 1551 |
- |
{ |
- |
| 1552 |
- |
foreach(lc, subquery->rtable) |
- |
| 1553 |
- |
{ |
- |
| 1554 |
- |
RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(lc); |
- |
| 1555 |
- |
|
- |
| 1556 |
- |
switch (child_rte->rtekind) |
- |
| 1557 |
- |
{ |
- |
| 1558 |
- |
case RTE_RELATION: |
- |
| 1559 |
- |
if (child_rte->tablesample) |
- |
| 1560 |
- |
child_rte->lateral = true; |
- |
| 1561 |
- |
break; |
- |
| 1562 |
- |
case RTE_SUBQUERY: |
- |
| 1563 |
- |
case RTE_FUNCTION: |
- |
| 1564 |
- |
case RTE_VALUES: |
- |
| 1565 |
- |
case RTE_TABLEFUNC: |
- |
| 1566 |
- |
child_rte->lateral = true; |
- |
| 1567 |
- |
break; |
- |
| 1568 |
- |
case RTE_JOIN: |
- |
| 1569 |
- |
case RTE_CTE: |
- |
| 1570 |
- |
case RTE_NAMEDTUPLESTORE: |
- |
| 1571 |
- |
case RTE_RESULT: |
- |
| 1572 |
- |
case RTE_GROUP: |
- |
| 1573 |
- |
/* these can't contain any lateral references */ |
- |
| 1574 |
- |
break; |
- |
| 1575 |
- |
case RTE_GRAPH_TABLE: |
86c14eaWIP: SQL Property Graph Queries (SQL/PGQ) |
| 1576 |
- |
/* shouldn't happen here */ |
86c14eaWIP: SQL Property Graph Queries (SQL/PGQ) |
| 1577 |
- |
Assert(false); |
86c14eaWIP: SQL Property Graph Queries (SQL/PGQ) |
| 1578 |
- |
break; |
86c14eaWIP: SQL Property Graph Queries (SQL/PGQ) |
| 1579 |
- |
} |
- |
| 1580 |
- |
} |
- |
| 1581 |
- |
} |
- |
| 1582 |
- |
|
- |
| 1583 |
- |
/* |
- |
| 1584 |
- |
* Now append the adjusted rtable entries and their perminfos to upper |
- |
| 1585 |
- |
* query. (We hold off until after fixing the upper rtable entries; no |
- |
| 1586 |
- |
* point in running that code on the subquery ones too.) |
- |
| 1587 |
- |
*/ |
- |
| 1588 |
- |
CombineRangeTables(&parse->rtable, &parse->rteperminfos, |
- |
| 1589 |
- |
subquery->rtable, subquery->rteperminfos); |
- |
| 1590 |
- |
|
- |
| 1591 |
- |
/* |
- |
| 1592 |
- |
* Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already |
- |
| 1593 |
- |
* adjusted the marker rtindexes, so just concat the lists.) |
- |
| 1594 |
- |
*/ |
- |
| 1595 |
- |
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); |
- |
| 1596 |
- |
|
- |
| 1597 |
- |
/* |
- |
| 1598 |
- |
* We also have to fix the relid sets of any PlaceHolderVar nodes in the |
- |
| 1599 |
- |
* parent query. (This could perhaps be done by pullup_replace_vars(), |
- |
| 1600 |
- |
* but it seems cleaner to use two passes.) Note in particular that any |
- |
| 1601 |
- |
* PlaceHolderVar nodes just created by pullup_replace_vars() will be |
- |
| 1602 |
- |
* adjusted, so having created them with the subquery's varno is correct. |
- |
| 1603 |
- |
* |
- |
| 1604 |
- |
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed. We |
- |
| 1605 |
- |
* already checked that this won't require introducing multiple subrelids |
- |
| 1606 |
- |
* into the single-slot AppendRelInfo structs. |
- |
| 1607 |
- |
*/ |
- |
| 1608 |
- |
if (root->glob->lastPHId != 0 || root->append_rel_list) |
- |
| 1609 |
- |
{ |
- |
| 1610 |
- |
Relids subrelids; |
- |
| 1611 |
- |
|
- |
| 1612 |
- |
subrelids = get_relids_in_jointree((Node *) subquery->jointree, |
- |
| 1613 |
- |
true, false); |
- |
| 1614 |
- |
if (root->glob->lastPHId != 0) |
- |
| 1615 |
- |
substitute_phv_relids((Node *) parse, varno, subrelids); |
- |
| 1616 |
- |
fix_append_rel_relids(root, varno, subrelids); |
- |
| 1617 |
- |
} |
- |
| 1618 |
- |
|
- |
| 1619 |
- |
/* |
- |
| 1620 |
- |
* And now add subquery's AppendRelInfos to our list. |
- |
| 1621 |
- |
*/ |
- |
| 1622 |
- |
root->append_rel_list = list_concat(root->append_rel_list, |
- |
| 1623 |
- |
subroot->append_rel_list); |
- |
| 1624 |
- |
|
- |
| 1625 |
- |
/* |
- |
| 1626 |
- |
* We don't have to do the equivalent bookkeeping for outer-join info, |
- |
| 1627 |
- |
* because that hasn't been set up yet. placeholder_list likewise. |
- |
| 1628 |
- |
*/ |
- |
| 1629 |
- |
Assert(root->join_info_list == NIL); |
- |
| 1630 |
- |
Assert(subroot->join_info_list == NIL); |
- |
| 1631 |
- |
Assert(root->placeholder_list == NIL); |
- |
| 1632 |
- |
Assert(subroot->placeholder_list == NIL); |
- |
| 1633 |
- |
|
- |
| 1634 |
- |
/* |
- |
| 1635 |
- |
* We no longer need the RTE's copy of the subquery's query tree. Getting |
- |
| 1636 |
- |
* rid of it saves nothing in particular so far as this level of query is |
- |
| 1637 |
- |
* concerned; but if this query level is in turn pulled up into a parent, |
- |
| 1638 |
- |
* we'd waste cycles copying the now-unused query tree. |
- |
| 1639 |
- |
*/ |
- |
| 1640 |
- |
rte->subquery = NULL; |
- |
| 1641 |
- |
|
- |
| 1642 |
- |
/* |
- |
| 1643 |
- |
* Miscellaneous housekeeping. |
- |
| 1644 |
- |
* |
- |
| 1645 |
- |
* Although replace_rte_variables() faithfully updated parse->hasSubLinks |
- |
| 1646 |
- |
* if it copied any SubLinks out of the subquery's targetlist, we still |
- |
| 1647 |
- |
* could have SubLinks added to the query in the expressions of FUNCTION |
- |
| 1648 |
- |
* and VALUES RTEs copied up from the subquery. So it's necessary to copy |
- |
| 1649 |
- |
* subquery->hasSubLinks anyway. Perhaps this can be improved someday. |
- |
| 1650 |
- |
*/ |
- |
| 1651 |
- |
parse->hasSubLinks |= subquery->hasSubLinks; |
- |
| 1652 |
- |
|
- |
| 1653 |
- |
/* If subquery had any RLS conditions, now main query does too */ |
- |
| 1654 |
- |
parse->hasRowSecurity |= subquery->hasRowSecurity; |
- |
| 1655 |
- |
|
- |
| 1656 |
- |
/* |
- |
| 1657 |
- |
* subquery won't be pulled up if it hasAggs, hasWindowFuncs, or |
- |
| 1658 |
- |
* hasTargetSRFs, so no work needed on those flags |
- |
| 1659 |
- |
*/ |
- |
| 1660 |
- |
|
- |
| 1661 |
- |
/* |
- |
| 1662 |
- |
* Return the adjusted subquery jointree to replace the RangeTblRef entry |
- |
| 1663 |
- |
* in parent's jointree; or, if the FromExpr is degenerate, just return |
- |
| 1664 |
- |
* its single member. |
- |
| 1665 |
- |
*/ |
- |
| 1666 |
- |
Assert(IsA(subquery->jointree, FromExpr)); |
- |
| 1667 |
- |
Assert(subquery->jointree->fromlist != NIL); |
- |
| 1668 |
- |
if (subquery->jointree->quals == NULL && |
- |
| 1669 |
- |
list_length(subquery->jointree->fromlist) == 1) |
- |
| 1670 |
- |
return (Node *) linitial(subquery->jointree->fromlist); |
- |
| 1671 |
- |
|
- |
| 1672 |
- |
return (Node *) subquery->jointree; |
- |
| 1673 |
- |
} |
- |