|
33 | 33 | #include "access/heapam.h" |
34 | 34 | #include "access/htup_details.h" |
35 | 35 | #include "access/sysattr.h" |
| 36 | +#include "catalog/partition.h" |
36 | 37 | #include "catalog/pg_inherits_fn.h" |
37 | 38 | #include "catalog/pg_type.h" |
38 | 39 | #include "miscadmin.h" |
@@ -100,6 +101,19 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, |
100 | 101 | static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); |
101 | 102 | static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, |
102 | 103 | Index rti); |
| 104 | +static void expand_partitioned_rtentry(PlannerInfo *root, |
| 105 | + RangeTblEntry *parentrte, |
| 106 | + Index parentRTindex, Relation parentrel, |
| 107 | + PlanRowMark *parentrc, PartitionDesc partdesc, |
| 108 | + LOCKMODE lockmode, |
| 109 | + bool *has_child, List **appinfos, |
| 110 | + List **partitioned_child_rels); |
| 111 | +static void expand_single_inheritance_child(PlannerInfo *root, |
| 112 | + RangeTblEntry *parentrte, |
| 113 | + Index parentRTindex, Relation parentrel, |
| 114 | + PlanRowMark *parentrc, Relation childrel, |
| 115 | + bool *has_child, List **appinfos, |
| 116 | + List **partitioned_child_rels); |
103 | 117 | static void make_inh_translation_list(Relation oldrelation, |
104 | 118 | Relation newrelation, |
105 | 119 | Index newvarno, |
@@ -1455,131 +1469,62 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) |
1455 | 1469 | /* Scan the inheritance set and expand it */ |
1456 | 1470 | appinfos = NIL; |
1457 | 1471 | has_child = false; |
1458 | | - foreach(l, inhOIDs) |
| 1472 | + if (RelationGetPartitionDesc(oldrelation) != NULL) |
1459 | 1473 | { |
1460 | | - Oid childOID = lfirst_oid(l); |
1461 | | - Relation newrelation; |
1462 | | - RangeTblEntry *childrte; |
1463 | | - Index childRTindex; |
1464 | | - AppendRelInfo *appinfo; |
1465 | | - |
1466 | | - /* Open rel if needed; we already have required locks */ |
1467 | | - if (childOID != parentOID) |
1468 | | - newrelation = heap_open(childOID, NoLock); |
1469 | | - else |
1470 | | - newrelation = oldrelation; |
1471 | | - |
1472 | | - /* |
1473 | | - * It is possible that the parent table has children that are temp |
1474 | | - * tables of other backends. We cannot safely access such tables |
1475 | | - * (because of buffering issues), and the best thing to do seems to be |
1476 | | - * to silently ignore them. |
1477 | | - */ |
1478 | | - if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) |
1479 | | - { |
1480 | | - heap_close(newrelation, lockmode); |
1481 | | - continue; |
1482 | | - } |
1483 | | - |
1484 | 1474 | /* |
1485 | | - * Build an RTE for the child, and attach to query's rangetable list. |
1486 | | - * We copy most fields of the parent's RTE, but replace relation OID |
1487 | | - * and relkind, and set inh = false. Also, set requiredPerms to zero |
1488 | | - * since all required permissions checks are done on the original RTE. |
1489 | | - * Likewise, set the child's securityQuals to empty, because we only |
1490 | | - * want to apply the parent's RLS conditions regardless of what RLS |
1491 | | - * properties individual children may have. (This is an intentional |
1492 | | - * choice to make inherited RLS work like regular permissions checks.) |
1493 | | - * The parent securityQuals will be propagated to children along with |
1494 | | - * other base restriction clauses, so we don't need to do it here. |
| 1475 | + * If this table has partitions, recursively expand them in the order |
| 1476 | + * in which they appear in the PartitionDesc. But first, expand the |
| 1477 | + * parent itself. |
1495 | 1478 | */ |
1496 | | - childrte = copyObject(rte); |
1497 | | - childrte->relid = childOID; |
1498 | | - childrte->relkind = newrelation->rd_rel->relkind; |
1499 | | - childrte->inh = false; |
1500 | | - childrte->requiredPerms = 0; |
1501 | | - childrte->securityQuals = NIL; |
1502 | | - parse->rtable = lappend(parse->rtable, childrte); |
1503 | | - childRTindex = list_length(parse->rtable); |
1504 | | - |
| 1479 | + expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, |
| 1480 | + oldrelation, |
| 1481 | + &has_child, &appinfos, |
| 1482 | + &partitioned_child_rels); |
| 1483 | + expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc, |
| 1484 | + RelationGetPartitionDesc(oldrelation), |
| 1485 | + lockmode, |
| 1486 | + &has_child, &appinfos, |
| 1487 | + &partitioned_child_rels); |
| 1488 | + } |
| 1489 | + else |
| 1490 | + { |
1505 | 1491 | /* |
1506 | | - * Build an AppendRelInfo for this parent and child, unless the child |
1507 | | - * is a partitioned table. |
| 1492 | + * This table has no partitions. Expand any plain inheritance |
| 1493 | + * children in the order the OIDs were returned by |
| 1494 | + * find_all_inheritors. |
1508 | 1495 | */ |
1509 | | - if (childrte->relkind != RELKIND_PARTITIONED_TABLE) |
| 1496 | + foreach(l, inhOIDs) |
1510 | 1497 | { |
1511 | | - /* Remember if we saw a real child. */ |
| 1498 | + Oid childOID = lfirst_oid(l); |
| 1499 | + Relation newrelation; |
| 1500 | + |
| 1501 | + /* Open rel if needed; we already have required locks */ |
1512 | 1502 | if (childOID != parentOID) |
1513 | | - has_child = true; |
1514 | | - |
1515 | | - appinfo = makeNode(AppendRelInfo); |
1516 | | - appinfo->parent_relid = rti; |
1517 | | - appinfo->child_relid = childRTindex; |
1518 | | - appinfo->parent_reltype = oldrelation->rd_rel->reltype; |
1519 | | - appinfo->child_reltype = newrelation->rd_rel->reltype; |
1520 | | - make_inh_translation_list(oldrelation, newrelation, childRTindex, |
1521 | | - &appinfo->translated_vars); |
1522 | | - appinfo->parent_reloid = parentOID; |
1523 | | - appinfos = lappend(appinfos, appinfo); |
| 1503 | + newrelation = heap_open(childOID, NoLock); |
| 1504 | + else |
| 1505 | + newrelation = oldrelation; |
1524 | 1506 |
|
1525 | 1507 | /* |
1526 | | - * Translate the column permissions bitmaps to the child's attnums |
1527 | | - * (we have to build the translated_vars list before we can do |
1528 | | - * this). But if this is the parent table, leave copyObject's |
1529 | | - * result alone. |
1530 | | - * |
1531 | | - * Note: we need to do this even though the executor won't run any |
1532 | | - * permissions checks on the child RTE. The |
1533 | | - * insertedCols/updatedCols bitmaps may be examined for |
1534 | | - * trigger-firing purposes. |
| 1508 | + * It is possible that the parent table has children that are temp |
| 1509 | + * tables of other backends. We cannot safely access such tables |
| 1510 | + * (because of buffering issues), and the best thing to do seems |
| 1511 | + * to be to silently ignore them. |
1535 | 1512 | */ |
1536 | | - if (childOID != parentOID) |
| 1513 | + if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) |
1537 | 1514 | { |
1538 | | - childrte->selectedCols = translate_col_privs(rte->selectedCols, |
1539 | | - appinfo->translated_vars); |
1540 | | - childrte->insertedCols = translate_col_privs(rte->insertedCols, |
1541 | | - appinfo->translated_vars); |
1542 | | - childrte->updatedCols = translate_col_privs(rte->updatedCols, |
1543 | | - appinfo->translated_vars); |
| 1515 | + heap_close(newrelation, lockmode); |
| 1516 | + continue; |
1544 | 1517 | } |
1545 | | - } |
1546 | | - else |
1547 | | - partitioned_child_rels = lappend_int(partitioned_child_rels, |
1548 | | - childRTindex); |
1549 | 1518 |
|
1550 | | - /* |
1551 | | - * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. |
1552 | | - */ |
1553 | | - if (oldrc) |
1554 | | - { |
1555 | | - PlanRowMark *newrc = makeNode(PlanRowMark); |
1556 | | - |
1557 | | - newrc->rti = childRTindex; |
1558 | | - newrc->prti = rti; |
1559 | | - newrc->rowmarkId = oldrc->rowmarkId; |
1560 | | - /* Reselect rowmark type, because relkind might not match parent */ |
1561 | | - newrc->markType = select_rowmark_type(childrte, oldrc->strength); |
1562 | | - newrc->allMarkTypes = (1 << newrc->markType); |
1563 | | - newrc->strength = oldrc->strength; |
1564 | | - newrc->waitPolicy = oldrc->waitPolicy; |
1565 | | - |
1566 | | - /* |
1567 | | - * We mark RowMarks for partitioned child tables as parent |
1568 | | - * RowMarks so that the executor ignores them (except their |
1569 | | - * existence means that the child tables be locked using |
1570 | | - * appropriate mode). |
1571 | | - */ |
1572 | | - newrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); |
1573 | | - |
1574 | | - /* Include child's rowmark type in parent's allMarkTypes */ |
1575 | | - oldrc->allMarkTypes |= newrc->allMarkTypes; |
| 1519 | + expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, |
| 1520 | + newrelation, |
| 1521 | + &has_child, &appinfos, |
| 1522 | + &partitioned_child_rels); |
1576 | 1523 |
|
1577 | | - root->rowMarks = lappend(root->rowMarks, newrc); |
| 1524 | + /* Close child relations, but keep locks */ |
| 1525 | + if (childOID != parentOID) |
| 1526 | + heap_close(newrelation, NoLock); |
1578 | 1527 | } |
1579 | | - |
1580 | | - /* Close child relations, but keep locks */ |
1581 | | - if (childOID != parentOID) |
1582 | | - heap_close(newrelation, NoLock); |
1583 | 1528 | } |
1584 | 1529 |
|
1585 | 1530 | heap_close(oldrelation, NoLock); |
@@ -1620,6 +1565,169 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) |
1620 | 1565 | root->append_rel_list = list_concat(root->append_rel_list, appinfos); |
1621 | 1566 | } |
1622 | 1567 |
|
| 1568 | +static void |
| 1569 | +expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, |
| 1570 | + Index parentRTindex, Relation parentrel, |
| 1571 | + PlanRowMark *parentrc, PartitionDesc partdesc, |
| 1572 | + LOCKMODE lockmode, |
| 1573 | + bool *has_child, List **appinfos, |
| 1574 | + List **partitioned_child_rels) |
| 1575 | +{ |
| 1576 | + int i; |
| 1577 | + |
| 1578 | + check_stack_depth(); |
| 1579 | + |
| 1580 | + for (i = 0; i < partdesc->nparts; i++) |
| 1581 | + { |
| 1582 | + Oid childOID = partdesc->oids[i]; |
| 1583 | + Relation childrel; |
| 1584 | + |
| 1585 | + /* Open rel; we already have required locks */ |
| 1586 | + childrel = heap_open(childOID, NoLock); |
| 1587 | + |
| 1588 | + /* As in expand_inherited_rtentry, skip non-local temp tables */ |
| 1589 | + if (RELATION_IS_OTHER_TEMP(childrel)) |
| 1590 | + { |
| 1591 | + heap_close(childrel, lockmode); |
| 1592 | + continue; |
| 1593 | + } |
| 1594 | + |
| 1595 | + expand_single_inheritance_child(root, parentrte, parentRTindex, |
| 1596 | + parentrel, parentrc, childrel, |
| 1597 | + has_child, appinfos, |
| 1598 | + partitioned_child_rels); |
| 1599 | + |
| 1600 | + /* If this child is itself partitioned, recurse */ |
| 1601 | + if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) |
| 1602 | + expand_partitioned_rtentry(root, parentrte, parentRTindex, |
| 1603 | + parentrel, parentrc, |
| 1604 | + RelationGetPartitionDesc(childrel), |
| 1605 | + lockmode, |
| 1606 | + has_child, appinfos, |
| 1607 | + partitioned_child_rels); |
| 1608 | + |
| 1609 | + /* Close child relation, but keep locks */ |
| 1610 | + heap_close(childrel, NoLock); |
| 1611 | + } |
| 1612 | +} |
| 1613 | + |
| 1614 | +/* |
| 1615 | + * expand_single_inheritance_child |
| 1616 | + * Expand a single inheritance child, if needed. |
| 1617 | + * |
| 1618 | + * If this is a temp table of another backend, we'll return without doing |
| 1619 | + * anything at all. Otherwise, we'll set "has_child" to true, build a |
| 1620 | + * RangeTblEntry and either a PartitionedChildRelInfo or AppendRelInfo as |
| 1621 | + * appropriate, plus maybe a PlanRowMark. |
| 1622 | + */ |
| 1623 | +static void |
| 1624 | +expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, |
| 1625 | + Index parentRTindex, Relation parentrel, |
| 1626 | + PlanRowMark *parentrc, Relation childrel, |
| 1627 | + bool *has_child, List **appinfos, |
| 1628 | + List **partitioned_child_rels) |
| 1629 | +{ |
| 1630 | + Query *parse = root->parse; |
| 1631 | + Oid parentOID = RelationGetRelid(parentrel); |
| 1632 | + Oid childOID = RelationGetRelid(childrel); |
| 1633 | + RangeTblEntry *childrte; |
| 1634 | + Index childRTindex; |
| 1635 | + AppendRelInfo *appinfo; |
| 1636 | + |
| 1637 | + /* |
| 1638 | + * Build an RTE for the child, and attach to query's rangetable list. We |
| 1639 | + * copy most fields of the parent's RTE, but replace relation OID and |
| 1640 | + * relkind, and set inh = false. Also, set requiredPerms to zero since |
| 1641 | + * all required permissions checks are done on the original RTE. Likewise, |
| 1642 | + * set the child's securityQuals to empty, because we only want to apply |
| 1643 | + * the parent's RLS conditions regardless of what RLS properties |
| 1644 | + * individual children may have. (This is an intentional choice to make |
| 1645 | + * inherited RLS work like regular permissions checks.) The parent |
| 1646 | + * securityQuals will be propagated to children along with other base |
| 1647 | + * restriction clauses, so we don't need to do it here. |
| 1648 | + */ |
| 1649 | + childrte = copyObject(parentrte); |
| 1650 | + childrte->relid = childOID; |
| 1651 | + childrte->relkind = childrel->rd_rel->relkind; |
| 1652 | + childrte->inh = false; |
| 1653 | + childrte->requiredPerms = 0; |
| 1654 | + childrte->securityQuals = NIL; |
| 1655 | + parse->rtable = lappend(parse->rtable, childrte); |
| 1656 | + childRTindex = list_length(parse->rtable); |
| 1657 | + |
| 1658 | + /* |
| 1659 | + * Build an AppendRelInfo for this parent and child, unless the child is a |
| 1660 | + * partitioned table. |
| 1661 | + */ |
| 1662 | + if (childrte->relkind != RELKIND_PARTITIONED_TABLE) |
| 1663 | + { |
| 1664 | + /* Remember if we saw a real child. */ |
| 1665 | + if (childOID != parentOID) |
| 1666 | + *has_child = true; |
| 1667 | + |
| 1668 | + appinfo = makeNode(AppendRelInfo); |
| 1669 | + appinfo->parent_relid = parentRTindex; |
| 1670 | + appinfo->child_relid = childRTindex; |
| 1671 | + appinfo->parent_reltype = parentrel->rd_rel->reltype; |
| 1672 | + appinfo->child_reltype = childrel->rd_rel->reltype; |
| 1673 | + make_inh_translation_list(parentrel, childrel, childRTindex, |
| 1674 | + &appinfo->translated_vars); |
| 1675 | + appinfo->parent_reloid = parentOID; |
| 1676 | + *appinfos = lappend(*appinfos, appinfo); |
| 1677 | + |
| 1678 | + /* |
| 1679 | + * Translate the column permissions bitmaps to the child's attnums (we |
| 1680 | + * have to build the translated_vars list before we can do this). But |
| 1681 | + * if this is the parent table, leave copyObject's result alone. |
| 1682 | + * |
| 1683 | + * Note: we need to do this even though the executor won't run any |
| 1684 | + * permissions checks on the child RTE. The insertedCols/updatedCols |
| 1685 | + * bitmaps may be examined for trigger-firing purposes. |
| 1686 | + */ |
| 1687 | + if (childOID != parentOID) |
| 1688 | + { |
| 1689 | + childrte->selectedCols = translate_col_privs(parentrte->selectedCols, |
| 1690 | + appinfo->translated_vars); |
| 1691 | + childrte->insertedCols = translate_col_privs(parentrte->insertedCols, |
| 1692 | + appinfo->translated_vars); |
| 1693 | + childrte->updatedCols = translate_col_privs(parentrte->updatedCols, |
| 1694 | + appinfo->translated_vars); |
| 1695 | + } |
| 1696 | + } |
| 1697 | + else |
| 1698 | + *partitioned_child_rels = lappend_int(*partitioned_child_rels, |
| 1699 | + childRTindex); |
| 1700 | + |
| 1701 | + /* |
| 1702 | + * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. |
| 1703 | + */ |
| 1704 | + if (parentrc) |
| 1705 | + { |
| 1706 | + PlanRowMark *childrc = makeNode(PlanRowMark); |
| 1707 | + |
| 1708 | + childrc->rti = childRTindex; |
| 1709 | + childrc->prti = parentRTindex; |
| 1710 | + childrc->rowmarkId = parentrc->rowmarkId; |
| 1711 | + /* Reselect rowmark type, because relkind might not match parent */ |
| 1712 | + childrc->markType = select_rowmark_type(childrte, parentrc->strength); |
| 1713 | + childrc->allMarkTypes = (1 << childrc->markType); |
| 1714 | + childrc->strength = parentrc->strength; |
| 1715 | + childrc->waitPolicy = parentrc->waitPolicy; |
| 1716 | + |
| 1717 | + /* |
| 1718 | + * We mark RowMarks for partitioned child tables as parent RowMarks so |
| 1719 | + * that the executor ignores them (except their existence means that |
| 1720 | + * the child tables be locked using appropriate mode). |
| 1721 | + */ |
| 1722 | + childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); |
| 1723 | + |
| 1724 | + /* Include child's rowmark type in parent's allMarkTypes */ |
| 1725 | + parentrc->allMarkTypes |= childrc->allMarkTypes; |
| 1726 | + |
| 1727 | + root->rowMarks = lappend(root->rowMarks, childrc); |
| 1728 | + } |
| 1729 | +} |
| 1730 | + |
1623 | 1731 | /* |
1624 | 1732 | * make_inh_translation_list |
1625 | 1733 | * Build the list of translations from parent Vars to child Vars for |
|
0 commit comments