LCOV - code coverage report
Current view: top level - src/backend/access/common - tupconvert.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 130 140 92.9 %
Date: 2017-09-29 13:40:31 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tupconvert.c
       4             :  *    Tuple conversion support.
       5             :  *
       6             :  * These functions provide conversion between rowtypes that are logically
       7             :  * equivalent but might have columns in a different order or different sets
       8             :  * of dropped columns.  There is some overlap of functionality with the
       9             :  * executor's "junkfilter" routines, but these functions work on bare
      10             :  * HeapTuples rather than TupleTableSlots.
      11             :  *
      12             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      13             :  * Portions Copyright (c) 1994, Regents of the University of California
      14             :  *
      15             :  *
      16             :  * IDENTIFICATION
      17             :  *    src/backend/access/common/tupconvert.c
      18             :  *
      19             :  *-------------------------------------------------------------------------
      20             :  */
      21             : #include "postgres.h"
      22             : 
      23             : #include "access/htup_details.h"
      24             : #include "access/tupconvert.h"
      25             : #include "utils/builtins.h"
      26             : 
      27             : 
      28             : /*
      29             :  * The conversion setup routines have the following common API:
      30             :  *
      31             :  * The setup routine checks whether the given source and destination tuple
      32             :  * descriptors are logically compatible.  If not, it throws an error.
      33             :  * If so, it returns NULL if they are physically compatible (ie, no conversion
      34             :  * is needed), else a TupleConversionMap that can be used by do_convert_tuple
      35             :  * to perform the conversion.
      36             :  *
      37             :  * The TupleConversionMap, if needed, is palloc'd in the caller's memory
      38             :  * context.  Also, the given tuple descriptors are referenced by the map,
      39             :  * so they must survive as long as the map is needed.
      40             :  *
      41             :  * The caller must supply a suitable primary error message to be used if
      42             :  * a compatibility error is thrown.  Recommended coding practice is to use
      43             :  * gettext_noop() on this string, so that it is translatable but won't
      44             :  * actually be translated unless the error gets thrown.
      45             :  *
      46             :  *
      47             :  * Implementation notes:
      48             :  *
      49             :  * The key component of a TupleConversionMap is an attrMap[] array with
      50             :  * one entry per output column.  This entry contains the 1-based index of
      51             :  * the corresponding input column, or zero to force a NULL value (for
      52             :  * a dropped output column).  The TupleConversionMap also contains workspace
      53             :  * arrays.
      54             :  */
      55             : 
      56             : 
      57             : /*
      58             :  * Set up for tuple conversion, matching input and output columns by
      59             :  * position.  (Dropped columns are ignored in both input and output.)
      60             :  *
      61             :  * Note: the errdetail messages speak of indesc as the "returned" rowtype,
      62             :  * outdesc as the "expected" rowtype.  This is okay for current uses but
      63             :  * might need generalization in future.
      64             :  */
      65             : TupleConversionMap *
      66        1769 : convert_tuples_by_position(TupleDesc indesc,
      67             :                            TupleDesc outdesc,
      68             :                            const char *msg)
      69             : {
      70             :     TupleConversionMap *map;
      71             :     AttrNumber *attrMap;
      72             :     int         nincols;
      73             :     int         noutcols;
      74             :     int         n;
      75             :     int         i;
      76             :     int         j;
      77             :     bool        same;
      78             : 
      79             :     /* Verify compatibility and prepare attribute-number map */
      80        1769 :     n = outdesc->natts;
      81        1769 :     attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
      82        1769 :     j = 0;                      /* j is next physical input attribute */
      83        1769 :     nincols = noutcols = 0;     /* these count non-dropped attributes */
      84        1769 :     same = true;
      85        8382 :     for (i = 0; i < n; i++)
      86             :     {
      87        6614 :         Form_pg_attribute att = TupleDescAttr(outdesc, i);
      88             :         Oid         atttypid;
      89             :         int32       atttypmod;
      90             : 
      91        6614 :         if (att->attisdropped)
      92          19 :             continue;           /* attrMap[i] is already 0 */
      93        6595 :         noutcols++;
      94        6595 :         atttypid = att->atttypid;
      95        6595 :         atttypmod = att->atttypmod;
      96       13194 :         for (; j < indesc->natts; j++)
      97             :         {
      98        6597 :             att = TupleDescAttr(indesc, j);
      99        6597 :             if (att->attisdropped)
     100           2 :                 continue;
     101        6595 :             nincols++;
     102             :             /* Found matching column, check type */
     103       13189 :             if (atttypid != att->atttypid ||
     104        6594 :                 (atttypmod != att->atttypmod && atttypmod >= 0))
     105           1 :                 ereport(ERROR,
     106             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
     107             :                          errmsg_internal("%s", _(msg)),
     108             :                          errdetail("Returned type %s does not match expected type %s in column %d.",
     109             :                                    format_type_with_typemod(att->atttypid,
     110             :                                                             att->atttypmod),
     111             :                                    format_type_with_typemod(atttypid,
     112             :                                                             atttypmod),
     113             :                                    noutcols)));
     114        6594 :             attrMap[i] = (AttrNumber) (j + 1);
     115        6594 :             j++;
     116        6594 :             break;
     117             :         }
     118        6594 :         if (attrMap[i] == 0)
     119           0 :             same = false;       /* we'll complain below */
     120             :     }
     121             : 
     122             :     /* Check for unused input columns */
     123        1768 :     for (; j < indesc->natts; j++)
     124             :     {
     125           0 :         if (TupleDescAttr(indesc, j)->attisdropped)
     126           0 :             continue;
     127           0 :         nincols++;
     128           0 :         same = false;           /* we'll complain below */
     129             :     }
     130             : 
     131             :     /* Report column count mismatch using the non-dropped-column counts */
     132        1768 :     if (!same)
     133           0 :         ereport(ERROR,
     134             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     135             :                  errmsg_internal("%s", _(msg)),
     136             :                  errdetail("Number of returned columns (%d) does not match "
     137             :                            "expected column count (%d).",
     138             :                            nincols, noutcols)));
     139             : 
     140             :     /*
     141             :      * Check to see if the map is one-to-one, in which case we need not do a
     142             :      * tuple conversion.  We must also insist that both tupdescs either
     143             :      * specify or don't specify an OID column, else we need a conversion to
     144             :      * add/remove space for that.  (For some callers, presence or absence of
     145             :      * an OID column perhaps would not really matter, but let's be safe.)
     146             :      */
     147        5282 :     if (indesc->natts == outdesc->natts &&
     148        1757 :         indesc->tdhasoid == outdesc->tdhasoid)
     149             :     {
     150        8323 :         for (i = 0; i < n; i++)
     151             :         {
     152             :             Form_pg_attribute inatt;
     153             :             Form_pg_attribute outatt;
     154             : 
     155        6566 :             if (attrMap[i] == (i + 1))
     156        6564 :                 continue;
     157             : 
     158             :             /*
     159             :              * If it's a dropped column and the corresponding input column is
     160             :              * also dropped, we needn't convert.  However, attlen and attalign
     161             :              * must agree.
     162             :              */
     163           2 :             inatt = TupleDescAttr(indesc, i);
     164           2 :             outatt = TupleDescAttr(outdesc, i);
     165           4 :             if (attrMap[i] == 0 &&
     166           4 :                 inatt->attisdropped &&
     167           4 :                 inatt->attlen == outatt->attlen &&
     168           2 :                 inatt->attalign == outatt->attalign)
     169           2 :                 continue;
     170             : 
     171           0 :             same = false;
     172           0 :             break;
     173             :         }
     174             :     }
     175             :     else
     176          11 :         same = false;
     177             : 
     178        1768 :     if (same)
     179             :     {
     180             :         /* Runtime conversion is not needed */
     181        1757 :         pfree(attrMap);
     182        1757 :         return NULL;
     183             :     }
     184             : 
     185             :     /* Prepare the map structure */
     186          11 :     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
     187          11 :     map->indesc = indesc;
     188          11 :     map->outdesc = outdesc;
     189          11 :     map->attrMap = attrMap;
     190             :     /* preallocate workspace for Datum arrays */
     191          11 :     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
     192          11 :     map->outisnull = (bool *) palloc(n * sizeof(bool));
     193          11 :     n = indesc->natts + 1;       /* +1 for NULL */
     194          11 :     map->invalues = (Datum *) palloc(n * sizeof(Datum));
     195          11 :     map->inisnull = (bool *) palloc(n * sizeof(bool));
     196          11 :     map->invalues[0] = (Datum) 0;    /* set up the NULL entry */
     197          11 :     map->inisnull[0] = true;
     198             : 
     199          11 :     return map;
     200             : }
     201             : 
     202             : /*
     203             :  * Set up for tuple conversion, matching input and output columns by name.
     204             :  * (Dropped columns are ignored in both input and output.)  This is intended
     205             :  * for use when the rowtypes are related by inheritance, so we expect an exact
     206             :  * match of both type and typmod.  The error messages will be a bit unhelpful
     207             :  * unless both rowtypes are named composite types.
     208             :  */
     209             : TupleConversionMap *
     210         390 : convert_tuples_by_name(TupleDesc indesc,
     211             :                        TupleDesc outdesc,
     212             :                        const char *msg)
     213             : {
     214             :     TupleConversionMap *map;
     215             :     AttrNumber *attrMap;
     216         390 :     int         n = outdesc->natts;
     217             :     int         i;
     218             :     bool        same;
     219             : 
     220             :     /* Verify compatibility and prepare attribute-number map */
     221         390 :     attrMap = convert_tuples_by_name_map(indesc, outdesc, msg);
     222             : 
     223             :     /*
     224             :      * Check to see if the map is one-to-one, in which case we need not do a
     225             :      * tuple conversion.  We must also insist that both tupdescs either
     226             :      * specify or don't specify an OID column, else we need a conversion to
     227             :      * add/remove space for that.  (For some callers, presence or absence of
     228             :      * an OID column perhaps would not really matter, but let's be safe.)
     229             :      */
     230        1054 :     if (indesc->natts == outdesc->natts &&
     231         332 :         indesc->tdhasoid == outdesc->tdhasoid)
     232             :     {
     233         332 :         same = true;
     234        1004 :         for (i = 0; i < n; i++)
     235             :         {
     236             :             Form_pg_attribute inatt;
     237             :             Form_pg_attribute outatt;
     238             : 
     239         717 :             if (attrMap[i] == (i + 1))
     240         669 :                 continue;
     241             : 
     242             :             /*
     243             :              * If it's a dropped column and the corresponding input column is
     244             :              * also dropped, we needn't convert.  However, attlen and attalign
     245             :              * must agree.
     246             :              */
     247          48 :             inatt = TupleDescAttr(indesc, i);
     248          48 :             outatt = TupleDescAttr(outdesc, i);
     249          57 :             if (attrMap[i] == 0 &&
     250          12 :                 inatt->attisdropped &&
     251           6 :                 inatt->attlen == outatt->attlen &&
     252           3 :                 inatt->attalign == outatt->attalign)
     253           3 :                 continue;
     254             : 
     255          45 :             same = false;
     256          45 :             break;
     257             :         }
     258             :     }
     259             :     else
     260          58 :         same = false;
     261             : 
     262         390 :     if (same)
     263             :     {
     264             :         /* Runtime conversion is not needed */
     265         287 :         pfree(attrMap);
     266         287 :         return NULL;
     267             :     }
     268             : 
     269             :     /* Prepare the map structure */
     270         103 :     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
     271         103 :     map->indesc = indesc;
     272         103 :     map->outdesc = outdesc;
     273         103 :     map->attrMap = attrMap;
     274             :     /* preallocate workspace for Datum arrays */
     275         103 :     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
     276         103 :     map->outisnull = (bool *) palloc(n * sizeof(bool));
     277         103 :     n = indesc->natts + 1;       /* +1 for NULL */
     278         103 :     map->invalues = (Datum *) palloc(n * sizeof(Datum));
     279         103 :     map->inisnull = (bool *) palloc(n * sizeof(bool));
     280         103 :     map->invalues[0] = (Datum) 0;    /* set up the NULL entry */
     281         103 :     map->inisnull[0] = true;
     282             : 
     283         103 :     return map;
     284             : }
     285             : 
     286             : /*
     287             :  * Return a palloc'd bare attribute map for tuple conversion, matching input
     288             :  * and output columns by name.  (Dropped columns are ignored in both input and
     289             :  * output.)  This is normally a subroutine for convert_tuples_by_name, but can
     290             :  * be used standalone.
     291             :  */
     292             : AttrNumber *
     293         609 : convert_tuples_by_name_map(TupleDesc indesc,
     294             :                            TupleDesc outdesc,
     295             :                            const char *msg)
     296             : {
     297             :     AttrNumber *attrMap;
     298             :     int         n;
     299             :     int         i;
     300             : 
     301         609 :     n = outdesc->natts;
     302         609 :     attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
     303        2071 :     for (i = 0; i < n; i++)
     304             :     {
     305        1462 :         Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
     306             :         char       *attname;
     307             :         Oid         atttypid;
     308             :         int32       atttypmod;
     309             :         int         j;
     310             : 
     311        1462 :         if (outatt->attisdropped)
     312          44 :             continue;           /* attrMap[i] is already 0 */
     313        1418 :         attname = NameStr(outatt->attname);
     314        1418 :         atttypid = outatt->atttypid;
     315        1418 :         atttypmod = outatt->atttypmod;
     316        2694 :         for (j = 0; j < indesc->natts; j++)
     317             :         {
     318        2694 :             Form_pg_attribute inatt = TupleDescAttr(indesc, j);
     319             : 
     320        2694 :             if (inatt->attisdropped)
     321         104 :                 continue;
     322        2590 :             if (strcmp(attname, NameStr(inatt->attname)) == 0)
     323             :             {
     324             :                 /* Found it, check type */
     325        1418 :                 if (atttypid != inatt->atttypid || atttypmod != inatt->atttypmod)
     326           0 :                     ereport(ERROR,
     327             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
     328             :                              errmsg_internal("%s", _(msg)),
     329             :                              errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
     330             :                                        attname,
     331             :                                        format_type_be(outdesc->tdtypeid),
     332             :                                        format_type_be(indesc->tdtypeid))));
     333        1418 :                 attrMap[i] = (AttrNumber) (j + 1);
     334        1418 :                 break;
     335             :             }
     336             :         }
     337        1418 :         if (attrMap[i] == 0)
     338           0 :             ereport(ERROR,
     339             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     340             :                      errmsg_internal("%s", _(msg)),
     341             :                      errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
     342             :                                attname,
     343             :                                format_type_be(outdesc->tdtypeid),
     344             :                                format_type_be(indesc->tdtypeid))));
     345             :     }
     346             : 
     347         609 :     return attrMap;
     348             : }
     349             : 
     350             : /*
     351             :  * Perform conversion of a tuple according to the map.
     352             :  */
     353             : HeapTuple
     354         165 : do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
     355             : {
     356         165 :     AttrNumber *attrMap = map->attrMap;
     357         165 :     Datum      *invalues = map->invalues;
     358         165 :     bool       *inisnull = map->inisnull;
     359         165 :     Datum      *outvalues = map->outvalues;
     360         165 :     bool       *outisnull = map->outisnull;
     361         165 :     int         outnatts = map->outdesc->natts;
     362             :     int         i;
     363             : 
     364             :     /*
     365             :      * Extract all the values of the old tuple, offsetting the arrays so that
     366             :      * invalues[0] is left NULL and invalues[1] is the first source attribute;
     367             :      * this exactly matches the numbering convention in attrMap.
     368             :      */
     369         165 :     heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
     370             : 
     371             :     /*
     372             :      * Transpose into proper fields of the new tuple.
     373             :      */
     374         657 :     for (i = 0; i < outnatts; i++)
     375             :     {
     376         492 :         int         j = attrMap[i];
     377             : 
     378         492 :         outvalues[i] = invalues[j];
     379         492 :         outisnull[i] = inisnull[j];
     380             :     }
     381             : 
     382             :     /*
     383             :      * Now form the new tuple.
     384             :      */
     385         165 :     return heap_form_tuple(map->outdesc, outvalues, outisnull);
     386             : }
     387             : 
     388             : /*
     389             :  * Free a TupleConversionMap structure.
     390             :  */
     391             : void
     392           5 : free_conversion_map(TupleConversionMap *map)
     393             : {
     394             :     /* indesc and outdesc are not ours to free */
     395           5 :     pfree(map->attrMap);
     396           5 :     pfree(map->invalues);
     397           5 :     pfree(map->inisnull);
     398           5 :     pfree(map->outvalues);
     399           5 :     pfree(map->outisnull);
     400           5 :     pfree(map);
     401           5 : }

Generated by: LCOV version 1.11