LCOV - code coverage report
Current view: top level - src/backend/catalog - toasting.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 123 134 91.8 %
Date: 2017-09-29 13:40:31 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * toasting.c
       4             :  *    This file contains routines to support creation of toast tables
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/toasting.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/tuptoaster.h"
      18             : #include "access/xact.h"
      19             : #include "catalog/binary_upgrade.h"
      20             : #include "catalog/dependency.h"
      21             : #include "catalog/heap.h"
      22             : #include "catalog/index.h"
      23             : #include "catalog/namespace.h"
      24             : #include "catalog/pg_am.h"
      25             : #include "catalog/pg_namespace.h"
      26             : #include "catalog/pg_opclass.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "catalog/toasting.h"
      29             : #include "miscadmin.h"
      30             : #include "nodes/makefuncs.h"
      31             : #include "storage/lock.h"
      32             : #include "utils/builtins.h"
      33             : #include "utils/rel.h"
      34             : #include "utils/syscache.h"
      35             : 
      36             : /* Potentially set by pg_upgrade_support functions */
      37             : Oid         binary_upgrade_next_toast_pg_type_oid = InvalidOid;
      38             : 
      39             : static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
      40             :                          LOCKMODE lockmode, bool check);
      41             : static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
      42             :                    Datum reloptions, LOCKMODE lockmode, bool check);
      43             : static bool needs_toast_table(Relation rel);
      44             : 
      45             : 
      46             : /*
      47             :  * CreateToastTable variants
      48             :  *      If the table needs a toast table, and doesn't already have one,
      49             :  *      then create a toast table for it.
      50             :  *
      51             :  * reloptions for the toast table can be passed, too.  Pass (Datum) 0
      52             :  * for default reloptions.
      53             :  *
      54             :  * We expect the caller to have verified that the relation is a table and have
      55             :  * already done any necessary permission checks.  Callers expect this function
      56             :  * to end with CommandCounterIncrement if it makes any changes.
      57             :  */
      58             : void
      59         785 : AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
      60             : {
      61         785 :     CheckAndCreateToastTable(relOid, reloptions, lockmode, true);
      62         785 : }
      63             : 
      64             : void
      65          53 : NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
      66             : {
      67          53 :     CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
      68          53 : }
      69             : 
      70             : void
      71        1505 : NewRelationCreateToastTable(Oid relOid, Datum reloptions)
      72             : {
      73        1505 :     CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
      74        1505 : }
      75             : 
      76             : static void
      77        2343 : CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
      78             : {
      79             :     Relation    rel;
      80             : 
      81        2343 :     rel = heap_open(relOid, lockmode);
      82             : 
      83             :     /* create_toast_table does all the work */
      84        2343 :     (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
      85             : 
      86        2343 :     heap_close(rel, NoLock);
      87        2343 : }
      88             : 
      89             : /*
      90             :  * Create a toast table during bootstrap
      91             :  *
      92             :  * Here we need to prespecify the OIDs of the toast table and its index
      93             :  */
      94             : void
      95          12 : BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
      96             : {
      97             :     Relation    rel;
      98             : 
      99          12 :     rel = heap_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock);
     100             : 
     101          12 :     if (rel->rd_rel->relkind != RELKIND_RELATION &&
     102           0 :         rel->rd_rel->relkind != RELKIND_MATVIEW)
     103           0 :         ereport(ERROR,
     104             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     105             :                  errmsg("\"%s\" is not a table or materialized view",
     106             :                         relName)));
     107             : 
     108             :     /* create_toast_table does all the work */
     109          12 :     if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,
     110             :                             AccessExclusiveLock, false))
     111           0 :         elog(ERROR, "\"%s\" does not require a toast table",
     112             :              relName);
     113             : 
     114          12 :     heap_close(rel, NoLock);
     115          12 : }
     116             : 
     117             : 
     118             : /*
     119             :  * create_toast_table --- internal workhorse
     120             :  *
     121             :  * rel is already opened and locked
     122             :  * toastOid and toastIndexOid are normally InvalidOid, but during
     123             :  * bootstrap they can be nonzero to specify hand-assigned OIDs
     124             :  */
     125             : static bool
     126        2355 : create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
     127             :                    Datum reloptions, LOCKMODE lockmode, bool check)
     128             : {
     129        2355 :     Oid         relOid = RelationGetRelid(rel);
     130             :     HeapTuple   reltup;
     131             :     TupleDesc   tupdesc;
     132             :     bool        shared_relation;
     133             :     bool        mapped_relation;
     134             :     Relation    toast_rel;
     135             :     Relation    class_rel;
     136             :     Oid         toast_relid;
     137        2355 :     Oid         toast_typid = InvalidOid;
     138             :     Oid         namespaceid;
     139             :     char        toast_relname[NAMEDATALEN];
     140             :     char        toast_idxname[NAMEDATALEN];
     141             :     IndexInfo  *indexInfo;
     142             :     Oid         collationObjectId[2];
     143             :     Oid         classObjectId[2];
     144             :     int16       coloptions[2];
     145             :     ObjectAddress baseobject,
     146             :                 toastobject;
     147             : 
     148             :     /*
     149             :      * Toast table is shared if and only if its parent is.
     150             :      *
     151             :      * We cannot allow toasting a shared relation after initdb (because
     152             :      * there's no way to mark it toasted in other databases' pg_class).
     153             :      */
     154        2355 :     shared_relation = rel->rd_rel->relisshared;
     155        2355 :     if (shared_relation && !IsBootstrapProcessingMode())
     156           0 :         ereport(ERROR,
     157             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     158             :                  errmsg("shared tables cannot be toasted after initdb")));
     159             : 
     160             :     /* It's mapped if and only if its parent is, too */
     161        2355 :     mapped_relation = RelationIsMapped(rel);
     162             : 
     163             :     /*
     164             :      * Is it already toasted?
     165             :      */
     166        2355 :     if (rel->rd_rel->reltoastrelid != InvalidOid)
     167         323 :         return false;
     168             : 
     169             :     /*
     170             :      * Check to see whether the table actually needs a TOAST table.
     171             :      */
     172        2032 :     if (!IsBinaryUpgrade)
     173             :     {
     174             :         /* Normal mode, normal check */
     175        2032 :         if (!needs_toast_table(rel))
     176        1339 :             return false;
     177             :     }
     178             :     else
     179             :     {
     180             :         /*
     181             :          * In binary-upgrade mode, create a TOAST table if and only if
     182             :          * pg_upgrade told us to (ie, a TOAST table OID has been provided).
     183             :          *
     184             :          * This indicates that the old cluster had a TOAST table for the
     185             :          * current table.  We must create a TOAST table to receive the old
     186             :          * TOAST file, even if the table seems not to need one.
     187             :          *
     188             :          * Contrariwise, if the old cluster did not have a TOAST table, we
     189             :          * should be able to get along without one even if the new version's
     190             :          * needs_toast_table rules suggest we should have one.  There is a lot
     191             :          * of daylight between where we will create a TOAST table and where
     192             :          * one is really necessary to avoid failures, so small cross-version
     193             :          * differences in the when-to-create heuristic shouldn't be a problem.
     194             :          * If we tried to create a TOAST table anyway, we would have the
     195             :          * problem that it might take up an OID that will conflict with some
     196             :          * old-cluster table we haven't seen yet.
     197             :          */
     198           0 :         if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid) ||
     199           0 :             !OidIsValid(binary_upgrade_next_toast_pg_type_oid))
     200           0 :             return false;
     201             :     }
     202             : 
     203             :     /*
     204             :      * If requested check lockmode is sufficient. This is a cross check in
     205             :      * case of errors or conflicting decisions in earlier code.
     206             :      */
     207         693 :     if (check && lockmode != AccessExclusiveLock)
     208           0 :         elog(ERROR, "AccessExclusiveLock required to add toast table.");
     209             : 
     210             :     /*
     211             :      * Create the toast table and its index
     212             :      */
     213         693 :     snprintf(toast_relname, sizeof(toast_relname),
     214             :              "pg_toast_%u", relOid);
     215         693 :     snprintf(toast_idxname, sizeof(toast_idxname),
     216             :              "pg_toast_%u_index", relOid);
     217             : 
     218             :     /* this is pretty painful...  need a tuple descriptor */
     219         693 :     tupdesc = CreateTemplateTupleDesc(3, false);
     220         693 :     TupleDescInitEntry(tupdesc, (AttrNumber) 1,
     221             :                        "chunk_id",
     222             :                        OIDOID,
     223             :                        -1, 0);
     224         693 :     TupleDescInitEntry(tupdesc, (AttrNumber) 2,
     225             :                        "chunk_seq",
     226             :                        INT4OID,
     227             :                        -1, 0);
     228         693 :     TupleDescInitEntry(tupdesc, (AttrNumber) 3,
     229             :                        "chunk_data",
     230             :                        BYTEAOID,
     231             :                        -1, 0);
     232             : 
     233             :     /*
     234             :      * Ensure that the toast table doesn't itself get toasted, or we'll be
     235             :      * toast :-(.  This is essential for chunk_data because type bytea is
     236             :      * toastable; hit the other two just to be sure.
     237             :      */
     238         693 :     TupleDescAttr(tupdesc, 0)->attstorage = 'p';
     239         693 :     TupleDescAttr(tupdesc, 1)->attstorage = 'p';
     240         693 :     TupleDescAttr(tupdesc, 2)->attstorage = 'p';
     241             : 
     242             :     /*
     243             :      * Toast tables for regular relations go in pg_toast; those for temp
     244             :      * relations go into the per-backend temp-toast-table namespace.
     245             :      */
     246         693 :     if (isTempOrTempToastNamespace(rel->rd_rel->relnamespace))
     247         108 :         namespaceid = GetTempToastNamespace();
     248             :     else
     249         585 :         namespaceid = PG_TOAST_NAMESPACE;
     250             : 
     251             :     /*
     252             :      * Use binary-upgrade override for pg_type.oid, if supplied.  We might be
     253             :      * in the post-schema-restore phase where we are doing ALTER TABLE to
     254             :      * create TOAST tables that didn't exist in the old cluster.
     255             :      */
     256         693 :     if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid))
     257             :     {
     258           0 :         toast_typid = binary_upgrade_next_toast_pg_type_oid;
     259           0 :         binary_upgrade_next_toast_pg_type_oid = InvalidOid;
     260             :     }
     261             : 
     262        2772 :     toast_relid = heap_create_with_catalog(toast_relname,
     263             :                                            namespaceid,
     264         693 :                                            rel->rd_rel->reltablespace,
     265             :                                            toastOid,
     266             :                                            toast_typid,
     267             :                                            InvalidOid,
     268         693 :                                            rel->rd_rel->relowner,
     269             :                                            tupdesc,
     270             :                                            NIL,
     271             :                                            RELKIND_TOASTVALUE,
     272         693 :                                            rel->rd_rel->relpersistence,
     273             :                                            shared_relation,
     274             :                                            mapped_relation,
     275             :                                            true,
     276             :                                            0,
     277             :                                            ONCOMMIT_NOOP,
     278             :                                            reloptions,
     279             :                                            false,
     280             :                                            true,
     281             :                                            true,
     282             :                                            NULL);
     283         693 :     Assert(toast_relid != InvalidOid);
     284             : 
     285             :     /* make the toast relation visible, else heap_open will fail */
     286         693 :     CommandCounterIncrement();
     287             : 
     288             :     /* ShareLock is not really needed here, but take it anyway */
     289         693 :     toast_rel = heap_open(toast_relid, ShareLock);
     290             : 
     291             :     /*
     292             :      * Create unique index on chunk_id, chunk_seq.
     293             :      *
     294             :      * NOTE: the normal TOAST access routines could actually function with a
     295             :      * single-column index on chunk_id only. However, the slice access
     296             :      * routines use both columns for faster access to an individual chunk. In
     297             :      * addition, we want it to be unique as a check against the possibility of
     298             :      * duplicate TOAST chunk OIDs. The index might also be a little more
     299             :      * efficient this way, since btree isn't all that happy with large numbers
     300             :      * of equal keys.
     301             :      */
     302             : 
     303         693 :     indexInfo = makeNode(IndexInfo);
     304         693 :     indexInfo->ii_NumIndexAttrs = 2;
     305         693 :     indexInfo->ii_KeyAttrNumbers[0] = 1;
     306         693 :     indexInfo->ii_KeyAttrNumbers[1] = 2;
     307         693 :     indexInfo->ii_Expressions = NIL;
     308         693 :     indexInfo->ii_ExpressionsState = NIL;
     309         693 :     indexInfo->ii_Predicate = NIL;
     310         693 :     indexInfo->ii_PredicateState = NULL;
     311         693 :     indexInfo->ii_ExclusionOps = NULL;
     312         693 :     indexInfo->ii_ExclusionProcs = NULL;
     313         693 :     indexInfo->ii_ExclusionStrats = NULL;
     314         693 :     indexInfo->ii_Unique = true;
     315         693 :     indexInfo->ii_ReadyForInserts = true;
     316         693 :     indexInfo->ii_Concurrent = false;
     317         693 :     indexInfo->ii_BrokenHotChain = false;
     318         693 :     indexInfo->ii_AmCache = NULL;
     319         693 :     indexInfo->ii_Context = CurrentMemoryContext;
     320             : 
     321         693 :     collationObjectId[0] = InvalidOid;
     322         693 :     collationObjectId[1] = InvalidOid;
     323             : 
     324         693 :     classObjectId[0] = OID_BTREE_OPS_OID;
     325         693 :     classObjectId[1] = INT4_BTREE_OPS_OID;
     326             : 
     327         693 :     coloptions[0] = 0;
     328         693 :     coloptions[1] = 0;
     329             : 
     330         693 :     index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid,
     331             :                  indexInfo,
     332             :                  list_make2("chunk_id", "chunk_seq"),
     333             :                  BTREE_AM_OID,
     334         693 :                  rel->rd_rel->reltablespace,
     335             :                  collationObjectId, classObjectId, coloptions, (Datum) 0,
     336             :                  true, false, false, false,
     337             :                  true, false, false, true, false);
     338             : 
     339         693 :     heap_close(toast_rel, NoLock);
     340             : 
     341             :     /*
     342             :      * Store the toast table's OID in the parent relation's pg_class row
     343             :      */
     344         693 :     class_rel = heap_open(RelationRelationId, RowExclusiveLock);
     345             : 
     346         693 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
     347         693 :     if (!HeapTupleIsValid(reltup))
     348           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
     349             : 
     350         693 :     ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
     351             : 
     352         693 :     if (!IsBootstrapProcessingMode())
     353             :     {
     354             :         /* normal case, use a transactional update */
     355         681 :         CatalogTupleUpdate(class_rel, &reltup->t_self, reltup);
     356             :     }
     357             :     else
     358             :     {
     359             :         /* While bootstrapping, we cannot UPDATE, so overwrite in-place */
     360          12 :         heap_inplace_update(class_rel, reltup);
     361             :     }
     362             : 
     363         693 :     heap_freetuple(reltup);
     364             : 
     365         693 :     heap_close(class_rel, RowExclusiveLock);
     366             : 
     367             :     /*
     368             :      * Register dependency from the toast table to the master, so that the
     369             :      * toast table will be deleted if the master is.  Skip this in bootstrap
     370             :      * mode.
     371             :      */
     372         693 :     if (!IsBootstrapProcessingMode())
     373             :     {
     374         681 :         baseobject.classId = RelationRelationId;
     375         681 :         baseobject.objectId = relOid;
     376         681 :         baseobject.objectSubId = 0;
     377         681 :         toastobject.classId = RelationRelationId;
     378         681 :         toastobject.objectId = toast_relid;
     379         681 :         toastobject.objectSubId = 0;
     380             : 
     381         681 :         recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
     382             :     }
     383             : 
     384             :     /*
     385             :      * Make changes visible
     386             :      */
     387         693 :     CommandCounterIncrement();
     388             : 
     389         693 :     return true;
     390             : }
     391             : 
     392             : /*
     393             :  * Check to see whether the table needs a TOAST table.  It does only if
     394             :  * (1) there are any toastable attributes, and (2) the maximum length
     395             :  * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
     396             :  * create a toast table for something like "f1 varchar(20)".)
     397             :  */
     398             : static bool
     399        2032 : needs_toast_table(Relation rel)
     400             : {
     401        2032 :     int32       data_length = 0;
     402        2032 :     bool        maxlength_unknown = false;
     403        2032 :     bool        has_toastable_attrs = false;
     404             :     TupleDesc   tupdesc;
     405             :     int32       tuple_length;
     406             :     int         i;
     407             : 
     408        2032 :     tupdesc = rel->rd_att;
     409             : 
     410        6680 :     for (i = 0; i < tupdesc->natts; i++)
     411             :     {
     412        4648 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     413             : 
     414        4648 :         if (att->attisdropped)
     415          97 :             continue;
     416        4551 :         data_length = att_align_nominal(data_length, att->attalign);
     417        4551 :         if (att->attlen > 0)
     418             :         {
     419             :             /* Fixed-length types are never toastable */
     420        3365 :             data_length += att->attlen;
     421             :         }
     422             :         else
     423             :         {
     424        1186 :             int32       maxlen = type_maximum_size(att->atttypid,
     425             :                                                    att->atttypmod);
     426             : 
     427        1186 :             if (maxlen < 0)
     428         963 :                 maxlength_unknown = true;
     429             :             else
     430         223 :                 data_length += maxlen;
     431        1186 :             if (att->attstorage != 'p')
     432        1181 :                 has_toastable_attrs = true;
     433             :         }
     434             :     }
     435        2032 :     if (!has_toastable_attrs)
     436        1175 :         return false;           /* nothing to toast? */
     437         857 :     if (maxlength_unknown)
     438         692 :         return true;            /* any unlimited-length attrs? */
     439         330 :     tuple_length = MAXALIGN(SizeofHeapTupleHeader +
     440         165 :                             BITMAPLEN(tupdesc->natts)) +
     441         165 :         MAXALIGN(data_length);
     442         165 :     return (tuple_length > TOAST_TUPLE_THRESHOLD);
     443             : }

Generated by: LCOV version 1.11