LCOV - code coverage report
Current view: top level - src/backend/utils/adt - partitionfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 85 85 100.0 %
Date: 2019-06-18 07:06:57 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * partitionfuncs.c
       4             :  *    Functions for accessing partition-related metadata
       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/utils/adt/partitionfuncs.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/htup_details.h"
      19             : #include "catalog/partition.h"
      20             : #include "catalog/pg_class.h"
      21             : #include "catalog/pg_inherits.h"
      22             : #include "catalog/pg_type.h"
      23             : #include "funcapi.h"
      24             : #include "utils/fmgrprotos.h"
      25             : #include "utils/lsyscache.h"
      26             : #include "utils/syscache.h"
      27             : 
      28             : /*
      29             :  * Checks if a given relation can be part of a partition tree.  Returns
      30             :  * false if the relation cannot be processed, in which case it is up to
      31             :  * the caller to decide what to do, by either raising an error or doing
      32             :  * something else.
      33             :  */
      34             : static bool
      35        2070 : check_rel_can_be_partition(Oid relid)
      36             : {
      37             :     char        relkind;
      38             :     bool        relispartition;
      39             : 
      40             :     /* Check if relation exists */
      41        2070 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
      42          12 :         return false;
      43             : 
      44        2058 :     relkind = get_rel_relkind(relid);
      45        2058 :     relispartition = get_rel_relispartition(relid);
      46             : 
      47             :     /* Only allow relation types that can appear in partition trees. */
      48        2058 :     if (!relispartition &&
      49         332 :         relkind != RELKIND_PARTITIONED_TABLE &&
      50             :         relkind != RELKIND_PARTITIONED_INDEX)
      51         300 :         return false;
      52             : 
      53        1758 :     return true;
      54             : }
      55             : 
      56             : /*
      57             :  * pg_partition_tree
      58             :  *
      59             :  * Produce a view with one row per member of a partition tree, beginning
      60             :  * from the top-most parent given by the caller.  This gives information
      61             :  * about each partition, its immediate partitioned parent, if it is
      62             :  * a leaf partition and its level in the hierarchy.
      63             :  */
      64             : Datum
      65         328 : pg_partition_tree(PG_FUNCTION_ARGS)
      66             : {
      67             : #define PG_PARTITION_TREE_COLS  4
      68         328 :     Oid         rootrelid = PG_GETARG_OID(0);
      69             :     FuncCallContext *funcctx;
      70             :     ListCell  **next;
      71             : 
      72             :     /* stuff done only on the first call of the function */
      73         328 :     if (SRF_IS_FIRSTCALL())
      74             :     {
      75             :         MemoryContext oldcxt;
      76             :         TupleDesc   tupdesc;
      77             :         List       *partitions;
      78             : 
      79             :         /* create a function context for cross-call persistence */
      80          84 :         funcctx = SRF_FIRSTCALL_INIT();
      81             : 
      82          84 :         if (!check_rel_can_be_partition(rootrelid))
      83          24 :             SRF_RETURN_DONE(funcctx);
      84             : 
      85             :         /* switch to memory context appropriate for multiple function calls */
      86          60 :         oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
      87             : 
      88             :         /*
      89             :          * Find all members of inheritance set.  We only need AccessShareLock
      90             :          * on the children for the partition information lookup.
      91             :          */
      92          60 :         partitions = find_all_inheritors(rootrelid, AccessShareLock, NULL);
      93             : 
      94          60 :         tupdesc = CreateTemplateTupleDesc(PG_PARTITION_TREE_COLS);
      95          60 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relid",
      96             :                            REGCLASSOID, -1, 0);
      97          60 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "parentid",
      98             :                            REGCLASSOID, -1, 0);
      99          60 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "isleaf",
     100             :                            BOOLOID, -1, 0);
     101          60 :         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "level",
     102             :                            INT4OID, -1, 0);
     103             : 
     104          60 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
     105             : 
     106             :         /* allocate memory for user context */
     107          60 :         next = (ListCell **) palloc(sizeof(ListCell *));
     108          60 :         *next = list_head(partitions);
     109          60 :         funcctx->user_fctx = (void *) next;
     110             : 
     111          60 :         MemoryContextSwitchTo(oldcxt);
     112             :     }
     113             : 
     114             :     /* stuff done on every call of the function */
     115         304 :     funcctx = SRF_PERCALL_SETUP();
     116         304 :     next = (ListCell **) funcctx->user_fctx;
     117             : 
     118         304 :     if (*next != NULL)
     119             :     {
     120             :         Datum       result;
     121             :         Datum       values[PG_PARTITION_TREE_COLS];
     122             :         bool        nulls[PG_PARTITION_TREE_COLS];
     123             :         HeapTuple   tuple;
     124         244 :         Oid         parentid = InvalidOid;
     125         244 :         Oid         relid = lfirst_oid(*next);
     126         244 :         char        relkind = get_rel_relkind(relid);
     127         244 :         int         level = 0;
     128         244 :         List       *ancestors = get_partition_ancestors(lfirst_oid(*next));
     129             :         ListCell   *lc;
     130             : 
     131             :         /*
     132             :          * Form tuple with appropriate data.
     133             :          */
     134         244 :         MemSet(nulls, 0, sizeof(nulls));
     135         244 :         MemSet(values, 0, sizeof(values));
     136             : 
     137             :         /* relid */
     138         244 :         values[0] = ObjectIdGetDatum(relid);
     139             : 
     140             :         /* parentid */
     141         244 :         if (ancestors != NIL)
     142         208 :             parentid = linitial_oid(ancestors);
     143         244 :         if (OidIsValid(parentid))
     144         208 :             values[1] = ObjectIdGetDatum(parentid);
     145             :         else
     146          36 :             nulls[1] = true;
     147             : 
     148             :         /* isleaf */
     149         244 :         values[2] = BoolGetDatum(relkind != RELKIND_PARTITIONED_TABLE &&
     150             :                                  relkind != RELKIND_PARTITIONED_INDEX);
     151             : 
     152             :         /* level */
     153         244 :         if (relid != rootrelid)
     154             :         {
     155         252 :             foreach(lc, ancestors)
     156             :             {
     157         252 :                 level++;
     158         252 :                 if (lfirst_oid(lc) == rootrelid)
     159         184 :                     break;
     160             :             }
     161             :         }
     162         244 :         values[3] = Int32GetDatum(level);
     163             : 
     164         244 :         *next = lnext(*next);
     165             : 
     166         244 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     167         244 :         result = HeapTupleGetDatum(tuple);
     168         244 :         SRF_RETURN_NEXT(funcctx, result);
     169             :     }
     170             : 
     171             :     /* done when there are no more elements left */
     172          60 :     SRF_RETURN_DONE(funcctx);
     173             : }
     174             : 
     175             : /*
     176             :  * pg_partition_root
     177             :  *
     178             :  * Returns the top-most parent of the partition tree to which a given
     179             :  * relation belongs, or NULL if it's not (or cannot be) part of any
     180             :  * partition tree.
     181             :  */
     182             : Datum
     183          64 : pg_partition_root(PG_FUNCTION_ARGS)
     184             : {
     185          64 :     Oid         relid = PG_GETARG_OID(0);
     186             :     Oid         rootrelid;
     187             :     List       *ancestors;
     188             : 
     189          64 :     if (!check_rel_can_be_partition(relid))
     190          24 :         PG_RETURN_NULL();
     191             : 
     192             :     /* fetch the list of ancestors */
     193          40 :     ancestors = get_partition_ancestors(relid);
     194             : 
     195             :     /*
     196             :      * If the input relation is already the top-most parent, just return
     197             :      * itself.
     198             :      */
     199          40 :     if (ancestors == NIL)
     200           8 :         PG_RETURN_OID(relid);
     201             : 
     202          32 :     rootrelid = llast_oid(ancestors);
     203          32 :     list_free(ancestors);
     204             : 
     205             :     /*
     206             :      * "rootrelid" must contain a valid OID, given that the input relation is
     207             :      * a valid partition tree member as checked above.
     208             :      */
     209             :     Assert(OidIsValid(rootrelid));
     210          32 :     PG_RETURN_OID(rootrelid);
     211             : }
     212             : 
     213             : /*
     214             :  * pg_partition_ancestors
     215             :  *
     216             :  * Produces a view with one row per ancestor of the given partition,
     217             :  * including the input relation itself.
     218             :  */
     219             : Datum
     220        4796 : pg_partition_ancestors(PG_FUNCTION_ARGS)
     221             : {
     222        4796 :     Oid         relid = PG_GETARG_OID(0);
     223             :     FuncCallContext *funcctx;
     224             :     ListCell  **next;
     225             : 
     226        4796 :     if (SRF_IS_FIRSTCALL())
     227             :     {
     228             :         MemoryContext oldcxt;
     229             :         List       *ancestors;
     230             : 
     231        1922 :         funcctx = SRF_FIRSTCALL_INIT();
     232             : 
     233        1922 :         if (!check_rel_can_be_partition(relid))
     234         264 :             SRF_RETURN_DONE(funcctx);
     235             : 
     236        1658 :         oldcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     237             : 
     238        1658 :         ancestors = get_partition_ancestors(relid);
     239        1658 :         ancestors = lcons_oid(relid, ancestors);
     240             : 
     241        1658 :         next = (ListCell **) palloc(sizeof(ListCell *));
     242        1658 :         *next = list_head(ancestors);
     243        1658 :         funcctx->user_fctx = (void *) next;
     244             : 
     245        1658 :         MemoryContextSwitchTo(oldcxt);
     246             :     }
     247             : 
     248        4532 :     funcctx = SRF_PERCALL_SETUP();
     249        4532 :     next = (ListCell **) funcctx->user_fctx;
     250             : 
     251        4532 :     if (*next != NULL)
     252             :     {
     253        2874 :         Oid         relid = lfirst_oid(*next);
     254             : 
     255        2874 :         *next = lnext(*next);
     256        2874 :         SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
     257             :     }
     258             : 
     259        1658 :     SRF_RETURN_DONE(funcctx);
     260             : }

Generated by: LCOV version 1.13