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 15:15:32 Functions: 90.9 % 22 20 2 19 1 2 19
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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
      75 CBC         843 : record_in(PG_FUNCTION_ARGS)
      76                 : {
      77             843 :     char       *string = PG_GETARG_CSTRING(0);
      78             843 :     Oid         tupType = PG_GETARG_OID(1);
      79             843 :     int32       tupTypmod = PG_GETARG_INT32(2);
      80 GNC         843 :     Node       *escontext = fcinfo->context;
      81 ECB             :     HeapTupleHeader result;
      82                 :     TupleDesc   tupdesc;
      83                 :     HeapTuple   tuple;
      84                 :     RecordIOData *my_extra;
      85 GIC         843 :     bool        needComma = false;
      86 ECB             :     int         ncolumns;
      87                 :     int         i;
      88                 :     char       *ptr;
      89                 :     Datum      *values;
      90                 :     bool       *nulls;
      91                 :     StringInfoData buf;
      92                 : 
      93 GIC         843 :     check_stack_depth();        /* recurses for record-type columns */
      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                 :      */
     103 GIC         843 :     if (tupType == RECORDOID && tupTypmod < 0)
     104 UNC           0 :         ereturn(escontext, (Datum) 0,
     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                 :      */
     113 GIC         843 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     114 CBC         843 :     ncolumns = tupdesc->natts;
     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                 :      */
     120 GIC         843 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
     121 CBC         843 :     if (my_extra == NULL ||
     122             492 :         my_extra->ncolumns != ncolumns)
     123 ECB             :     {
     124 GIC         702 :         fcinfo->flinfo->fn_extra =
     125 CBC         351 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     126 ECB             :                                offsetof(RecordIOData, columns) +
     127 GIC         351 :                                ncolumns * sizeof(ColumnIOData));
     128 CBC         351 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
     129             351 :         my_extra->record_type = InvalidOid;
     130             351 :         my_extra->record_typmod = 0;
     131 ECB             :     }
     132                 : 
     133 GIC         843 :     if (my_extra->record_type != tupType ||
     134 CBC         492 :         my_extra->record_typmod != tupTypmod)
     135 ECB             :     {
     136 GIC        8413 :         MemSet(my_extra, 0,
     137 ECB             :                offsetof(RecordIOData, columns) +
     138                 :                ncolumns * sizeof(ColumnIOData));
     139 GIC         351 :         my_extra->record_type = tupType;
     140 CBC         351 :         my_extra->record_typmod = tupTypmod;
     141             351 :         my_extra->ncolumns = ncolumns;
     142 ECB             :     }
     143                 : 
     144 GIC         843 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
     145 CBC         843 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
     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                 :      */
     151 GIC         843 :     ptr = string;
     152 ECB             :     /* Allow leading whitespace */
     153 GIC         846 :     while (*ptr && isspace((unsigned char) *ptr))
     154 CBC           3 :         ptr++;
     155             843 :     if (*ptr++ != '(')
     156                 :     {
     157 GNC           5 :         errsave(escontext,
     158                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     159 ECB             :                  errmsg("malformed record literal: \"%s\"", string),
     160                 :                  errdetail("Missing left parenthesis.")));
     161 UNC           0 :         goto fail;
     162                 :     }
     163                 : 
     164 GIC         838 :     initStringInfo(&buf);
     165 EUB             : 
     166 GIC        3966 :     for (i = 0; i < ncolumns; i++)
     167                 :     {
     168 CBC        3147 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     169 GIC        3147 :         ColumnIOData *column_info = &my_extra->columns[i];
     170 CBC        3147 :         Oid         column_type = att->atttypid;
     171                 :         char       *column_data;
     172 ECB             : 
     173                 :         /* Ignore dropped columns in datatype, but fill with nulls */
     174 CBC        3147 :         if (att->attisdropped)
     175                 :         {
     176 GIC         165 :             values[i] = (Datum) 0;
     177             165 :             nulls[i] = true;
     178 CBC         165 :             continue;
     179                 :         }
     180 ECB             : 
     181 CBC        2982 :         if (needComma)
     182 ECB             :         {
     183                 :             /* Skip comma that separates prior field from this one */
     184 GIC        2144 :             if (*ptr == ',')
     185 CBC        2141 :                 ptr++;
     186                 :             else
     187                 :                 /* *ptr must be ')' */
     188                 :             {
     189 GNC           3 :                 errsave(escontext,
     190 ECB             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     191                 :                          errmsg("malformed record literal: \"%s\"", string),
     192                 :                          errdetail("Too few columns.")));
     193 UNC           0 :                 goto fail;
     194                 :             }
     195                 :         }
     196 ECB             : 
     197                 :         /* Check for null: completely empty input means null */
     198 GIC        2979 :         if (*ptr == ',' || *ptr == ')')
     199                 :         {
     200 GBC         235 :             column_data = NULL;
     201 GIC         235 :             nulls[i] = true;
     202                 :         }
     203                 :         else
     204                 :         {
     205 ECB             :             /* Extract string for this column */
     206 GIC        2744 :             bool        inquote = false;
     207 ECB             : 
     208 CBC        2744 :             resetStringInfo(&buf);
     209 GIC       15974 :             while (inquote || !(*ptr == ',' || *ptr == ')'))
     210                 :             {
     211           13233 :                 char        ch = *ptr++;
     212                 : 
     213 CBC       13233 :                 if (ch == '\0')
     214                 :                 {
     215 GNC           3 :                     errsave(escontext,
     216 ECB             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     217                 :                              errmsg("malformed record literal: \"%s\"",
     218                 :                                     string),
     219                 :                              errdetail("Unexpected end of input.")));
     220 GNC           3 :                     goto fail;
     221                 :                 }
     222 GIC       13230 :                 if (ch == '\\')
     223 ECB             :                 {
     224 GIC           3 :                     if (*ptr == '\0')
     225                 :                     {
     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                 :                     }
     233 CBC           3 :                     appendStringInfoChar(&buf, *ptr++);
     234                 :                 }
     235           13227 :                 else if (ch == '"')
     236                 :                 {
     237             883 :                     if (!inquote)
     238 GIC         426 :                         inquote = true;
     239 GBC         457 :                     else if (*ptr == '"')
     240                 :                     {
     241                 :                         /* doubled quote within quote sequence */
     242 GIC          31 :                         appendStringInfoChar(&buf, *ptr++);
     243                 :                     }
     244 EUB             :                     else
     245 GIC         426 :                         inquote = false;
     246 ECB             :                 }
     247                 :                 else
     248 CBC       12344 :                     appendStringInfoChar(&buf, ch);
     249                 :             }
     250 ECB             : 
     251 CBC        2741 :             column_data = buf.data;
     252            2741 :             nulls[i] = false;
     253                 :         }
     254                 : 
     255 ECB             :         /*
     256                 :          * Convert the column value
     257                 :          */
     258 CBC        2976 :         if (column_info->column_type != column_type)
     259                 :         {
     260 GIC         887 :             getTypeInputInfo(column_type,
     261 ECB             :                              &column_info->typiofunc,
     262                 :                              &column_info->typioparam);
     263 GIC         887 :             fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
     264 CBC         887 :                           fcinfo->flinfo->fn_mcxt);
     265             887 :             column_info->column_type = column_type;
     266                 :         }
     267                 : 
     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                 : 
     276 ECB             :         /*
     277                 :          * Prep for next column
     278                 :          */
     279 CBC        2963 :         needComma = true;
     280 ECB             :     }
     281                 : 
     282 GIC         819 :     if (*ptr++ != ')')
     283                 :     {
     284 GNC           3 :         errsave(escontext,
     285 ECB             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     286                 :                  errmsg("malformed record literal: \"%s\"", string),
     287                 :                  errdetail("Too many columns.")));
     288 UNC           0 :         goto fail;
     289                 :     }
     290                 :     /* Allow trailing whitespace */
     291 GIC         825 :     while (*ptr && isspace((unsigned char) *ptr))
     292 CBC           9 :         ptr++;
     293             816 :     if (*ptr)
     294                 :     {
     295 GNC           3 :         errsave(escontext,
     296                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     297                 :                  errmsg("malformed record literal: \"%s\"", string),
     298                 :                  errdetail("Junk after right parenthesis.")));
     299 UNC           0 :         goto fail;
     300                 :     }
     301 ECB             : 
     302 GIC         813 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     303                 : 
     304 ECB             :     /*
     305                 :      * We cannot return tuple->t_data because heap_form_tuple allocates it as
     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                 :      */
     309 GIC         813 :     result = (HeapTupleHeader) palloc(tuple->t_len);
     310 GBC         813 :     memcpy(result, tuple->t_data, tuple->t_len);
     311                 : 
     312 GIC         813 :     heap_freetuple(tuple);
     313 CBC         813 :     pfree(buf.data);
     314             813 :     pfree(values);
     315             813 :     pfree(nulls);
     316 GIC         813 :     ReleaseTupleDesc(tupdesc);
     317 ECB             : 
     318 GIC         813 :     PG_RETURN_HEAPTUPLEHEADER(result);
     319                 : 
     320                 :     /* exit here once we've done lookup_rowtype_tupdesc */
     321 GNC          12 : fail:
     322              12 :     ReleaseTupleDesc(tupdesc);
     323              12 :     PG_RETURN_NULL();
     324                 : }
     325                 : 
     326 EUB             : /*
     327                 :  * record_out       - output routine for any composite type.
     328                 :  */
     329 ECB             : Datum
     330 GIC       17268 : record_out(PG_FUNCTION_ARGS)
     331                 : {
     332           17268 :     HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
     333                 :     Oid         tupType;
     334                 :     int32       tupTypmod;
     335                 :     TupleDesc   tupdesc;
     336 ECB             :     HeapTupleData tuple;
     337                 :     RecordIOData *my_extra;
     338 GIC       17268 :     bool        needComma = false;
     339 ECB             :     int         ncolumns;
     340                 :     int         i;
     341                 :     Datum      *values;
     342                 :     bool       *nulls;
     343                 :     StringInfoData buf;
     344                 : 
     345 CBC       17268 :     check_stack_depth();        /* recurses for record-type columns */
     346                 : 
     347                 :     /* Extract type info from the tuple itself */
     348           17268 :     tupType = HeapTupleHeaderGetTypeId(rec);
     349           17268 :     tupTypmod = HeapTupleHeaderGetTypMod(rec);
     350           17268 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     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;
     357 CBC       17268 :     tuple.t_data = rec;
     358                 : 
     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                 :      */
     363 GIC       17268 :     my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
     364           17268 :     if (my_extra == NULL ||
     365 CBC       15001 :         my_extra->ncolumns != ncolumns)
     366                 :     {
     367 GIC        4558 :         fcinfo->flinfo->fn_extra =
     368            2279 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     369                 :                                offsetof(RecordIOData, columns) +
     370            2279 :                                ncolumns * sizeof(ColumnIOData));
     371            2279 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
     372 CBC        2279 :         my_extra->record_type = InvalidOid;
     373 GIC        2279 :         my_extra->record_typmod = 0;
     374                 :     }
     375 ECB             : 
     376 CBC       17268 :     if (my_extra->record_type != tupType ||
     377           14989 :         my_extra->record_typmod != tupTypmod)
     378 ECB             :     {
     379 GIC       51321 :         MemSet(my_extra, 0,
     380                 :                offsetof(RecordIOData, columns) +
     381 ECB             :                ncolumns * sizeof(ColumnIOData));
     382 CBC        2299 :         my_extra->record_type = tupType;
     383            2299 :         my_extra->record_typmod = tupTypmod;
     384            2299 :         my_extra->ncolumns = ncolumns;
     385                 :     }
     386                 : 
     387 GIC       17268 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
     388           17268 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
     389                 : 
     390 ECB             :     /* Break down the tuple into fields */
     391 CBC       17268 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
     392 ECB             : 
     393                 :     /* And build the result string */
     394 CBC       17268 :     initStringInfo(&buf);
     395 ECB             : 
     396 GIC       17268 :     appendStringInfoChar(&buf, '(');
     397 ECB             : 
     398 CBC      105281 :     for (i = 0; i < ncolumns; i++)
     399 ECB             :     {
     400 CBC       88013 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     401 GIC       88013 :         ColumnIOData *column_info = &my_extra->columns[i];
     402           88013 :         Oid         column_type = att->atttypid;
     403 ECB             :         Datum       attr;
     404                 :         char       *value;
     405                 :         char       *tmp;
     406                 :         bool        nq;
     407                 : 
     408                 :         /* Ignore dropped columns in datatype */
     409 CBC       88013 :         if (att->attisdropped)
     410             246 :             continue;
     411 ECB             : 
     412 GIC       87767 :         if (needComma)
     413           70502 :             appendStringInfoChar(&buf, ',');
     414 CBC       87767 :         needComma = true;
     415 ECB             : 
     416 GIC       87767 :         if (nulls[i])
     417                 :         {
     418 ECB             :             /* emit nothing... */
     419 GIC        2017 :             continue;
     420                 :         }
     421 ECB             : 
     422                 :         /*
     423                 :          * Convert the column value to text
     424                 :          */
     425 CBC       85750 :         if (column_info->column_type != column_type)
     426                 :         {
     427            5245 :             getTypeOutputInfo(column_type,
     428 ECB             :                               &column_info->typiofunc,
     429                 :                               &column_info->typisvarlena);
     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                 : 
     435           85750 :         attr = values[i];
     436 CBC       85750 :         value = OutputFunctionCall(&column_info->proc, attr);
     437 ECB             : 
     438                 :         /* Detect whether we need double quotes for this value */
     439 CBC       85750 :         nq = (value[0] == '\0');    /* force quotes for empty string */
     440        54127965 :         for (tmp = value; *tmp; tmp++)
     441 ECB             :         {
     442 GIC    54068033 :             char        ch = *tmp;
     443 ECB             : 
     444 GIC    54068033 :             if (ch == '"' || ch == '\\' ||
     445        54067592 :                 ch == '(' || ch == ')' || ch == ',' ||
     446 CBC    54067143 :                 isspace((unsigned char) ch))
     447                 :             {
     448 GIC       25818 :                 nq = true;
     449           25818 :                 break;
     450                 :             }
     451                 :         }
     452 ECB             : 
     453                 :         /* And emit the string */
     454 CBC       85750 :         if (nq)
     455 GIC       25820 :             appendStringInfoCharMacro(&buf, '"');
     456        54388799 :         for (tmp = value; *tmp; tmp++)
     457 ECB             :         {
     458 CBC    54303049 :             char        ch = *tmp;
     459 ECB             : 
     460 GIC    54303049 :             if (ch == '"' || ch == '\\')
     461             480 :                 appendStringInfoCharMacro(&buf, ch);
     462 CBC    54303049 :             appendStringInfoCharMacro(&buf, ch);
     463 ECB             :         }
     464 GIC       85750 :         if (nq)
     465           25820 :             appendStringInfoCharMacro(&buf, '"');
     466 ECB             :     }
     467                 : 
     468 GIC       17268 :     appendStringInfoChar(&buf, ')');
     469 ECB             : 
     470 GIC       17268 :     pfree(values);
     471 CBC       17268 :     pfree(nulls);
     472           17268 :     ReleaseTupleDesc(tupdesc);
     473 ECB             : 
     474 GIC       17268 :     PG_RETURN_CSTRING(buf.data);
     475 ECB             : }
     476                 : 
     477                 : /*
     478                 :  * record_recv      - binary input routine for any composite type.
     479                 :  */
     480                 : Datum
     481 LBC           0 : record_recv(PG_FUNCTION_ARGS)
     482 ECB             : {
     483 LBC           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     484 UIC           0 :     Oid         tupType = PG_GETARG_OID(1);
     485 LBC           0 :     int32       tupTypmod = PG_GETARG_INT32(2);
     486                 :     HeapTupleHeader result;
     487 ECB             :     TupleDesc   tupdesc;
     488                 :     HeapTuple   tuple;
     489                 :     RecordIOData *my_extra;
     490                 :     int         ncolumns;
     491                 :     int         usercols;
     492                 :     int         validcols;
     493                 :     int         i;
     494                 :     Datum      *values;
     495                 :     bool       *nulls;
     496                 : 
     497 LBC           0 :     check_stack_depth();        /* recurses for record-type columns */
     498 ECB             : 
     499                 :     /*
     500                 :      * Give a friendly error message if we did not get enough info to identify
     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                 :      */
     507 UIC           0 :     if (tupType == RECORDOID && tupTypmod < 0)
     508 UBC           0 :         ereport(ERROR,
     509                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     510 EUB             :                  errmsg("input of anonymous composite types is not implemented")));
     511                 : 
     512 UBC           0 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     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 =
     524 UBC           0 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     525                 :                                offsetof(RecordIOData, columns) +
     526 UIC           0 :                                ncolumns * sizeof(ColumnIOData));
     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)
     534 EUB             :     {
     535 UBC           0 :         MemSet(my_extra, 0,
     536                 :                offsetof(RecordIOData, columns) +
     537                 :                ncolumns * sizeof(ColumnIOData));
     538 UIC           0 :         my_extra->record_type = tupType;
     539 UBC           0 :         my_extra->record_typmod = tupTypmod;
     540               0 :         my_extra->ncolumns = ncolumns;
     541                 :     }
     542                 : 
     543 UIC           0 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
     544               0 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
     545                 : 
     546 EUB             :     /* Fetch number of columns user thinks it has */
     547 UBC           0 :     usercols = pq_getmsgint(buf, 4);
     548 EUB             : 
     549                 :     /* Need to scan to count nondeleted columns */
     550 UBC           0 :     validcols = 0;
     551               0 :     for (i = 0; i < ncolumns; i++)
     552                 :     {
     553               0 :         if (!TupleDescAttr(tupdesc, i)->attisdropped)
     554               0 :             validcols++;
     555 EUB             :     }
     556 UBC           0 :     if (usercols != validcols)
     557 UIC           0 :         ereport(ERROR,
     558                 :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     559 EUB             :                  errmsg("wrong number of columns: %d, expected %d",
     560                 :                         usercols, validcols)));
     561                 : 
     562                 :     /* Process each column */
     563 UIC           0 :     for (i = 0; i < ncolumns; i++)
     564                 :     {
     565 UBC           0 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     566               0 :         ColumnIOData *column_info = &my_extra->columns[i];
     567               0 :         Oid         column_type = att->atttypid;
     568                 :         Oid         coltypoid;
     569                 :         int         itemlen;
     570 EUB             :         StringInfoData item_buf;
     571                 :         StringInfo  bufptr;
     572                 :         char        csave;
     573                 : 
     574                 :         /* Ignore dropped columns in datatype, but fill with nulls */
     575 UIC           0 :         if (att->attisdropped)
     576                 :         {
     577 UBC           0 :             values[i] = (Datum) 0;
     578               0 :             nulls[i] = true;
     579 UIC           0 :             continue;
     580 EUB             :         }
     581                 : 
     582                 :         /* Check column type recorded in the data */
     583 UBC           0 :         coltypoid = pq_getmsgint(buf, sizeof(Oid));
     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                 :          */
     596 UIC           0 :         if (coltypoid != column_type &&
     597               0 :             coltypoid < FirstGenbkiObjectId &&
     598                 :             column_type < FirstGenbkiObjectId)
     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",
     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                 : 
     610                 :         /* Get and check the item length */
     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 */
     620               0 :             bufptr = NULL;
     621               0 :             nulls[i] = true;
     622               0 :             csave = 0;          /* keep compiler quiet */
     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                 :              */
     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;
     638 EUB             : 
     639 UBC           0 :             csave = buf->data[buf->cursor];
     640               0 :             buf->data[buf->cursor] = '\0';
     641                 : 
     642 UIC           0 :             bufptr = &item_buf;
     643               0 :             nulls[i] = false;
     644 EUB             :         }
     645                 : 
     646                 :         /* Now call the column's receiveproc */
     647 UBC           0 :         if (column_info->column_type != column_type)
     648 EUB             :         {
     649 UBC           0 :             getTypeBinaryInputInfo(column_type,
     650                 :                                    &column_info->typiofunc,
     651                 :                                    &column_info->typioparam);
     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,
     659 EUB             :                                         column_info->typioparam,
     660                 :                                         att->atttypmod);
     661                 : 
     662 UBC           0 :         if (bufptr)
     663                 :         {
     664 EUB             :             /* Trouble if it didn't eat the whole buffer */
     665 UIC           0 :             if (item_buf.cursor != itemlen)
     666 UBC           0 :                 ereport(ERROR,
     667 EUB             :                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     668                 :                          errmsg("improper binary format in record column %d",
     669                 :                                 i + 1)));
     670                 : 
     671 UIC           0 :             buf->data[buf->cursor] = csave;
     672                 :         }
     673                 :     }
     674 EUB             : 
     675 UIC           0 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     676 EUB             : 
     677                 :     /*
     678                 :      * We cannot return tuple->t_data because heap_form_tuple allocates it as
     679                 :      * part of a larger chunk, and our caller may expect to be able to pfree
     680                 :      * our result.  So must copy the info into a new palloc chunk.
     681                 :      */
     682 UIC           0 :     result = (HeapTupleHeader) palloc(tuple->t_len);
     683               0 :     memcpy(result, tuple->t_data, tuple->t_len);
     684 EUB             : 
     685 UIC           0 :     heap_freetuple(tuple);
     686               0 :     pfree(values);
     687               0 :     pfree(nulls);
     688               0 :     ReleaseTupleDesc(tupdesc);
     689 EUB             : 
     690 UIC           0 :     PG_RETURN_HEAPTUPLEHEADER(result);
     691                 : }
     692 EUB             : 
     693                 : /*
     694                 :  * record_send      - binary output routine for any composite type.
     695                 :  */
     696                 : Datum
     697 UIC           0 : record_send(PG_FUNCTION_ARGS)
     698 EUB             : {
     699 UIC           0 :     HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
     700                 :     Oid         tupType;
     701                 :     int32       tupTypmod;
     702 EUB             :     TupleDesc   tupdesc;
     703                 :     HeapTupleData tuple;
     704                 :     RecordIOData *my_extra;
     705                 :     int         ncolumns;
     706                 :     int         validcols;
     707                 :     int         i;
     708                 :     Datum      *values;
     709                 :     bool       *nulls;
     710                 :     StringInfoData buf;
     711                 : 
     712 UBC           0 :     check_stack_depth();        /* recurses for record-type columns */
     713 EUB             : 
     714                 :     /* Extract type info from the tuple itself */
     715 UBC           0 :     tupType = HeapTupleHeaderGetTypeId(rec);
     716 UIC           0 :     tupTypmod = HeapTupleHeaderGetTypMod(rec);
     717 UBC           0 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     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;
     724 UBC           0 :     tuple.t_data = rec;
     725                 : 
     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                 :      */
     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) +
     737               0 :                                ncolumns * sizeof(ColumnIOData));
     738               0 :         my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
     739 UBC           0 :         my_extra->record_type = InvalidOid;
     740 UIC           0 :         my_extra->record_typmod = 0;
     741                 :     }
     742 EUB             : 
     743 UBC           0 :     if (my_extra->record_type != tupType ||
     744               0 :         my_extra->record_typmod != tupTypmod)
     745 EUB             :     {
     746 UIC           0 :         MemSet(my_extra, 0,
     747                 :                offsetof(RecordIOData, columns) +
     748 EUB             :                ncolumns * sizeof(ColumnIOData));
     749 UBC           0 :         my_extra->record_type = tupType;
     750               0 :         my_extra->record_typmod = tupTypmod;
     751               0 :         my_extra->ncolumns = ncolumns;
     752                 :     }
     753                 : 
     754 UIC           0 :     values = (Datum *) palloc(ncolumns * sizeof(Datum));
     755               0 :     nulls = (bool *) palloc(ncolumns * sizeof(bool));
     756                 : 
     757 EUB             :     /* Break down the tuple into fields */
     758 UBC           0 :     heap_deform_tuple(&tuple, tupdesc, values, nulls);
     759 EUB             : 
     760                 :     /* And build the result string */
     761 UBC           0 :     pq_begintypsend(&buf);
     762 EUB             : 
     763                 :     /* Need to scan to count nondeleted columns */
     764 UBC           0 :     validcols = 0;
     765               0 :     for (i = 0; i < ncolumns; i++)
     766 EUB             :     {
     767 UBC           0 :         if (!TupleDescAttr(tupdesc, i)->attisdropped)
     768 UIC           0 :             validcols++;
     769                 :     }
     770 UBC           0 :     pq_sendint32(&buf, validcols);
     771 EUB             : 
     772 UIC           0 :     for (i = 0; i < ncolumns; i++)
     773 EUB             :     {
     774 UIC           0 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     775               0 :         ColumnIOData *column_info = &my_extra->columns[i];
     776 UBC           0 :         Oid         column_type = att->atttypid;
     777 EUB             :         Datum       attr;
     778                 :         bytea      *outputbytes;
     779                 : 
     780                 :         /* Ignore dropped columns in datatype */
     781 UBC           0 :         if (att->attisdropped)
     782               0 :             continue;
     783                 : 
     784 UIC           0 :         pq_sendint32(&buf, column_type);
     785 EUB             : 
     786 UIC           0 :         if (nulls[i])
     787                 :         {
     788 EUB             :             /* emit -1 data length to signify a NULL */
     789 UIC           0 :             pq_sendint32(&buf, -1);
     790               0 :             continue;
     791 EUB             :         }
     792                 : 
     793                 :         /*
     794                 :          * Convert the column value to binary
     795                 :          */
     796 UIC           0 :         if (column_info->column_type != column_type)
     797 EUB             :         {
     798 UIC           0 :             getTypeBinaryOutputInfo(column_type,
     799 EUB             :                                     &column_info->typiofunc,
     800                 :                                     &column_info->typisvarlena);
     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                 : 
     806 UIC           0 :         attr = values[i];
     807               0 :         outputbytes = SendFunctionCall(&column_info->proc, attr);
     808 UBC           0 :         pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
     809               0 :         pq_sendbytes(&buf, VARDATA(outputbytes),
     810 UIC           0 :                      VARSIZE(outputbytes) - VARHDRSZ);
     811 EUB             :     }
     812                 : 
     813 UBC           0 :     pfree(values);
     814 UIC           0 :     pfree(nulls);
     815               0 :     ReleaseTupleDesc(tupdesc);
     816 EUB             : 
     817 UBC           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     818                 : }
     819                 : 
     820                 : 
     821                 : /*
     822                 :  * record_cmp()
     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
     833 GBC        2242 : record_cmp(FunctionCallInfo fcinfo)
     834 EUB             : {
     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;
     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                 : 
     858 GIC        2242 :     check_stack_depth();        /* recurses for record-type columns */
     859                 : 
     860 ECB             :     /* Extract type info from the tuples */
     861 GIC        2242 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
     862 CBC        2242 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
     863            2242 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
     864            2242 :     ncolumns1 = tupdesc1->natts;
     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);
     885 CBC        2242 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
     886 GIC        2242 :     if (my_extra == NULL ||
     887            2036 :         my_extra->ncolumns < ncols)
     888 ECB             :     {
     889 CBC         412 :         fcinfo->flinfo->fn_extra =
     890             206 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     891             206 :                                offsetof(RecordCompareData, columns) +
     892 ECB             :                                ncols * sizeof(ColumnCompareData));
     893 CBC         206 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
     894             206 :         my_extra->ncolumns = ncols;
     895             206 :         my_extra->record1_type = InvalidOid;
     896 GIC         206 :         my_extra->record1_typmod = 0;
     897             206 :         my_extra->record2_type = InvalidOid;
     898 CBC         206 :         my_extra->record2_typmod = 0;
     899 ECB             :     }
     900                 : 
     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)
     905 ECB             :     {
     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;
     911 ECB             :     }
     912                 : 
     913                 :     /* Break down the tuples into fields */
     914 CBC        2242 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
     915 GIC        2242 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
     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));
     919 GIC        2242 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
     920 ECB             : 
     921                 :     /*
     922                 :      * Scan corresponding columns, allowing for dropped columns in different
     923                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
     924                 :      * the logical column index.
     925                 :      */
     926 GIC        2242 :     i1 = i2 = j = 0;
     927            3395 :     while (i1 < ncolumns1 || i2 < ncolumns2)
     928 ECB             :     {
     929                 :         Form_pg_attribute att1;
     930                 :         Form_pg_attribute att2;
     931                 :         TypeCacheEntry *typentry;
     932                 :         Oid         collation;
     933                 : 
     934                 :         /*
     935                 :          * Skip dropped columns
     936                 :          */
     937 CBC        2998 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
     938                 :         {
     939 UIC           0 :             i1++;
     940               0 :             continue;
     941 ECB             :         }
     942 CBC        2998 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
     943 ECB             :         {
     944 LBC           0 :             i2++;
     945               0 :             continue;
     946 ECB             :         }
     947 GIC        2998 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
     948                 :             break;              /* we'll deal with mismatch below loop */
     949                 : 
     950            2995 :         att1 = TupleDescAttr(tupdesc1, i1);
     951            2995 :         att2 = TupleDescAttr(tupdesc2, i2);
     952                 : 
     953 ECB             :         /*
     954                 :          * Have two matching columns, they must be same type
     955                 :          */
     956 GIC        2995 :         if (att1->atttypid != att2->atttypid)
     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                 : 
     964 ECB             :         /*
     965                 :          * If they're not same collation, we don't complain here, but the
     966 EUB             :          * comparison function might.
     967                 :          */
     968 GIC        2992 :         collation = att1->attcollation;
     969 CBC        2992 :         if (collation != att2->attcollation)
     970 UIC           0 :             collation = InvalidOid;
     971 EUB             : 
     972                 :         /*
     973                 :          * Lookup the comparison function if not done already
     974 ECB             :          */
     975 GIC        2992 :         typentry = my_extra->columns[j].typentry;
     976            2992 :         if (typentry == NULL ||
     977 CBC        2643 :             typentry->type_id != att1->atttypid)
     978 ECB             :         {
     979 GIC         349 :             typentry = lookup_type_cache(att1->atttypid,
     980                 :                                          TYPECACHE_CMP_PROC_FINFO);
     981             349 :             if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
     982               3 :                 ereport(ERROR,
     983 ECB             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
     984                 :                          errmsg("could not identify a comparison function for type %s",
     985                 :                                 format_type_be(typentry->type_id))));
     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                 :         {
     994            2984 :             LOCAL_FCINFO(locfcinfo, 2);
     995 ECB             :             int32       cmpresult;
     996                 : 
     997 GBC        2984 :             if (nulls1[i1])
     998                 :             {
     999                 :                 /* arg1 is greater than arg2 */
    1000 GIC          12 :                 result = 1;
    1001            1836 :                 break;
    1002 ECB             :             }
    1003 CBC        2972 :             if (nulls2[i2])
    1004 ECB             :             {
    1005                 :                 /* arg1 is less than arg2 */
    1006 LBC           0 :                 result = -1;
    1007 UIC           0 :                 break;
    1008 ECB             :             }
    1009                 : 
    1010                 :             /* Compare the pair of elements */
    1011 GIC        2972 :             InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    1012                 :                                      collation, NULL, NULL);
    1013 CBC        2972 :             locfcinfo->args[0].value = values1[i1];
    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                 : 
    1019 ECB             :             /* We don't expect comparison support functions to return null */
    1020 GIC        2972 :             Assert(!locfcinfo->isnull);
    1021 ECB             : 
    1022 GIC        2972 :             if (cmpresult < 0)
    1023                 :             {
    1024 ECB             :                 /* arg1 is less than arg2 */
    1025 GIC         933 :                 result = -1;
    1026             933 :                 break;
    1027 ECB             :             }
    1028 CBC        2039 :             else if (cmpresult > 0)
    1029                 :             {
    1030 ECB             :                 /* arg1 is greater than arg2 */
    1031 GIC         891 :                 result = 1;
    1032             891 :                 break;
    1033 EUB             :             }
    1034                 :         }
    1035                 : 
    1036                 :         /* equal, so continue to next column */
    1037 GIC        1153 :         i1++, i2++, j++;
    1038 ECB             :     }
    1039                 : 
    1040                 :     /*
    1041                 :      * If we didn't break out of the loop early, check for column count
    1042                 :      * mismatch.  (We do not report such mismatch if we found unequal column
    1043                 :      * values; is that a feature or a bug?)
    1044                 :      */
    1045 GIC        2236 :     if (result == 0)
    1046                 :     {
    1047 CBC         400 :         if (i1 != ncolumns1 || i2 != ncolumns2)
    1048 GIC           3 :             ereport(ERROR,
    1049 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1050                 :                      errmsg("cannot compare record types with different numbers of columns")));
    1051                 :     }
    1052                 : 
    1053 CBC        2233 :     pfree(values1);
    1054 GIC        2233 :     pfree(nulls1);
    1055 CBC        2233 :     pfree(values2);
    1056 GIC        2233 :     pfree(nulls2);
    1057            2233 :     ReleaseTupleDesc(tupdesc1);
    1058 CBC        2233 :     ReleaseTupleDesc(tupdesc2);
    1059 ECB             : 
    1060                 :     /* Avoid leaking memory when handed toasted input. */
    1061 GIC        2233 :     PG_FREE_IF_COPY(record1, 0);
    1062            2233 :     PG_FREE_IF_COPY(record2, 1);
    1063                 : 
    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.
    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
    1077 GIC        1875 : record_eq(PG_FUNCTION_ARGS)
    1078                 : {
    1079            1875 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
    1080 CBC        1875 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
    1081            1875 :     bool        result = true;
    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                 : 
    1102 GIC        1875 :     check_stack_depth();        /* recurses for record-type columns */
    1103                 : 
    1104 ECB             :     /* Extract type info from the tuples */
    1105 GIC        1875 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
    1106 CBC        1875 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
    1107            1875 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
    1108            1875 :     ncolumns1 = tupdesc1->natts;
    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);
    1129 CBC        1875 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
    1130 GIC        1875 :     if (my_extra == NULL ||
    1131            1684 :         my_extra->ncolumns < ncols)
    1132 ECB             :     {
    1133 CBC         382 :         fcinfo->flinfo->fn_extra =
    1134             191 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1135             191 :                                offsetof(RecordCompareData, columns) +
    1136 ECB             :                                ncols * sizeof(ColumnCompareData));
    1137 CBC         191 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
    1138             191 :         my_extra->ncolumns = ncols;
    1139             191 :         my_extra->record1_type = InvalidOid;
    1140 GIC         191 :         my_extra->record1_typmod = 0;
    1141             191 :         my_extra->record2_type = InvalidOid;
    1142 CBC         191 :         my_extra->record2_typmod = 0;
    1143 ECB             :     }
    1144                 : 
    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)
    1149 ECB             :     {
    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;
    1155 ECB             :     }
    1156                 : 
    1157                 :     /* Break down the tuples into fields */
    1158 CBC        1875 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
    1159 GIC        1875 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
    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));
    1163 GIC        1875 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
    1164 ECB             : 
    1165                 :     /*
    1166                 :      * Scan corresponding columns, allowing for dropped columns in different
    1167                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
    1168                 :      * the logical column index.
    1169                 :      */
    1170 GIC        1875 :     i1 = i2 = j = 0;
    1171            3124 :     while (i1 < ncolumns1 || i2 < ncolumns2)
    1172 ECB             :     {
    1173 CBC        2725 :         LOCAL_FCINFO(locfcinfo, 2);
    1174 ECB             :         Form_pg_attribute att1;
    1175                 :         Form_pg_attribute att2;
    1176                 :         TypeCacheEntry *typentry;
    1177                 :         Oid         collation;
    1178                 :         bool        oprresult;
    1179                 : 
    1180                 :         /*
    1181                 :          * Skip dropped columns
    1182                 :          */
    1183 GIC        2725 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
    1184                 :         {
    1185 LBC           0 :             i1++;
    1186               0 :             continue;
    1187 ECB             :         }
    1188 CBC        2725 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
    1189 ECB             :         {
    1190 LBC           0 :             i2++;
    1191 UIC           0 :             continue;
    1192                 :         }
    1193 GIC        2725 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
    1194                 :             break;              /* we'll deal with mismatch below loop */
    1195                 : 
    1196            2722 :         att1 = TupleDescAttr(tupdesc1, i1);
    1197 CBC        2722 :         att2 = TupleDescAttr(tupdesc2, i2);
    1198 ECB             : 
    1199                 :         /*
    1200                 :          * Have two matching columns, they must be same type
    1201                 :          */
    1202 GIC        2722 :         if (att1->atttypid != att2->atttypid)
    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                 : 
    1210 ECB             :         /*
    1211                 :          * If they're not same collation, we don't complain here, but the
    1212 EUB             :          * equality function might.
    1213                 :          */
    1214 GIC        2716 :         collation = att1->attcollation;
    1215 CBC        2716 :         if (collation != att2->attcollation)
    1216 UIC           0 :             collation = InvalidOid;
    1217 EUB             : 
    1218                 :         /*
    1219                 :          * Lookup the equality function if not done already
    1220 ECB             :          */
    1221 GIC        2716 :         typentry = my_extra->columns[j].typentry;
    1222            2716 :         if (typentry == NULL ||
    1223 CBC        2336 :             typentry->type_id != att1->atttypid)
    1224 ECB             :         {
    1225 GIC         380 :             typentry = lookup_type_cache(att1->atttypid,
    1226                 :                                          TYPECACHE_EQ_OPR_FINFO);
    1227             380 :             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    1228               3 :                 ereport(ERROR,
    1229 ECB             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1230                 :                          errmsg("could not identify an equality operator for type %s",
    1231                 :                                 format_type_be(typentry->type_id))));
    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])
    1241 ECB             :             {
    1242 CBC           3 :                 result = false;
    1243 GBC           3 :                 break;
    1244                 :             }
    1245                 : 
    1246                 :             /* Compare the pair of elements */
    1247 GIC        2585 :             InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    1248 ECB             :                                      collation, NULL, NULL);
    1249 CBC        2585 :             locfcinfo->args[0].value = values1[i1];
    1250            2585 :             locfcinfo->args[0].isnull = false;
    1251 GIC        2585 :             locfcinfo->args[1].value = values2[i2];
    1252 CBC        2585 :             locfcinfo->args[1].isnull = false;
    1253 GIC        2585 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    1254 CBC        2585 :             if (locfcinfo->isnull || !oprresult)
    1255 ECB             :             {
    1256 GIC        1461 :                 result = false;
    1257            1461 :                 break;
    1258                 :             }
    1259 ECB             :         }
    1260                 : 
    1261                 :         /* equal, so continue to next column */
    1262 GIC        1249 :         i1++, i2++, j++;
    1263                 :     }
    1264                 : 
    1265 ECB             :     /*
    1266                 :      * If we didn't break out of the loop early, check for column count
    1267                 :      * mismatch.  (We do not report such mismatch if we found unequal column
    1268                 :      * values; is that a feature or a bug?)
    1269                 :      */
    1270 CBC        1866 :     if (result)
    1271                 :     {
    1272 GIC         402 :         if (i1 != ncolumns1 || i2 != ncolumns2)
    1273               3 :             ereport(ERROR,
    1274 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1275                 :                      errmsg("cannot compare record types with different numbers of columns")));
    1276                 :     }
    1277                 : 
    1278 CBC        1863 :     pfree(values1);
    1279            1863 :     pfree(nulls1);
    1280            1863 :     pfree(values2);
    1281            1863 :     pfree(nulls2);
    1282 GIC        1863 :     ReleaseTupleDesc(tupdesc1);
    1283 CBC        1863 :     ReleaseTupleDesc(tupdesc2);
    1284 ECB             : 
    1285                 :     /* Avoid leaking memory when handed toasted input. */
    1286 GIC        1863 :     PG_FREE_IF_COPY(record1, 0);
    1287            1863 :     PG_FREE_IF_COPY(record2, 1);
    1288                 : 
    1289 CBC        1863 :     PG_RETURN_BOOL(result);
    1290                 : }
    1291                 : 
    1292                 : Datum
    1293 GIC          27 : record_ne(PG_FUNCTION_ARGS)
    1294                 : {
    1295              27 :     PG_RETURN_BOOL(!DatumGetBool(record_eq(fcinfo)));
    1296                 : }
    1297 ECB             : 
    1298                 : Datum
    1299 CBC          18 : record_lt(PG_FUNCTION_ARGS)
    1300 ECB             : {
    1301 GIC          18 :     PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
    1302                 : }
    1303                 : 
    1304                 : Datum
    1305 CBC           6 : record_gt(PG_FUNCTION_ARGS)
    1306 ECB             : {
    1307 CBC           6 :     PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
    1308 ECB             : }
    1309                 : 
    1310                 : Datum
    1311 GIC           6 : record_le(PG_FUNCTION_ARGS)
    1312                 : {
    1313 CBC           6 :     PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
    1314 ECB             : }
    1315                 : 
    1316                 : Datum
    1317 GIC          21 : record_ge(PG_FUNCTION_ARGS)
    1318                 : {
    1319              21 :     PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
    1320 ECB             : }
    1321                 : 
    1322                 : Datum
    1323 GIC        2191 : btrecordcmp(PG_FUNCTION_ARGS)
    1324                 : {
    1325            2191 :     PG_RETURN_INT32(record_cmp(fcinfo));
    1326 ECB             : }
    1327                 : 
    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                 :  */
    1340                 : static int
    1341 GIC         426 : record_image_cmp(FunctionCallInfo fcinfo)
    1342                 : {
    1343             426 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
    1344 CBC         426 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
    1345 GIC         426 :     int         result = 0;
    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 */
    1367 GIC         426 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
    1368 CBC         426 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
    1369 GIC         426 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
    1370 CBC         426 :     ncolumns1 = tupdesc1->natts;
    1371             426 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
    1372             426 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
    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)
    1394 ECB             :     {
    1395 CBC         246 :         fcinfo->flinfo->fn_extra =
    1396             123 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1397             123 :                                offsetof(RecordCompareData, columns) +
    1398 ECB             :                                ncols * sizeof(ColumnCompareData));
    1399 CBC         123 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
    1400             123 :         my_extra->ncolumns = ncols;
    1401             123 :         my_extra->record1_type = InvalidOid;
    1402 GIC         123 :         my_extra->record1_typmod = 0;
    1403             123 :         my_extra->record2_type = InvalidOid;
    1404 CBC         123 :         my_extra->record2_typmod = 0;
    1405 ECB             :     }
    1406                 : 
    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)
    1411 ECB             :     {
    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;
    1417 ECB             :     }
    1418                 : 
    1419                 :     /* Break down the tuples into fields */
    1420 CBC         426 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
    1421 GIC         426 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
    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));
    1425 GIC         426 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
    1426 ECB             : 
    1427                 :     /*
    1428                 :      * Scan corresponding columns, allowing for dropped columns in different
    1429                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
    1430                 :      * the logical column index.
    1431                 :      */
    1432 GIC         426 :     i1 = i2 = j = 0;
    1433             833 :     while (i1 < ncolumns1 || i2 < ncolumns2)
    1434 ECB             :     {
    1435                 :         Form_pg_attribute att1;
    1436                 :         Form_pg_attribute att2;
    1437                 : 
    1438                 :         /*
    1439                 :          * Skip dropped columns
    1440                 :          */
    1441 CBC         749 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
    1442 ECB             :         {
    1443 LBC           0 :             i1++;
    1444 UIC           0 :             continue;
    1445                 :         }
    1446 GIC         749 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
    1447 ECB             :         {
    1448 LBC           0 :             i2++;
    1449               0 :             continue;
    1450 ECB             :         }
    1451 CBC         749 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
    1452 ECB             :             break;              /* we'll deal with mismatch below loop */
    1453                 : 
    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
    1459 ECB             :          */
    1460 CBC         746 :         if (att1->atttypid != att2->atttypid)
    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                 : 
    1468 ECB             :         /*
    1469                 :          * The same type should have the same length (or both should be
    1470 EUB             :          * variable).
    1471                 :          */
    1472 GIC         743 :         Assert(att1->attlen == att2->attlen);
    1473 ECB             : 
    1474                 :         /*
    1475 EUB             :          * We consider two NULLs equal; NULL > not-NULL.
    1476                 :          */
    1477 GIC         743 :         if (!nulls1[i1] || !nulls2[i2])
    1478 ECB             :         {
    1479 GIC         743 :             int         cmpresult = 0;
    1480                 : 
    1481 CBC         743 :             if (nulls1[i1])
    1482 ECB             :             {
    1483                 :                 /* arg1 is greater than arg2 */
    1484 UIC           0 :                 result = 1;
    1485               0 :                 break;
    1486                 :             }
    1487 CBC         743 :             if (nulls2[i2])
    1488 ECB             :             {
    1489                 :                 /* arg1 is less than arg2 */
    1490 UIC           0 :                 result = -1;
    1491               0 :                 break;
    1492                 :             }
    1493                 : 
    1494                 :             /* Compare the pair of elements */
    1495 GIC         743 :             if (att1->attbyval)
    1496                 :             {
    1497             489 :                 if (values1[i1] != values2[i2])
    1498             256 :                     cmpresult = (values1[i1] < values2[i2]) ? -1 : 1;
    1499 ECB             :             }
    1500 GIC         254 :             else if (att1->attlen > 0)
    1501                 :             {
    1502              18 :                 cmpresult = memcmp(DatumGetPointer(values1[i1]),
    1503              18 :                                    DatumGetPointer(values2[i2]),
    1504 CBC          18 :                                    att1->attlen);
    1505                 :             }
    1506             236 :             else if (att1->attlen == -1)
    1507                 :             {
    1508 ECB             :                 Size        len1,
    1509                 :                             len2;
    1510                 :                 struct varlena *arg1val;
    1511 EUB             :                 struct varlena *arg2val;
    1512                 : 
    1513 GIC         236 :                 len1 = toast_raw_datum_size(values1[i1]);
    1514 CBC         236 :                 len2 = toast_raw_datum_size(values2[i2]);
    1515 GIC         236 :                 arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
    1516             236 :                 arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
    1517 EUB             : 
    1518 GBC         236 :                 cmpresult = memcmp(VARDATA_ANY(arg1val),
    1519 GIC         236 :                                    VARDATA_ANY(arg2val),
    1520             236 :                                    Min(len1, len2) - VARHDRSZ);
    1521             236 :                 if ((cmpresult == 0) && (len1 != len2))
    1522 CBC           3 :                     cmpresult = (len1 < len2) ? -1 : 1;
    1523                 : 
    1524             236 :                 if ((Pointer) arg1val != (Pointer) values1[i1])
    1525 LBC           0 :                     pfree(arg1val);
    1526 GIC         236 :                 if ((Pointer) arg2val != (Pointer) values2[i2])
    1527 LBC           0 :                     pfree(arg2val);
    1528                 :             }
    1529 ECB             :             else
    1530 LBC           0 :                 elog(ERROR, "unexpected attlen: %d", att1->attlen);
    1531 ECB             : 
    1532 GIC         743 :             if (cmpresult < 0)
    1533 ECB             :             {
    1534                 :                 /* arg1 is less than arg2 */
    1535 GIC         184 :                 result = -1;
    1536             184 :                 break;
    1537                 :             }
    1538             559 :             else if (cmpresult > 0)
    1539                 :             {
    1540 ECB             :                 /* arg1 is greater than arg2 */
    1541 CBC         152 :                 result = 1;
    1542             152 :                 break;
    1543 ECB             :             }
    1544                 :         }
    1545                 : 
    1546                 :         /* equal, so continue to next column */
    1547 CBC         407 :         i1++, i2++, j++;
    1548 ECB             :     }
    1549                 : 
    1550                 :     /*
    1551                 :      * If we didn't break out of the loop early, check for column count
    1552 EUB             :      * mismatch.  (We do not report such mismatch if we found unequal column
    1553 ECB             :      * values; is that a feature or a bug?)
    1554 EUB             :      */
    1555 GIC         423 :     if (result == 0)
    1556                 :     {
    1557 GBC          87 :         if (i1 != ncolumns1 || i2 != ncolumns2)
    1558 GIC           3 :             ereport(ERROR,
    1559 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1560                 :                      errmsg("cannot compare record types with different numbers of columns")));
    1561                 :     }
    1562                 : 
    1563 CBC         420 :     pfree(values1);
    1564 GIC         420 :     pfree(nulls1);
    1565 CBC         420 :     pfree(values2);
    1566 GIC         420 :     pfree(nulls2);
    1567             420 :     ReleaseTupleDesc(tupdesc1);
    1568 CBC         420 :     ReleaseTupleDesc(tupdesc2);
    1569 ECB             : 
    1570                 :     /* Avoid leaking memory when handed toasted input. */
    1571 GIC         420 :     PG_FREE_IF_COPY(record1, 0);
    1572             420 :     PG_FREE_IF_COPY(record2, 1);
    1573                 : 
    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.
    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
    1587 GIC         130 : record_image_eq(PG_FUNCTION_ARGS)
    1588                 : {
    1589             130 :     HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
    1590 CBC         130 :     HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
    1591             130 :     bool        result = true;
    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 */
    1613 GIC         130 :     tupType1 = HeapTupleHeaderGetTypeId(record1);
    1614 CBC         130 :     tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
    1615 GIC         130 :     tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
    1616 CBC         130 :     ncolumns1 = tupdesc1->natts;
    1617             130 :     tupType2 = HeapTupleHeaderGetTypeId(record2);
    1618             130 :     tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
    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)
    1640 ECB             :     {
    1641 CBC         130 :         fcinfo->flinfo->fn_extra =
    1642              65 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1643              65 :                                offsetof(RecordCompareData, columns) +
    1644 ECB             :                                ncols * sizeof(ColumnCompareData));
    1645 CBC          65 :         my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
    1646              65 :         my_extra->ncolumns = ncols;
    1647              65 :         my_extra->record1_type = InvalidOid;
    1648 GIC          65 :         my_extra->record1_typmod = 0;
    1649              65 :         my_extra->record2_type = InvalidOid;
    1650 CBC          65 :         my_extra->record2_typmod = 0;
    1651 ECB             :     }
    1652                 : 
    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)
    1657 ECB             :     {
    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;
    1663 ECB             :     }
    1664                 : 
    1665                 :     /* Break down the tuples into fields */
    1666 CBC         130 :     values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
    1667 GIC         130 :     nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
    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));
    1671 GIC         130 :     heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
    1672 ECB             : 
    1673                 :     /*
    1674                 :      * Scan corresponding columns, allowing for dropped columns in different
    1675                 :      * places in the two rows.  i1 and i2 are physical column indexes, j is
    1676                 :      * the logical column index.
    1677                 :      */
    1678 GIC         130 :     i1 = i2 = j = 0;
    1679             494 :     while (i1 < ncolumns1 || i2 < ncolumns2)
    1680 ECB             :     {
    1681                 :         Form_pg_attribute att1;
    1682                 :         Form_pg_attribute att2;
    1683                 : 
    1684                 :         /*
    1685                 :          * Skip dropped columns
    1686                 :          */
    1687 CBC         399 :         if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
    1688 ECB             :         {
    1689 LBC           0 :             i1++;
    1690 UIC           0 :             continue;
    1691                 :         }
    1692 GIC         399 :         if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
    1693 ECB             :         {
    1694 LBC           0 :             i2++;
    1695               0 :             continue;
    1696 ECB             :         }
    1697 CBC         399 :         if (i1 >= ncolumns1 || i2 >= ncolumns2)
    1698 ECB             :             break;              /* we'll deal with mismatch below loop */
    1699                 : 
    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
    1705 ECB             :          */
    1706 CBC         396 :         if (att1->atttypid != att2->atttypid)
    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                 : 
    1714 ECB             :         /*
    1715                 :          * We consider two NULLs equal; NULL > not-NULL.
    1716 EUB             :          */
    1717 GBC         393 :         if (!nulls1[i1] || !nulls2[i2])
    1718                 :         {
    1719 CBC         387 :             if (nulls1[i1] || nulls2[i2])
    1720                 :             {
    1721 UBC           0 :                 result = false;
    1722               0 :                 break;
    1723                 :             }
    1724 ECB             : 
    1725                 :             /* Compare the pair of elements */
    1726 GIC         387 :             result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen);
    1727 CBC         387 :             if (!result)
    1728              29 :                 break;
    1729                 :         }
    1730                 : 
    1731                 :         /* equal, so continue to next column */
    1732 GIC         364 :         i1++, i2++, j++;
    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                 :      */
    1740 GIC         127 :     if (result)
    1741                 :     {
    1742              98 :         if (i1 != ncolumns1 || i2 != ncolumns2)
    1743               3 :             ereport(ERROR,
    1744 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1745                 :                      errmsg("cannot compare record types with different numbers of columns")));
    1746                 :     }
    1747                 : 
    1748 GBC         124 :     pfree(values1);
    1749             124 :     pfree(nulls1);
    1750 GIC         124 :     pfree(values2);
    1751             124 :     pfree(nulls2);
    1752             124 :     ReleaseTupleDesc(tupdesc1);
    1753 CBC         124 :     ReleaseTupleDesc(tupdesc2);
    1754 ECB             : 
    1755                 :     /* Avoid leaking memory when handed toasted input. */
    1756 GIC         124 :     PG_FREE_IF_COPY(record1, 0);
    1757             124 :     PG_FREE_IF_COPY(record2, 1);
    1758                 : 
    1759 CBC         124 :     PG_RETURN_BOOL(result);
    1760                 : }
    1761                 : 
    1762                 : Datum
    1763 GIC          24 : record_image_ne(PG_FUNCTION_ARGS)
    1764                 : {
    1765              24 :     PG_RETURN_BOOL(!DatumGetBool(record_image_eq(fcinfo)));
    1766                 : }
    1767 ECB             : 
    1768                 : Datum
    1769 CBC          36 : record_image_lt(PG_FUNCTION_ARGS)
    1770 ECB             : {
    1771 GIC          36 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
    1772                 : }
    1773                 : 
    1774                 : Datum
    1775 CBC           9 : record_image_gt(PG_FUNCTION_ARGS)
    1776 ECB             : {
    1777 CBC           9 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
    1778 ECB             : }
    1779                 : 
    1780                 : Datum
    1781 GIC           6 : record_image_le(PG_FUNCTION_ARGS)
    1782                 : {
    1783 CBC           6 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
    1784 ECB             : }
    1785                 : 
    1786                 : Datum
    1787 GIC           9 : record_image_ge(PG_FUNCTION_ARGS)
    1788                 : {
    1789               9 :     PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
    1790 ECB             : }
    1791                 : 
    1792                 : Datum
    1793 GIC         366 : btrecordimagecmp(PG_FUNCTION_ARGS)
    1794                 : {
    1795             366 :     PG_RETURN_INT32(record_image_cmp(fcinfo));
    1796 ECB             : }
    1797                 : 
    1798                 : 
    1799                 : /*
    1800                 :  * Row type hash functions
    1801                 :  */
    1802                 : 
    1803                 : Datum
    1804 CBC         450 : hash_record(PG_FUNCTION_ARGS)
    1805                 : {
    1806 GIC         450 :     HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
    1807             450 :     uint32      result = 0;
    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                 : 
    1817 GIC         450 :     check_stack_depth();        /* recurses for record-type columns */
    1818                 : 
    1819                 :     /* Extract type info from tuple */
    1820 CBC         450 :     tupType = HeapTupleHeaderGetTypeId(record);
    1821 GIC         450 :     tupTypmod = HeapTupleHeaderGetTypMod(record);
    1822 CBC         450 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
    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                 : 
    1831 ECB             :     /*
    1832                 :      * We arrange to look up the needed hashing info just once per series of
    1833                 :      * calls, assuming the record type doesn't change underneath us.
    1834                 :      */
    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;
    1844 CBC          24 :         my_extra->ncolumns = ncolumns;
    1845 GIC          24 :         my_extra->record1_type = InvalidOid;
    1846              24 :         my_extra->record1_typmod = 0;
    1847 ECB             :     }
    1848                 : 
    1849 CBC         450 :     if (my_extra->record1_type != tupType ||
    1850             426 :         my_extra->record1_typmod != tupTypmod)
    1851                 :     {
    1852 GIC          75 :         MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
    1853 CBC          24 :         my_extra->record1_type = tupType;
    1854              24 :         my_extra->record1_typmod = tupTypmod;
    1855 ECB             :     }
    1856                 : 
    1857                 :     /* Break down the tuple into fields */
    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                 : 
    1862 CBC        1365 :     for (int i = 0; i < ncolumns; i++)
    1863 ECB             :     {
    1864                 :         Form_pg_attribute att;
    1865                 :         TypeCacheEntry *typentry;
    1866                 :         uint32      element_hash;
    1867                 : 
    1868 CBC         918 :         att = TupleDescAttr(tupdesc, i);
    1869                 : 
    1870             918 :         if (att->attisdropped)
    1871 LBC           0 :             continue;
    1872 ECB             : 
    1873                 :         /*
    1874                 :          * Lookup the hash function if not done already
    1875                 :          */
    1876 CBC         918 :         typentry = my_extra->columns[i].typentry;
    1877             918 :         if (typentry == NULL ||
    1878 GIC         870 :             typentry->type_id != att->atttypid)
    1879 ECB             :         {
    1880 CBC          48 :             typentry = lookup_type_cache(att->atttypid,
    1881 ECB             :                                          TYPECACHE_HASH_PROC_FINFO);
    1882 GIC          48 :             if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
    1883               3 :                 ereport(ERROR,
    1884                 :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1885 ECB             :                          errmsg("could not identify a hash function for type %s",
    1886                 :                                 format_type_be(typentry->type_id))));
    1887 CBC          45 :             my_extra->columns[i].typentry = typentry;
    1888                 :         }
    1889 ECB             : 
    1890                 :         /* Compute hash of element */
    1891 GIC         915 :         if (nulls[i])
    1892                 :         {
    1893 UIC           0 :             element_hash = 0;
    1894                 :         }
    1895 ECB             :         else
    1896                 :         {
    1897 CBC         915 :             LOCAL_FCINFO(locfcinfo, 1);
    1898 EUB             : 
    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;
    1903 CBC         915 :             element_hash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
    1904 ECB             : 
    1905                 :             /* We don't expect hash support functions to return null */
    1906 GIC         915 :             Assert(!locfcinfo->isnull);
    1907 ECB             :         }
    1908                 : 
    1909                 :         /* see hash_array() */
    1910 CBC         915 :         result = (result << 5) - result + element_hash;
    1911                 :     }
    1912                 : 
    1913 GIC         447 :     pfree(values);
    1914 CBC         447 :     pfree(nulls);
    1915 GIC         447 :     ReleaseTupleDesc(tupdesc);
    1916                 : 
    1917                 :     /* Avoid leaking memory when handed toasted input. */
    1918 CBC         447 :     PG_FREE_IF_COPY(record, 0);
    1919                 : 
    1920 GBC         447 :     PG_RETURN_UINT32(result);
    1921                 : }
    1922                 : 
    1923                 : Datum
    1924 CBC          15 : hash_record_extended(PG_FUNCTION_ARGS)
    1925                 : {
    1926              15 :     HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
    1927 GIC          15 :     uint64      seed = PG_GETARG_INT64(1);
    1928 CBC          15 :     uint64      result = 0;
    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                 : 
    1938 GIC          15 :     check_stack_depth();        /* recurses for record-type columns */
    1939                 : 
    1940 ECB             :     /* Extract type info from tuple */
    1941 CBC          15 :     tupType = HeapTupleHeaderGetTypeId(record);
    1942              15 :     tupTypmod = HeapTupleHeaderGetTypMod(record);
    1943 GIC          15 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
    1944              15 :     ncolumns = tupdesc->natts;
    1945 ECB             : 
    1946                 :     /* Build temporary HeapTuple control structure */
    1947 CBC          15 :     tuple.t_len = HeapTupleHeaderGetDatumLength(record);
    1948 GIC          15 :     ItemPointerSetInvalid(&(tuple.t_self));
    1949              15 :     tuple.t_tableOid = InvalidOid;
    1950              15 :     tuple.t_data = record;
    1951 ECB             : 
    1952                 :     /*
    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.
    1955                 :      */
    1956 GIC          15 :     my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
    1957              15 :     if (my_extra == NULL ||
    1958 UIC           0 :         my_extra->ncolumns < ncolumns)
    1959                 :     {
    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;
    1965 CBC          15 :         my_extra->ncolumns = ncolumns;
    1966 GIC          15 :         my_extra->record1_type = InvalidOid;
    1967              15 :         my_extra->record1_typmod = 0;
    1968 ECB             :     }
    1969                 : 
    1970 CBC          15 :     if (my_extra->record1_type != tupType ||
    1971 LBC           0 :         my_extra->record1_typmod != tupTypmod)
    1972                 :     {
    1973 GIC          45 :         MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
    1974 CBC          15 :         my_extra->record1_type = tupType;
    1975              15 :         my_extra->record1_typmod = tupTypmod;
    1976 ECB             :     }
    1977                 : 
    1978                 :     /* Break down the tuple into fields */
    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                 : 
    1983 CBC          39 :     for (int i = 0; i < ncolumns; i++)
    1984 ECB             :     {
    1985 EUB             :         Form_pg_attribute att;
    1986                 :         TypeCacheEntry *typentry;
    1987 ECB             :         uint64      element_hash;
    1988                 : 
    1989 CBC          27 :         att = TupleDescAttr(tupdesc, i);
    1990                 : 
    1991              27 :         if (att->attisdropped)
    1992 LBC           0 :             continue;
    1993 ECB             : 
    1994                 :         /*
    1995                 :          * Lookup the hash function if not done already
    1996                 :          */
    1997 CBC          27 :         typentry = my_extra->columns[i].typentry;
    1998 GBC          27 :         if (typentry == NULL ||
    1999 UIC           0 :             typentry->type_id != att->atttypid)
    2000 ECB             :         {
    2001 CBC          27 :             typentry = lookup_type_cache(att->atttypid,
    2002 ECB             :                                          TYPECACHE_HASH_EXTENDED_PROC_FINFO);
    2003 GIC          27 :             if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
    2004               3 :                 ereport(ERROR,
    2005                 :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    2006 ECB             :                          errmsg("could not identify an extended hash function for type %s",
    2007                 :                                 format_type_be(typentry->type_id))));
    2008 CBC          24 :             my_extra->columns[i].typentry = typentry;
    2009                 :         }
    2010 ECB             : 
    2011                 :         /* Compute hash of element */
    2012 GIC          24 :         if (nulls[i])
    2013                 :         {
    2014 UIC           0 :             element_hash = 0;
    2015                 :         }
    2016 ECB             :         else
    2017                 :         {
    2018 CBC          24 :             LOCAL_FCINFO(locfcinfo, 2);
    2019 EUB             : 
    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;
    2024 CBC          24 :             locfcinfo->args[1].value = Int64GetDatum(seed);
    2025              24 :             locfcinfo->args[0].isnull = false;
    2026 GBC          24 :             element_hash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
    2027                 : 
    2028 ECB             :             /* We don't expect hash support functions to return null */
    2029 GIC          24 :             Assert(!locfcinfo->isnull);
    2030 ECB             :         }
    2031                 : 
    2032                 :         /* see hash_array_extended() */
    2033 GIC          24 :         result = (result << 5) - result + element_hash;
    2034                 :     }
    2035 ECB             : 
    2036 GIC          12 :     pfree(values);
    2037              12 :     pfree(nulls);
    2038              12 :     ReleaseTupleDesc(tupdesc);
    2039 ECB             : 
    2040                 :     /* Avoid leaking memory when handed toasted input. */
    2041 GBC          12 :     PG_FREE_IF_COPY(record, 0);
    2042                 : 
    2043 GIC          12 :     PG_RETURN_UINT64(result);
    2044                 : }
        

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