Header And Logo

PostgreSQL
| The world's most advanced open source database.

indextuple.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * indextuple.c
00004  *     This file contains index tuple accessor and mutator routines,
00005  *     as well as various tuple utilities.
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/access/common/indextuple.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 
00017 #include "postgres.h"
00018 
00019 #include "access/heapam.h"
00020 #include "access/itup.h"
00021 #include "access/tuptoaster.h"
00022 
00023 
00024 /* ----------------------------------------------------------------
00025  *                index_ tuple interface routines
00026  * ----------------------------------------------------------------
00027  */
00028 
00029 /* ----------------
00030  *      index_form_tuple
00031  * ----------------
00032  */
00033 IndexTuple
00034 index_form_tuple(TupleDesc tupleDescriptor,
00035                  Datum *values,
00036                  bool *isnull)
00037 {
00038     char       *tp;             /* tuple pointer */
00039     IndexTuple  tuple;          /* return tuple */
00040     Size        size,
00041                 data_size,
00042                 hoff;
00043     int         i;
00044     unsigned short infomask = 0;
00045     bool        hasnull = false;
00046     uint16      tupmask = 0;
00047     int         numberOfAttributes = tupleDescriptor->natts;
00048 
00049 #ifdef TOAST_INDEX_HACK
00050     Datum       untoasted_values[INDEX_MAX_KEYS];
00051     bool        untoasted_free[INDEX_MAX_KEYS];
00052 #endif
00053 
00054     if (numberOfAttributes > INDEX_MAX_KEYS)
00055         ereport(ERROR,
00056                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
00057                  errmsg("number of index columns (%d) exceeds limit (%d)",
00058                         numberOfAttributes, INDEX_MAX_KEYS)));
00059 
00060 #ifdef TOAST_INDEX_HACK
00061     for (i = 0; i < numberOfAttributes; i++)
00062     {
00063         Form_pg_attribute att = tupleDescriptor->attrs[i];
00064 
00065         untoasted_values[i] = values[i];
00066         untoasted_free[i] = false;
00067 
00068         /* Do nothing if value is NULL or not of varlena type */
00069         if (isnull[i] || att->attlen != -1)
00070             continue;
00071 
00072         /*
00073          * If value is stored EXTERNAL, must fetch it so we are not depending
00074          * on outside storage.  This should be improved someday.
00075          */
00076         if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
00077         {
00078             untoasted_values[i] =
00079                 PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
00080                                                 DatumGetPointer(values[i])));
00081             untoasted_free[i] = true;
00082         }
00083 
00084         /*
00085          * If value is above size target, and is of a compressible datatype,
00086          * try to compress it in-line.
00087          */
00088         if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
00089         VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
00090             (att->attstorage == 'x' || att->attstorage == 'm'))
00091         {
00092             Datum       cvalue = toast_compress_datum(untoasted_values[i]);
00093 
00094             if (DatumGetPointer(cvalue) != NULL)
00095             {
00096                 /* successful compression */
00097                 if (untoasted_free[i])
00098                     pfree(DatumGetPointer(untoasted_values[i]));
00099                 untoasted_values[i] = cvalue;
00100                 untoasted_free[i] = true;
00101             }
00102         }
00103     }
00104 #endif
00105 
00106     for (i = 0; i < numberOfAttributes; i++)
00107     {
00108         if (isnull[i])
00109         {
00110             hasnull = true;
00111             break;
00112         }
00113     }
00114 
00115     if (hasnull)
00116         infomask |= INDEX_NULL_MASK;
00117 
00118     hoff = IndexInfoFindDataOffset(infomask);
00119 #ifdef TOAST_INDEX_HACK
00120     data_size = heap_compute_data_size(tupleDescriptor,
00121                                        untoasted_values, isnull);
00122 #else
00123     data_size = heap_compute_data_size(tupleDescriptor,
00124                                        values, isnull);
00125 #endif
00126     size = hoff + data_size;
00127     size = MAXALIGN(size);      /* be conservative */
00128 
00129     tp = (char *) palloc0(size);
00130     tuple = (IndexTuple) tp;
00131 
00132     heap_fill_tuple(tupleDescriptor,
00133 #ifdef TOAST_INDEX_HACK
00134                     untoasted_values,
00135 #else
00136                     values,
00137 #endif
00138                     isnull,
00139                     (char *) tp + hoff,
00140                     data_size,
00141                     &tupmask,
00142                     (hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
00143 
00144 #ifdef TOAST_INDEX_HACK
00145     for (i = 0; i < numberOfAttributes; i++)
00146     {
00147         if (untoasted_free[i])
00148             pfree(DatumGetPointer(untoasted_values[i]));
00149     }
00150 #endif
00151 
00152     /*
00153      * We do this because heap_fill_tuple wants to initialize a "tupmask"
00154      * which is used for HeapTuples, but we want an indextuple infomask. The
00155      * only relevant info is the "has variable attributes" field. We have
00156      * already set the hasnull bit above.
00157      */
00158     if (tupmask & HEAP_HASVARWIDTH)
00159         infomask |= INDEX_VAR_MASK;
00160 
00161     /*
00162      * Here we make sure that the size will fit in the field reserved for it
00163      * in t_info.
00164      */
00165     if ((size & INDEX_SIZE_MASK) != size)
00166         ereport(ERROR,
00167                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00168                  errmsg("index row requires %lu bytes, maximum size is %lu",
00169                         (unsigned long) size,
00170                         (unsigned long) INDEX_SIZE_MASK)));
00171 
00172     infomask |= size;
00173 
00174     /*
00175      * initialize metadata
00176      */
00177     tuple->t_info = infomask;
00178     return tuple;
00179 }
00180 
00181 /* ----------------
00182  *      nocache_index_getattr
00183  *
00184  *      This gets called from index_getattr() macro, and only in cases
00185  *      where we can't use cacheoffset and the value is not null.
00186  *
00187  *      This caches attribute offsets in the attribute descriptor.
00188  *
00189  *      An alternative way to speed things up would be to cache offsets
00190  *      with the tuple, but that seems more difficult unless you take
00191  *      the storage hit of actually putting those offsets into the
00192  *      tuple you send to disk.  Yuck.
00193  *
00194  *      This scheme will be slightly slower than that, but should
00195  *      perform well for queries which hit large #'s of tuples.  After
00196  *      you cache the offsets once, examining all the other tuples using
00197  *      the same attribute descriptor will go much quicker. -cim 5/4/91
00198  * ----------------
00199  */
00200 Datum
00201 nocache_index_getattr(IndexTuple tup,
00202                       int attnum,
00203                       TupleDesc tupleDesc)
00204 {
00205     Form_pg_attribute *att = tupleDesc->attrs;
00206     char       *tp;             /* ptr to data part of tuple */
00207     bits8      *bp = NULL;      /* ptr to null bitmap in tuple */
00208     bool        slow = false;   /* do we have to walk attrs? */
00209     int         data_off;       /* tuple data offset */
00210     int         off;            /* current offset within data */
00211 
00212     /* ----------------
00213      *   Three cases:
00214      *
00215      *   1: No nulls and no variable-width attributes.
00216      *   2: Has a null or a var-width AFTER att.
00217      *   3: Has nulls or var-widths BEFORE att.
00218      * ----------------
00219      */
00220 
00221     data_off = IndexInfoFindDataOffset(tup->t_info);
00222 
00223     attnum--;
00224 
00225     if (IndexTupleHasNulls(tup))
00226     {
00227         /*
00228          * there's a null somewhere in the tuple
00229          *
00230          * check to see if desired att is null
00231          */
00232 
00233         /* XXX "knows" t_bits are just after fixed tuple header! */
00234         bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));
00235 
00236         /*
00237          * Now check to see if any preceding bits are null...
00238          */
00239         {
00240             int         byte = attnum >> 3;
00241             int         finalbit = attnum & 0x07;
00242 
00243             /* check for nulls "before" final bit of last byte */
00244             if ((~bp[byte]) & ((1 << finalbit) - 1))
00245                 slow = true;
00246             else
00247             {
00248                 /* check for nulls in any "earlier" bytes */
00249                 int         i;
00250 
00251                 for (i = 0; i < byte; i++)
00252                 {
00253                     if (bp[i] != 0xFF)
00254                     {
00255                         slow = true;
00256                         break;
00257                     }
00258                 }
00259             }
00260         }
00261     }
00262 
00263     tp = (char *) tup + data_off;
00264 
00265     if (!slow)
00266     {
00267         /*
00268          * If we get here, there are no nulls up to and including the target
00269          * attribute.  If we have a cached offset, we can use it.
00270          */
00271         if (att[attnum]->attcacheoff >= 0)
00272         {
00273             return fetchatt(att[attnum],
00274                             tp + att[attnum]->attcacheoff);
00275         }
00276 
00277         /*
00278          * Otherwise, check for non-fixed-length attrs up to and including
00279          * target.  If there aren't any, it's safe to cheaply initialize the
00280          * cached offsets for these attrs.
00281          */
00282         if (IndexTupleHasVarwidths(tup))
00283         {
00284             int         j;
00285 
00286             for (j = 0; j <= attnum; j++)
00287             {
00288                 if (att[j]->attlen <= 0)
00289                 {
00290                     slow = true;
00291                     break;
00292                 }
00293             }
00294         }
00295     }
00296 
00297     if (!slow)
00298     {
00299         int         natts = tupleDesc->natts;
00300         int         j = 1;
00301 
00302         /*
00303          * If we get here, we have a tuple with no nulls or var-widths up to
00304          * and including the target attribute, so we can use the cached offset
00305          * ... only we don't have it yet, or we'd not have got here.  Since
00306          * it's cheap to compute offsets for fixed-width columns, we take the
00307          * opportunity to initialize the cached offsets for *all* the leading
00308          * fixed-width columns, in hope of avoiding future visits to this
00309          * routine.
00310          */
00311         att[0]->attcacheoff = 0;
00312 
00313         /* we might have set some offsets in the slow path previously */
00314         while (j < natts && att[j]->attcacheoff > 0)
00315             j++;
00316 
00317         off = att[j - 1]->attcacheoff + att[j - 1]->attlen;
00318 
00319         for (; j < natts; j++)
00320         {
00321             if (att[j]->attlen <= 0)
00322                 break;
00323 
00324             off = att_align_nominal(off, att[j]->attalign);
00325 
00326             att[j]->attcacheoff = off;
00327 
00328             off += att[j]->attlen;
00329         }
00330 
00331         Assert(j > attnum);
00332 
00333         off = att[attnum]->attcacheoff;
00334     }
00335     else
00336     {
00337         bool        usecache = true;
00338         int         i;
00339 
00340         /*
00341          * Now we know that we have to walk the tuple CAREFULLY.  But we still
00342          * might be able to cache some offsets for next time.
00343          *
00344          * Note - This loop is a little tricky.  For each non-null attribute,
00345          * we have to first account for alignment padding before the attr,
00346          * then advance over the attr based on its length.  Nulls have no
00347          * storage and no alignment padding either.  We can use/set
00348          * attcacheoff until we reach either a null or a var-width attribute.
00349          */
00350         off = 0;
00351         for (i = 0;; i++)       /* loop exit is at "break" */
00352         {
00353             if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
00354             {
00355                 usecache = false;
00356                 continue;       /* this cannot be the target att */
00357             }
00358 
00359             /* If we know the next offset, we can skip the rest */
00360             if (usecache && att[i]->attcacheoff >= 0)
00361                 off = att[i]->attcacheoff;
00362             else if (att[i]->attlen == -1)
00363             {
00364                 /*
00365                  * We can only cache the offset for a varlena attribute if the
00366                  * offset is already suitably aligned, so that there would be
00367                  * no pad bytes in any case: then the offset will be valid for
00368                  * either an aligned or unaligned value.
00369                  */
00370                 if (usecache &&
00371                     off == att_align_nominal(off, att[i]->attalign))
00372                     att[i]->attcacheoff = off;
00373                 else
00374                 {
00375                     off = att_align_pointer(off, att[i]->attalign, -1,
00376                                             tp + off);
00377                     usecache = false;
00378                 }
00379             }
00380             else
00381             {
00382                 /* not varlena, so safe to use att_align_nominal */
00383                 off = att_align_nominal(off, att[i]->attalign);
00384 
00385                 if (usecache)
00386                     att[i]->attcacheoff = off;
00387             }
00388 
00389             if (i == attnum)
00390                 break;
00391 
00392             off = att_addlength_pointer(off, att[i]->attlen, tp + off);
00393 
00394             if (usecache && att[i]->attlen <= 0)
00395                 usecache = false;
00396         }
00397     }
00398 
00399     return fetchatt(att[attnum], tp + off);
00400 }
00401 
00402 /*
00403  * Convert an index tuple into Datum/isnull arrays.
00404  *
00405  * The caller must allocate sufficient storage for the output arrays.
00406  * (INDEX_MAX_KEYS entries should be enough.)
00407  */
00408 void
00409 index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
00410                    Datum *values, bool *isnull)
00411 {
00412     int         i;
00413 
00414     /* Assert to protect callers who allocate fixed-size arrays */
00415     Assert(tupleDescriptor->natts <= INDEX_MAX_KEYS);
00416 
00417     for (i = 0; i < tupleDescriptor->natts; i++)
00418     {
00419         values[i] = index_getattr(tup, i + 1, tupleDescriptor, &isnull[i]);
00420     }
00421 }
00422 
00423 /*
00424  * Create a palloc'd copy of an index tuple.
00425  */
00426 IndexTuple
00427 CopyIndexTuple(IndexTuple source)
00428 {
00429     IndexTuple  result;
00430     Size        size;
00431 
00432     size = IndexTupleSize(source);
00433     result = (IndexTuple) palloc(size);
00434     memcpy(result, source, size);
00435     return result;
00436 }