LCOV - code coverage report
Current view: top level - src/backend/utils/cache - partcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 118 123 95.9 %
Date: 2019-09-19 02:07:14 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13