LCOV - differential code coverage report
Current view: top level - src/backend/utils/cache - partcache.c (source / functions) Coverage Total Hit UNC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 95.6 % 135 129 1 4 1 2 67 5 55 3 71 3
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 5 5 3 1 1 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * partcache.c
       4                 :  *      Support routines for manipulating partition information cached in
       5                 :  *      relcache
       6                 :  *
       7                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       8                 :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *        src/backend/utils/cache/partcache.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 : */
      15                 : #include "postgres.h"
      16                 : 
      17                 : #include "access/hash.h"
      18                 : #include "access/htup_details.h"
      19                 : #include "access/nbtree.h"
      20                 : #include "access/relation.h"
      21                 : #include "catalog/partition.h"
      22                 : #include "catalog/pg_inherits.h"
      23                 : #include "catalog/pg_opclass.h"
      24                 : #include "catalog/pg_partitioned_table.h"
      25                 : #include "miscadmin.h"
      26                 : #include "nodes/makefuncs.h"
      27                 : #include "nodes/nodeFuncs.h"
      28                 : #include "optimizer/optimizer.h"
      29                 : #include "partitioning/partbounds.h"
      30                 : #include "rewrite/rewriteHandler.h"
      31                 : #include "utils/builtins.h"
      32                 : #include "utils/datum.h"
      33                 : #include "utils/lsyscache.h"
      34                 : #include "utils/memutils.h"
      35                 : #include "utils/partcache.h"
      36                 : #include "utils/rel.h"
      37                 : #include "utils/syscache.h"
      38                 : 
      39                 : 
      40                 : static void RelationBuildPartitionKey(Relation relation);
      41                 : static List *generate_partition_qual(Relation rel);
      42                 : 
      43                 : /*
      44                 :  * RelationGetPartitionKey -- get partition key, if relation is partitioned
      45                 :  *
      46                 :  * Note: partition keys are not allowed to change after the partitioned rel
      47                 :  * is created.  RelationClearRelation knows this and preserves rd_partkey
      48                 :  * across relcache rebuilds, as long as the relation is open.  Therefore,
      49                 :  * even though we hand back a direct pointer into the relcache entry, it's
      50                 :  * safe for callers to continue to use that pointer as long as they hold
      51                 :  * the relation open.
      52                 :  */
      53                 : PartitionKey
      54 CBC       51182 : RelationGetPartitionKey(Relation rel)
      55                 : {
      56           51182 :     if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
      57               6 :         return NULL;
      58                 : 
      59           51176 :     if (unlikely(rel->rd_partkey == NULL))
      60            8022 :         RelationBuildPartitionKey(rel);
      61                 : 
      62           51176 :     return rel->rd_partkey;
      63                 : }
      64                 : 
      65                 : /*
      66                 :  * RelationBuildPartitionKey
      67                 :  *      Build partition key data of relation, and attach to relcache
      68                 :  *
      69                 :  * Partitioning key data is a complex structure; to avoid complicated logic to
      70                 :  * free individual elements whenever the relcache entry is flushed, we give it
      71                 :  * its own memory context, a child of CacheMemoryContext, which can easily be
      72                 :  * deleted on its own.  To avoid leaking memory in that context in case of an
      73                 :  * error partway through this function, the context is initially created as a
      74                 :  * child of CurTransactionContext and only re-parented to CacheMemoryContext
      75                 :  * at the end, when no further errors are possible.  Also, we don't make this
      76                 :  * context the current context except in very brief code sections, out of fear
      77                 :  * that some of our callees allocate memory on their own which would be leaked
      78                 :  * permanently.
      79                 :  */
      80                 : static void
      81            8022 : RelationBuildPartitionKey(Relation relation)
      82                 : {
      83                 :     Form_pg_partitioned_table form;
      84                 :     HeapTuple   tuple;
      85                 :     bool        isnull;
      86                 :     int         i;
      87                 :     PartitionKey key;
      88                 :     AttrNumber *attrs;
      89                 :     oidvector  *opclass;
      90                 :     oidvector  *collation;
      91                 :     ListCell   *partexprs_item;
      92                 :     Datum       datum;
      93                 :     MemoryContext partkeycxt,
      94                 :                 oldcxt;
      95                 :     int16       procnum;
      96                 : 
      97            8022 :     tuple = SearchSysCache1(PARTRELID,
      98                 :                             ObjectIdGetDatum(RelationGetRelid(relation)));
      99                 : 
     100            8022 :     if (!HeapTupleIsValid(tuple))
     101 UBC           0 :         elog(ERROR, "cache lookup failed for partition key of relation %u",
     102                 :              RelationGetRelid(relation));
     103                 : 
     104 CBC        8022 :     partkeycxt = AllocSetContextCreate(CurTransactionContext,
     105                 :                                        "partition key",
     106                 :                                        ALLOCSET_SMALL_SIZES);
     107            8022 :     MemoryContextCopyAndSetIdentifier(partkeycxt,
     108                 :                                       RelationGetRelationName(relation));
     109                 : 
     110            8022 :     key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
     111                 :                                                 sizeof(PartitionKeyData));
     112                 : 
     113                 :     /* Fixed-length attributes */
     114            8022 :     form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
     115            8022 :     key->strategy = form->partstrat;
     116            8022 :     key->partnatts = form->partnatts;
     117                 : 
     118                 :     /* Validate partition strategy code */
     119 GNC        8022 :     if (key->strategy != PARTITION_STRATEGY_LIST &&
     120            4220 :         key->strategy != PARTITION_STRATEGY_RANGE &&
     121             421 :         key->strategy != PARTITION_STRATEGY_HASH)
     122 UNC           0 :         elog(ERROR, "invalid partition strategy \"%c\"", key->strategy);
     123                 : 
     124                 :     /*
     125 ECB             :      * We can rely on the first variable-length attribute being mapped to the
     126                 :      * relevant field of the catalog's C struct, because all previous
     127                 :      * attributes are non-nullable and fixed-length.
     128 EUB             :      */
     129 GIC        8022 :     attrs = form->partattrs.values;
     130                 : 
     131                 :     /* But use the hard way to retrieve further variable-length attributes */
     132                 :     /* Operator class */
     133 GNC        8022 :     datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
     134                 :                                    Anum_pg_partitioned_table_partclass);
     135 GIC        8022 :     opclass = (oidvector *) DatumGetPointer(datum);
     136                 : 
     137                 :     /* Collation */
     138 GNC        8022 :     datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
     139                 :                                    Anum_pg_partitioned_table_partcollation);
     140 GIC        8022 :     collation = (oidvector *) DatumGetPointer(datum);
     141                 : 
     142 ECB             :     /* Expressions */
     143 GIC        8022 :     datum = SysCacheGetAttr(PARTRELID, tuple,
     144 ECB             :                             Anum_pg_partitioned_table_partexprs, &isnull);
     145 GIC        8022 :     if (!isnull)
     146                 :     {
     147 ECB             :         char       *exprString;
     148                 :         Node       *expr;
     149                 : 
     150 GIC         463 :         exprString = TextDatumGetCString(datum);
     151             463 :         expr = stringToNode(exprString);
     152             463 :         pfree(exprString);
     153                 : 
     154 ECB             :         /*
     155                 :          * Run the expressions through const-simplification since the planner
     156                 :          * will be comparing them to similarly-processed qual clause operands,
     157                 :          * and may fail to detect valid matches without this step; fix
     158                 :          * opfuncids while at it.  We don't need to bother with
     159                 :          * canonicalize_qual() though, because partition expressions should be
     160                 :          * in canonical form already (ie, no need for OR-merging or constant
     161                 :          * elimination).
     162                 :          */
     163 GIC         463 :         expr = eval_const_expressions(NULL, expr);
     164             463 :         fix_opfuncids(expr);
     165                 : 
     166             463 :         oldcxt = MemoryContextSwitchTo(partkeycxt);
     167 CBC         463 :         key->partexprs = (List *) copyObject(expr);
     168             463 :         MemoryContextSwitchTo(oldcxt);
     169                 :     }
     170 ECB             : 
     171                 :     /* Allocate assorted arrays in the partkeycxt, which we'll fill below */
     172 CBC        8022 :     oldcxt = MemoryContextSwitchTo(partkeycxt);
     173 GIC        8022 :     key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
     174            8022 :     key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
     175            8022 :     key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
     176 CBC        8022 :     key->partsupfunc = (FmgrInfo *) palloc0(key->partnatts * sizeof(FmgrInfo));
     177 ECB             : 
     178 CBC        8022 :     key->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid));
     179            8022 :     key->parttypid = (Oid *) palloc0(key->partnatts * sizeof(Oid));
     180            8022 :     key->parttypmod = (int32 *) palloc0(key->partnatts * sizeof(int32));
     181 GIC        8022 :     key->parttyplen = (int16 *) palloc0(key->partnatts * sizeof(int16));
     182 CBC        8022 :     key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
     183            8022 :     key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
     184            8022 :     key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
     185            8022 :     MemoryContextSwitchTo(oldcxt);
     186 ECB             : 
     187                 :     /* determine support function number to search for */
     188 CBC        8022 :     procnum = (key->strategy == PARTITION_STRATEGY_HASH) ?
     189 ECB             :         HASHEXTENDED_PROC : BTORDER_PROC;
     190                 : 
     191                 :     /* Copy partattrs and fill other per-attribute info */
     192 CBC        8022 :     memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
     193 GIC        8022 :     partexprs_item = list_head(key->partexprs);
     194           16876 :     for (i = 0; i < key->partnatts; i++)
     195                 :     {
     196 CBC        8854 :         AttrNumber  attno = key->partattrs[i];
     197 ECB             :         HeapTuple   opclasstup;
     198                 :         Form_pg_opclass opclassform;
     199                 :         Oid         funcid;
     200                 : 
     201                 :         /* Collect opfamily information */
     202 GIC        8854 :         opclasstup = SearchSysCache1(CLAOID,
     203                 :                                      ObjectIdGetDatum(opclass->values[i]));
     204            8854 :         if (!HeapTupleIsValid(opclasstup))
     205 UIC           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclass->values[i]);
     206 ECB             : 
     207 GIC        8854 :         opclassform = (Form_pg_opclass) GETSTRUCT(opclasstup);
     208 CBC        8854 :         key->partopfamily[i] = opclassform->opcfamily;
     209 GBC        8854 :         key->partopcintype[i] = opclassform->opcintype;
     210                 : 
     211 ECB             :         /* Get a support function for the specified opfamily and datatypes */
     212 CBC        8854 :         funcid = get_opfamily_proc(opclassform->opcfamily,
     213 ECB             :                                    opclassform->opcintype,
     214                 :                                    opclassform->opcintype,
     215                 :                                    procnum);
     216 CBC        8854 :         if (!OidIsValid(funcid))
     217 UIC           0 :             ereport(ERROR,
     218                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     219                 :                      errmsg("operator class \"%s\" of access method %s is missing support function %d for type %s",
     220 ECB             :                             NameStr(opclassform->opcname),
     221 EUB             :                             (key->strategy == PARTITION_STRATEGY_HASH) ?
     222                 :                             "hash" : "btree",
     223                 :                             procnum,
     224                 :                             format_type_be(opclassform->opcintype))));
     225                 : 
     226 GIC        8854 :         fmgr_info_cxt(funcid, &key->partsupfunc[i], partkeycxt);
     227                 : 
     228                 :         /* Collation */
     229            8854 :         key->partcollation[i] = collation->values[i];
     230 ECB             : 
     231                 :         /* Collect type information */
     232 GIC        8854 :         if (attno != 0)
     233 ECB             :         {
     234 GIC        8355 :             Form_pg_attribute att = TupleDescAttr(relation->rd_att, attno - 1);
     235                 : 
     236 CBC        8355 :             key->parttypid[i] = att->atttypid;
     237 GIC        8355 :             key->parttypmod[i] = att->atttypmod;
     238 CBC        8355 :             key->parttypcoll[i] = att->attcollation;
     239                 :         }
     240 ECB             :         else
     241                 :         {
     242 CBC         499 :             if (partexprs_item == NULL)
     243 UIC           0 :                 elog(ERROR, "wrong number of partition key expressions");
     244                 : 
     245 GIC         499 :             key->parttypid[i] = exprType(lfirst(partexprs_item));
     246 CBC         499 :             key->parttypmod[i] = exprTypmod(lfirst(partexprs_item));
     247 GBC         499 :             key->parttypcoll[i] = exprCollation(lfirst(partexprs_item));
     248                 : 
     249 CBC         499 :             partexprs_item = lnext(key->partexprs, partexprs_item);
     250 ECB             :         }
     251 CBC        8854 :         get_typlenbyvalalign(key->parttypid[i],
     252 GIC        8854 :                              &key->parttyplen[i],
     253 CBC        8854 :                              &key->parttypbyval[i],
     254 GIC        8854 :                              &key->parttypalign[i]);
     255 ECB             : 
     256 CBC        8854 :         ReleaseSysCache(opclasstup);
     257 ECB             :     }
     258                 : 
     259 GIC        8022 :     ReleaseSysCache(tuple);
     260 ECB             : 
     261                 :     /* Assert that we're not leaking any old data during assignments below */
     262 GIC        8022 :     Assert(relation->rd_partkeycxt == NULL);
     263 CBC        8022 :     Assert(relation->rd_partkey == NULL);
     264                 : 
     265                 :     /*
     266 ECB             :      * Success --- reparent our context and make the relcache point to the
     267                 :      * newly constructed key
     268                 :      */
     269 GIC        8022 :     MemoryContextSetParent(partkeycxt, CacheMemoryContext);
     270            8022 :     relation->rd_partkeycxt = partkeycxt;
     271            8022 :     relation->rd_partkey = key;
     272            8022 : }
     273 ECB             : 
     274                 : /*
     275                 :  * RelationGetPartitionQual
     276                 :  *
     277                 :  * Returns a list of partition quals
     278                 :  */
     279                 : List *
     280 GIC       10947 : RelationGetPartitionQual(Relation rel)
     281                 : {
     282                 :     /* Quick exit */
     283           10947 :     if (!rel->rd_rel->relispartition)
     284 CBC        6657 :         return NIL;
     285                 : 
     286 GIC        4290 :     return generate_partition_qual(rel);
     287 ECB             : }
     288                 : 
     289                 : /*
     290                 :  * get_partition_qual_relid
     291                 :  *
     292                 :  * Returns an expression tree describing the passed-in relation's partition
     293                 :  * constraint.
     294                 :  *
     295                 :  * If the relation is not found, or is not a partition, or there is no
     296                 :  * partition constraint, return NULL.  We must guard against the first two
     297                 :  * cases because this supports a SQL function that could be passed any OID.
     298                 :  * The last case can happen even if relispartition is true, when a default
     299                 :  * partition is the only partition.
     300                 :  */
     301                 : Expr *
     302 GIC         121 : get_partition_qual_relid(Oid relid)
     303                 : {
     304             121 :     Expr       *result = NULL;
     305                 : 
     306 ECB             :     /* Do the work only if this relation exists and is a partition. */
     307 GIC         121 :     if (get_rel_relispartition(relid))
     308 ECB             :     {
     309 GIC         121 :         Relation    rel = relation_open(relid, AccessShareLock);
     310                 :         List       *and_args;
     311 ECB             : 
     312 GIC         121 :         and_args = generate_partition_qual(rel);
     313 ECB             : 
     314                 :         /* Convert implicit-AND list format to boolean expression */
     315 GIC         121 :         if (and_args == NIL)
     316 CBC           9 :             result = NULL;
     317 GIC         112 :         else if (list_length(and_args) > 1)
     318             106 :             result = makeBoolExpr(AND_EXPR, and_args, -1);
     319 ECB             :         else
     320 CBC           6 :             result = linitial(and_args);
     321 ECB             : 
     322                 :         /* Keep the lock, to allow safe deparsing against the rel by caller. */
     323 GIC         121 :         relation_close(rel, NoLock);
     324 ECB             :     }
     325                 : 
     326 GIC         121 :     return result;
     327 ECB             : }
     328                 : 
     329                 : /*
     330                 :  * generate_partition_qual
     331                 :  *
     332                 :  * Generate partition predicate from rel's partition bound expression. The
     333                 :  * function returns a NIL list if there is no predicate.
     334                 :  *
     335                 :  * We cache a copy of the result in the relcache entry, after constructing
     336                 :  * it using the caller's context.  This approach avoids leaking any data
     337                 :  * into long-lived cache contexts, especially if we fail partway through.
     338                 :  */
     339                 : static List *
     340 GIC        4694 : generate_partition_qual(Relation rel)
     341                 : {
     342                 :     HeapTuple   tuple;
     343                 :     MemoryContext oldcxt;
     344 ECB             :     Datum       boundDatum;
     345                 :     bool        isnull;
     346 GIC        4694 :     List       *my_qual = NIL,
     347            4694 :                *result = NIL;
     348                 :     Oid         parentrelid;
     349                 :     Relation    parent;
     350 ECB             : 
     351                 :     /* Guard against stack overflow due to overly deep partition tree */
     352 GIC        4694 :     check_stack_depth();
     353                 : 
     354                 :     /* If we already cached the result, just return a copy */
     355            4694 :     if (rel->rd_partcheckvalid)
     356 CBC        3120 :         return copyObject(rel->rd_partcheck);
     357                 : 
     358                 :     /*
     359 ECB             :      * Grab at least an AccessShareLock on the parent table.  Must do this
     360                 :      * even if the partition has been partially detached, because transactions
     361                 :      * concurrent with the detach might still be trying to use a partition
     362                 :      * descriptor that includes it.
     363                 :      */
     364 GIC        1574 :     parentrelid = get_partition_parent(RelationGetRelid(rel), true);
     365            1574 :     parent = relation_open(parentrelid, AccessShareLock);
     366                 : 
     367                 :     /* Get pg_class.relpartbound */
     368 CBC        1574 :     tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
     369            1574 :     if (!HeapTupleIsValid(tuple))
     370 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u",
     371                 :              RelationGetRelid(rel));
     372 ECB             : 
     373 CBC        1574 :     boundDatum = SysCacheGetAttr(RELOID, tuple,
     374 EUB             :                                  Anum_pg_class_relpartbound,
     375                 :                                  &isnull);
     376 GIC        1574 :     if (!isnull)
     377 ECB             :     {
     378                 :         PartitionBoundSpec *bound;
     379                 : 
     380 CBC        1568 :         bound = castNode(PartitionBoundSpec,
     381                 :                          stringToNode(TextDatumGetCString(boundDatum)));
     382                 : 
     383 GIC        1568 :         my_qual = get_qual_from_partbound(parent, bound);
     384 ECB             :     }
     385                 : 
     386 GIC        1574 :     ReleaseSysCache(tuple);
     387 ECB             : 
     388                 :     /* Add the parent's quals to the list (if any) */
     389 GIC        1574 :     if (parent->rd_rel->relispartition)
     390 CBC         283 :         result = list_concat(generate_partition_qual(parent), my_qual);
     391                 :     else
     392 GIC        1291 :         result = my_qual;
     393 ECB             : 
     394                 :     /*
     395                 :      * Change Vars to have partition's attnos instead of the parent's. We do
     396                 :      * this after we concatenate the parent's quals, because we want every Var
     397                 :      * in it to bear this relation's attnos. It's safe to assume varno = 1
     398                 :      * here.
     399                 :      */
     400 GIC        1574 :     result = map_partition_varattnos(result, 1, rel, parent);
     401                 : 
     402                 :     /* Assert that we're not leaking any old data during assignments below */
     403            1574 :     Assert(rel->rd_partcheckcxt == NULL);
     404 CBC        1574 :     Assert(rel->rd_partcheck == NIL);
     405                 : 
     406                 :     /*
     407 ECB             :      * Save a copy in the relcache.  The order of these operations is fairly
     408                 :      * critical to avoid memory leaks and ensure that we don't leave a corrupt
     409                 :      * relcache entry if we fail partway through copyObject.
     410                 :      *
     411                 :      * If, as is definitely possible, the partcheck list is NIL, then we do
     412                 :      * not need to make a context to hold it.
     413                 :      */
     414 GIC        1574 :     if (result != NIL)
     415                 :     {
     416            1545 :         rel->rd_partcheckcxt = AllocSetContextCreate(CacheMemoryContext,
     417                 :                                                      "partition constraint",
     418 ECB             :                                                      ALLOCSET_SMALL_SIZES);
     419 GIC        1545 :         MemoryContextCopyAndSetIdentifier(rel->rd_partcheckcxt,
     420 ECB             :                                           RelationGetRelationName(rel));
     421 GIC        1545 :         oldcxt = MemoryContextSwitchTo(rel->rd_partcheckcxt);
     422            1545 :         rel->rd_partcheck = copyObject(result);
     423 CBC        1545 :         MemoryContextSwitchTo(oldcxt);
     424                 :     }
     425 ECB             :     else
     426 CBC          29 :         rel->rd_partcheck = NIL;
     427            1574 :     rel->rd_partcheckvalid = true;
     428                 : 
     429                 :     /* Keep the parent locked until commit */
     430            1574 :     relation_close(parent, NoLock);
     431 ECB             : 
     432                 :     /* Return the working copy to the caller */
     433 GIC        1574 :     return result;
     434 ECB             : }
        

Generated by: LCOV version v1.16-55-g56c0a2a