LCOV - code coverage report
Current view: top level - src/backend/catalog - partition.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 102 105 97.1 %
Date: 2019-09-22 07:07:17 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-2019, 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/genam.h"
      18             : #include "access/htup_details.h"
      19             : #include "access/table.h"
      20             : #include "access/tupconvert.h"
      21             : #include "access/sysattr.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             : 
      36             : static Oid  get_partition_parent_worker(Relation inhRel, Oid relid);
      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             :  * Note: Because this function assumes that the relation whose OID is passed
      47             :  * as an argument will have precisely one parent, it should only be called
      48             :  * when it is known that the relation is a partition.
      49             :  */
      50             : Oid
      51        6932 : get_partition_parent(Oid relid)
      52             : {
      53             :     Relation    catalogRelation;
      54             :     Oid         result;
      55             : 
      56        6932 :     catalogRelation = table_open(InheritsRelationId, AccessShareLock);
      57             : 
      58        6932 :     result = get_partition_parent_worker(catalogRelation, relid);
      59             : 
      60        6932 :     if (!OidIsValid(result))
      61           0 :         elog(ERROR, "could not find tuple for parent of relation %u", relid);
      62             : 
      63        6932 :     table_close(catalogRelation, AccessShareLock);
      64             : 
      65        6932 :     return result;
      66             : }
      67             : 
      68             : /*
      69             :  * get_partition_parent_worker
      70             :  *      Scan the pg_inherits relation to return the OID of the parent of the
      71             :  *      given relation
      72             :  */
      73             : static Oid
      74       11008 : get_partition_parent_worker(Relation inhRel, Oid relid)
      75             : {
      76             :     SysScanDesc scan;
      77             :     ScanKeyData key[2];
      78       11008 :     Oid         result = InvalidOid;
      79             :     HeapTuple   tuple;
      80             : 
      81       11008 :     ScanKeyInit(&key[0],
      82             :                 Anum_pg_inherits_inhrelid,
      83             :                 BTEqualStrategyNumber, F_OIDEQ,
      84             :                 ObjectIdGetDatum(relid));
      85       11008 :     ScanKeyInit(&key[1],
      86             :                 Anum_pg_inherits_inhseqno,
      87             :                 BTEqualStrategyNumber, F_INT4EQ,
      88             :                 Int32GetDatum(1));
      89             : 
      90       11008 :     scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
      91             :                               NULL, 2, key);
      92       11008 :     tuple = systable_getnext(scan);
      93       11008 :     if (HeapTupleIsValid(tuple))
      94             :     {
      95        8708 :         Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
      96             : 
      97        8708 :         result = form->inhparent;
      98             :     }
      99             : 
     100       11008 :     systable_endscan(scan);
     101             : 
     102       11008 :     return result;
     103             : }
     104             : 
     105             : /*
     106             :  * get_partition_ancestors
     107             :  *      Obtain ancestors of given relation
     108             :  *
     109             :  * Returns a list of ancestors of the given relation.
     110             :  *
     111             :  * Note: Because this function assumes that the relation whose OID is passed
     112             :  * as an argument and each ancestor will have precisely one parent, it should
     113             :  * only be called when it is known that the relation is a partition.
     114             :  */
     115             : List *
     116        2300 : get_partition_ancestors(Oid relid)
     117             : {
     118        2300 :     List       *result = NIL;
     119             :     Relation    inhRel;
     120             : 
     121        2300 :     inhRel = table_open(InheritsRelationId, AccessShareLock);
     122             : 
     123        2300 :     get_partition_ancestors_worker(inhRel, relid, &result);
     124             : 
     125        2300 :     table_close(inhRel, AccessShareLock);
     126             : 
     127        2300 :     return result;
     128             : }
     129             : 
     130             : /*
     131             :  * get_partition_ancestors_worker
     132             :  *      recursive worker for get_partition_ancestors
     133             :  */
     134             : static void
     135        4076 : get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
     136             : {
     137             :     Oid         parentOid;
     138             : 
     139             :     /* Recursion ends at the topmost level, ie., when there's no parent */
     140        4076 :     parentOid = get_partition_parent_worker(inhRel, relid);
     141        4076 :     if (parentOid == InvalidOid)
     142        2300 :         return;
     143             : 
     144        1776 :     *ancestors = lappend_oid(*ancestors, parentOid);
     145        1776 :     get_partition_ancestors_worker(inhRel, parentOid, ancestors);
     146             : }
     147             : 
     148             : /*
     149             :  * index_get_partition
     150             :  *      Return the OID of index of the given partition that is a child
     151             :  *      of the given index, or InvalidOid if there isn't one.
     152             :  */
     153             : Oid
     154         394 : index_get_partition(Relation partition, Oid indexId)
     155             : {
     156         394 :     List       *idxlist = RelationGetIndexList(partition);
     157             :     ListCell   *l;
     158             : 
     159         716 :     foreach(l, idxlist)
     160             :     {
     161         524 :         Oid         partIdx = lfirst_oid(l);
     162             :         HeapTuple   tup;
     163             :         Form_pg_class classForm;
     164             :         bool        ispartition;
     165             : 
     166         524 :         tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx));
     167         524 :         if (!HeapTupleIsValid(tup))
     168           0 :             elog(ERROR, "cache lookup failed for relation %u", partIdx);
     169         524 :         classForm = (Form_pg_class) GETSTRUCT(tup);
     170         524 :         ispartition = classForm->relispartition;
     171         524 :         ReleaseSysCache(tup);
     172         524 :         if (!ispartition)
     173         310 :             continue;
     174         214 :         if (get_partition_parent(lfirst_oid(l)) == indexId)
     175             :         {
     176         202 :             list_free(idxlist);
     177         202 :             return partIdx;
     178             :         }
     179             :     }
     180             : 
     181         192 :     return InvalidOid;
     182             : }
     183             : 
     184             : /*
     185             :  * map_partition_varattnos - maps varattno of any Vars in expr from the
     186             :  * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
     187             :  * may be either a leaf partition or a partitioned table, but both of which
     188             :  * must be from the same partitioning hierarchy.
     189             :  *
     190             :  * Even though all of the same column names must be present in all relations
     191             :  * in the hierarchy, and they must also have the same types, the attnos may
     192             :  * be different.
     193             :  *
     194             :  * If found_whole_row is not NULL, *found_whole_row returns whether a
     195             :  * whole-row variable was found in the input expression.
     196             :  *
     197             :  * Note: this will work on any node tree, so really the argument and result
     198             :  * should be declared "Node *".  But a substantial majority of the callers
     199             :  * are working on Lists, so it's less messy to do the casts internally.
     200             :  */
     201             : List *
     202        5186 : map_partition_varattnos(List *expr, int fromrel_varno,
     203             :                         Relation to_rel, Relation from_rel,
     204             :                         bool *found_whole_row)
     205             : {
     206        5186 :     bool        my_found_whole_row = false;
     207             : 
     208        5186 :     if (expr != NIL)
     209             :     {
     210             :         AttrNumber *part_attnos;
     211             : 
     212        4954 :         part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel),
     213             :                                                  RelationGetDescr(from_rel));
     214        9908 :         expr = (List *) map_variable_attnos((Node *) expr,
     215             :                                             fromrel_varno, 0,
     216             :                                             part_attnos,
     217        4954 :                                             RelationGetDescr(from_rel)->natts,
     218        4954 :                                             RelationGetForm(to_rel)->reltype,
     219             :                                             &my_found_whole_row);
     220             :     }
     221             : 
     222        5186 :     if (found_whole_row)
     223        4792 :         *found_whole_row = my_found_whole_row;
     224             : 
     225        5186 :     return expr;
     226             : }
     227             : 
     228             : /*
     229             :  * Checks if any of the 'attnums' is a partition key attribute for rel
     230             :  *
     231             :  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
     232             :  * partition key expression.  It's possible for a column to be both used
     233             :  * directly and as part of an expression; if that happens, *used_in_expr may
     234             :  * end up as either true or false.  That's OK for current uses of this
     235             :  * function, because *used_in_expr is only used to tailor the error message
     236             :  * text.
     237             :  */
     238             : bool
     239        7726 : has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
     240             : {
     241             :     PartitionKey key;
     242             :     int         partnatts;
     243             :     List       *partexprs;
     244             :     ListCell   *partexprs_item;
     245             :     int         i;
     246             : 
     247        7726 :     if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     248        6860 :         return false;
     249             : 
     250         866 :     key = RelationGetPartitionKey(rel);
     251         866 :     partnatts = get_partition_natts(key);
     252         866 :     partexprs = get_partition_exprs(key);
     253             : 
     254         866 :     partexprs_item = list_head(partexprs);
     255        1412 :     for (i = 0; i < partnatts; i++)
     256             :     {
     257         962 :         AttrNumber  partattno = get_partition_col_attnum(key, i);
     258             : 
     259         962 :         if (partattno != 0)
     260             :         {
     261         886 :             if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
     262             :                               attnums))
     263             :             {
     264         392 :                 if (used_in_expr)
     265          20 :                     *used_in_expr = false;
     266         392 :                 return true;
     267             :             }
     268             :         }
     269             :         else
     270             :         {
     271             :             /* Arbitrary expression */
     272          76 :             Node       *expr = (Node *) lfirst(partexprs_item);
     273          76 :             Bitmapset  *expr_attrs = NULL;
     274             : 
     275             :             /* Find all attributes referenced */
     276          76 :             pull_varattnos(expr, 1, &expr_attrs);
     277          76 :             partexprs_item = lnext(partexprs, partexprs_item);
     278             : 
     279          76 :             if (bms_overlap(attnums, expr_attrs))
     280             :             {
     281          24 :                 if (used_in_expr)
     282          12 :                     *used_in_expr = true;
     283          24 :                 return true;
     284             :             }
     285             :         }
     286             :     }
     287             : 
     288         450 :     return false;
     289             : }
     290             : 
     291             : /*
     292             :  * get_default_partition_oid
     293             :  *
     294             :  * Given a relation OID, return the OID of the default partition, if one
     295             :  * exists.  Use get_default_oid_from_partdesc where possible, for
     296             :  * efficiency.
     297             :  */
     298             : Oid
     299        4354 : get_default_partition_oid(Oid parentId)
     300             : {
     301             :     HeapTuple   tuple;
     302        4354 :     Oid         defaultPartId = InvalidOid;
     303             : 
     304        4354 :     tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
     305             : 
     306        4354 :     if (HeapTupleIsValid(tuple))
     307             :     {
     308             :         Form_pg_partitioned_table part_table_form;
     309             : 
     310        4354 :         part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     311        4354 :         defaultPartId = part_table_form->partdefid;
     312        4354 :         ReleaseSysCache(tuple);
     313             :     }
     314             : 
     315        4354 :     return defaultPartId;
     316             : }
     317             : 
     318             : /*
     319             :  * update_default_partition_oid
     320             :  *
     321             :  * Update pg_partitioned_table.partdefid with a new default partition OID.
     322             :  */
     323             : void
     324         400 : update_default_partition_oid(Oid parentId, Oid defaultPartId)
     325             : {
     326             :     HeapTuple   tuple;
     327             :     Relation    pg_partitioned_table;
     328             :     Form_pg_partitioned_table part_table_form;
     329             : 
     330         400 :     pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
     331             : 
     332         400 :     tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
     333             : 
     334         400 :     if (!HeapTupleIsValid(tuple))
     335           0 :         elog(ERROR, "cache lookup failed for partition key of relation %u",
     336             :              parentId);
     337             : 
     338         400 :     part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     339         400 :     part_table_form->partdefid = defaultPartId;
     340         400 :     CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
     341             : 
     342         400 :     heap_freetuple(tuple);
     343         400 :     table_close(pg_partitioned_table, RowExclusiveLock);
     344         400 : }
     345             : 
     346             : /*
     347             :  * get_proposed_default_constraint
     348             :  *
     349             :  * This function returns the negation of new_part_constraints, which
     350             :  * would be an integral part of the default partition constraints after
     351             :  * addition of the partition to which the new_part_constraints belongs.
     352             :  */
     353             : List *
     354         298 : get_proposed_default_constraint(List *new_part_constraints)
     355             : {
     356             :     Expr       *defPartConstraint;
     357             : 
     358         298 :     defPartConstraint = make_ands_explicit(new_part_constraints);
     359             : 
     360             :     /*
     361             :      * Derive the partition constraints of default partition by negating the
     362             :      * given partition constraints. The partition constraint never evaluates
     363             :      * to NULL, so negating it like this is safe.
     364             :      */
     365         298 :     defPartConstraint = makeBoolExpr(NOT_EXPR,
     366         298 :                                      list_make1(defPartConstraint),
     367             :                                      -1);
     368             : 
     369             :     /* Simplify, to put the negated expression into canonical form */
     370         298 :     defPartConstraint =
     371             :         (Expr *) eval_const_expressions(NULL,
     372             :                                         (Node *) defPartConstraint);
     373         298 :     defPartConstraint = canonicalize_qual(defPartConstraint, true);
     374             : 
     375         298 :     return make_ands_implicit(defPartConstraint);
     376             : }

Generated by: LCOV version 1.13