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