LCOV - code coverage report
Current view: top level - src/backend/catalog - partition.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 101 104 97.1 %
Date: 2019-06-18 07:06:57 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        6724 : get_partition_parent(Oid relid)
      52             : {
      53             :     Relation    catalogRelation;
      54             :     Oid         result;
      55             : 
      56        6724 :     catalogRelation = table_open(InheritsRelationId, AccessShareLock);
      57             : 
      58        6724 :     result = get_partition_parent_worker(catalogRelation, relid);
      59             : 
      60        6724 :     if (!OidIsValid(result))
      61           0 :         elog(ERROR, "could not find tuple for parent of relation %u", relid);
      62             : 
      63        6724 :     table_close(catalogRelation, AccessShareLock);
      64             : 
      65        6724 :     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       10474 : get_partition_parent_worker(Relation inhRel, Oid relid)
      75             : {
      76             :     SysScanDesc scan;
      77             :     ScanKeyData key[2];
      78       10474 :     Oid         result = InvalidOid;
      79             :     HeapTuple   tuple;
      80             : 
      81       10474 :     ScanKeyInit(&key[0],
      82             :                 Anum_pg_inherits_inhrelid,
      83             :                 BTEqualStrategyNumber, F_OIDEQ,
      84             :                 ObjectIdGetDatum(relid));
      85       10474 :     ScanKeyInit(&key[1],
      86             :                 Anum_pg_inherits_inhseqno,
      87             :                 BTEqualStrategyNumber, F_INT4EQ,
      88             :                 Int32GetDatum(1));
      89             : 
      90       10474 :     scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
      91             :                               NULL, 2, key);
      92       10474 :     tuple = systable_getnext(scan);
      93       10474 :     if (HeapTupleIsValid(tuple))
      94             :     {
      95        8416 :         Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
      96             : 
      97        8416 :         result = form->inhparent;
      98             :     }
      99             : 
     100       10474 :     systable_endscan(scan);
     101             : 
     102       10474 :     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        2058 : get_partition_ancestors(Oid relid)
     117             : {
     118        2058 :     List       *result = NIL;
     119             :     Relation    inhRel;
     120             : 
     121        2058 :     inhRel = table_open(InheritsRelationId, AccessShareLock);
     122             : 
     123        2058 :     get_partition_ancestors_worker(inhRel, relid, &result);
     124             : 
     125        2058 :     table_close(inhRel, AccessShareLock);
     126             : 
     127        2058 :     return result;
     128             : }
     129             : 
     130             : /*
     131             :  * get_partition_ancestors_worker
     132             :  *      recursive worker for get_partition_ancestors
     133             :  */
     134             : static void
     135        3750 : 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        3750 :     parentOid = get_partition_parent_worker(inhRel, relid);
     141        3750 :     if (parentOid == InvalidOid)
     142        2058 :         return;
     143             : 
     144        1692 :     *ancestors = lappend_oid(*ancestors, parentOid);
     145        1692 :     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        4940 : map_partition_varattnos(List *expr, int fromrel_varno,
     203             :                         Relation to_rel, Relation from_rel,
     204             :                         bool *found_whole_row)
     205             : {
     206        4940 :     bool        my_found_whole_row = false;
     207             : 
     208        4940 :     if (expr != NIL)
     209             :     {
     210             :         AttrNumber *part_attnos;
     211             : 
     212        4716 :         part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel),
     213             :                                                  RelationGetDescr(from_rel),
     214             :                                                  gettext_noop("could not convert row type"));
     215        9432 :         expr = (List *) map_variable_attnos((Node *) expr,
     216             :                                             fromrel_varno, 0,
     217             :                                             part_attnos,
     218        4716 :                                             RelationGetDescr(from_rel)->natts,
     219        4716 :                                             RelationGetForm(to_rel)->reltype,
     220             :                                             &my_found_whole_row);
     221             :     }
     222             : 
     223        4940 :     if (found_whole_row)
     224        4688 :         *found_whole_row = my_found_whole_row;
     225             : 
     226        4940 :     return expr;
     227             : }
     228             : 
     229             : /*
     230             :  * Checks if any of the 'attnums' is a partition key attribute for rel
     231             :  *
     232             :  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
     233             :  * partition key expression.  It's possible for a column to be both used
     234             :  * directly and as part of an expression; if that happens, *used_in_expr may
     235             :  * end up as either true or false.  That's OK for current uses of this
     236             :  * function, because *used_in_expr is only used to tailor the error message
     237             :  * text.
     238             :  */
     239             : bool
     240        7622 : has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
     241             : {
     242             :     PartitionKey key;
     243             :     int         partnatts;
     244             :     List       *partexprs;
     245             :     ListCell   *partexprs_item;
     246             :     int         i;
     247             : 
     248        7622 :     if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     249        6764 :         return false;
     250             : 
     251         858 :     key = RelationGetPartitionKey(rel);
     252         858 :     partnatts = get_partition_natts(key);
     253         858 :     partexprs = get_partition_exprs(key);
     254             : 
     255         858 :     partexprs_item = list_head(partexprs);
     256        1404 :     for (i = 0; i < partnatts; i++)
     257             :     {
     258         954 :         AttrNumber  partattno = get_partition_col_attnum(key, i);
     259             : 
     260         954 :         if (partattno != 0)
     261             :         {
     262         882 :             if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
     263             :                               attnums))
     264             :             {
     265         388 :                 if (used_in_expr)
     266          16 :                     *used_in_expr = false;
     267         388 :                 return true;
     268             :             }
     269             :         }
     270             :         else
     271             :         {
     272             :             /* Arbitrary expression */
     273          72 :             Node       *expr = (Node *) lfirst(partexprs_item);
     274          72 :             Bitmapset  *expr_attrs = NULL;
     275             : 
     276             :             /* Find all attributes referenced */
     277          72 :             pull_varattnos(expr, 1, &expr_attrs);
     278          72 :             partexprs_item = lnext(partexprs_item);
     279             : 
     280          72 :             if (bms_overlap(attnums, expr_attrs))
     281             :             {
     282          20 :                 if (used_in_expr)
     283           8 :                     *used_in_expr = true;
     284          20 :                 return true;
     285             :             }
     286             :         }
     287             :     }
     288             : 
     289         450 :     return false;
     290             : }
     291             : 
     292             : /*
     293             :  * get_default_partition_oid
     294             :  *
     295             :  * Given a relation OID, return the OID of the default partition, if one
     296             :  * exists.  Use get_default_oid_from_partdesc where possible, for
     297             :  * efficiency.
     298             :  */
     299             : Oid
     300        4132 : get_default_partition_oid(Oid parentId)
     301             : {
     302             :     HeapTuple   tuple;
     303        4132 :     Oid         defaultPartId = InvalidOid;
     304             : 
     305        4132 :     tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
     306             : 
     307        4132 :     if (HeapTupleIsValid(tuple))
     308             :     {
     309             :         Form_pg_partitioned_table part_table_form;
     310             : 
     311        4132 :         part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     312        4132 :         defaultPartId = part_table_form->partdefid;
     313        4132 :         ReleaseSysCache(tuple);
     314             :     }
     315             : 
     316        4132 :     return defaultPartId;
     317             : }
     318             : 
     319             : /*
     320             :  * update_default_partition_oid
     321             :  *
     322             :  * Update pg_partitioned_table.partdefid with a new default partition OID.
     323             :  */
     324             : void
     325         386 : update_default_partition_oid(Oid parentId, Oid defaultPartId)
     326             : {
     327             :     HeapTuple   tuple;
     328             :     Relation    pg_partitioned_table;
     329             :     Form_pg_partitioned_table part_table_form;
     330             : 
     331         386 :     pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
     332             : 
     333         386 :     tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
     334             : 
     335         386 :     if (!HeapTupleIsValid(tuple))
     336           0 :         elog(ERROR, "cache lookup failed for partition key of relation %u",
     337             :              parentId);
     338             : 
     339         386 :     part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     340         386 :     part_table_form->partdefid = defaultPartId;
     341         386 :     CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
     342             : 
     343         386 :     heap_freetuple(tuple);
     344         386 :     table_close(pg_partitioned_table, RowExclusiveLock);
     345         386 : }
     346             : 
     347             : /*
     348             :  * get_proposed_default_constraint
     349             :  *
     350             :  * This function returns the negation of new_part_constraints, which
     351             :  * would be an integral part of the default partition constraints after
     352             :  * addition of the partition to which the new_part_constraints belongs.
     353             :  */
     354             : List *
     355         268 : get_proposed_default_constraint(List *new_part_constraints)
     356             : {
     357             :     Expr       *defPartConstraint;
     358             : 
     359         268 :     defPartConstraint = make_ands_explicit(new_part_constraints);
     360             : 
     361             :     /*
     362             :      * Derive the partition constraints of default partition by negating the
     363             :      * given partition constraints. The partition constraint never evaluates
     364             :      * to NULL, so negating it like this is safe.
     365             :      */
     366         268 :     defPartConstraint = makeBoolExpr(NOT_EXPR,
     367             :                                      list_make1(defPartConstraint),
     368             :                                      -1);
     369             : 
     370             :     /* Simplify, to put the negated expression into canonical form */
     371         268 :     defPartConstraint =
     372             :         (Expr *) eval_const_expressions(NULL,
     373             :                                         (Node *) defPartConstraint);
     374         268 :     defPartConstraint = canonicalize_qual(defPartConstraint, true);
     375             : 
     376         268 :     return make_ands_implicit(defPartConstraint);
     377             : }

Generated by: LCOV version 1.13