Header And Logo

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

tupconvert.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tupconvert.c
00004  *    Tuple conversion support.
00005  *
00006  * These functions provide conversion between rowtypes that are logically
00007  * equivalent but might have columns in a different order or different sets
00008  * of dropped columns.  There is some overlap of functionality with the
00009  * executor's "junkfilter" routines, but these functions work on bare
00010  * HeapTuples rather than TupleTableSlots.
00011  *
00012  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00013  * Portions Copyright (c) 1994, Regents of the University of California
00014  *
00015  *
00016  * IDENTIFICATION
00017  *    src/backend/access/common/tupconvert.c
00018  *
00019  *-------------------------------------------------------------------------
00020  */
00021 #include "postgres.h"
00022 
00023 #include "access/htup_details.h"
00024 #include "access/tupconvert.h"
00025 #include "utils/builtins.h"
00026 
00027 
00028 /*
00029  * The conversion setup routines have the following common API:
00030  *
00031  * The setup routine checks whether the given source and destination tuple
00032  * descriptors are logically compatible.  If not, it throws an error.
00033  * If so, it returns NULL if they are physically compatible (ie, no conversion
00034  * is needed), else a TupleConversionMap that can be used by do_convert_tuple
00035  * to perform the conversion.
00036  *
00037  * The TupleConversionMap, if needed, is palloc'd in the caller's memory
00038  * context.  Also, the given tuple descriptors are referenced by the map,
00039  * so they must survive as long as the map is needed.
00040  *
00041  * The caller must supply a suitable primary error message to be used if
00042  * a compatibility error is thrown.  Recommended coding practice is to use
00043  * gettext_noop() on this string, so that it is translatable but won't
00044  * actually be translated unless the error gets thrown.
00045  *
00046  *
00047  * Implementation notes:
00048  *
00049  * The key component of a TupleConversionMap is an attrMap[] array with
00050  * one entry per output column.  This entry contains the 1-based index of
00051  * the corresponding input column, or zero to force a NULL value (for
00052  * a dropped output column).  The TupleConversionMap also contains workspace
00053  * arrays.
00054  */
00055 
00056 
00057 /*
00058  * Set up for tuple conversion, matching input and output columns by
00059  * position.  (Dropped columns are ignored in both input and output.)
00060  *
00061  * Note: the errdetail messages speak of indesc as the "returned" rowtype,
00062  * outdesc as the "expected" rowtype.  This is okay for current uses but
00063  * might need generalization in future.
00064  */
00065 TupleConversionMap *
00066 convert_tuples_by_position(TupleDesc indesc,
00067                            TupleDesc outdesc,
00068                            const char *msg)
00069 {
00070     TupleConversionMap *map;
00071     AttrNumber *attrMap;
00072     int         nincols;
00073     int         noutcols;
00074     int         n;
00075     int         i;
00076     int         j;
00077     bool        same;
00078 
00079     /* Verify compatibility and prepare attribute-number map */
00080     n = outdesc->natts;
00081     attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
00082     j = 0;                      /* j is next physical input attribute */
00083     nincols = noutcols = 0;     /* these count non-dropped attributes */
00084     same = true;
00085     for (i = 0; i < n; i++)
00086     {
00087         Form_pg_attribute att = outdesc->attrs[i];
00088         Oid         atttypid;
00089         int32       atttypmod;
00090 
00091         if (att->attisdropped)
00092             continue;           /* attrMap[i] is already 0 */
00093         noutcols++;
00094         atttypid = att->atttypid;
00095         atttypmod = att->atttypmod;
00096         for (; j < indesc->natts; j++)
00097         {
00098             att = indesc->attrs[j];
00099             if (att->attisdropped)
00100                 continue;
00101             nincols++;
00102             /* Found matching column, check type */
00103             if (atttypid != att->atttypid ||
00104                 (atttypmod != att->atttypmod && atttypmod >= 0))
00105                 ereport(ERROR,
00106                         (errcode(ERRCODE_DATATYPE_MISMATCH),
00107                          errmsg_internal("%s", _(msg)),
00108                          errdetail("Returned type %s does not match expected type %s in column %d.",
00109                                    format_type_with_typemod(att->atttypid,
00110                                                             att->atttypmod),
00111                                    format_type_with_typemod(atttypid,
00112                                                             atttypmod),
00113                                    noutcols)));
00114             attrMap[i] = (AttrNumber) (j + 1);
00115             j++;
00116             break;
00117         }
00118         if (attrMap[i] == 0)
00119             same = false;       /* we'll complain below */
00120     }
00121 
00122     /* Check for unused input columns */
00123     for (; j < indesc->natts; j++)
00124     {
00125         if (indesc->attrs[j]->attisdropped)
00126             continue;
00127         nincols++;
00128         same = false;           /* we'll complain below */
00129     }
00130 
00131     /* Report column count mismatch using the non-dropped-column counts */
00132     if (!same)
00133         ereport(ERROR,
00134                 (errcode(ERRCODE_DATATYPE_MISMATCH),
00135                  errmsg_internal("%s", _(msg)),
00136                  errdetail("Number of returned columns (%d) does not match "
00137                            "expected column count (%d).",
00138                            nincols, noutcols)));
00139 
00140     /*
00141      * Check to see if the map is one-to-one and the tuple types are the same.
00142      * (We check the latter because if they're not, we want to do conversion
00143      * to inject the right OID into the tuple datum.)
00144      */
00145     if (indesc->natts == outdesc->natts &&
00146         indesc->tdtypeid == outdesc->tdtypeid)
00147     {
00148         for (i = 0; i < n; i++)
00149         {
00150             if (attrMap[i] == (i + 1))
00151                 continue;
00152 
00153             /*
00154              * If it's a dropped column and the corresponding input column is
00155              * also dropped, we needn't convert.  However, attlen and attalign
00156              * must agree.
00157              */
00158             if (attrMap[i] == 0 &&
00159                 indesc->attrs[i]->attisdropped &&
00160                 indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
00161                 indesc->attrs[i]->attalign == outdesc->attrs[i]->attalign)
00162                 continue;
00163 
00164             same = false;
00165             break;
00166         }
00167     }
00168     else
00169         same = false;
00170 
00171     if (same)
00172     {
00173         /* Runtime conversion is not needed */
00174         pfree(attrMap);
00175         return NULL;
00176     }
00177 
00178     /* Prepare the map structure */
00179     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
00180     map->indesc = indesc;
00181     map->outdesc = outdesc;
00182     map->attrMap = attrMap;
00183     /* preallocate workspace for Datum arrays */
00184     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
00185     map->outisnull = (bool *) palloc(n * sizeof(bool));
00186     n = indesc->natts + 1;      /* +1 for NULL */
00187     map->invalues = (Datum *) palloc(n * sizeof(Datum));
00188     map->inisnull = (bool *) palloc(n * sizeof(bool));
00189     map->invalues[0] = (Datum) 0;       /* set up the NULL entry */
00190     map->inisnull[0] = true;
00191 
00192     return map;
00193 }
00194 
00195 /*
00196  * Set up for tuple conversion, matching input and output columns by name.
00197  * (Dropped columns are ignored in both input and output.)  This is intended
00198  * for use when the rowtypes are related by inheritance, so we expect an exact
00199  * match of both type and typmod.  The error messages will be a bit unhelpful
00200  * unless both rowtypes are named composite types.
00201  */
00202 TupleConversionMap *
00203 convert_tuples_by_name(TupleDesc indesc,
00204                        TupleDesc outdesc,
00205                        const char *msg)
00206 {
00207     TupleConversionMap *map;
00208     AttrNumber *attrMap;
00209     int         n;
00210     int         i;
00211     bool        same;
00212 
00213     /* Verify compatibility and prepare attribute-number map */
00214     n = outdesc->natts;
00215     attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
00216     for (i = 0; i < n; i++)
00217     {
00218         Form_pg_attribute att = outdesc->attrs[i];
00219         char       *attname;
00220         Oid         atttypid;
00221         int32       atttypmod;
00222         int         j;
00223 
00224         if (att->attisdropped)
00225             continue;           /* attrMap[i] is already 0 */
00226         attname = NameStr(att->attname);
00227         atttypid = att->atttypid;
00228         atttypmod = att->atttypmod;
00229         for (j = 0; j < indesc->natts; j++)
00230         {
00231             att = indesc->attrs[j];
00232             if (att->attisdropped)
00233                 continue;
00234             if (strcmp(attname, NameStr(att->attname)) == 0)
00235             {
00236                 /* Found it, check type */
00237                 if (atttypid != att->atttypid || atttypmod != att->atttypmod)
00238                     ereport(ERROR,
00239                             (errcode(ERRCODE_DATATYPE_MISMATCH),
00240                              errmsg_internal("%s", _(msg)),
00241                              errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
00242                                        attname,
00243                                        format_type_be(outdesc->tdtypeid),
00244                                        format_type_be(indesc->tdtypeid))));
00245                 attrMap[i] = (AttrNumber) (j + 1);
00246                 break;
00247             }
00248         }
00249         if (attrMap[i] == 0)
00250             ereport(ERROR,
00251                     (errcode(ERRCODE_DATATYPE_MISMATCH),
00252                      errmsg_internal("%s", _(msg)),
00253                      errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
00254                                attname,
00255                                format_type_be(outdesc->tdtypeid),
00256                                format_type_be(indesc->tdtypeid))));
00257     }
00258 
00259     /*
00260      * Check to see if the map is one-to-one and the tuple types are the same.
00261      * (We check the latter because if they're not, we want to do conversion
00262      * to inject the right OID into the tuple datum.)
00263      */
00264     if (indesc->natts == outdesc->natts &&
00265         indesc->tdtypeid == outdesc->tdtypeid)
00266     {
00267         same = true;
00268         for (i = 0; i < n; i++)
00269         {
00270             if (attrMap[i] == (i + 1))
00271                 continue;
00272 
00273             /*
00274              * If it's a dropped column and the corresponding input column is
00275              * also dropped, we needn't convert.  However, attlen and attalign
00276              * must agree.
00277              */
00278             if (attrMap[i] == 0 &&
00279                 indesc->attrs[i]->attisdropped &&
00280                 indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
00281                 indesc->attrs[i]->attalign == outdesc->attrs[i]->attalign)
00282                 continue;
00283 
00284             same = false;
00285             break;
00286         }
00287     }
00288     else
00289         same = false;
00290 
00291     if (same)
00292     {
00293         /* Runtime conversion is not needed */
00294         pfree(attrMap);
00295         return NULL;
00296     }
00297 
00298     /* Prepare the map structure */
00299     map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
00300     map->indesc = indesc;
00301     map->outdesc = outdesc;
00302     map->attrMap = attrMap;
00303     /* preallocate workspace for Datum arrays */
00304     map->outvalues = (Datum *) palloc(n * sizeof(Datum));
00305     map->outisnull = (bool *) palloc(n * sizeof(bool));
00306     n = indesc->natts + 1;      /* +1 for NULL */
00307     map->invalues = (Datum *) palloc(n * sizeof(Datum));
00308     map->inisnull = (bool *) palloc(n * sizeof(bool));
00309     map->invalues[0] = (Datum) 0;       /* set up the NULL entry */
00310     map->inisnull[0] = true;
00311 
00312     return map;
00313 }
00314 
00315 /*
00316  * Perform conversion of a tuple according to the map.
00317  */
00318 HeapTuple
00319 do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
00320 {
00321     AttrNumber *attrMap = map->attrMap;
00322     Datum      *invalues = map->invalues;
00323     bool       *inisnull = map->inisnull;
00324     Datum      *outvalues = map->outvalues;
00325     bool       *outisnull = map->outisnull;
00326     int         outnatts = map->outdesc->natts;
00327     int         i;
00328 
00329     /*
00330      * Extract all the values of the old tuple, offsetting the arrays so that
00331      * invalues[0] is left NULL and invalues[1] is the first source attribute;
00332      * this exactly matches the numbering convention in attrMap.
00333      */
00334     heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
00335 
00336     /*
00337      * Transpose into proper fields of the new tuple.
00338      */
00339     for (i = 0; i < outnatts; i++)
00340     {
00341         int         j = attrMap[i];
00342 
00343         outvalues[i] = invalues[j];
00344         outisnull[i] = inisnull[j];
00345     }
00346 
00347     /*
00348      * Now form the new tuple.
00349      */
00350     return heap_form_tuple(map->outdesc, outvalues, outisnull);
00351 }
00352 
00353 /*
00354  * Free a TupleConversionMap structure.
00355  */
00356 void
00357 free_conversion_map(TupleConversionMap *map)
00358 {
00359     /* indesc and outdesc are not ours to free */
00360     pfree(map->attrMap);
00361     pfree(map->invalues);
00362     pfree(map->inisnull);
00363     pfree(map->outvalues);
00364     pfree(map->outisnull);
00365     pfree(map);
00366 }