LCOV - code coverage report
Current view: top level - src/backend/access/common - tupconvert.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 161 166 97.0 %
Date: 2019-11-21 13:06:38 Functions: 7 7 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 of
       8             :  * dropped columns.
       9             :  *
      10             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
      11             :  * Portions Copyright (c) 1994, Regents of the University of California
      12             :  *
      13             :  *
      14             :  * IDENTIFICATION
      15             :  *    src/backend/access/common/tupconvert.c
      16             :  *
      17             :  *-------------------------------------------------------------------------
      18             :  */
      19             : #include "postgres.h"
      20             : 
      21             : #include "access/htup_details.h"
      22             : #include "access/tupconvert.h"
      23             : #include "executor/tuptable.h"
      24             : #include "utils/builtins.h"
      25             : 
      26             : 
      27             : /*
      28             :  * The conversion setup routines have the following common API:
      29             :  *
      30             :  * The setup routine checks whether the given source and destination tuple
      31             :  * descriptors are logically compatible.  If not, it throws an error.
      32             :  * If so, it returns NULL if they are physically compatible (ie, no conversion
      33             :  * is needed), else a TupleConversionMap that can be used by execute_attr_map_tuple
      34             :  * to perform the conversion.
      35             :  *
      36             :  * The TupleConversionMap, if needed, is palloc'd in the caller's memory
      37             :  * context.  Also, the given tuple descriptors are referenced by the map,
      38             :  * so they must survive as long as the map is needed.
      39             :  *
      40             :  * The caller must supply a suitable primary error message to be used if
      41             :  * a compatibility error is thrown.  Recommended coding practice is to use
      42             :  * gettext_noop() on this string, so that it is translatable but won't
      43             :  * actually be translated unless the error gets thrown.
      44             :  *
      45             :  *
      46             :  * Implementation notes:
      47             :  *
      48             :  * The key component of a TupleConversionMap is an attrMap[] array with
      49             :  * one entry per output column.  This entry contains the 1-based index of
      50             :  * the corresponding input column, or zero to force a NULL value (for
      51             :  * a dropped output column).  The TupleConversionMap also contains workspace
      52             :  * arrays.
      53             :  */
      54             : 
      55             : 
      56             : /*
      57             :  * Set up for tuple conversion, matching input and output columns by
      58             :  * position.  (Dropped columns are ignored in both input and output.)
      59             :  *
      60             :  * Note: the errdetail messages speak of indesc as the "returned" rowtype,
      61             :  * outdesc as the "expected" rowtype.  This is okay for current uses but
      62             :  * might need generalization in future.
      63             :  */
      64             : TupleConversionMap *
      65         980 : convert_tuples_by_position(TupleDesc indesc,
      66             :                            TupleDesc outdesc,
      67             :                            const char *msg)
      68             : {
      69             :     TupleConversionMap *map;
      70             :     AttrNumber *attrMap;
      71             :     int         nincols;
      72             :     int         noutcols;
      73             :     int         n;
      74             :     int         i;
      75             :     int         j;
      76             :     bool        same;
      77             : 
      78             :     /* Verify compatibility and prepare attribute-number map */
      79         980 :     n = outdesc->natts;
      80         980 :     attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
      81         980 :     j = 0;                      /* j is next physical input attribute */
      82         980 :     nincols = noutcols = 0;     /* these count non-dropped attributes */
      83         980 :     same = true;
      84        3036 :     for (i = 0; i < n; i++)
      85             :     {
      86        2072 :         Form_pg_attribute att = TupleDescAttr(outdesc, i);
      87             :         Oid         atttypid;
      88             :         int32       atttypmod;
      89             : 
      90        2072 :         if (att->attisdropped)
      91          96 :             continue;           /* attrMap[i] is already 0 */
      92        1976 :         noutcols++;
      93        1976 :         atttypid = att->atttypid;
      94        1976 :         atttypmod = att->atttypmod;
      95        3988 :         for (; j < indesc->natts; j++)
      96             :         {
      97        1986 :             att = TupleDescAttr(indesc, j);
      98        1986 :             if (att->attisdropped)
      99          18 :                 continue;
     100        1968 :             nincols++;
     101             :             /* Found matching column, check type */
     102        3920 :             if (atttypid != att->atttypid ||
     103        1952 :                 (atttypmod != att->atttypmod && atttypmod >= 0))
     104          16 :                 ereport(ERROR,
     105             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
     106             :                          errmsg_internal("%s", _(msg)),
     107             :                          errdetail("Returned type %s does not match expected type %s in column %d.",
     108             :                                    format_type_with_typemod(att->atttypid,
     109             :                                                             att->atttypmod),
     110             :                                    format_type_with_typemod(atttypid,
     111             :                                                             atttypmod),
     112             :                                    noutcols)));
     113        1952 :             attrMap[i] = (AttrNumber) (j + 1);
     114        1952 :             j++;
     115        1952 :             break;
     116             :         }
     117        1960 :         if (attrMap[i] == 0)
     118           8 :             same = false;       /* we'll complain below */
     119             :     }
     120             : 
     121             :     /* Check for unused input columns */
     122         970 :     for (; j < indesc->natts; j++)
     123             :     {
     124           6 :         if (TupleDescAttr(indesc, j)->attisdropped)
     125           0 :             continue;
     126           6 :         nincols++;
     127           6 :         same = false;           /* we'll complain below */
     128             :     }
     129             : 
     130             :     /* Report column count mismatch using the non-dropped-column counts */
     131         964 :     if (!same)
     132          14 :         ereport(ERROR,
     133             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     134             :                  errmsg_internal("%s", _(msg)),
     135             :                  errdetail("Number of returned columns (%d) does not match "
     136             :                            "expected column count (%d).",
     137             :                            nincols, noutcols)));
     138             : 
     139             :     /*
     140             :      * Check to see if the map is one-to-one, in which case we need not do a
     141             :      * tuple conversion.
     142             :      */
     143         950 :     if (indesc->natts == outdesc->natts)
     144             :     {
     145        2668 :         for (i = 0; i < n; i++)
     146             :         {
     147             :             Form_pg_attribute inatt;
     148             :             Form_pg_attribute outatt;
     149             : 
     150        1778 :             if (attrMap[i] == (i + 1))
     151        1772 :                 continue;
     152             : 
     153             :             /*
     154             :              * If it's a dropped column and the corresponding input column is
     155             :              * also dropped, we needn't convert.  However, attlen and attalign
     156             :              * must agree.
     157             :              */
     158           6 :             inatt = TupleDescAttr(indesc, i);
     159           6 :             outatt = TupleDescAttr(outdesc, i);
     160          12 :             if (attrMap[i] == 0 &&
     161          12 :                 inatt->attisdropped &&
     162          12 :                 inatt->attlen == outatt->attlen &&
     163           6 :                 inatt->attalign == outatt->attalign)
     164           6 :                 continue;
     165             : 
     166           0 :             same = false;
     167           0 :             break;
     168             :         }
     169             :     }
     170             :     else
     171          60 :         same = false;
     172             : 
     173         950 :     if (same)
     174             :     {
     175             :         /* Runtime conversion is not needed */
     176         890 :         pfree(attrMap);
     177         890 :         return NULL;
     178             :     }
     179             : 
     180             :     /* Prepare the map structure */
     181          60 :     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
     182          60 :     map->indesc = indesc;
     183          60 :     map->outdesc = outdesc;
     184          60 :     map->attrMap = attrMap;
     185             :     /* preallocate workspace for Datum arrays */
     186          60 :     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
     187          60 :     map->outisnull = (bool *) palloc(n * sizeof(bool));
     188          60 :     n = indesc->natts + 1;       /* +1 for NULL */
     189          60 :     map->invalues = (Datum *) palloc(n * sizeof(Datum));
     190          60 :     map->inisnull = (bool *) palloc(n * sizeof(bool));
     191          60 :     map->invalues[0] = (Datum) 0;    /* set up the NULL entry */
     192          60 :     map->inisnull[0] = true;
     193             : 
     194          60 :     return map;
     195             : }
     196             : 
     197             : /*
     198             :  * Set up for tuple conversion, matching input and output columns by name.
     199             :  * (Dropped columns are ignored in both input and output.)  This is intended
     200             :  * for use when the rowtypes are related by inheritance, so we expect an exact
     201             :  * match of both type and typmod.  The error messages will be a bit unhelpful
     202             :  * unless both rowtypes are named composite types.
     203             :  */
     204             : TupleConversionMap *
     205        4842 : convert_tuples_by_name(TupleDesc indesc,
     206             :                        TupleDesc outdesc)
     207             : {
     208             :     TupleConversionMap *map;
     209             :     AttrNumber *attrMap;
     210        4842 :     int         n = outdesc->natts;
     211             : 
     212             :     /* Verify compatibility and prepare attribute-number map */
     213        4842 :     attrMap = convert_tuples_by_name_map_if_req(indesc, outdesc);
     214             : 
     215        4842 :     if (attrMap == NULL)
     216             :     {
     217             :         /* runtime conversion is not needed */
     218        3380 :         return NULL;
     219             :     }
     220             : 
     221             :     /* Prepare the map structure */
     222        1462 :     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
     223        1462 :     map->indesc = indesc;
     224        1462 :     map->outdesc = outdesc;
     225        1462 :     map->attrMap = attrMap;
     226             :     /* preallocate workspace for Datum arrays */
     227        1462 :     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
     228        1462 :     map->outisnull = (bool *) palloc(n * sizeof(bool));
     229        1462 :     n = indesc->natts + 1;       /* +1 for NULL */
     230        1462 :     map->invalues = (Datum *) palloc(n * sizeof(Datum));
     231        1462 :     map->inisnull = (bool *) palloc(n * sizeof(bool));
     232        1462 :     map->invalues[0] = (Datum) 0;    /* set up the NULL entry */
     233        1462 :     map->inisnull[0] = true;
     234             : 
     235        1462 :     return map;
     236             : }
     237             : 
     238             : /*
     239             :  * Return a palloc'd bare attribute map for tuple conversion, matching input
     240             :  * and output columns by name.  (Dropped columns are ignored in both input and
     241             :  * output.)  This is normally a subroutine for convert_tuples_by_name, but can
     242             :  * be used standalone.
     243             :  */
     244             : AttrNumber *
     245       17014 : convert_tuples_by_name_map(TupleDesc indesc,
     246             :                            TupleDesc outdesc)
     247             : {
     248             :     AttrNumber *attrMap;
     249             :     int         outnatts;
     250             :     int         innatts;
     251             :     int         i;
     252       17014 :     int         nextindesc = -1;
     253             : 
     254       17014 :     outnatts = outdesc->natts;
     255       17014 :     innatts = indesc->natts;
     256             : 
     257       17014 :     attrMap = (AttrNumber *) palloc0(outnatts * sizeof(AttrNumber));
     258       61182 :     for (i = 0; i < outnatts; i++)
     259             :     {
     260       44168 :         Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
     261             :         char       *attname;
     262             :         Oid         atttypid;
     263             :         int32       atttypmod;
     264             :         int         j;
     265             : 
     266       44168 :         if (outatt->attisdropped)
     267        3028 :             continue;           /* attrMap[i] is already 0 */
     268       41140 :         attname = NameStr(outatt->attname);
     269       41140 :         atttypid = outatt->atttypid;
     270       41140 :         atttypmod = outatt->atttypmod;
     271             : 
     272             :         /*
     273             :          * Now search for an attribute with the same name in the indesc. It
     274             :          * seems likely that a partitioned table will have the attributes in
     275             :          * the same order as the partition, so the search below is optimized
     276             :          * for that case.  It is possible that columns are dropped in one of
     277             :          * the relations, but not the other, so we use the 'nextindesc'
     278             :          * counter to track the starting point of the search.  If the inner
     279             :          * loop encounters dropped columns then it will have to skip over
     280             :          * them, but it should leave 'nextindesc' at the correct position for
     281             :          * the next outer loop.
     282             :          */
     283       54904 :         for (j = 0; j < innatts; j++)
     284             :         {
     285             :             Form_pg_attribute inatt;
     286             : 
     287       54904 :             nextindesc++;
     288       54904 :             if (nextindesc >= innatts)
     289        3786 :                 nextindesc = 0;
     290             : 
     291       54904 :             inatt = TupleDescAttr(indesc, nextindesc);
     292       54904 :             if (inatt->attisdropped)
     293        2284 :                 continue;
     294       52620 :             if (strcmp(attname, NameStr(inatt->attname)) == 0)
     295             :             {
     296             :                 /* Found it, check type */
     297       41140 :                 if (atttypid != inatt->atttypid || atttypmod != inatt->atttypmod)
     298           0 :                     ereport(ERROR,
     299             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
     300             :                              errmsg("could not convert row type"),
     301             :                              errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
     302             :                                        attname,
     303             :                                        format_type_be(outdesc->tdtypeid),
     304             :                                        format_type_be(indesc->tdtypeid))));
     305       41140 :                 attrMap[i] = inatt->attnum;
     306       41140 :                 break;
     307             :             }
     308             :         }
     309       41140 :         if (attrMap[i] == 0)
     310           0 :             ereport(ERROR,
     311             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     312             :                      errmsg("could not convert row type"),
     313             :                      errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
     314             :                                attname,
     315             :                                format_type_be(outdesc->tdtypeid),
     316             :                                format_type_be(indesc->tdtypeid))));
     317             :     }
     318       17014 :     return attrMap;
     319             : }
     320             : 
     321             : /*
     322             :  * Returns mapping created by convert_tuples_by_name_map, or NULL if no
     323             :  * conversion not required. This is a convenience routine for
     324             :  * convert_tuples_by_name() and other functions.
     325             :  */
     326             : AttrNumber *
     327        5688 : convert_tuples_by_name_map_if_req(TupleDesc indesc,
     328             :                                   TupleDesc outdesc)
     329             : {
     330             :     AttrNumber *attrMap;
     331        5688 :     int         n = outdesc->natts;
     332             :     int         i;
     333             :     bool        same;
     334             : 
     335             :     /* Verify compatibility and prepare attribute-number map */
     336        5688 :     attrMap = convert_tuples_by_name_map(indesc, outdesc);
     337             : 
     338             :     /*
     339             :      * Check to see if the map is one-to-one, in which case we need not do a
     340             :      * tuple conversion.
     341             :      */
     342        5688 :     if (indesc->natts == outdesc->natts)
     343             :     {
     344        4852 :         same = true;
     345       14190 :         for (i = 0; i < n; i++)
     346             :         {
     347             :             Form_pg_attribute inatt;
     348             :             Form_pg_attribute outatt;
     349             : 
     350       10386 :             if (attrMap[i] == (i + 1))
     351        9294 :                 continue;
     352             : 
     353             :             /*
     354             :              * If it's a dropped column and the corresponding input column is
     355             :              * also dropped, we needn't convert.  However, attlen and attalign
     356             :              * must agree.
     357             :              */
     358        1092 :             inatt = TupleDescAttr(indesc, i);
     359        1092 :             outatt = TupleDescAttr(outdesc, i);
     360        1160 :             if (attrMap[i] == 0 &&
     361         112 :                 inatt->attisdropped &&
     362          88 :                 inatt->attlen == outatt->attlen &&
     363          44 :                 inatt->attalign == outatt->attalign)
     364          44 :                 continue;
     365             : 
     366        1048 :             same = false;
     367        1048 :             break;
     368             :         }
     369             :     }
     370             :     else
     371         836 :         same = false;
     372             : 
     373        5688 :     if (same)
     374             :     {
     375             :         /* Runtime conversion is not needed */
     376        3804 :         pfree(attrMap);
     377        3804 :         return NULL;
     378             :     }
     379             :     else
     380        1884 :         return attrMap;
     381             : }
     382             : 
     383             : /*
     384             :  * Perform conversion of a tuple according to the map.
     385             :  */
     386             : HeapTuple
     387       64386 : execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map)
     388             : {
     389       64386 :     AttrNumber *attrMap = map->attrMap;
     390       64386 :     Datum      *invalues = map->invalues;
     391       64386 :     bool       *inisnull = map->inisnull;
     392       64386 :     Datum      *outvalues = map->outvalues;
     393       64386 :     bool       *outisnull = map->outisnull;
     394       64386 :     int         outnatts = map->outdesc->natts;
     395             :     int         i;
     396             : 
     397             :     /*
     398             :      * Extract all the values of the old tuple, offsetting the arrays so that
     399             :      * invalues[0] is left NULL and invalues[1] is the first source attribute;
     400             :      * this exactly matches the numbering convention in attrMap.
     401             :      */
     402       64386 :     heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
     403             : 
     404             :     /*
     405             :      * Transpose into proper fields of the new tuple.
     406             :      */
     407      255964 :     for (i = 0; i < outnatts; i++)
     408             :     {
     409      191578 :         int         j = attrMap[i];
     410             : 
     411      191578 :         outvalues[i] = invalues[j];
     412      191578 :         outisnull[i] = inisnull[j];
     413             :     }
     414             : 
     415             :     /*
     416             :      * Now form the new tuple.
     417             :      */
     418       64386 :     return heap_form_tuple(map->outdesc, outvalues, outisnull);
     419             : }
     420             : 
     421             : /*
     422             :  * Perform conversion of a tuple slot according to the map.
     423             :  */
     424             : TupleTableSlot *
     425       94106 : execute_attr_map_slot(AttrNumber *attrMap,
     426             :                       TupleTableSlot *in_slot,
     427             :                       TupleTableSlot *out_slot)
     428             : {
     429             :     Datum      *invalues;
     430             :     bool       *inisnull;
     431             :     Datum      *outvalues;
     432             :     bool       *outisnull;
     433             :     int         outnatts;
     434             :     int         i;
     435             : 
     436             :     /* Sanity checks */
     437             :     Assert(in_slot->tts_tupleDescriptor != NULL &&
     438             :            out_slot->tts_tupleDescriptor != NULL);
     439             :     Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
     440             : 
     441       94106 :     outnatts = out_slot->tts_tupleDescriptor->natts;
     442             : 
     443             :     /* Extract all the values of the in slot. */
     444       94106 :     slot_getallattrs(in_slot);
     445             : 
     446             :     /* Before doing the mapping, clear any old contents from the out slot */
     447       94106 :     ExecClearTuple(out_slot);
     448             : 
     449       94106 :     invalues = in_slot->tts_values;
     450       94106 :     inisnull = in_slot->tts_isnull;
     451       94106 :     outvalues = out_slot->tts_values;
     452       94106 :     outisnull = out_slot->tts_isnull;
     453             : 
     454             :     /* Transpose into proper fields of the out slot. */
     455      379406 :     for (i = 0; i < outnatts; i++)
     456             :     {
     457      285300 :         int         j = attrMap[i] - 1;
     458             : 
     459             :         /* attrMap[i] == 0 means it's a NULL datum. */
     460      285300 :         if (j == -1)
     461             :         {
     462        1368 :             outvalues[i] = (Datum) 0;
     463        1368 :             outisnull[i] = true;
     464             :         }
     465             :         else
     466             :         {
     467      283932 :             outvalues[i] = invalues[j];
     468      283932 :             outisnull[i] = inisnull[j];
     469             :         }
     470             :     }
     471             : 
     472       94106 :     ExecStoreVirtualTuple(out_slot);
     473             : 
     474       94106 :     return out_slot;
     475             : }
     476             : 
     477             : /*
     478             :  * Free a TupleConversionMap structure.
     479             :  */
     480             : void
     481         208 : free_conversion_map(TupleConversionMap *map)
     482             : {
     483             :     /* indesc and outdesc are not ours to free */
     484         208 :     pfree(map->attrMap);
     485         208 :     pfree(map->invalues);
     486         208 :     pfree(map->inisnull);
     487         208 :     pfree(map->outvalues);
     488         208 :     pfree(map->outisnull);
     489         208 :     pfree(map);
     490         208 : }

Generated by: LCOV version 1.13