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