LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - rowtypes.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 78.6 % 845 664 7 22 100 52 18 381 13 252 111 385 5
Current Date: 2023-04-08 17:13:01 Functions: 90.9 % 22 20 2 19 1 2 19
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (120,180] days: 65.0 % 20 13 7 13
Legend: Lines: hit not hit (240..) days: 78.9 % 825 651 22 100 52 18 381 252 111 371
Function coverage date bins:
(240..) days: 46.5 % 43 20 2 19 1 2 19

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * rowtypes.c
                                  4                 :  *    I/O and comparison functions for generic composite types.
                                  5                 :  *
                                  6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
                                  7                 :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :  *
                                  9                 :  *
                                 10                 :  * IDENTIFICATION
                                 11                 :  *    src/backend/utils/adt/rowtypes.c
                                 12                 :  *
                                 13                 :  *-------------------------------------------------------------------------
                                 14                 :  */
                                 15                 : #include "postgres.h"
                                 16                 : 
                                 17                 : #include <ctype.h>
                                 18                 : 
                                 19                 : #include "access/detoast.h"
                                 20                 : #include "access/htup_details.h"
                                 21                 : #include "catalog/pg_type.h"
                                 22                 : #include "common/hashfn.h"
                                 23                 : #include "funcapi.h"
                                 24                 : #include "libpq/pqformat.h"
                                 25                 : #include "miscadmin.h"
                                 26                 : #include "utils/builtins.h"
                                 27                 : #include "utils/datum.h"
                                 28                 : #include "utils/lsyscache.h"
                                 29                 : #include "utils/typcache.h"
                                 30                 : 
                                 31                 : 
                                 32                 : /*
                                 33                 :  * structure to cache metadata needed for record I/O
                                 34                 :  */
                                 35                 : typedef struct ColumnIOData
                                 36                 : {
                                 37                 :     Oid         column_type;
                                 38                 :     Oid         typiofunc;
                                 39                 :     Oid         typioparam;
                                 40                 :     bool        typisvarlena;
                                 41                 :     FmgrInfo    proc;
                                 42                 : } ColumnIOData;
                                 43                 : 
                                 44                 : typedef struct RecordIOData
                                 45                 : {
                                 46                 :     Oid         record_type;
                                 47                 :     int32       record_typmod;
                                 48                 :     int         ncolumns;
                                 49                 :     ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
                                 50                 : } RecordIOData;
                                 51                 : 
                                 52                 : /*
                                 53                 :  * structure to cache metadata needed for record comparison
                                 54                 :  */
                                 55                 : typedef struct ColumnCompareData
                                 56                 : {
                                 57                 :     TypeCacheEntry *typentry;   /* has everything we need, actually */
                                 58                 : } ColumnCompareData;
                                 59                 : 
                                 60                 : typedef struct RecordCompareData
                                 61                 : {
                                 62                 :     int         ncolumns;       /* allocated length of columns[] */
                                 63                 :     Oid         record1_type;
                                 64                 :     int32       record1_typmod;
                                 65                 :     Oid         record2_type;
                                 66                 :     int32       record2_typmod;
                                 67                 :     ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER];
                                 68                 : } RecordCompareData;
                                 69                 : 
                                 70                 : 
                                 71                 : /*
                                 72                 :  * record_in        - input routine for any composite type.
                                 73                 :  */
                                 74                 : Datum
 6947 tgl                        75 CBC         843 : record_in(PG_FUNCTION_ARGS)
                                 76                 : {
 6881                            77             843 :     char       *string = PG_GETARG_CSTRING(0);
                                 78             843 :     Oid         tupType = PG_GETARG_OID(1);
 2788                            79             843 :     int32       tupTypmod = PG_GETARG_INT32(2);
  121 tgl                        80 GNC         843 :     Node       *escontext = fcinfo->context;
 6565 tgl                        81 ECB             :     HeapTupleHeader result;
                                 82                 :     TupleDesc   tupdesc;
                                 83                 :     HeapTuple   tuple;
                                 84                 :     RecordIOData *my_extra;
 6822 tgl                        85 GIC         843 :     bool        needComma = false;
 6881 tgl                        86 ECB             :     int         ncolumns;
                                 87                 :     int         i;
                                 88                 :     char       *ptr;
                                 89                 :     Datum      *values;
                                 90                 :     bool       *nulls;
                                 91                 :     StringInfoData buf;
                                 92                 : 
 2743 noah                       93 GIC         843 :     check_stack_depth();        /* recurses for record-type columns */
 2743 noah                       94 ECB             : 
                                 95                 :     /*
                                 96                 :      * Give a friendly error message if we did not get enough info to identify
                                 97                 :      * the target record type.  (lookup_rowtype_tupdesc would fail anyway, but
                                 98                 :      * with a non-user-friendly message.)  In ordinary SQL usage, we'll get -1
                                 99                 :      * for typmod, since composite types and RECORD have no type modifiers at
                                100                 :      * the SQL level, and thus must fail for RECORD.  However some callers can
                                101                 :      * supply a valid typmod, and then we can do something useful for RECORD.
                                102                 :      */
 2788 tgl                       103 GIC         843 :     if (tupType == RECORDOID && tupTypmod < 0)
  121 tgl                       104 UNC           0 :         ereturn(escontext, (Datum) 0,
 6881 tgl                       105 EUB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                106                 :                  errmsg("input of anonymous composite types is not implemented")));
                                107                 : 
                                108                 :     /*
                                109                 :      * This comes from the composite type's pg_type.oid and stores system oids
                                110                 :      * in user tables, specifically DatumTupleFields. This oid must be
                                111                 :      * preserved by binary upgrades.
                                112                 :      */
 6881 tgl                       113 GIC         843 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 6881 tgl                       114 CBC         843 :     ncolumns = tupdesc->natts;
 6881 tgl                       115 ECB             : 
                                116                 :     /*
                                117                 :      * We arrange to look up the needed I/O info just once per series of
                                118                 :      * calls, assuming the record type doesn't change underneath us.
                                119                 :      */
 6881 tgl                       120 GIC         843 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 6881 tgl                       121 CBC         843 :     if (my_extra == NULL ||
                                122             492 :         my_extra->ncolumns != ncolumns)
 6881 tgl                       123 ECB             :     {
 6881 tgl                       124 GIC         702 :         fcinfo->flinfo->fn_extra =
 6881 tgl                       125 CBC         351 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 2970 tgl                       126 ECB             :                                offsetof(RecordIOData, columns) +
 2970 tgl                       127 GIC         351 :                                ncolumns * sizeof(ColumnIOData));
 6881 tgl                       128 CBC         351 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
                                129             351 :         my_extra->record_type = InvalidOid;
                                130             351 :         my_extra->record_typmod = 0;
 6881 tgl                       131 ECB             :     }
                                132                 : 
 6881 tgl                       133 GIC         843 :     if (my_extra->record_type != tupType ||
 6881 tgl                       134 CBC         492 :         my_extra->record_typmod != tupTypmod)
 6881 tgl                       135 ECB             :     {
 6881 tgl                       136 GIC        8413 :         MemSet(my_extra, 0,
 2970 tgl                       137 ECB             :                offsetof(RecordIOData, columns) +
                                138                 :                ncolumns * sizeof(ColumnIOData));
 6881 tgl                       139 GIC         351 :         my_extra->record_type = tupType;
 6881 tgl                       140 CBC         351 :         my_extra->record_typmod = tupTypmod;
                                141             351 :         my_extra->ncolumns = ncolumns;
 6881 tgl                       142 ECB             :     }
                                143                 : 
 6881 tgl                       144 GIC         843 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
 5271 tgl                       145 CBC         843 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
 6881 tgl                       146 ECB             : 
                                147                 :     /*
                                148                 :      * Scan the string.  We use "buf" to accumulate the de-quoted data for
                                149                 :      * each column, which is then fed to the appropriate input converter.
                                150                 :      */
 6881 tgl                       151 GIC         843 :     ptr = string;
 6881 tgl                       152 ECB             :     /* Allow leading whitespace */
 6881 tgl                       153 GIC         846 :     while (*ptr && isspace((unsigned char) *ptr))
 6881 tgl                       154 CBC           3 :         ptr++;
                                155             843 :     if (*ptr++ != '(')
                                156                 :     {
  121 tgl                       157 GNC           5 :         errsave(escontext,
                                158                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 6881 tgl                       159 ECB             :                  errmsg("malformed record literal: \"%s\"", string),
                                160                 :                  errdetail("Missing left parenthesis.")));
  121 tgl                       161 UNC           0 :         goto fail;
                                162                 :     }
                                163                 : 
 6881 tgl                       164 GIC         838 :     initStringInfo(&buf);
 6881 tgl                       165 EUB             : 
 6881 tgl                       166 GIC        3966 :     for (i = 0; i < ncolumns; i++)
                                167                 :     {
 2058 andres                    168 CBC        3147 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
 6881 tgl                       169 GIC        3147 :         ColumnIOData *column_info = &my_extra->columns[i];
 2058 andres                    170 CBC        3147 :         Oid         column_type = att->atttypid;
                                171                 :         char       *column_data;
 6881 tgl                       172 ECB             : 
 6822                           173                 :         /* Ignore dropped columns in datatype, but fill with nulls */
 2058 andres                    174 CBC        3147 :         if (att->attisdropped)
                                175                 :         {
 6822 tgl                       176 GIC         165 :             values[i] = (Datum) 0;
 5271                           177             165 :             nulls[i] = true;
 6822 tgl                       178 CBC         165 :             continue;
                                179                 :         }
 6822 tgl                       180 ECB             : 
 6822 tgl                       181 CBC        2982 :         if (needComma)
 6822 tgl                       182 ECB             :         {
                                183                 :             /* Skip comma that separates prior field from this one */
 6822 tgl                       184 GIC        2144 :             if (*ptr == ',')
 6822 tgl                       185 CBC        2141 :                 ptr++;
                                186                 :             else
                                187                 :                 /* *ptr must be ')' */
                                188                 :             {
  121 tgl                       189 GNC           3 :                 errsave(escontext,
 6822 tgl                       190 ECB             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                191                 :                          errmsg("malformed record literal: \"%s\"", string),
                                192                 :                          errdetail("Too few columns.")));
  121 tgl                       193 UNC           0 :                 goto fail;
                                194                 :             }
                                195                 :         }
 6822 tgl                       196 ECB             : 
                                197                 :         /* Check for null: completely empty input means null */
 6881 tgl                       198 GIC        2979 :         if (*ptr == ',' || *ptr == ')')
                                199                 :         {
 6214 tgl                       200 GBC         235 :             column_data = NULL;
 5271 tgl                       201 GIC         235 :             nulls[i] = true;
                                202                 :         }
                                203                 :         else
                                204                 :         {
 6881 tgl                       205 ECB             :             /* Extract string for this column */
 6797 bruce                     206 GIC        2744 :             bool        inquote = false;
 6881 tgl                       207 ECB             : 
 5881 neilc                     208 CBC        2744 :             resetStringInfo(&buf);
 6881 tgl                       209 GIC       15974 :             while (inquote || !(*ptr == ',' || *ptr == ')'))
                                210                 :             {
 6797 bruce                     211           13233 :                 char        ch = *ptr++;
                                212                 : 
 6881 tgl                       213 CBC       13233 :                 if (ch == '\0')
                                214                 :                 {
  121 tgl                       215 GNC           3 :                     errsave(escontext,
 6881 tgl                       216 ECB             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                217                 :                              errmsg("malformed record literal: \"%s\"",
                                218                 :                                     string),
                                219                 :                              errdetail("Unexpected end of input.")));
  121 tgl                       220 GNC           3 :                     goto fail;
                                221                 :                 }
 6881 tgl                       222 GIC       13230 :                 if (ch == '\\')
 6881 tgl                       223 ECB             :                 {
 6881 tgl                       224 GIC           3 :                     if (*ptr == '\0')
                                225                 :                     {
  121 tgl                       226 UNC           0 :                         errsave(escontext,
                                227                 :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                228                 :                                  errmsg("malformed record literal: \"%s\"",
                                229                 :                                         string),
                                230                 :                                  errdetail("Unexpected end of input.")));
                                231               0 :                         goto fail;
                                232                 :                     }
 6881 tgl                       233 CBC           3 :                     appendStringInfoChar(&buf, *ptr++);
                                234                 :                 }
 2665 peter_e                   235           13227 :                 else if (ch == '"')
                                236                 :                 {
 6881 tgl                       237             883 :                     if (!inquote)
 6881 tgl                       238 GIC         426 :                         inquote = true;
 2665 peter_e                   239 GBC         457 :                     else if (*ptr == '"')
                                240                 :                     {
                                241                 :                         /* doubled quote within quote sequence */
 6881 tgl                       242 GIC          31 :                         appendStringInfoChar(&buf, *ptr++);
                                243                 :                     }
 6881 tgl                       244 EUB             :                     else
 6881 tgl                       245 GIC         426 :                         inquote = false;
 6881 tgl                       246 ECB             :                 }
                                247                 :                 else
 6881 tgl                       248 CBC       12344 :                     appendStringInfoChar(&buf, ch);
                                249                 :             }
 6881 tgl                       250 ECB             : 
 6214 tgl                       251 CBC        2741 :             column_data = buf.data;
 5271                           252            2741 :             nulls[i] = false;
                                253                 :         }
                                254                 : 
 6214 tgl                       255 ECB             :         /*
                                256                 :          * Convert the column value
                                257                 :          */
 6214 tgl                       258 CBC        2976 :         if (column_info->column_type != column_type)
                                259                 :         {
 6214 tgl                       260 GIC         887 :             getTypeInputInfo(column_type,
 6214 tgl                       261 ECB             :                              &column_info->typiofunc,
                                262                 :                              &column_info->typioparam);
 6214 tgl                       263 GIC         887 :             fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
 6214 tgl                       264 CBC         887 :                           fcinfo->flinfo->fn_mcxt);
                                265             887 :             column_info->column_type = column_type;
                                266                 :         }
                                267                 : 
  121 tgl                       268 GNC        2972 :         if (!InputFunctionCallSafe(&column_info->proc,
                                269                 :                                    column_data,
                                270                 :                                    column_info->typioparam,
                                271                 :                                    att->atttypmod,
                                272                 :                                    escontext,
                                273            2976 :                                    &values[i]))
                                274               9 :             goto fail;
                                275                 : 
 6881 tgl                       276 ECB             :         /*
                                277                 :          * Prep for next column
                                278                 :          */
 6822 tgl                       279 CBC        2963 :         needComma = true;
 6881 tgl                       280 ECB             :     }
                                281                 : 
 6881 tgl                       282 GIC         819 :     if (*ptr++ != ')')
                                283                 :     {
  121 tgl                       284 GNC           3 :         errsave(escontext,
 6881 tgl                       285 ECB             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                286                 :                  errmsg("malformed record literal: \"%s\"", string),
                                287                 :                  errdetail("Too many columns.")));
  121 tgl                       288 UNC           0 :         goto fail;
                                289                 :     }
                                290                 :     /* Allow trailing whitespace */
 6881 tgl                       291 GIC         825 :     while (*ptr && isspace((unsigned char) *ptr))
 6881 tgl                       292 CBC           9 :         ptr++;
                                293             816 :     if (*ptr)
                                294                 :     {
  121 tgl                       295 GNC           3 :         errsave(escontext,
                                296                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                297                 :                  errmsg("malformed record literal: \"%s\"", string),
                                298                 :                  errdetail("Junk after right parenthesis.")));
  121 tgl                       299 UNC           0 :         goto fail;
                                300                 :     }
 6881 tgl                       301 ECB             : 
 5271 tgl                       302 GIC         813 :     tuple = heap_form_tuple(tupdesc, values, nulls);
                                303                 : 
 6565 tgl                       304 ECB             :     /*
                                305                 :      * We cannot return tuple->t_data because heap_form_tuple allocates it as
 6385 bruce                     306                 :      * part of a larger chunk, and our caller may expect to be able to pfree
                                307                 :      * our result.  So must copy the info into a new palloc chunk.
                                308                 :      */
 6565 tgl                       309 GIC         813 :     result = (HeapTupleHeader) palloc(tuple->t_len);
 6565 tgl                       310 GBC         813 :     memcpy(result, tuple->t_data, tuple->t_len);
                                311                 : 
 6565 tgl                       312 GIC         813 :     heap_freetuple(tuple);
 6881 tgl                       313 CBC         813 :     pfree(buf.data);
                                314             813 :     pfree(values);
                                315             813 :     pfree(nulls);
 6141 tgl                       316 GIC         813 :     ReleaseTupleDesc(tupdesc);
 6881 tgl                       317 ECB             : 
 6565 tgl                       318 GIC         813 :     PG_RETURN_HEAPTUPLEHEADER(result);
                                319                 : 
                                320                 :     /* exit here once we've done lookup_rowtype_tupdesc */
  121 tgl                       321 GNC          12 : fail:
                                322              12 :     ReleaseTupleDesc(tupdesc);
                                323              12 :     PG_RETURN_NULL();
                                324                 : }
                                325                 : 
 6947 tgl                       326 EUB             : /*
                                327                 :  * record_out       - output routine for any composite type.
                                328                 :  */
 6947 tgl                       329 ECB             : Datum
 6947 tgl                       330 GIC       17268 : record_out(PG_FUNCTION_ARGS)
                                331                 : {
 6881                           332           17268 :     HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
                                333                 :     Oid         tupType;
                                334                 :     int32       tupTypmod;
                                335                 :     TupleDesc   tupdesc;
 6881 tgl                       336 ECB             :     HeapTupleData tuple;
                                337                 :     RecordIOData *my_extra;
 6822 tgl                       338 GIC       17268 :     bool        needComma = false;
 6881 tgl                       339 ECB             :     int         ncolumns;
                                340                 :     int         i;
                                341                 :     Datum      *values;
 5271                           342                 :     bool       *nulls;
 6881                           343                 :     StringInfoData buf;
                                344                 : 
 2743 noah                      345 CBC       17268 :     check_stack_depth();        /* recurses for record-type columns */
                                346                 : 
                                347                 :     /* Extract type info from the tuple itself */
 6553 tgl                       348           17268 :     tupType = HeapTupleHeaderGetTypeId(rec);
                                349           17268 :     tupTypmod = HeapTupleHeaderGetTypMod(rec);
 6881                           350           17268 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 6881 tgl                       351 GIC       17268 :     ncolumns = tupdesc->natts;
                                352                 : 
                                353                 :     /* Build a temporary HeapTuple control structure */
                                354           17268 :     tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
                                355           17268 :     ItemPointerSetInvalid(&(tuple.t_self));
                                356           17268 :     tuple.t_tableOid = InvalidOid;
 6881 tgl                       357 CBC       17268 :     tuple.t_data = rec;
                                358                 : 
 6881 tgl                       359 ECB             :     /*
                                360                 :      * We arrange to look up the needed I/O info just once per series of
                                361                 :      * calls, assuming the record type doesn't change underneath us.
                                362                 :      */
 6881 tgl                       363 GIC       17268 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
                                364           17268 :     if (my_extra == NULL ||
 6881 tgl                       365 CBC       15001 :         my_extra->ncolumns != ncolumns)
                                366                 :     {
 6881 tgl                       367 GIC        4558 :         fcinfo->flinfo->fn_extra =
                                368            2279 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                369                 :                                offsetof(RecordIOData, columns) +
 2970                           370            2279 :                                ncolumns * sizeof(ColumnIOData));
 6881                           371            2279 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 6881 tgl                       372 CBC        2279 :         my_extra->record_type = InvalidOid;
 6881 tgl                       373 GIC        2279 :         my_extra->record_typmod = 0;
                                374                 :     }
 6881 tgl                       375 ECB             : 
 6881 tgl                       376 CBC       17268 :     if (my_extra->record_type != tupType ||
                                377           14989 :         my_extra->record_typmod != tupTypmod)
 6881 tgl                       378 ECB             :     {
 6881 tgl                       379 GIC       51321 :         MemSet(my_extra, 0,
                                380                 :                offsetof(RecordIOData, columns) +
 2970 tgl                       381 ECB             :                ncolumns * sizeof(ColumnIOData));
 6881 tgl                       382 CBC        2299 :         my_extra->record_type = tupType;
                                383            2299 :         my_extra->record_typmod = tupTypmod;
                                384            2299 :         my_extra->ncolumns = ncolumns;
                                385                 :     }
                                386                 : 
 6881 tgl                       387 GIC       17268 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
 5271                           388           17268 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
                                389                 : 
 6881 tgl                       390 ECB             :     /* Break down the tuple into fields */
 5271 tgl                       391 CBC       17268 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
 6881 tgl                       392 ECB             : 
                                393                 :     /* And build the result string */
 6881 tgl                       394 CBC       17268 :     initStringInfo(&buf);
 6881 tgl                       395 ECB             : 
 6881 tgl                       396 GIC       17268 :     appendStringInfoChar(&buf, '(');
 6881 tgl                       397 ECB             : 
 6881 tgl                       398 CBC      105281 :     for (i = 0; i < ncolumns; i++)
 6881 tgl                       399 ECB             :     {
 2058 andres                    400 CBC       88013 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
 6881 tgl                       401 GIC       88013 :         ColumnIOData *column_info = &my_extra->columns[i];
 2058 andres                    402           88013 :         Oid         column_type = att->atttypid;
 3799 tgl                       403 ECB             :         Datum       attr;
 6797 bruce                     404                 :         char       *value;
                                405                 :         char       *tmp;
                                406                 :         bool        nq;
                                407                 : 
                                408                 :         /* Ignore dropped columns in datatype */
 2058 andres                    409 CBC       88013 :         if (att->attisdropped)
 6822 tgl                       410             246 :             continue;
 6822 tgl                       411 ECB             : 
 6822 tgl                       412 GIC       87767 :         if (needComma)
 6881                           413           70502 :             appendStringInfoChar(&buf, ',');
 6822 tgl                       414 CBC       87767 :         needComma = true;
 6881 tgl                       415 ECB             : 
 5271 tgl                       416 GIC       87767 :         if (nulls[i])
                                417                 :         {
 6881 tgl                       418 ECB             :             /* emit nothing... */
 6881 tgl                       419 GIC        2017 :             continue;
                                420                 :         }
 6881 tgl                       421 ECB             : 
                                422                 :         /*
                                423                 :          * Convert the column value to text
                                424                 :          */
 6881 tgl                       425 CBC       85750 :         if (column_info->column_type != column_type)
                                426                 :         {
                                427            5245 :             getTypeOutputInfo(column_type,
 6881 tgl                       428 ECB             :                               &column_info->typiofunc,
 3799                           429                 :                               &column_info->typisvarlena);
 6881 tgl                       430 GIC        5245 :             fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
                                431            5245 :                           fcinfo->flinfo->fn_mcxt);
                                432            5245 :             column_info->column_type = column_type;
                                433                 :         }
                                434                 : 
 3444                           435           85750 :         attr = values[i];
 3799 tgl                       436 CBC       85750 :         value = OutputFunctionCall(&column_info->proc, attr);
 6881 tgl                       437 ECB             : 
                                438                 :         /* Detect whether we need double quotes for this value */
 6881 tgl                       439 CBC       85750 :         nq = (value[0] == '\0');    /* force quotes for empty string */
                                440        54127965 :         for (tmp = value; *tmp; tmp++)
 6881 tgl                       441 ECB             :         {
 6881 tgl                       442 GIC    54068033 :             char        ch = *tmp;
 6881 tgl                       443 ECB             : 
 6881 tgl                       444 GIC    54068033 :             if (ch == '"' || ch == '\\' ||
                                445        54067592 :                 ch == '(' || ch == ')' || ch == ',' ||
 6881 tgl                       446 CBC    54067143 :                 isspace((unsigned char) ch))
                                447                 :             {
 6881 tgl                       448 GIC       25818 :                 nq = true;
                                449           25818 :                 break;
                                450                 :             }
                                451                 :         }
 6881 tgl                       452 ECB             : 
                                453                 :         /* And emit the string */
 6881 tgl                       454 CBC       85750 :         if (nq)
 3799 tgl                       455 GIC       25820 :             appendStringInfoCharMacro(&buf, '"');
 6881                           456        54388799 :         for (tmp = value; *tmp; tmp++)
 6881 tgl                       457 ECB             :         {
 6881 tgl                       458 CBC    54303049 :             char        ch = *tmp;
 6881 tgl                       459 ECB             : 
 6881 tgl                       460 GIC    54303049 :             if (ch == '"' || ch == '\\')
 3799                           461             480 :                 appendStringInfoCharMacro(&buf, ch);
 3799 tgl                       462 CBC    54303049 :             appendStringInfoCharMacro(&buf, ch);
 6881 tgl                       463 ECB             :         }
 6881 tgl                       464 GIC       85750 :         if (nq)
 3799                           465           25820 :             appendStringInfoCharMacro(&buf, '"');
 6881 tgl                       466 ECB             :     }
                                467                 : 
 6881 tgl                       468 GIC       17268 :     appendStringInfoChar(&buf, ')');
 6881 tgl                       469 ECB             : 
 6881 tgl                       470 GIC       17268 :     pfree(values);
 6881 tgl                       471 CBC       17268 :     pfree(nulls);
 6141                           472           17268 :     ReleaseTupleDesc(tupdesc);
 6881 tgl                       473 ECB             : 
 6881 tgl                       474 GIC       17268 :     PG_RETURN_CSTRING(buf.data);
 6947 tgl                       475 ECB             : }
                                476                 : 
                                477                 : /*
                                478                 :  * record_recv      - binary input routine for any composite type.
                                479                 :  */
                                480                 : Datum
 6947 tgl                       481 LBC           0 : record_recv(PG_FUNCTION_ARGS)
 6947 tgl                       482 ECB             : {
 6881 tgl                       483 LBC           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
 6881 tgl                       484 UIC           0 :     Oid         tupType = PG_GETARG_OID(1);
 2788 tgl                       485 LBC           0 :     int32       tupTypmod = PG_GETARG_INT32(2);
                                486                 :     HeapTupleHeader result;
 6881 tgl                       487 ECB             :     TupleDesc   tupdesc;
                                488                 :     HeapTuple   tuple;
                                489                 :     RecordIOData *my_extra;
                                490                 :     int         ncolumns;
 6822                           491                 :     int         usercols;
                                492                 :     int         validcols;
                                493                 :     int         i;
                                494                 :     Datum      *values;
 5271                           495                 :     bool       *nulls;
                                496                 : 
 2743 noah                      497 LBC           0 :     check_stack_depth();        /* recurses for record-type columns */
 2743 noah                      498 ECB             : 
 6881 tgl                       499                 :     /*
                                500                 :      * Give a friendly error message if we did not get enough info to identify
 2788                           501                 :      * the target record type.  (lookup_rowtype_tupdesc would fail anyway, but
                                502                 :      * with a non-user-friendly message.)  In ordinary SQL usage, we'll get -1
                                503                 :      * for typmod, since composite types and RECORD have no type modifiers at
                                504                 :      * the SQL level, and thus must fail for RECORD.  However some callers can
                                505                 :      * supply a valid typmod, and then we can do something useful for RECORD.
                                506                 :      */
 2788 tgl                       507 UIC           0 :     if (tupType == RECORDOID && tupTypmod < 0)
 6881 tgl                       508 UBC           0 :         ereport(ERROR,
                                509                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 2118 tgl                       510 EUB             :                  errmsg("input of anonymous composite types is not implemented")));
 2788                           511                 : 
 6881 tgl                       512 UBC           0 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 6881 tgl                       513 UIC           0 :     ncolumns = tupdesc->natts;
                                514                 : 
                                515                 :     /*
                                516                 :      * We arrange to look up the needed I/O info just once per series of
                                517                 :      * calls, assuming the record type doesn't change underneath us.
                                518                 :      */
                                519               0 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
                                520               0 :     if (my_extra == NULL ||
                                521               0 :         my_extra->ncolumns != ncolumns)
                                522                 :     {
                                523               0 :         fcinfo->flinfo->fn_extra =
 6881 tgl                       524 UBC           0 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                525                 :                                offsetof(RecordIOData, columns) +
 2970 tgl                       526 UIC           0 :                                ncolumns * sizeof(ColumnIOData));
 6881                           527               0 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
                                528               0 :         my_extra->record_type = InvalidOid;
                                529               0 :         my_extra->record_typmod = 0;
                                530                 :     }
                                531                 : 
                                532               0 :     if (my_extra->record_type != tupType ||
                                533               0 :         my_extra->record_typmod != tupTypmod)
 6881 tgl                       534 EUB             :     {
 6881 tgl                       535 UBC           0 :         MemSet(my_extra, 0,
                                536                 :                offsetof(RecordIOData, columns) +
                                537                 :                ncolumns * sizeof(ColumnIOData));
 6881 tgl                       538 UIC           0 :         my_extra->record_type = tupType;
 6881 tgl                       539 UBC           0 :         my_extra->record_typmod = tupTypmod;
                                540               0 :         my_extra->ncolumns = ncolumns;
                                541                 :     }
                                542                 : 
 6881 tgl                       543 UIC           0 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
 5271                           544               0 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
                                545                 : 
 6822 tgl                       546 EUB             :     /* Fetch number of columns user thinks it has */
 6822 tgl                       547 UBC           0 :     usercols = pq_getmsgint(buf, 4);
 6822 tgl                       548 EUB             : 
                                549                 :     /* Need to scan to count nondeleted columns */
 6822 tgl                       550 UBC           0 :     validcols = 0;
                                551               0 :     for (i = 0; i < ncolumns; i++)
                                552                 :     {
 2058 andres                    553               0 :         if (!TupleDescAttr(tupdesc, i)->attisdropped)
 6822 tgl                       554               0 :             validcols++;
 6822 tgl                       555 EUB             :     }
 6822 tgl                       556 UBC           0 :     if (usercols != validcols)
 6881 tgl                       557 UIC           0 :         ereport(ERROR,
                                558                 :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
 6881 tgl                       559 EUB             :                  errmsg("wrong number of columns: %d, expected %d",
 6822                           560                 :                         usercols, validcols)));
                                561                 : 
 6881                           562                 :     /* Process each column */
 6881 tgl                       563 UIC           0 :     for (i = 0; i < ncolumns; i++)
                                564                 :     {
 2058 andres                    565 UBC           0 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
 6881 tgl                       566               0 :         ColumnIOData *column_info = &my_extra->columns[i];
 2058 andres                    567               0 :         Oid         column_type = att->atttypid;
                                568                 :         Oid         coltypoid;
                                569                 :         int         itemlen;
 6214 tgl                       570 EUB             :         StringInfoData item_buf;
                                571                 :         StringInfo  bufptr;
                                572                 :         char        csave;
                                573                 : 
 6822                           574                 :         /* Ignore dropped columns in datatype, but fill with nulls */
 2058 andres                    575 UIC           0 :         if (att->attisdropped)
                                576                 :         {
 6822 tgl                       577 UBC           0 :             values[i] = (Datum) 0;
 5271                           578               0 :             nulls[i] = true;
 6822 tgl                       579 UIC           0 :             continue;
 6822 tgl                       580 EUB             :         }
                                581                 : 
                                582                 :         /* Check column type recorded in the data */
 6881 tgl                       583 UBC           0 :         coltypoid = pq_getmsgint(buf, sizeof(Oid));
  992 tgl                       584 EUB             : 
                                585                 :         /*
                                586                 :          * From a security standpoint, it doesn't matter whether the input's
                                587                 :          * column type matches what we expect: the column type's receive
                                588                 :          * function has to be robust enough to cope with invalid data.
                                589                 :          * However, from a user-friendliness standpoint, it's nicer to
                                590                 :          * complain about type mismatches than to throw "improper binary
                                591                 :          * format" errors.  But there's a problem: only built-in types have
                                592                 :          * OIDs that are stable enough to believe that a mismatch is a real
                                593                 :          * issue.  So complain only if both OIDs are in the built-in range.
                                594                 :          * Otherwise, carry on with the column type we "should" be getting.
                                595                 :          */
  992 tgl                       596 UIC           0 :         if (coltypoid != column_type &&
                                597               0 :             coltypoid < FirstGenbkiObjectId &&
                                598                 :             column_type < FirstGenbkiObjectId)
 6822                           599               0 :             ereport(ERROR,
                                600                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                                601                 :                      errmsg("binary data has type %u (%s) instead of expected %u (%s) in record column %d",
  992 tgl                       602 EUB             :                             coltypoid,
                                603                 :                             format_type_extended(coltypoid, -1,
                                604                 :                                                  FORMAT_TYPE_ALLOW_INVALID),
                                605                 :                             column_type,
                                606                 :                             format_type_extended(column_type, -1,
                                607                 :                                                  FORMAT_TYPE_ALLOW_INVALID),
                                608                 :                             i + 1)));
                                609                 : 
 6881                           610                 :         /* Get and check the item length */
 6881 tgl                       611 UIC           0 :         itemlen = pq_getmsgint(buf, 4);
                                612               0 :         if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
                                613               0 :             ereport(ERROR,
                                614                 :                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                                615                 :                      errmsg("insufficient data left in message")));
                                616                 : 
                                617               0 :         if (itemlen == -1)
                                618                 :         {
                                619                 :             /* -1 length means NULL */
 6214                           620               0 :             bufptr = NULL;
 5271                           621               0 :             nulls[i] = true;
 6214                           622               0 :             csave = 0;          /* keep compiler quiet */
 6881 tgl                       623 EUB             :         }
                                624                 :         else
                                625                 :         {
                                626                 :             /*
                                627                 :              * Rather than copying data around, we just set up a phony
                                628                 :              * StringInfo pointing to the correct portion of the input buffer.
                                629                 :              * We assume we can scribble on the input buffer so as to maintain
                                630                 :              * the convention that StringInfos have a trailing null.
                                631                 :              */
 6881 tgl                       632 UIC           0 :             item_buf.data = &buf->data[buf->cursor];
                                633               0 :             item_buf.maxlen = itemlen + 1;
                                634               0 :             item_buf.len = itemlen;
                                635               0 :             item_buf.cursor = 0;
                                636                 : 
                                637               0 :             buf->cursor += itemlen;
 6881 tgl                       638 EUB             : 
 6881 tgl                       639 UBC           0 :             csave = buf->data[buf->cursor];
                                640               0 :             buf->data[buf->cursor] = '\0';
                                641                 : 
 6214 tgl                       642 UIC           0 :             bufptr = &item_buf;
 5271                           643               0 :             nulls[i] = false;
 6214 tgl                       644 EUB             :         }
                                645                 : 
                                646                 :         /* Now call the column's receiveproc */
 6214 tgl                       647 UBC           0 :         if (column_info->column_type != column_type)
 6214 tgl                       648 EUB             :         {
 6214 tgl                       649 UBC           0 :             getTypeBinaryInputInfo(column_type,
                                650                 :                                    &column_info->typiofunc,
                                651                 :                                    &column_info->typioparam);
 6214 tgl                       652 UIC           0 :             fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
                                653               0 :                           fcinfo->flinfo->fn_mcxt);
                                654               0 :             column_info->column_type = column_type;
                                655                 :         }
                                656                 : 
                                657               0 :         values[i] = ReceiveFunctionCall(&column_info->proc,
                                658                 :                                         bufptr,
 6214 tgl                       659 EUB             :                                         column_info->typioparam,
 2058 andres                    660                 :                                         att->atttypmod);
 6214 tgl                       661                 : 
 6214 tgl                       662 UBC           0 :         if (bufptr)
                                663                 :         {
 6881 tgl                       664 EUB             :             /* Trouble if it didn't eat the whole buffer */
 6881 tgl                       665 UIC           0 :             if (item_buf.cursor != itemlen)
 6881 tgl                       666 UBC           0 :                 ereport(ERROR,
 6881 tgl                       667 EUB             :                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                                668                 :                          errmsg("improper binary format in record column %d",
 6385 bruce                     669                 :                                 i + 1)));
 6881 tgl                       670                 : 
 6881 tgl                       671 UIC           0 :             buf->data[buf->cursor] = csave;
                                672                 :         }
                                673                 :     }
 6881 tgl                       674 EUB             : 
 5271 tgl                       675 UIC           0 :     tuple = heap_form_tuple(tupdesc, values, nulls);
 6881 tgl                       676 EUB             : 
                                677                 :     /*
                                678                 :      * We cannot return tuple->t_data because heap_form_tuple allocates it as
 6385 bruce                     679                 :      * part of a larger chunk, and our caller may expect to be able to pfree
 3260                           680                 :      * our result.  So must copy the info into a new palloc chunk.
 6565 tgl                       681                 :      */
 6565 tgl                       682 UIC           0 :     result = (HeapTupleHeader) palloc(tuple->t_len);
                                683               0 :     memcpy(result, tuple->t_data, tuple->t_len);
 6565 tgl                       684 EUB             : 
 6565 tgl                       685 UIC           0 :     heap_freetuple(tuple);
 6881                           686               0 :     pfree(values);
                                687               0 :     pfree(nulls);
 6141                           688               0 :     ReleaseTupleDesc(tupdesc);
 6881 tgl                       689 EUB             : 
 6565 tgl                       690 UIC           0 :     PG_RETURN_HEAPTUPLEHEADER(result);
                                691                 : }
 6947 tgl                       692 EUB             : 
                                693                 : /*
                                694                 :  * record_send      - binary output routine for any composite type.
                                695                 :  */
                                696                 : Datum
 6947 tgl                       697 UIC           0 : record_send(PG_FUNCTION_ARGS)
 6947 tgl                       698 EUB             : {
 6881 tgl                       699 UIC           0 :     HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
                                700                 :     Oid         tupType;
                                701                 :     int32       tupTypmod;
 6881 tgl                       702 EUB             :     TupleDesc   tupdesc;
                                703                 :     HeapTupleData tuple;
                                704                 :     RecordIOData *my_extra;
                                705                 :     int         ncolumns;
                                706                 :     int         validcols;
                                707                 :     int         i;
                                708                 :     Datum      *values;
 5271                           709                 :     bool       *nulls;
 6881                           710                 :     StringInfoData buf;
                                711                 : 
 2743 noah                      712 UBC           0 :     check_stack_depth();        /* recurses for record-type columns */
 2743 noah                      713 EUB             : 
 6553 tgl                       714                 :     /* Extract type info from the tuple itself */
 6553 tgl                       715 UBC           0 :     tupType = HeapTupleHeaderGetTypeId(rec);
 6553 tgl                       716 UIC           0 :     tupTypmod = HeapTupleHeaderGetTypMod(rec);
 6881 tgl                       717 UBC           0 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 6881 tgl                       718 UIC           0 :     ncolumns = tupdesc->natts;
                                719                 : 
                                720                 :     /* Build a temporary HeapTuple control structure */
                                721               0 :     tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
                                722               0 :     ItemPointerSetInvalid(&(tuple.t_self));
                                723               0 :     tuple.t_tableOid = InvalidOid;
 6881 tgl                       724 UBC           0 :     tuple.t_data = rec;
                                725                 : 
 6881 tgl                       726 EUB             :     /*
                                727                 :      * We arrange to look up the needed I/O info just once per series of
                                728                 :      * calls, assuming the record type doesn't change underneath us.
                                729                 :      */
 6881 tgl                       730 UIC           0 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
                                731               0 :     if (my_extra == NULL ||
                                732               0 :         my_extra->ncolumns != ncolumns)
                                733                 :     {
                                734               0 :         fcinfo->flinfo->fn_extra =
                                735               0 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                736                 :                                offsetof(RecordIOData, columns) +
 2970                           737               0 :                                ncolumns * sizeof(ColumnIOData));
 6881                           738               0 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 6881 tgl                       739 UBC           0 :         my_extra->record_type = InvalidOid;
 6881 tgl                       740 UIC           0 :         my_extra->record_typmod = 0;
                                741                 :     }
 6881 tgl                       742 EUB             : 
 6881 tgl                       743 UBC           0 :     if (my_extra->record_type != tupType ||
                                744               0 :         my_extra->record_typmod != tupTypmod)
 6881 tgl                       745 EUB             :     {
 6881 tgl                       746 UIC           0 :         MemSet(my_extra, 0,
                                747                 :                offsetof(RecordIOData, columns) +
 2970 tgl                       748 EUB             :                ncolumns * sizeof(ColumnIOData));
 6881 tgl                       749 UBC           0 :         my_extra->record_type = tupType;
                                750               0 :         my_extra->record_typmod = tupTypmod;
                                751               0 :         my_extra->ncolumns = ncolumns;
                                752                 :     }
                                753                 : 
 6881 tgl                       754 UIC           0 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
 5271                           755               0 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
                                756                 : 
 6881 tgl                       757 EUB             :     /* Break down the tuple into fields */
 5271 tgl                       758 UBC           0 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
 6881 tgl                       759 EUB             : 
                                760                 :     /* And build the result string */
 6881 tgl                       761 UBC           0 :     pq_begintypsend(&buf);
 6881 tgl                       762 EUB             : 
                                763                 :     /* Need to scan to count nondeleted columns */
 6822 tgl                       764 UBC           0 :     validcols = 0;
                                765               0 :     for (i = 0; i < ncolumns; i++)
 6822 tgl                       766 EUB             :     {
 2058 andres                    767 UBC           0 :         if (!TupleDescAttr(tupdesc, i)->attisdropped)
 6822 tgl                       768 UIC           0 :             validcols++;
                                769                 :     }
 2006 andres                    770 UBC           0 :     pq_sendint32(&buf, validcols);
 6881 tgl                       771 EUB             : 
 6881 tgl                       772 UIC           0 :     for (i = 0; i < ncolumns; i++)
 6881 tgl                       773 EUB             :     {
 2058 andres                    774 UIC           0 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
 6881 tgl                       775               0 :         ColumnIOData *column_info = &my_extra->columns[i];
 2058 andres                    776 UBC           0 :         Oid         column_type = att->atttypid;
 3799 tgl                       777 EUB             :         Datum       attr;
 6881                           778                 :         bytea      *outputbytes;
                                779                 : 
                                780                 :         /* Ignore dropped columns in datatype */
 2058 andres                    781 UBC           0 :         if (att->attisdropped)
 6822 tgl                       782               0 :             continue;
                                783                 : 
 2006 andres                    784 UIC           0 :         pq_sendint32(&buf, column_type);
 6881 tgl                       785 EUB             : 
 5271 tgl                       786 UIC           0 :         if (nulls[i])
                                787                 :         {
 6881 tgl                       788 EUB             :             /* emit -1 data length to signify a NULL */
 2006 andres                    789 UIC           0 :             pq_sendint32(&buf, -1);
 6881 tgl                       790               0 :             continue;
 6881 tgl                       791 EUB             :         }
                                792                 : 
                                793                 :         /*
                                794                 :          * Convert the column value to binary
                                795                 :          */
 6881 tgl                       796 UIC           0 :         if (column_info->column_type != column_type)
 6881 tgl                       797 EUB             :         {
 6881 tgl                       798 UIC           0 :             getTypeBinaryOutputInfo(column_type,
 6881 tgl                       799 EUB             :                                     &column_info->typiofunc,
                                800                 :                                     &column_info->typisvarlena);
 6881 tgl                       801 UBC           0 :             fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
                                802               0 :                           fcinfo->flinfo->fn_mcxt);
                                803               0 :             column_info->column_type = column_type;
                                804                 :         }
                                805                 : 
 3444 tgl                       806 UIC           0 :         attr = values[i];
 3799                           807               0 :         outputbytes = SendFunctionCall(&column_info->proc, attr);
 2006 andres                    808 UBC           0 :         pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
 6881 tgl                       809               0 :         pq_sendbytes(&buf, VARDATA(outputbytes),
 6881 tgl                       810 UIC           0 :                      VARSIZE(outputbytes) - VARHDRSZ);
 6881 tgl                       811 EUB             :     }
                                812                 : 
 6881 tgl                       813 UBC           0 :     pfree(values);
 6881 tgl                       814 UIC           0 :     pfree(nulls);
 6141                           815               0 :     ReleaseTupleDesc(tupdesc);
 6947 tgl                       816 EUB             : 
 6881 tgl                       817 UBC           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
                                818                 : }
                                819                 : 
                                820                 : 
                                821                 : /*
                                822                 :  * record_cmp()
 5291 tgl                       823 EUB             :  * Internal comparison function for records.
                                824                 :  *
                                825                 :  * Returns -1, 0 or 1
                                826                 :  *
                                827                 :  * Do not assume that the two inputs are exactly the same record type;
                                828                 :  * for instance we might be comparing an anonymous ROW() construct against a
                                829                 :  * named composite type.  We will compare as long as they have the same number
                                830                 :  * of non-dropped columns of the same types.
                                831                 :  */
                                832                 : static int
 5291 tgl                       833 GBC        2242 : record_cmp(FunctionCallInfo fcinfo)
 5291 tgl                       834 EUB             : {
 5291 tgl                       835 GBC        2242 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
                                836            2242 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
                                837            2242 :     int         result = 0;
                                838                 :     Oid         tupType1;
                                839                 :     Oid         tupType2;
 5291 tgl                       840 EUB             :     int32       tupTypmod1;
                                841                 :     int32       tupTypmod2;
                                842                 :     TupleDesc   tupdesc1;
                                843                 :     TupleDesc   tupdesc2;
                                844                 :     HeapTupleData tuple1;
                                845                 :     HeapTupleData tuple2;
                                846                 :     int         ncolumns1;
                                847                 :     int         ncolumns2;
                                848                 :     RecordCompareData *my_extra;
                                849                 :     int         ncols;
                                850                 :     Datum      *values1;
                                851                 :     Datum      *values2;
                                852                 :     bool       *nulls1;
                                853                 :     bool       *nulls2;
                                854                 :     int         i1;
                                855                 :     int         i2;
                                856                 :     int         j;
                                857                 : 
 2743 noah                      858 GIC        2242 :     check_stack_depth();        /* recurses for record-type columns */
                                859                 : 
 5291 tgl                       860 ECB             :     /* Extract type info from the tuples */
 5291 tgl                       861 GIC        2242 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
 5291 tgl                       862 CBC        2242 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
                                863            2242 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
                                864            2242 :     ncolumns1 = tupdesc1->natts;
 5291 tgl                       865 GIC        2242 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
                                866            2242 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
                                867            2242 :     tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
                                868            2242 :     ncolumns2 = tupdesc2->natts;
                                869                 : 
                                870                 :     /* Build temporary HeapTuple control structures */
                                871            2242 :     tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
                                872            2242 :     ItemPointerSetInvalid(&(tuple1.t_self));
                                873            2242 :     tuple1.t_tableOid = InvalidOid;
                                874            2242 :     tuple1.t_data = record1;
                                875            2242 :     tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
                                876            2242 :     ItemPointerSetInvalid(&(tuple2.t_self));
                                877            2242 :     tuple2.t_tableOid = InvalidOid;
                                878            2242 :     tuple2.t_data = record2;
                                879                 : 
                                880                 :     /*
                                881                 :      * We arrange to look up the needed comparison info just once per series
                                882                 :      * of calls, assuming the record types don't change underneath us.
                                883                 :      */
                                884            2242 :     ncols = Max(ncolumns1, ncolumns2);
 5291 tgl                       885 CBC        2242 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
 5291 tgl                       886 GIC        2242 :     if (my_extra == NULL ||
                                887            2036 :         my_extra->ncolumns < ncols)
 5291 tgl                       888 ECB             :     {
 5291 tgl                       889 CBC         412 :         fcinfo->flinfo->fn_extra =
                                890             206 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 2970                           891             206 :                                offsetof(RecordCompareData, columns) +
 2970 tgl                       892 ECB             :                                ncols * sizeof(ColumnCompareData));
 5291 tgl                       893 CBC         206 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                                894             206 :         my_extra->ncolumns = ncols;
                                895             206 :         my_extra->record1_type = InvalidOid;
 5291 tgl                       896 GIC         206 :         my_extra->record1_typmod = 0;
                                897             206 :         my_extra->record2_type = InvalidOid;
 5291 tgl                       898 CBC         206 :         my_extra->record2_typmod = 0;
 5291 tgl                       899 ECB             :     }
                                900                 : 
 5291 tgl                       901 CBC        2242 :     if (my_extra->record1_type != tupType1 ||
                                902            2036 :         my_extra->record1_typmod != tupTypmod1 ||
                                903            2033 :         my_extra->record2_type != tupType2 ||
                                904            2033 :         my_extra->record2_typmod != tupTypmod2)
 5291 tgl                       905 ECB             :     {
 5291 tgl                       906 GIC         688 :         MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
                                907             209 :         my_extra->record1_type = tupType1;
                                908             209 :         my_extra->record1_typmod = tupTypmod1;
                                909             209 :         my_extra->record2_type = tupType2;
                                910             209 :         my_extra->record2_typmod = tupTypmod2;
 5291 tgl                       911 ECB             :     }
                                912                 : 
                                913                 :     /* Break down the tuples into fields */
 5291 tgl                       914 CBC        2242 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
 5291 tgl                       915 GIC        2242 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
 5291 tgl                       916 CBC        2242 :     heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
                                917            2242 :     values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
                                918            2242 :     nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
 5291 tgl                       919 GIC        2242 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 5291 tgl                       920 ECB             : 
                                921                 :     /*
                                922                 :      * Scan corresponding columns, allowing for dropped columns in different
 3260 bruce                     923                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
 5050                           924                 :      * the logical column index.
 5291 tgl                       925                 :      */
 5291 tgl                       926 GIC        2242 :     i1 = i2 = j = 0;
                                927            3395 :     while (i1 < ncolumns1 || i2 < ncolumns2)
 5291 tgl                       928 ECB             :     {
 2058 andres                    929                 :         Form_pg_attribute att1;
                                930                 :         Form_pg_attribute att2;
 5291 tgl                       931                 :         TypeCacheEntry *typentry;
                                932                 :         Oid         collation;
                                933                 : 
                                934                 :         /*
                                935                 :          * Skip dropped columns
                                936                 :          */
 2058 andres                    937 CBC        2998 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
                                938                 :         {
 5291 tgl                       939 UIC           0 :             i1++;
                                940               0 :             continue;
 5291 tgl                       941 ECB             :         }
 2058 andres                    942 CBC        2998 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
 5291 tgl                       943 ECB             :         {
 5291 tgl                       944 LBC           0 :             i2++;
                                945               0 :             continue;
 5291 tgl                       946 ECB             :         }
 5291 tgl                       947 GIC        2998 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
                                948                 :             break;              /* we'll deal with mismatch below loop */
                                949                 : 
 2058 andres                    950            2995 :         att1 = TupleDescAttr(tupdesc1, i1);
                                951            2995 :         att2 = TupleDescAttr(tupdesc2, i2);
                                952                 : 
 5291 tgl                       953 ECB             :         /*
                                954                 :          * Have two matching columns, they must be same type
                                955                 :          */
 2058 andres                    956 GIC        2995 :         if (att1->atttypid != att2->atttypid)
 5291 tgl                       957               3 :             ereport(ERROR,
                                958                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                                959                 :                      errmsg("cannot compare dissimilar column types %s and %s at record column %d",
                                960                 :                             format_type_be(att1->atttypid),
                                961                 :                             format_type_be(att2->atttypid),
                                962                 :                             j + 1)));
                                963                 : 
 4380 tgl                       964 ECB             :         /*
                                965                 :          * If they're not same collation, we don't complain here, but the
 4380 tgl                       966 EUB             :          * comparison function might.
                                967                 :          */
 2058 andres                    968 GIC        2992 :         collation = att1->attcollation;
 2058 andres                    969 CBC        2992 :         if (collation != att2->attcollation)
 4380 tgl                       970 UIC           0 :             collation = InvalidOid;
 4380 tgl                       971 EUB             : 
 5291                           972                 :         /*
                                973                 :          * Lookup the comparison function if not done already
 5291 tgl                       974 ECB             :          */
 5291 tgl                       975 GIC        2992 :         typentry = my_extra->columns[j].typentry;
                                976            2992 :         if (typentry == NULL ||
 2058 andres                    977 CBC        2643 :             typentry->type_id != att1->atttypid)
 5291 tgl                       978 ECB             :         {
 2058 andres                    979 GIC         349 :             typentry = lookup_type_cache(att1->atttypid,
                                980                 :                                          TYPECACHE_CMP_PROC_FINFO);
 5291 tgl                       981             349 :             if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
                                982               3 :                 ereport(ERROR,
 5291 tgl                       983 ECB             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
 2118                           984                 :                          errmsg("could not identify a comparison function for type %s",
                                985                 :                                 format_type_be(typentry->type_id))));
 5291 tgl                       986 GIC         346 :             my_extra->columns[j].typentry = typentry;
                                987                 :         }
                                988                 : 
                                989                 :         /*
                                990                 :          * We consider two NULLs equal; NULL > not-NULL.
                                991                 :          */
                                992            2989 :         if (!nulls1[i1] || !nulls2[i2])
                                993                 :         {
 1534 andres                    994            2984 :             LOCAL_FCINFO(locfcinfo, 2);
 3467 kgrittn                   995 ECB             :             int32       cmpresult;
                                996                 : 
 5291 tgl                       997 GBC        2984 :             if (nulls1[i1])
                                998                 :             {
                                999                 :                 /* arg1 is greater than arg2 */
 5291 tgl                      1000 GIC          12 :                 result = 1;
                               1001            1836 :                 break;
 5291 tgl                      1002 ECB             :             }
 5291 tgl                      1003 CBC        2972 :             if (nulls2[i2])
 5291 tgl                      1004 ECB             :             {
                               1005                 :                 /* arg1 is less than arg2 */
 5291 tgl                      1006 LBC           0 :                 result = -1;
 5291 tgl                      1007 UIC           0 :                 break;
 5291 tgl                      1008 ECB             :             }
                               1009                 : 
                               1010                 :             /* Compare the pair of elements */
 1534 andres                   1011 GIC        2972 :             InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
                               1012                 :                                      collation, NULL, NULL);
 1534 andres                   1013 CBC        2972 :             locfcinfo->args[0].value = values1[i1];
 1534 andres                   1014 GIC        2972 :             locfcinfo->args[0].isnull = false;
                               1015            2972 :             locfcinfo->args[1].value = values2[i2];
                               1016            2972 :             locfcinfo->args[1].isnull = false;
                               1017            2972 :             cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
                               1018                 : 
 1083 tgl                      1019 ECB             :             /* We don't expect comparison support functions to return null */
 1083 tgl                      1020 GIC        2972 :             Assert(!locfcinfo->isnull);
 1083 tgl                      1021 ECB             : 
 5291 tgl                      1022 GIC        2972 :             if (cmpresult < 0)
                               1023                 :             {
 5291 tgl                      1024 ECB             :                 /* arg1 is less than arg2 */
 5291 tgl                      1025 GIC         933 :                 result = -1;
                               1026             933 :                 break;
 5291 tgl                      1027 ECB             :             }
 5291 tgl                      1028 CBC        2039 :             else if (cmpresult > 0)
                               1029                 :             {
 5291 tgl                      1030 ECB             :                 /* arg1 is greater than arg2 */
 5291 tgl                      1031 GIC         891 :                 result = 1;
                               1032             891 :                 break;
 5291 tgl                      1033 EUB             :             }
                               1034                 :         }
                               1035                 : 
                               1036                 :         /* equal, so continue to next column */
 5291 tgl                      1037 GIC        1153 :         i1++, i2++, j++;
 5291 tgl                      1038 ECB             :     }
                               1039                 : 
                               1040                 :     /*
                               1041                 :      * If we didn't break out of the loop early, check for column count
 5050 bruce                    1042                 :      * mismatch.  (We do not report such mismatch if we found unequal column
                               1043                 :      * values; is that a feature or a bug?)
 5291 tgl                      1044                 :      */
 5291 tgl                      1045 GIC        2236 :     if (result == 0)
                               1046                 :     {
 5291 tgl                      1047 CBC         400 :         if (i1 != ncolumns1 || i2 != ncolumns2)
 5291 tgl                      1048 GIC           3 :             ereport(ERROR,
 5291 tgl                      1049 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1050                 :                      errmsg("cannot compare record types with different numbers of columns")));
                               1051                 :     }
                               1052                 : 
 5291 tgl                      1053 CBC        2233 :     pfree(values1);
 5291 tgl                      1054 GIC        2233 :     pfree(nulls1);
 5291 tgl                      1055 CBC        2233 :     pfree(values2);
 5291 tgl                      1056 GIC        2233 :     pfree(nulls2);
                               1057            2233 :     ReleaseTupleDesc(tupdesc1);
 5291 tgl                      1058 CBC        2233 :     ReleaseTupleDesc(tupdesc2);
 5291 tgl                      1059 ECB             : 
                               1060                 :     /* Avoid leaking memory when handed toasted input. */
 5291 tgl                      1061 GIC        2233 :     PG_FREE_IF_COPY(record1, 0);
                               1062            2233 :     PG_FREE_IF_COPY(record2, 1);
                               1063                 : 
 5291 tgl                      1064 CBC        2233 :     return result;
                               1065                 : }
                               1066                 : 
                               1067                 : /*
                               1068                 :  * record_eq :
                               1069                 :  *        compares two records for equality
                               1070                 :  * result :
                               1071                 :  *        returns true if the records are equal, false otherwise.
 5291 tgl                      1072 ECB             :  *
                               1073                 :  * Note: we do not use record_cmp here, since equality may be meaningful in
                               1074                 :  * datatypes that don't have a total ordering (and hence no btree support).
                               1075                 :  */
                               1076                 : Datum
 5291 tgl                      1077 GIC        1875 : record_eq(PG_FUNCTION_ARGS)
                               1078                 : {
                               1079            1875 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
 5291 tgl                      1080 CBC        1875 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
                               1081            1875 :     bool        result = true;
 5291 tgl                      1082 ECB             :     Oid         tupType1;
                               1083                 :     Oid         tupType2;
                               1084                 :     int32       tupTypmod1;
                               1085                 :     int32       tupTypmod2;
                               1086                 :     TupleDesc   tupdesc1;
                               1087                 :     TupleDesc   tupdesc2;
                               1088                 :     HeapTupleData tuple1;
                               1089                 :     HeapTupleData tuple2;
                               1090                 :     int         ncolumns1;
                               1091                 :     int         ncolumns2;
                               1092                 :     RecordCompareData *my_extra;
                               1093                 :     int         ncols;
                               1094                 :     Datum      *values1;
                               1095                 :     Datum      *values2;
                               1096                 :     bool       *nulls1;
                               1097                 :     bool       *nulls2;
                               1098                 :     int         i1;
                               1099                 :     int         i2;
                               1100                 :     int         j;
                               1101                 : 
 2743 noah                     1102 GIC        1875 :     check_stack_depth();        /* recurses for record-type columns */
                               1103                 : 
 5291 tgl                      1104 ECB             :     /* Extract type info from the tuples */
 5291 tgl                      1105 GIC        1875 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
 5291 tgl                      1106 CBC        1875 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
                               1107            1875 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
                               1108            1875 :     ncolumns1 = tupdesc1->natts;
 5291 tgl                      1109 GIC        1875 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
                               1110            1875 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
                               1111            1875 :     tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
                               1112            1875 :     ncolumns2 = tupdesc2->natts;
                               1113                 : 
                               1114                 :     /* Build temporary HeapTuple control structures */
                               1115            1875 :     tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
                               1116            1875 :     ItemPointerSetInvalid(&(tuple1.t_self));
                               1117            1875 :     tuple1.t_tableOid = InvalidOid;
                               1118            1875 :     tuple1.t_data = record1;
                               1119            1875 :     tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
                               1120            1875 :     ItemPointerSetInvalid(&(tuple2.t_self));
                               1121            1875 :     tuple2.t_tableOid = InvalidOid;
                               1122            1875 :     tuple2.t_data = record2;
                               1123                 : 
                               1124                 :     /*
                               1125                 :      * We arrange to look up the needed comparison info just once per series
                               1126                 :      * of calls, assuming the record types don't change underneath us.
                               1127                 :      */
                               1128            1875 :     ncols = Max(ncolumns1, ncolumns2);
 5291 tgl                      1129 CBC        1875 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
 5291 tgl                      1130 GIC        1875 :     if (my_extra == NULL ||
                               1131            1684 :         my_extra->ncolumns < ncols)
 5291 tgl                      1132 ECB             :     {
 5291 tgl                      1133 CBC         382 :         fcinfo->flinfo->fn_extra =
                               1134             191 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 2970                          1135             191 :                                offsetof(RecordCompareData, columns) +
 2970 tgl                      1136 ECB             :                                ncols * sizeof(ColumnCompareData));
 5291 tgl                      1137 CBC         191 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1138             191 :         my_extra->ncolumns = ncols;
                               1139             191 :         my_extra->record1_type = InvalidOid;
 5291 tgl                      1140 GIC         191 :         my_extra->record1_typmod = 0;
                               1141             191 :         my_extra->record2_type = InvalidOid;
 5291 tgl                      1142 CBC         191 :         my_extra->record2_typmod = 0;
 5291 tgl                      1143 ECB             :     }
                               1144                 : 
 5291 tgl                      1145 CBC        1875 :     if (my_extra->record1_type != tupType1 ||
                               1146            1684 :         my_extra->record1_typmod != tupTypmod1 ||
                               1147            1684 :         my_extra->record2_type != tupType2 ||
                               1148            1684 :         my_extra->record2_typmod != tupTypmod2)
 5291 tgl                      1149 ECB             :     {
 5291 tgl                      1150 GIC         592 :         MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
                               1151             191 :         my_extra->record1_type = tupType1;
                               1152             191 :         my_extra->record1_typmod = tupTypmod1;
                               1153             191 :         my_extra->record2_type = tupType2;
                               1154             191 :         my_extra->record2_typmod = tupTypmod2;
 5291 tgl                      1155 ECB             :     }
                               1156                 : 
                               1157                 :     /* Break down the tuples into fields */
 5291 tgl                      1158 CBC        1875 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
 5291 tgl                      1159 GIC        1875 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
 5291 tgl                      1160 CBC        1875 :     heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
                               1161            1875 :     values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
                               1162            1875 :     nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
 5291 tgl                      1163 GIC        1875 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 5291 tgl                      1164 ECB             : 
                               1165                 :     /*
                               1166                 :      * Scan corresponding columns, allowing for dropped columns in different
 3260 bruce                    1167                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
 5050                          1168                 :      * the logical column index.
 5291 tgl                      1169                 :      */
 5291 tgl                      1170 GIC        1875 :     i1 = i2 = j = 0;
                               1171            3124 :     while (i1 < ncolumns1 || i2 < ncolumns2)
 5291 tgl                      1172 ECB             :     {
 1534 andres                   1173 CBC        2725 :         LOCAL_FCINFO(locfcinfo, 2);
 2058 andres                   1174 ECB             :         Form_pg_attribute att1;
                               1175                 :         Form_pg_attribute att2;
                               1176                 :         TypeCacheEntry *typentry;
 4380 tgl                      1177                 :         Oid         collation;
 5291                          1178                 :         bool        oprresult;
                               1179                 : 
                               1180                 :         /*
                               1181                 :          * Skip dropped columns
                               1182                 :          */
 2058 andres                   1183 GIC        2725 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
                               1184                 :         {
 5291 tgl                      1185 LBC           0 :             i1++;
                               1186               0 :             continue;
 5291 tgl                      1187 ECB             :         }
 2058 andres                   1188 CBC        2725 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
 5291 tgl                      1189 ECB             :         {
 5291 tgl                      1190 LBC           0 :             i2++;
 5291 tgl                      1191 UIC           0 :             continue;
                               1192                 :         }
 5291 tgl                      1193 GIC        2725 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
                               1194                 :             break;              /* we'll deal with mismatch below loop */
                               1195                 : 
 2058 andres                   1196            2722 :         att1 = TupleDescAttr(tupdesc1, i1);
 2058 andres                   1197 CBC        2722 :         att2 = TupleDescAttr(tupdesc2, i2);
 2058 andres                   1198 ECB             : 
                               1199                 :         /*
 5291 tgl                      1200                 :          * Have two matching columns, they must be same type
                               1201                 :          */
 2058 andres                   1202 GIC        2722 :         if (att1->atttypid != att2->atttypid)
 5291 tgl                      1203               6 :             ereport(ERROR,
                               1204                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1205                 :                      errmsg("cannot compare dissimilar column types %s and %s at record column %d",
                               1206                 :                             format_type_be(att1->atttypid),
                               1207                 :                             format_type_be(att2->atttypid),
                               1208                 :                             j + 1)));
                               1209                 : 
 4380 tgl                      1210 ECB             :         /*
                               1211                 :          * If they're not same collation, we don't complain here, but the
 4380 tgl                      1212 EUB             :          * equality function might.
                               1213                 :          */
 2058 andres                   1214 GIC        2716 :         collation = att1->attcollation;
 2058 andres                   1215 CBC        2716 :         if (collation != att2->attcollation)
 4380 tgl                      1216 UIC           0 :             collation = InvalidOid;
 4380 tgl                      1217 EUB             : 
 5291                          1218                 :         /*
                               1219                 :          * Lookup the equality function if not done already
 5291 tgl                      1220 ECB             :          */
 5291 tgl                      1221 GIC        2716 :         typentry = my_extra->columns[j].typentry;
                               1222            2716 :         if (typentry == NULL ||
 2058 andres                   1223 CBC        2336 :             typentry->type_id != att1->atttypid)
 5291 tgl                      1224 ECB             :         {
 2058 andres                   1225 GIC         380 :             typentry = lookup_type_cache(att1->atttypid,
                               1226                 :                                          TYPECACHE_EQ_OPR_FINFO);
 5291 tgl                      1227             380 :             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
                               1228               3 :                 ereport(ERROR,
 5291 tgl                      1229 ECB             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
 2118                          1230                 :                          errmsg("could not identify an equality operator for type %s",
                               1231                 :                                 format_type_be(typentry->type_id))));
 5291 tgl                      1232 GIC         377 :             my_extra->columns[j].typentry = typentry;
                               1233                 :         }
                               1234                 : 
                               1235                 :         /*
                               1236                 :          * We consider two NULLs equal; NULL > not-NULL.
                               1237                 :          */
                               1238            2713 :         if (!nulls1[i1] || !nulls2[i2])
                               1239                 :         {
                               1240            2588 :             if (nulls1[i1] || nulls2[i2])
 5291 tgl                      1241 ECB             :             {
 5291 tgl                      1242 CBC           3 :                 result = false;
 5291 tgl                      1243 GBC           3 :                 break;
                               1244                 :             }
                               1245                 : 
                               1246                 :             /* Compare the pair of elements */
 1534 andres                   1247 GIC        2585 :             InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
 4380 tgl                      1248 ECB             :                                      collation, NULL, NULL);
 1534 andres                   1249 CBC        2585 :             locfcinfo->args[0].value = values1[i1];
                               1250            2585 :             locfcinfo->args[0].isnull = false;
 1534 andres                   1251 GIC        2585 :             locfcinfo->args[1].value = values2[i2];
 1534 andres                   1252 CBC        2585 :             locfcinfo->args[1].isnull = false;
 1534 andres                   1253 GIC        2585 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
 1083 tgl                      1254 CBC        2585 :             if (locfcinfo->isnull || !oprresult)
 5291 tgl                      1255 ECB             :             {
 5291 tgl                      1256 GIC        1461 :                 result = false;
                               1257            1461 :                 break;
                               1258                 :             }
 5291 tgl                      1259 ECB             :         }
                               1260                 : 
                               1261                 :         /* equal, so continue to next column */
 5291 tgl                      1262 GIC        1249 :         i1++, i2++, j++;
                               1263                 :     }
                               1264                 : 
 5291 tgl                      1265 ECB             :     /*
                               1266                 :      * If we didn't break out of the loop early, check for column count
 5050 bruce                    1267                 :      * mismatch.  (We do not report such mismatch if we found unequal column
                               1268                 :      * values; is that a feature or a bug?)
 5291 tgl                      1269                 :      */
 5291 tgl                      1270 CBC        1866 :     if (result)
                               1271                 :     {
 5291 tgl                      1272 GIC         402 :         if (i1 != ncolumns1 || i2 != ncolumns2)
                               1273               3 :             ereport(ERROR,
 5291 tgl                      1274 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1275                 :                      errmsg("cannot compare record types with different numbers of columns")));
                               1276                 :     }
                               1277                 : 
 5291 tgl                      1278 CBC        1863 :     pfree(values1);
                               1279            1863 :     pfree(nulls1);
                               1280            1863 :     pfree(values2);
                               1281            1863 :     pfree(nulls2);
 5291 tgl                      1282 GIC        1863 :     ReleaseTupleDesc(tupdesc1);
 5291 tgl                      1283 CBC        1863 :     ReleaseTupleDesc(tupdesc2);
 5291 tgl                      1284 ECB             : 
                               1285                 :     /* Avoid leaking memory when handed toasted input. */
 5291 tgl                      1286 GIC        1863 :     PG_FREE_IF_COPY(record1, 0);
                               1287            1863 :     PG_FREE_IF_COPY(record2, 1);
                               1288                 : 
 5291 tgl                      1289 CBC        1863 :     PG_RETURN_BOOL(result);
                               1290                 : }
                               1291                 : 
                               1292                 : Datum
 5291 tgl                      1293 GIC          27 : record_ne(PG_FUNCTION_ARGS)
                               1294                 : {
                               1295              27 :     PG_RETURN_BOOL(!DatumGetBool(record_eq(fcinfo)));
                               1296                 : }
 5291 tgl                      1297 ECB             : 
                               1298                 : Datum
 5291 tgl                      1299 CBC          18 : record_lt(PG_FUNCTION_ARGS)
 5291 tgl                      1300 ECB             : {
 5291 tgl                      1301 GIC          18 :     PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
                               1302                 : }
                               1303                 : 
                               1304                 : Datum
 5291 tgl                      1305 CBC           6 : record_gt(PG_FUNCTION_ARGS)
 5291 tgl                      1306 ECB             : {
 5291 tgl                      1307 CBC           6 :     PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
 5291 tgl                      1308 ECB             : }
                               1309                 : 
                               1310                 : Datum
 5291 tgl                      1311 GIC           6 : record_le(PG_FUNCTION_ARGS)
                               1312                 : {
 5291 tgl                      1313 CBC           6 :     PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
 5291 tgl                      1314 ECB             : }
                               1315                 : 
                               1316                 : Datum
 5291 tgl                      1317 GIC          21 : record_ge(PG_FUNCTION_ARGS)
                               1318                 : {
                               1319              21 :     PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
 5291 tgl                      1320 ECB             : }
                               1321                 : 
                               1322                 : Datum
 5291 tgl                      1323 GIC        2191 : btrecordcmp(PG_FUNCTION_ARGS)
                               1324                 : {
                               1325            2191 :     PG_RETURN_INT32(record_cmp(fcinfo));
 5291 tgl                      1326 ECB             : }
                               1327                 : 
 3469 kgrittn                  1328                 : 
                               1329                 : /*
                               1330                 :  * record_image_cmp :
                               1331                 :  * Internal byte-oriented comparison function for records.
                               1332                 :  *
                               1333                 :  * Returns -1, 0 or 1
                               1334                 :  *
                               1335                 :  * Note: The normal concepts of "equality" do not apply here; different
                               1336                 :  * representation of values considered to be equal are not considered to be
                               1337                 :  * identical.  As an example, for the citext type 'A' and 'a' are equal, but
                               1338                 :  * they are not identical.
                               1339                 :  */
 3467                          1340                 : static int
 3467 kgrittn                  1341 GIC         426 : record_image_cmp(FunctionCallInfo fcinfo)
                               1342                 : {
 3469                          1343             426 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
 3469 kgrittn                  1344 CBC         426 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
 3467 kgrittn                  1345 GIC         426 :     int         result = 0;
 3469 kgrittn                  1346 ECB             :     Oid         tupType1;
                               1347                 :     Oid         tupType2;
                               1348                 :     int32       tupTypmod1;
                               1349                 :     int32       tupTypmod2;
                               1350                 :     TupleDesc   tupdesc1;
                               1351                 :     TupleDesc   tupdesc2;
                               1352                 :     HeapTupleData tuple1;
                               1353                 :     HeapTupleData tuple2;
                               1354                 :     int         ncolumns1;
                               1355                 :     int         ncolumns2;
                               1356                 :     RecordCompareData *my_extra;
                               1357                 :     int         ncols;
                               1358                 :     Datum      *values1;
                               1359                 :     Datum      *values2;
                               1360                 :     bool       *nulls1;
                               1361                 :     bool       *nulls2;
                               1362                 :     int         i1;
                               1363                 :     int         i2;
                               1364                 :     int         j;
                               1365                 : 
                               1366                 :     /* Extract type info from the tuples */
 3469 kgrittn                  1367 GIC         426 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
 3469 kgrittn                  1368 CBC         426 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
 3469 kgrittn                  1369 GIC         426 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
 3469 kgrittn                  1370 CBC         426 :     ncolumns1 = tupdesc1->natts;
                               1371             426 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
                               1372             426 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
 3469 kgrittn                  1373 GIC         426 :     tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
                               1374             426 :     ncolumns2 = tupdesc2->natts;
                               1375                 : 
                               1376                 :     /* Build temporary HeapTuple control structures */
                               1377             426 :     tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
                               1378             426 :     ItemPointerSetInvalid(&(tuple1.t_self));
                               1379             426 :     tuple1.t_tableOid = InvalidOid;
                               1380             426 :     tuple1.t_data = record1;
                               1381             426 :     tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
                               1382             426 :     ItemPointerSetInvalid(&(tuple2.t_self));
                               1383             426 :     tuple2.t_tableOid = InvalidOid;
                               1384             426 :     tuple2.t_data = record2;
                               1385                 : 
                               1386                 :     /*
                               1387                 :      * We arrange to look up the needed comparison info just once per series
                               1388                 :      * of calls, assuming the record types don't change underneath us.
                               1389                 :      */
                               1390             426 :     ncols = Max(ncolumns1, ncolumns2);
                               1391             426 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1392             426 :     if (my_extra == NULL ||
                               1393             303 :         my_extra->ncolumns < ncols)
 3469 kgrittn                  1394 ECB             :     {
 3469 kgrittn                  1395 CBC         246 :         fcinfo->flinfo->fn_extra =
                               1396             123 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 2970 tgl                      1397             123 :                                offsetof(RecordCompareData, columns) +
 2970 tgl                      1398 ECB             :                                ncols * sizeof(ColumnCompareData));
 3469 kgrittn                  1399 CBC         123 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1400             123 :         my_extra->ncolumns = ncols;
                               1401             123 :         my_extra->record1_type = InvalidOid;
 3469 kgrittn                  1402 GIC         123 :         my_extra->record1_typmod = 0;
                               1403             123 :         my_extra->record2_type = InvalidOid;
 3469 kgrittn                  1404 CBC         123 :         my_extra->record2_typmod = 0;
 3469 kgrittn                  1405 ECB             :     }
                               1406                 : 
 3469 kgrittn                  1407 CBC         426 :     if (my_extra->record1_type != tupType1 ||
                               1408             303 :         my_extra->record1_typmod != tupTypmod1 ||
                               1409             303 :         my_extra->record2_type != tupType2 ||
                               1410             303 :         my_extra->record2_typmod != tupTypmod2)
 3469 kgrittn                  1411 ECB             :     {
 3469 kgrittn                  1412 GIC         438 :         MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
                               1413             123 :         my_extra->record1_type = tupType1;
                               1414             123 :         my_extra->record1_typmod = tupTypmod1;
                               1415             123 :         my_extra->record2_type = tupType2;
                               1416             123 :         my_extra->record2_typmod = tupTypmod2;
 3469 kgrittn                  1417 ECB             :     }
                               1418                 : 
                               1419                 :     /* Break down the tuples into fields */
 3469 kgrittn                  1420 CBC         426 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
 3469 kgrittn                  1421 GIC         426 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
 3469 kgrittn                  1422 CBC         426 :     heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
                               1423             426 :     values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
                               1424             426 :     nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
 3469 kgrittn                  1425 GIC         426 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 3469 kgrittn                  1426 ECB             : 
                               1427                 :     /*
                               1428                 :      * Scan corresponding columns, allowing for dropped columns in different
 3260 bruce                    1429                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
 3469 kgrittn                  1430                 :      * the logical column index.
                               1431                 :      */
 3469 kgrittn                  1432 GIC         426 :     i1 = i2 = j = 0;
                               1433             833 :     while (i1 < ncolumns1 || i2 < ncolumns2)
 3469 kgrittn                  1434 ECB             :     {
 2058 andres                   1435                 :         Form_pg_attribute att1;
                               1436                 :         Form_pg_attribute att2;
                               1437                 : 
                               1438                 :         /*
 3469 kgrittn                  1439                 :          * Skip dropped columns
                               1440                 :          */
 2058 andres                   1441 CBC         749 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
 3469 kgrittn                  1442 ECB             :         {
 3469 kgrittn                  1443 LBC           0 :             i1++;
 3469 kgrittn                  1444 UIC           0 :             continue;
                               1445                 :         }
 2058 andres                   1446 GIC         749 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
 3469 kgrittn                  1447 ECB             :         {
 3469 kgrittn                  1448 LBC           0 :             i2++;
                               1449               0 :             continue;
 3469 kgrittn                  1450 ECB             :         }
 3469 kgrittn                  1451 CBC         749 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
 3469 kgrittn                  1452 ECB             :             break;              /* we'll deal with mismatch below loop */
                               1453                 : 
 2058 andres                   1454 GIC         746 :         att1 = TupleDescAttr(tupdesc1, i1);
                               1455             746 :         att2 = TupleDescAttr(tupdesc2, i2);
                               1456                 : 
                               1457                 :         /*
                               1458                 :          * Have two matching columns, they must be same type
 3469 kgrittn                  1459 ECB             :          */
 2058 andres                   1460 CBC         746 :         if (att1->atttypid != att2->atttypid)
 3469 kgrittn                  1461 GIC           3 :             ereport(ERROR,
                               1462                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1463                 :                      errmsg("cannot compare dissimilar column types %s and %s at record column %d",
                               1464                 :                             format_type_be(att1->atttypid),
                               1465                 :                             format_type_be(att2->atttypid),
                               1466                 :                             j + 1)));
                               1467                 : 
 3260 bruce                    1468 ECB             :         /*
                               1469                 :          * The same type should have the same length (or both should be
 3260 bruce                    1470 EUB             :          * variable).
                               1471                 :          */
 2058 andres                   1472 GIC         743 :         Assert(att1->attlen == att2->attlen);
 3467 kgrittn                  1473 ECB             : 
                               1474                 :         /*
 3469 kgrittn                  1475 EUB             :          * We consider two NULLs equal; NULL > not-NULL.
                               1476                 :          */
 3469 kgrittn                  1477 GIC         743 :         if (!nulls1[i1] || !nulls2[i2])
 3469 kgrittn                  1478 ECB             :         {
 3456 tgl                      1479 GIC         743 :             int         cmpresult = 0;
                               1480                 : 
 3469 kgrittn                  1481 CBC         743 :             if (nulls1[i1])
 3469 kgrittn                  1482 ECB             :             {
                               1483                 :                 /* arg1 is greater than arg2 */
 3469 kgrittn                  1484 UIC           0 :                 result = 1;
                               1485               0 :                 break;
                               1486                 :             }
 3469 kgrittn                  1487 CBC         743 :             if (nulls2[i2])
 3469 kgrittn                  1488 ECB             :             {
                               1489                 :                 /* arg1 is less than arg2 */
 3469 kgrittn                  1490 UIC           0 :                 result = -1;
                               1491               0 :                 break;
                               1492                 :             }
                               1493                 : 
                               1494                 :             /* Compare the pair of elements */
 1252 peter                    1495 GIC         743 :             if (att1->attbyval)
                               1496                 :             {
                               1497             489 :                 if (values1[i1] != values2[i2])
                               1498             256 :                     cmpresult = (values1[i1] < values2[i2]) ? -1 : 1;
 1252 peter                    1499 ECB             :             }
 1252 peter                    1500 GIC         254 :             else if (att1->attlen > 0)
                               1501                 :             {
                               1502              18 :                 cmpresult = memcmp(DatumGetPointer(values1[i1]),
                               1503              18 :                                    DatumGetPointer(values2[i2]),
 1252 peter                    1504 CBC          18 :                                    att1->attlen);
                               1505                 :             }
                               1506             236 :             else if (att1->attlen == -1)
                               1507                 :             {
 3469 kgrittn                  1508 ECB             :                 Size        len1,
                               1509                 :                             len2;
                               1510                 :                 struct varlena *arg1val;
 3260 bruce                    1511 EUB             :                 struct varlena *arg2val;
 3469 kgrittn                  1512                 : 
 3469 kgrittn                  1513 GIC         236 :                 len1 = toast_raw_datum_size(values1[i1]);
 3469 kgrittn                  1514 CBC         236 :                 len2 = toast_raw_datum_size(values2[i2]);
 3469 kgrittn                  1515 GIC         236 :                 arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
                               1516             236 :                 arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
 3469 kgrittn                  1517 EUB             : 
 3469 kgrittn                  1518 GBC         236 :                 cmpresult = memcmp(VARDATA_ANY(arg1val),
 3469 kgrittn                  1519 GIC         236 :                                    VARDATA_ANY(arg2val),
 3467                          1520             236 :                                    Min(len1, len2) - VARHDRSZ);
 3469                          1521             236 :                 if ((cmpresult == 0) && (len1 != len2))
 3469 kgrittn                  1522 CBC           3 :                     cmpresult = (len1 < len2) ? -1 : 1;
                               1523                 : 
                               1524             236 :                 if ((Pointer) arg1val != (Pointer) values1[i1])
 3469 kgrittn                  1525 LBC           0 :                     pfree(arg1val);
 3469 kgrittn                  1526 GIC         236 :                 if ((Pointer) arg2val != (Pointer) values2[i2])
 3469 kgrittn                  1527 LBC           0 :                     pfree(arg2val);
                               1528                 :             }
 3469 kgrittn                  1529 ECB             :             else
 1252 peter                    1530 LBC           0 :                 elog(ERROR, "unexpected attlen: %d", att1->attlen);
 3469 kgrittn                  1531 ECB             : 
 3469 kgrittn                  1532 GIC         743 :             if (cmpresult < 0)
 3469 kgrittn                  1533 ECB             :             {
                               1534                 :                 /* arg1 is less than arg2 */
 3469 kgrittn                  1535 GIC         184 :                 result = -1;
                               1536             184 :                 break;
                               1537                 :             }
                               1538             559 :             else if (cmpresult > 0)
                               1539                 :             {
 3469 kgrittn                  1540 ECB             :                 /* arg1 is greater than arg2 */
 3469 kgrittn                  1541 CBC         152 :                 result = 1;
                               1542             152 :                 break;
 3469 kgrittn                  1543 ECB             :             }
                               1544                 :         }
                               1545                 : 
                               1546                 :         /* equal, so continue to next column */
 3469 kgrittn                  1547 CBC         407 :         i1++, i2++, j++;
 3469 kgrittn                  1548 ECB             :     }
                               1549                 : 
                               1550                 :     /*
                               1551                 :      * If we didn't break out of the loop early, check for column count
 3469 kgrittn                  1552 EUB             :      * mismatch.  (We do not report such mismatch if we found unequal column
 3469 kgrittn                  1553 ECB             :      * values; is that a feature or a bug?)
 3469 kgrittn                  1554 EUB             :      */
 3469 kgrittn                  1555 GIC         423 :     if (result == 0)
                               1556                 :     {
 3469 kgrittn                  1557 GBC          87 :         if (i1 != ncolumns1 || i2 != ncolumns2)
 3469 kgrittn                  1558 GIC           3 :             ereport(ERROR,
 3469 kgrittn                  1559 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1560                 :                      errmsg("cannot compare record types with different numbers of columns")));
                               1561                 :     }
                               1562                 : 
 3469 kgrittn                  1563 CBC         420 :     pfree(values1);
 3469 kgrittn                  1564 GIC         420 :     pfree(nulls1);
 3469 kgrittn                  1565 CBC         420 :     pfree(values2);
 3469 kgrittn                  1566 GIC         420 :     pfree(nulls2);
                               1567             420 :     ReleaseTupleDesc(tupdesc1);
 3469 kgrittn                  1568 CBC         420 :     ReleaseTupleDesc(tupdesc2);
 3469 kgrittn                  1569 ECB             : 
                               1570                 :     /* Avoid leaking memory when handed toasted input. */
 3469 kgrittn                  1571 GIC         420 :     PG_FREE_IF_COPY(record1, 0);
                               1572             420 :     PG_FREE_IF_COPY(record2, 1);
                               1573                 : 
 3469 kgrittn                  1574 CBC         420 :     return result;
                               1575                 : }
                               1576                 : 
                               1577                 : /*
                               1578                 :  * record_image_eq :
                               1579                 :  *        compares two records for identical contents, based on byte images
                               1580                 :  * result :
                               1581                 :  *        returns true if the records are identical, false otherwise.
 3469 kgrittn                  1582 ECB             :  *
                               1583                 :  * Note: we do not use record_image_cmp here, since we can avoid
                               1584                 :  * de-toasting for unequal lengths this way.
                               1585                 :  */
                               1586                 : Datum
 3469 kgrittn                  1587 GIC         130 : record_image_eq(PG_FUNCTION_ARGS)
                               1588                 : {
                               1589             130 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
 3469 kgrittn                  1590 CBC         130 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
                               1591             130 :     bool        result = true;
 3469 kgrittn                  1592 ECB             :     Oid         tupType1;
                               1593                 :     Oid         tupType2;
                               1594                 :     int32       tupTypmod1;
                               1595                 :     int32       tupTypmod2;
                               1596                 :     TupleDesc   tupdesc1;
                               1597                 :     TupleDesc   tupdesc2;
                               1598                 :     HeapTupleData tuple1;
                               1599                 :     HeapTupleData tuple2;
                               1600                 :     int         ncolumns1;
                               1601                 :     int         ncolumns2;
                               1602                 :     RecordCompareData *my_extra;
                               1603                 :     int         ncols;
                               1604                 :     Datum      *values1;
                               1605                 :     Datum      *values2;
                               1606                 :     bool       *nulls1;
                               1607                 :     bool       *nulls2;
                               1608                 :     int         i1;
                               1609                 :     int         i2;
                               1610                 :     int         j;
                               1611                 : 
                               1612                 :     /* Extract type info from the tuples */
 3469 kgrittn                  1613 GIC         130 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
 3469 kgrittn                  1614 CBC         130 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
 3469 kgrittn                  1615 GIC         130 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
 3469 kgrittn                  1616 CBC         130 :     ncolumns1 = tupdesc1->natts;
                               1617             130 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
                               1618             130 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
 3469 kgrittn                  1619 GIC         130 :     tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
                               1620             130 :     ncolumns2 = tupdesc2->natts;
                               1621                 : 
                               1622                 :     /* Build temporary HeapTuple control structures */
                               1623             130 :     tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
                               1624             130 :     ItemPointerSetInvalid(&(tuple1.t_self));
                               1625             130 :     tuple1.t_tableOid = InvalidOid;
                               1626             130 :     tuple1.t_data = record1;
                               1627             130 :     tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
                               1628             130 :     ItemPointerSetInvalid(&(tuple2.t_self));
                               1629             130 :     tuple2.t_tableOid = InvalidOid;
                               1630             130 :     tuple2.t_data = record2;
                               1631                 : 
                               1632                 :     /*
                               1633                 :      * We arrange to look up the needed comparison info just once per series
                               1634                 :      * of calls, assuming the record types don't change underneath us.
                               1635                 :      */
                               1636             130 :     ncols = Max(ncolumns1, ncolumns2);
                               1637             130 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1638             130 :     if (my_extra == NULL ||
                               1639              65 :         my_extra->ncolumns < ncols)
 3469 kgrittn                  1640 ECB             :     {
 3469 kgrittn                  1641 CBC         130 :         fcinfo->flinfo->fn_extra =
                               1642              65 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 2970 tgl                      1643              65 :                                offsetof(RecordCompareData, columns) +
 2970 tgl                      1644 ECB             :                                ncols * sizeof(ColumnCompareData));
 3469 kgrittn                  1645 CBC          65 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1646              65 :         my_extra->ncolumns = ncols;
                               1647              65 :         my_extra->record1_type = InvalidOid;
 3469 kgrittn                  1648 GIC          65 :         my_extra->record1_typmod = 0;
                               1649              65 :         my_extra->record2_type = InvalidOid;
 3469 kgrittn                  1650 CBC          65 :         my_extra->record2_typmod = 0;
 3469 kgrittn                  1651 ECB             :     }
                               1652                 : 
 3469 kgrittn                  1653 CBC         130 :     if (my_extra->record1_type != tupType1 ||
                               1654              65 :         my_extra->record1_typmod != tupTypmod1 ||
                               1655              65 :         my_extra->record2_type != tupType2 ||
                               1656              65 :         my_extra->record2_typmod != tupTypmod2)
 3469 kgrittn                  1657 ECB             :     {
 3469 kgrittn                  1658 GIC         212 :         MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
                               1659              65 :         my_extra->record1_type = tupType1;
                               1660              65 :         my_extra->record1_typmod = tupTypmod1;
                               1661              65 :         my_extra->record2_type = tupType2;
                               1662              65 :         my_extra->record2_typmod = tupTypmod2;
 3469 kgrittn                  1663 ECB             :     }
                               1664                 : 
                               1665                 :     /* Break down the tuples into fields */
 3469 kgrittn                  1666 CBC         130 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
 3469 kgrittn                  1667 GIC         130 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
 3469 kgrittn                  1668 CBC         130 :     heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
                               1669             130 :     values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
                               1670             130 :     nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
 3469 kgrittn                  1671 GIC         130 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
 3469 kgrittn                  1672 ECB             : 
                               1673                 :     /*
                               1674                 :      * Scan corresponding columns, allowing for dropped columns in different
 3260 bruce                    1675                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
 3469 kgrittn                  1676                 :      * the logical column index.
                               1677                 :      */
 3469 kgrittn                  1678 GIC         130 :     i1 = i2 = j = 0;
                               1679             494 :     while (i1 < ncolumns1 || i2 < ncolumns2)
 3469 kgrittn                  1680 ECB             :     {
 2058 andres                   1681                 :         Form_pg_attribute att1;
                               1682                 :         Form_pg_attribute att2;
                               1683                 : 
                               1684                 :         /*
 3469 kgrittn                  1685                 :          * Skip dropped columns
                               1686                 :          */
 2058 andres                   1687 CBC         399 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
 3469 kgrittn                  1688 ECB             :         {
 3469 kgrittn                  1689 LBC           0 :             i1++;
 3469 kgrittn                  1690 UIC           0 :             continue;
                               1691                 :         }
 2058 andres                   1692 GIC         399 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
 3469 kgrittn                  1693 ECB             :         {
 3469 kgrittn                  1694 LBC           0 :             i2++;
                               1695               0 :             continue;
 3469 kgrittn                  1696 ECB             :         }
 3469 kgrittn                  1697 CBC         399 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
 3469 kgrittn                  1698 ECB             :             break;              /* we'll deal with mismatch below loop */
                               1699                 : 
 2058 andres                   1700 GIC         396 :         att1 = TupleDescAttr(tupdesc1, i1);
                               1701             396 :         att2 = TupleDescAttr(tupdesc2, i2);
                               1702                 : 
                               1703                 :         /*
                               1704                 :          * Have two matching columns, they must be same type
 3469 kgrittn                  1705 ECB             :          */
 2058 andres                   1706 CBC         396 :         if (att1->atttypid != att2->atttypid)
 3469 kgrittn                  1707 GIC           3 :             ereport(ERROR,
                               1708                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1709                 :                      errmsg("cannot compare dissimilar column types %s and %s at record column %d",
                               1710                 :                             format_type_be(att1->atttypid),
                               1711                 :                             format_type_be(att2->atttypid),
                               1712                 :                             j + 1)));
                               1713                 : 
 3469 kgrittn                  1714 ECB             :         /*
                               1715                 :          * We consider two NULLs equal; NULL > not-NULL.
 3469 kgrittn                  1716 EUB             :          */
 3469 kgrittn                  1717 GBC         393 :         if (!nulls1[i1] || !nulls2[i2])
                               1718                 :         {
 3469 kgrittn                  1719 CBC         387 :             if (nulls1[i1] || nulls2[i2])
                               1720                 :             {
 3469 kgrittn                  1721 UBC           0 :                 result = false;
                               1722               0 :                 break;
                               1723                 :             }
 3469 kgrittn                  1724 ECB             : 
                               1725                 :             /* Compare the pair of elements */
 1483 peter                    1726 GIC         387 :             result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen);
 3469 kgrittn                  1727 CBC         387 :             if (!result)
                               1728              29 :                 break;
                               1729                 :         }
                               1730                 : 
                               1731                 :         /* equal, so continue to next column */
 3469 kgrittn                  1732 GIC         364 :         i1++, i2++, j++;
 3469 kgrittn                  1733 ECB             :     }
                               1734                 : 
                               1735                 :     /*
                               1736                 :      * If we didn't break out of the loop early, check for column count
                               1737                 :      * mismatch.  (We do not report such mismatch if we found unequal column
                               1738                 :      * values; is that a feature or a bug?)
                               1739                 :      */
 3469 kgrittn                  1740 GIC         127 :     if (result)
                               1741                 :     {
                               1742              98 :         if (i1 != ncolumns1 || i2 != ncolumns2)
                               1743               3 :             ereport(ERROR,
 3469 kgrittn                  1744 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                               1745                 :                      errmsg("cannot compare record types with different numbers of columns")));
                               1746                 :     }
                               1747                 : 
 3469 kgrittn                  1748 GBC         124 :     pfree(values1);
                               1749             124 :     pfree(nulls1);
 3469 kgrittn                  1750 GIC         124 :     pfree(values2);
                               1751             124 :     pfree(nulls2);
                               1752             124 :     ReleaseTupleDesc(tupdesc1);
 3469 kgrittn                  1753 CBC         124 :     ReleaseTupleDesc(tupdesc2);
 3469 kgrittn                  1754 ECB             : 
                               1755                 :     /* Avoid leaking memory when handed toasted input. */
 3469 kgrittn                  1756 GIC         124 :     PG_FREE_IF_COPY(record1, 0);
                               1757             124 :     PG_FREE_IF_COPY(record2, 1);
                               1758                 : 
 3469 kgrittn                  1759 CBC         124 :     PG_RETURN_BOOL(result);
                               1760                 : }
                               1761                 : 
                               1762                 : Datum
 3469 kgrittn                  1763 GIC          24 : record_image_ne(PG_FUNCTION_ARGS)
                               1764                 : {
                               1765              24 :     PG_RETURN_BOOL(!DatumGetBool(record_image_eq(fcinfo)));
                               1766                 : }
 3469 kgrittn                  1767 ECB             : 
                               1768                 : Datum
 3469 kgrittn                  1769 CBC          36 : record_image_lt(PG_FUNCTION_ARGS)
 3469 kgrittn                  1770 ECB             : {
 3469 kgrittn                  1771 GIC          36 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
                               1772                 : }
                               1773                 : 
                               1774                 : Datum
 3469 kgrittn                  1775 CBC           9 : record_image_gt(PG_FUNCTION_ARGS)
 3469 kgrittn                  1776 ECB             : {
 3469 kgrittn                  1777 CBC           9 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
 3469 kgrittn                  1778 ECB             : }
                               1779                 : 
                               1780                 : Datum
 3469 kgrittn                  1781 GIC           6 : record_image_le(PG_FUNCTION_ARGS)
                               1782                 : {
 3469 kgrittn                  1783 CBC           6 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
 3469 kgrittn                  1784 ECB             : }
                               1785                 : 
                               1786                 : Datum
 3469 kgrittn                  1787 GIC           9 : record_image_ge(PG_FUNCTION_ARGS)
                               1788                 : {
                               1789               9 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
 3469 kgrittn                  1790 ECB             : }
                               1791                 : 
                               1792                 : Datum
 3469 kgrittn                  1793 GIC         366 : btrecordimagecmp(PG_FUNCTION_ARGS)
                               1794                 : {
                               1795             366 :     PG_RETURN_INT32(record_image_cmp(fcinfo));
 3469 kgrittn                  1796 ECB             : }
                               1797                 : 
  871 peter                    1798                 : 
                               1799                 : /*
                               1800                 :  * Row type hash functions
                               1801                 :  */
                               1802                 : 
                               1803                 : Datum
  871 peter                    1804 CBC         450 : hash_record(PG_FUNCTION_ARGS)
                               1805                 : {
  871 peter                    1806 GIC         450 :     HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
                               1807             450 :     uint32      result = 0;
  871 peter                    1808 ECB             :     Oid         tupType;
                               1809                 :     int32       tupTypmod;
                               1810                 :     TupleDesc   tupdesc;
                               1811                 :     HeapTupleData tuple;
                               1812                 :     int         ncolumns;
                               1813                 :     RecordCompareData *my_extra;
                               1814                 :     Datum      *values;
                               1815                 :     bool       *nulls;
                               1816                 : 
  871 peter                    1817 GIC         450 :     check_stack_depth();        /* recurses for record-type columns */
                               1818                 : 
                               1819                 :     /* Extract type info from tuple */
  871 peter                    1820 CBC         450 :     tupType = HeapTupleHeaderGetTypeId(record);
  871 peter                    1821 GIC         450 :     tupTypmod = HeapTupleHeaderGetTypMod(record);
  871 peter                    1822 CBC         450 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
  871 peter                    1823 GIC         450 :     ncolumns = tupdesc->natts;
                               1824                 : 
                               1825                 :     /* Build temporary HeapTuple control structure */
                               1826             450 :     tuple.t_len = HeapTupleHeaderGetDatumLength(record);
                               1827             450 :     ItemPointerSetInvalid(&(tuple.t_self));
                               1828             450 :     tuple.t_tableOid = InvalidOid;
                               1829             450 :     tuple.t_data = record;
                               1830                 : 
  871 peter                    1831 ECB             :     /*
                               1832                 :      * We arrange to look up the needed hashing info just once per series of
  697 tgl                      1833                 :      * calls, assuming the record type doesn't change underneath us.
  871 peter                    1834                 :      */
  871 peter                    1835 GIC         450 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1836             450 :     if (my_extra == NULL ||
                               1837             426 :         my_extra->ncolumns < ncolumns)
                               1838                 :     {
                               1839              48 :         fcinfo->flinfo->fn_extra =
                               1840              24 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                               1841              24 :                                offsetof(RecordCompareData, columns) +
                               1842                 :                                ncolumns * sizeof(ColumnCompareData));
                               1843              24 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
  871 peter                    1844 CBC          24 :         my_extra->ncolumns = ncolumns;
  871 peter                    1845 GIC          24 :         my_extra->record1_type = InvalidOid;
                               1846              24 :         my_extra->record1_typmod = 0;
  871 peter                    1847 ECB             :     }
                               1848                 : 
  871 peter                    1849 CBC         450 :     if (my_extra->record1_type != tupType ||
                               1850             426 :         my_extra->record1_typmod != tupTypmod)
                               1851                 :     {
  871 peter                    1852 GIC          75 :         MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
  871 peter                    1853 CBC          24 :         my_extra->record1_type = tupType;
                               1854              24 :         my_extra->record1_typmod = tupTypmod;
  871 peter                    1855 ECB             :     }
                               1856                 : 
                               1857                 :     /* Break down the tuple into fields */
  871 peter                    1858 GIC         450 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
                               1859             450 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
                               1860             450 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
                               1861                 : 
  871 peter                    1862 CBC        1365 :     for (int i = 0; i < ncolumns; i++)
  871 peter                    1863 ECB             :     {
                               1864                 :         Form_pg_attribute att;
                               1865                 :         TypeCacheEntry *typentry;
                               1866                 :         uint32      element_hash;
                               1867                 : 
  871 peter                    1868 CBC         918 :         att = TupleDescAttr(tupdesc, i);
                               1869                 : 
                               1870             918 :         if (att->attisdropped)
  871 peter                    1871 LBC           0 :             continue;
  871 peter                    1872 ECB             : 
                               1873                 :         /*
                               1874                 :          * Lookup the hash function if not done already
                               1875                 :          */
  871 peter                    1876 CBC         918 :         typentry = my_extra->columns[i].typentry;
                               1877             918 :         if (typentry == NULL ||
  871 peter                    1878 GIC         870 :             typentry->type_id != att->atttypid)
  871 peter                    1879 ECB             :         {
  871 peter                    1880 CBC          48 :             typentry = lookup_type_cache(att->atttypid,
  871 peter                    1881 ECB             :                                          TYPECACHE_HASH_PROC_FINFO);
  871 peter                    1882 GIC          48 :             if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
                               1883               3 :                 ereport(ERROR,
                               1884                 :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
  871 peter                    1885 ECB             :                          errmsg("could not identify a hash function for type %s",
                               1886                 :                                 format_type_be(typentry->type_id))));
  871 peter                    1887 CBC          45 :             my_extra->columns[i].typentry = typentry;
                               1888                 :         }
  871 peter                    1889 ECB             : 
                               1890                 :         /* Compute hash of element */
  871 peter                    1891 GIC         915 :         if (nulls[i])
                               1892                 :         {
  871 peter                    1893 UIC           0 :             element_hash = 0;
                               1894                 :         }
  871 peter                    1895 ECB             :         else
                               1896                 :         {
  871 peter                    1897 CBC         915 :             LOCAL_FCINFO(locfcinfo, 1);
  871 peter                    1898 EUB             : 
  871 peter                    1899 GIC         915 :             InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
                               1900                 :                                      att->attcollation, NULL, NULL);
                               1901             915 :             locfcinfo->args[0].value = values[i];
                               1902             915 :             locfcinfo->args[0].isnull = false;
  871 peter                    1903 CBC         915 :             element_hash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
  871 peter                    1904 ECB             : 
                               1905                 :             /* We don't expect hash support functions to return null */
  871 peter                    1906 GIC         915 :             Assert(!locfcinfo->isnull);
  871 peter                    1907 ECB             :         }
                               1908                 : 
                               1909                 :         /* see hash_array() */
  871 peter                    1910 CBC         915 :         result = (result << 5) - result + element_hash;
                               1911                 :     }
                               1912                 : 
  871 peter                    1913 GIC         447 :     pfree(values);
  871 peter                    1914 CBC         447 :     pfree(nulls);
  871 peter                    1915 GIC         447 :     ReleaseTupleDesc(tupdesc);
                               1916                 : 
                               1917                 :     /* Avoid leaking memory when handed toasted input. */
  871 peter                    1918 CBC         447 :     PG_FREE_IF_COPY(record, 0);
                               1919                 : 
  871 peter                    1920 GBC         447 :     PG_RETURN_UINT32(result);
                               1921                 : }
                               1922                 : 
                               1923                 : Datum
  871 peter                    1924 CBC          15 : hash_record_extended(PG_FUNCTION_ARGS)
                               1925                 : {
                               1926              15 :     HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
  871 peter                    1927 GIC          15 :     uint64      seed = PG_GETARG_INT64(1);
  871 peter                    1928 CBC          15 :     uint64      result = 0;
  871 peter                    1929 ECB             :     Oid         tupType;
                               1930                 :     int32       tupTypmod;
                               1931                 :     TupleDesc   tupdesc;
                               1932                 :     HeapTupleData tuple;
                               1933                 :     int         ncolumns;
                               1934                 :     RecordCompareData *my_extra;
                               1935                 :     Datum      *values;
                               1936                 :     bool       *nulls;
                               1937                 : 
  871 peter                    1938 GIC          15 :     check_stack_depth();        /* recurses for record-type columns */
                               1939                 : 
  871 peter                    1940 ECB             :     /* Extract type info from tuple */
  871 peter                    1941 CBC          15 :     tupType = HeapTupleHeaderGetTypeId(record);
                               1942              15 :     tupTypmod = HeapTupleHeaderGetTypMod(record);
  871 peter                    1943 GIC          15 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
                               1944              15 :     ncolumns = tupdesc->natts;
  871 peter                    1945 ECB             : 
                               1946                 :     /* Build temporary HeapTuple control structure */
  871 peter                    1947 CBC          15 :     tuple.t_len = HeapTupleHeaderGetDatumLength(record);
  871 peter                    1948 GIC          15 :     ItemPointerSetInvalid(&(tuple.t_self));
                               1949              15 :     tuple.t_tableOid = InvalidOid;
                               1950              15 :     tuple.t_data = record;
  871 peter                    1951 ECB             : 
                               1952                 :     /*
  697 tgl                      1953                 :      * We arrange to look up the needed hashing info just once per series of
                               1954                 :      * calls, assuming the record type doesn't change underneath us.
  871 peter                    1955                 :      */
  871 peter                    1956 GIC          15 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
                               1957              15 :     if (my_extra == NULL ||
  871 peter                    1958 UIC           0 :         my_extra->ncolumns < ncolumns)
                               1959                 :     {
  871 peter                    1960 GIC          30 :         fcinfo->flinfo->fn_extra =
                               1961              15 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                               1962              15 :                                offsetof(RecordCompareData, columns) +
                               1963                 :                                ncolumns * sizeof(ColumnCompareData));
                               1964              15 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
  871 peter                    1965 CBC          15 :         my_extra->ncolumns = ncolumns;
  871 peter                    1966 GIC          15 :         my_extra->record1_type = InvalidOid;
                               1967              15 :         my_extra->record1_typmod = 0;
  871 peter                    1968 ECB             :     }
                               1969                 : 
  871 peter                    1970 CBC          15 :     if (my_extra->record1_type != tupType ||
  871 peter                    1971 LBC           0 :         my_extra->record1_typmod != tupTypmod)
                               1972                 :     {
  871 peter                    1973 GIC          45 :         MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
  871 peter                    1974 CBC          15 :         my_extra->record1_type = tupType;
                               1975              15 :         my_extra->record1_typmod = tupTypmod;
  871 peter                    1976 ECB             :     }
                               1977                 : 
                               1978                 :     /* Break down the tuple into fields */
  871 peter                    1979 GIC          15 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
                               1980              15 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
                               1981              15 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
                               1982                 : 
  871 peter                    1983 CBC          39 :     for (int i = 0; i < ncolumns; i++)
  871 peter                    1984 ECB             :     {
  871 peter                    1985 EUB             :         Form_pg_attribute att;
                               1986                 :         TypeCacheEntry *typentry;
  871 peter                    1987 ECB             :         uint64      element_hash;
                               1988                 : 
  871 peter                    1989 CBC          27 :         att = TupleDescAttr(tupdesc, i);
                               1990                 : 
                               1991              27 :         if (att->attisdropped)
  871 peter                    1992 LBC           0 :             continue;
  871 peter                    1993 ECB             : 
                               1994                 :         /*
                               1995                 :          * Lookup the hash function if not done already
                               1996                 :          */
  871 peter                    1997 CBC          27 :         typentry = my_extra->columns[i].typentry;
  871 peter                    1998 GBC          27 :         if (typentry == NULL ||
  871 peter                    1999 UIC           0 :             typentry->type_id != att->atttypid)
  871 peter                    2000 ECB             :         {
  871 peter                    2001 CBC          27 :             typentry = lookup_type_cache(att->atttypid,
  871 peter                    2002 ECB             :                                          TYPECACHE_HASH_EXTENDED_PROC_FINFO);
  871 peter                    2003 GIC          27 :             if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
                               2004               3 :                 ereport(ERROR,
                               2005                 :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
  871 peter                    2006 ECB             :                          errmsg("could not identify an extended hash function for type %s",
                               2007                 :                                 format_type_be(typentry->type_id))));
  871 peter                    2008 CBC          24 :             my_extra->columns[i].typentry = typentry;
                               2009                 :         }
  871 peter                    2010 ECB             : 
                               2011                 :         /* Compute hash of element */
  871 peter                    2012 GIC          24 :         if (nulls[i])
                               2013                 :         {
  871 peter                    2014 UIC           0 :             element_hash = 0;
                               2015                 :         }
  871 peter                    2016 ECB             :         else
                               2017                 :         {
  871 peter                    2018 CBC          24 :             LOCAL_FCINFO(locfcinfo, 2);
  871 peter                    2019 EUB             : 
  871 peter                    2020 GIC          24 :             InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
                               2021                 :                                      att->attcollation, NULL, NULL);
                               2022              24 :             locfcinfo->args[0].value = values[i];
                               2023              24 :             locfcinfo->args[0].isnull = false;
  871 peter                    2024 CBC          24 :             locfcinfo->args[1].value = Int64GetDatum(seed);
                               2025              24 :             locfcinfo->args[0].isnull = false;
  871 peter                    2026 GBC          24 :             element_hash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
                               2027                 : 
  871 peter                    2028 ECB             :             /* We don't expect hash support functions to return null */
  871 peter                    2029 GIC          24 :             Assert(!locfcinfo->isnull);
  871 peter                    2030 ECB             :         }
                               2031                 : 
                               2032                 :         /* see hash_array_extended() */
  871 peter                    2033 GIC          24 :         result = (result << 5) - result + element_hash;
                               2034                 :     }
  871 peter                    2035 ECB             : 
  871 peter                    2036 GIC          12 :     pfree(values);
                               2037              12 :     pfree(nulls);
                               2038              12 :     ReleaseTupleDesc(tupdesc);
  871 peter                    2039 ECB             : 
                               2040                 :     /* Avoid leaking memory when handed toasted input. */
  871 peter                    2041 GBC          12 :     PG_FREE_IF_COPY(record, 0);
                               2042                 : 
  871 peter                    2043 GIC          12 :     PG_RETURN_UINT64(result);
                               2044                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a