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