LCOV - code coverage report
Current view: top level - src/backend/catalog - partition.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 103 107 96.3 %
Date: 2023-05-30 17:15:13 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * partition.c
       4             :  *        Partitioning related data structures and functions.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *        src/backend/catalog/partition.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             : */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/attmap.h"
      18             : #include "access/genam.h"
      19             : #include "access/htup_details.h"
      20             : #include "access/sysattr.h"
      21             : #include "access/table.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/partition.h"
      24             : #include "catalog/pg_inherits.h"
      25             : #include "catalog/pg_partitioned_table.h"
      26             : #include "nodes/makefuncs.h"
      27             : #include "optimizer/optimizer.h"
      28             : #include "partitioning/partbounds.h"
      29             : #include "rewrite/rewriteManip.h"
      30             : #include "utils/fmgroids.h"
      31             : #include "utils/partcache.h"
      32             : #include "utils/rel.h"
      33             : #include "utils/syscache.h"
      34             : 
      35             : static Oid  get_partition_parent_worker(Relation inhRel, Oid relid,
      36             :                                         bool *detach_pending);
      37             : static void get_partition_ancestors_worker(Relation inhRel, Oid relid,
      38             :                                            List **ancestors);
      39             : 
      40             : /*
      41             :  * get_partition_parent
      42             :  *      Obtain direct parent of given relation
      43             :  *
      44             :  * Returns inheritance parent of a partition by scanning pg_inherits
      45             :  *
      46             :  * If the partition is in the process of being detached, an error is thrown,
      47             :  * unless even_if_detached is passed as true.
      48             :  *
      49             :  * Note: Because this function assumes that the relation whose OID is passed
      50             :  * as an argument will have precisely one parent, it should only be called
      51             :  * when it is known that the relation is a partition.
      52             :  */
      53             : Oid
      54       10902 : get_partition_parent(Oid relid, bool even_if_detached)
      55             : {
      56             :     Relation    catalogRelation;
      57             :     Oid         result;
      58             :     bool        detach_pending;
      59             : 
      60       10902 :     catalogRelation = table_open(InheritsRelationId, AccessShareLock);
      61             : 
      62       10902 :     result = get_partition_parent_worker(catalogRelation, relid,
      63             :                                          &detach_pending);
      64             : 
      65       10902 :     if (!OidIsValid(result))
      66           0 :         elog(ERROR, "could not find tuple for parent of relation %u", relid);
      67             : 
      68       10902 :     if (detach_pending && !even_if_detached)
      69           0 :         elog(ERROR, "relation %u has no parent because it's being detached",
      70             :              relid);
      71             : 
      72       10902 :     table_close(catalogRelation, AccessShareLock);
      73             : 
      74       10902 :     return result;
      75             : }
      76             : 
      77             : /*
      78             :  * get_partition_parent_worker
      79             :  *      Scan the pg_inherits relation to return the OID of the parent of the
      80             :  *      given relation
      81             :  *
      82             :  * If the partition is being detached, *detach_pending is set true (but the
      83             :  * original parent is still returned.)
      84             :  */
      85             : static Oid
      86       21654 : get_partition_parent_worker(Relation inhRel, Oid relid, bool *detach_pending)
      87             : {
      88             :     SysScanDesc scan;
      89             :     ScanKeyData key[2];
      90       21654 :     Oid         result = InvalidOid;
      91             :     HeapTuple   tuple;
      92             : 
      93       21654 :     *detach_pending = false;
      94             : 
      95       21654 :     ScanKeyInit(&key[0],
      96             :                 Anum_pg_inherits_inhrelid,
      97             :                 BTEqualStrategyNumber, F_OIDEQ,
      98             :                 ObjectIdGetDatum(relid));
      99       21654 :     ScanKeyInit(&key[1],
     100             :                 Anum_pg_inherits_inhseqno,
     101             :                 BTEqualStrategyNumber, F_INT4EQ,
     102             :                 Int32GetDatum(1));
     103             : 
     104       21654 :     scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
     105             :                               NULL, 2, key);
     106       21654 :     tuple = systable_getnext(scan);
     107       21654 :     if (HeapTupleIsValid(tuple))
     108             :     {
     109       16410 :         Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
     110             : 
     111             :         /* Let caller know of partition being detached */
     112       16410 :         if (form->inhdetachpending)
     113          74 :             *detach_pending = true;
     114       16410 :         result = form->inhparent;
     115             :     }
     116             : 
     117       21654 :     systable_endscan(scan);
     118             : 
     119       21654 :     return result;
     120             : }
     121             : 
     122             : /*
     123             :  * get_partition_ancestors
     124             :  *      Obtain ancestors of given relation
     125             :  *
     126             :  * Returns a list of ancestors of the given relation.
     127             :  *
     128             :  * Note: Because this function assumes that the relation whose OID is passed
     129             :  * as an argument and each ancestor will have precisely one parent, it should
     130             :  * only be called when it is known that the relation is a partition.
     131             :  */
     132             : List *
     133        5246 : get_partition_ancestors(Oid relid)
     134             : {
     135        5246 :     List       *result = NIL;
     136             :     Relation    inhRel;
     137             : 
     138        5246 :     inhRel = table_open(InheritsRelationId, AccessShareLock);
     139             : 
     140        5246 :     get_partition_ancestors_worker(inhRel, relid, &result);
     141             : 
     142        5246 :     table_close(inhRel, AccessShareLock);
     143             : 
     144        5246 :     return result;
     145             : }
     146             : 
     147             : /*
     148             :  * get_partition_ancestors_worker
     149             :  *      recursive worker for get_partition_ancestors
     150             :  */
     151             : static void
     152       10752 : get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
     153             : {
     154             :     Oid         parentOid;
     155             :     bool        detach_pending;
     156             : 
     157             :     /*
     158             :      * Recursion ends at the topmost level, ie., when there's no parent; also
     159             :      * when the partition is being detached.
     160             :      */
     161       10752 :     parentOid = get_partition_parent_worker(inhRel, relid, &detach_pending);
     162       10752 :     if (parentOid == InvalidOid || detach_pending)
     163        5246 :         return;
     164             : 
     165        5506 :     *ancestors = lappend_oid(*ancestors, parentOid);
     166        5506 :     get_partition_ancestors_worker(inhRel, parentOid, ancestors);
     167             : }
     168             : 
     169             : /*
     170             :  * index_get_partition
     171             :  *      Return the OID of index of the given partition that is a child
     172             :  *      of the given index, or InvalidOid if there isn't one.
     173             :  */
     174             : Oid
     175         994 : index_get_partition(Relation partition, Oid indexId)
     176             : {
     177         994 :     List       *idxlist = RelationGetIndexList(partition);
     178             :     ListCell   *l;
     179             : 
     180        1530 :     foreach(l, idxlist)
     181             :     {
     182        1200 :         Oid         partIdx = lfirst_oid(l);
     183             :         HeapTuple   tup;
     184             :         Form_pg_class classForm;
     185             :         bool        ispartition;
     186             : 
     187        1200 :         tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx));
     188        1200 :         if (!HeapTupleIsValid(tup))
     189           0 :             elog(ERROR, "cache lookup failed for relation %u", partIdx);
     190        1200 :         classForm = (Form_pg_class) GETSTRUCT(tup);
     191        1200 :         ispartition = classForm->relispartition;
     192        1200 :         ReleaseSysCache(tup);
     193        1200 :         if (!ispartition)
     194         506 :             continue;
     195         694 :         if (get_partition_parent(partIdx, false) == indexId)
     196             :         {
     197         664 :             list_free(idxlist);
     198         664 :             return partIdx;
     199             :         }
     200             :     }
     201             : 
     202         330 :     list_free(idxlist);
     203         330 :     return InvalidOid;
     204             : }
     205             : 
     206             : /*
     207             :  * map_partition_varattnos - maps varattnos of all Vars in 'expr' (that have
     208             :  * varno 'fromrel_varno') from the attnums of 'from_rel' to the attnums of
     209             :  * 'to_rel', each of which may be either a leaf partition or a partitioned
     210             :  * table, but both of which must be from the same partitioning hierarchy.
     211             :  *
     212             :  * We need this because even though all of the same column names must be
     213             :  * present in all relations in the hierarchy, and they must also have the
     214             :  * same types, the attnums may be different.
     215             :  *
     216             :  * Note: this will work on any node tree, so really the argument and result
     217             :  * should be declared "Node *".  But a substantial majority of the callers
     218             :  * are working on Lists, so it's less messy to do the casts internally.
     219             :  */
     220             : List *
     221        7134 : map_partition_varattnos(List *expr, int fromrel_varno,
     222             :                         Relation to_rel, Relation from_rel)
     223             : {
     224        7134 :     if (expr != NIL)
     225             :     {
     226             :         AttrMap    *part_attmap;
     227             :         bool        found_whole_row;
     228             : 
     229        5972 :         part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel),
     230             :                                             RelationGetDescr(from_rel),
     231             :                                             false);
     232        5972 :         expr = (List *) map_variable_attnos((Node *) expr,
     233             :                                             fromrel_varno, 0,
     234             :                                             part_attmap,
     235        5972 :                                             RelationGetForm(to_rel)->reltype,
     236             :                                             &found_whole_row);
     237             :         /* Since we provided a to_rowtype, we may ignore found_whole_row. */
     238             :     }
     239             : 
     240        7134 :     return expr;
     241             : }
     242             : 
     243             : /*
     244             :  * Checks if any of the 'attnums' is a partition key attribute for rel
     245             :  *
     246             :  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
     247             :  * partition key expression.  It's possible for a column to be both used
     248             :  * directly and as part of an expression; if that happens, *used_in_expr may
     249             :  * end up as either true or false.  That's OK for current uses of this
     250             :  * function, because *used_in_expr is only used to tailor the error message
     251             :  * text.
     252             :  */
     253             : bool
     254       17770 : has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
     255             : {
     256             :     PartitionKey key;
     257             :     int         partnatts;
     258             :     List       *partexprs;
     259             :     ListCell   *partexprs_item;
     260             :     int         i;
     261             : 
     262       17770 :     if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     263       15678 :         return false;
     264             : 
     265        2092 :     key = RelationGetPartitionKey(rel);
     266        2092 :     partnatts = get_partition_natts(key);
     267        2092 :     partexprs = get_partition_exprs(key);
     268             : 
     269        2092 :     partexprs_item = list_head(partexprs);
     270        3308 :     for (i = 0; i < partnatts; i++)
     271             :     {
     272        2232 :         AttrNumber  partattno = get_partition_col_attnum(key, i);
     273             : 
     274        2232 :         if (partattno != 0)
     275             :         {
     276        2122 :             if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
     277             :                               attnums))
     278             :             {
     279         980 :                 if (used_in_expr)
     280          30 :                     *used_in_expr = false;
     281         980 :                 return true;
     282             :             }
     283             :         }
     284             :         else
     285             :         {
     286             :             /* Arbitrary expression */
     287         110 :             Node       *expr = (Node *) lfirst(partexprs_item);
     288         110 :             Bitmapset  *expr_attrs = NULL;
     289             : 
     290             :             /* Find all attributes referenced */
     291         110 :             pull_varattnos(expr, 1, &expr_attrs);
     292         110 :             partexprs_item = lnext(partexprs, partexprs_item);
     293             : 
     294         110 :             if (bms_overlap(attnums, expr_attrs))
     295             :             {
     296          36 :                 if (used_in_expr)
     297          18 :                     *used_in_expr = true;
     298          36 :                 return true;
     299             :             }
     300             :         }
     301             :     }
     302             : 
     303        1076 :     return false;
     304             : }
     305             : 
     306             : /*
     307             :  * get_default_partition_oid
     308             :  *
     309             :  * Given a relation OID, return the OID of the default partition, if one
     310             :  * exists.  Use get_default_oid_from_partdesc where possible, for
     311             :  * efficiency.
     312             :  */
     313             : Oid
     314        7936 : get_default_partition_oid(Oid parentId)
     315             : {
     316             :     HeapTuple   tuple;
     317        7936 :     Oid         defaultPartId = InvalidOid;
     318             : 
     319        7936 :     tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
     320             : 
     321        7936 :     if (HeapTupleIsValid(tuple))
     322             :     {
     323             :         Form_pg_partitioned_table part_table_form;
     324             : 
     325        7936 :         part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     326        7936 :         defaultPartId = part_table_form->partdefid;
     327        7936 :         ReleaseSysCache(tuple);
     328             :     }
     329             : 
     330        7936 :     return defaultPartId;
     331             : }
     332             : 
     333             : /*
     334             :  * update_default_partition_oid
     335             :  *
     336             :  * Update pg_partitioned_table.partdefid with a new default partition OID.
     337             :  */
     338             : void
     339         910 : update_default_partition_oid(Oid parentId, Oid defaultPartId)
     340             : {
     341             :     HeapTuple   tuple;
     342             :     Relation    pg_partitioned_table;
     343             :     Form_pg_partitioned_table part_table_form;
     344             : 
     345         910 :     pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
     346             : 
     347         910 :     tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
     348             : 
     349         910 :     if (!HeapTupleIsValid(tuple))
     350           0 :         elog(ERROR, "cache lookup failed for partition key of relation %u",
     351             :              parentId);
     352             : 
     353         910 :     part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     354         910 :     part_table_form->partdefid = defaultPartId;
     355         910 :     CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
     356             : 
     357         910 :     heap_freetuple(tuple);
     358         910 :     table_close(pg_partitioned_table, RowExclusiveLock);
     359         910 : }
     360             : 
     361             : /*
     362             :  * get_proposed_default_constraint
     363             :  *
     364             :  * This function returns the negation of new_part_constraints, which
     365             :  * would be an integral part of the default partition constraints after
     366             :  * addition of the partition to which the new_part_constraints belongs.
     367             :  */
     368             : List *
     369         482 : get_proposed_default_constraint(List *new_part_constraints)
     370             : {
     371             :     Expr       *defPartConstraint;
     372             : 
     373         482 :     defPartConstraint = make_ands_explicit(new_part_constraints);
     374             : 
     375             :     /*
     376             :      * Derive the partition constraints of default partition by negating the
     377             :      * given partition constraints. The partition constraint never evaluates
     378             :      * to NULL, so negating it like this is safe.
     379             :      */
     380         482 :     defPartConstraint = makeBoolExpr(NOT_EXPR,
     381         482 :                                      list_make1(defPartConstraint),
     382             :                                      -1);
     383             : 
     384             :     /* Simplify, to put the negated expression into canonical form */
     385             :     defPartConstraint =
     386         482 :         (Expr *) eval_const_expressions(NULL,
     387             :                                         (Node *) defPartConstraint);
     388         482 :     defPartConstraint = canonicalize_qual(defPartConstraint, true);
     389             : 
     390         482 :     return make_ands_implicit(defPartConstraint);
     391             : }

Generated by: LCOV version 1.14