PostgreSQL Source Code  git master
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-2024, 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  */
34 typedef struct ColumnIOData
35 {
37  Oid typiofunc;
40  FmgrInfo proc;
42 
43 typedef struct RecordIOData
44 {
47  int ncolumns;
50 
51 /*
52  * structure to cache metadata needed for record comparison
53  */
54 typedef struct ColumnCompareData
55 {
56  TypeCacheEntry *typentry; /* has everything we need, actually */
58 
59 typedef struct RecordCompareData
60 {
61  int ncolumns; /* allocated length of columns[] */
68 
69 
70 /*
71  * record_in - input routine for any composite type.
72  */
73 Datum
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;
88  Datum *values;
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 */
320 fail:
321  ReleaseTupleDesc(tupdesc);
322  PG_RETURN_NULL();
323 }
324 
325 /*
326  * record_out - output routine for any composite type.
327  */
328 Datum
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 */
354  ItemPointerSetInvalid(&(tuple.t_self));
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 
395  appendStringInfoChar(&buf, '(');
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)
412  appendStringInfoChar(&buf, ',');
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 
467  appendStringInfoChar(&buf, ')');
468 
469  pfree(values);
470  pfree(nulls);
471  ReleaseTupleDesc(tupdesc);
472 
473  PG_RETURN_CSTRING(buf.data);
474 }
475 
476 /*
477  * record_recv - binary input routine for any composite type.
478  */
479 Datum
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)
507  ereport(ERROR,
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)
556  ereport(ERROR,
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)
597  ereport(ERROR,
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))
611  ereport(ERROR,
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)
658  ereport(ERROR,
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  */
686 Datum
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 */
712  ItemPointerSetInvalid(&(tuple.t_self));
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  */
822 static 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  {
919  Form_pg_attribute att1;
920  Form_pg_attribute att2;
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)
947  ereport(ERROR,
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))
972  ereport(ERROR,
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  */
1066 Datum
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 
1282 Datum
1284 {
1286 }
1287 
1288 Datum
1290 {
1291  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1292 }
1293 
1294 Datum
1296 {
1297  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1298 }
1299 
1300 Datum
1302 {
1303  PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
1304 }
1305 
1306 Datum
1308 {
1309  PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
1310 }
1311 
1312 Datum
1314 {
1315  PG_RETURN_INT32(record_cmp(fcinfo));
1316 }
1317 
1318 Datum
1320 {
1321  if (record_cmp(fcinfo) > 0)
1323  else
1325 }
1326 
1327 Datum
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  */
1348 static 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  */
1594 Datum
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 
1770 Datum
1772 {
1774 }
1775 
1776 Datum
1778 {
1779  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1780 }
1781 
1782 Datum
1784 {
1785  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1786 }
1787 
1788 Datum
1790 {
1791  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1792 }
1793 
1794 Datum
1796 {
1797  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1798 }
1799 
1800 Datum
1802 {
1804 }
1805 
1806 
1807 /*
1808  * Row type hash functions
1809  */
1810 
1811 Datum
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  {
1872  Form_pg_attribute att;
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 
1931 Datum
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  {
1993  Form_pg_attribute att;
1994  TypeCacheEntry *typentry;
1995  uint64 element_hash;
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,
2011  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
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:150
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:125
unsigned int uint32
Definition: c.h:518
#define Min(x, y)
Definition: c.h:1007
signed int int32
Definition: c.h:508
#define Max(x, y)
Definition: c.h:1001
char * Pointer
Definition: c.h:498
#define VARHDRSZ
Definition: c.h:695
#define Assert(condition)
Definition: c.h:861
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:413
#define MemSet(start, val, len)
Definition: c.h:1023
#define OidIsValid(objectId)
Definition: c.h:778
size_t Size
Definition: c.h:608
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
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext, Datum *result)
Definition: fmgr.c:1585
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1744
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:1116
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
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 @160 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 pfree(void *pointer)
Definition: mcxt.c:1521
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void * palloc(Size size)
Definition: mcxt.c:1317
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static char * buf
Definition: pg_test_fsync.c:72
void check_stack_depth(void)
Definition: postgres.c:3563
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 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:690
#define FirstGenbkiObjectId
Definition: transam.h:195
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
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