PostgreSQL Source Code  git master
rowtypes.c File Reference
#include "postgres.h"
#include <ctype.h>
#include "access/detoast.h"
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
Include dependency graph for rowtypes.c:

Go to the source code of this file.

Data Structures

struct  ColumnIOData
 
struct  RecordIOData
 
struct  ColumnCompareData
 
struct  RecordCompareData
 

Typedefs

typedef struct ColumnIOData ColumnIOData
 
typedef struct RecordIOData RecordIOData
 
typedef struct ColumnCompareData ColumnCompareData
 
typedef struct RecordCompareData RecordCompareData
 

Functions

Datum record_in (PG_FUNCTION_ARGS)
 
Datum record_out (PG_FUNCTION_ARGS)
 
Datum record_recv (PG_FUNCTION_ARGS)
 
Datum record_send (PG_FUNCTION_ARGS)
 
static int record_cmp (FunctionCallInfo fcinfo)
 
Datum record_eq (PG_FUNCTION_ARGS)
 
Datum record_ne (PG_FUNCTION_ARGS)
 
Datum record_lt (PG_FUNCTION_ARGS)
 
Datum record_gt (PG_FUNCTION_ARGS)
 
Datum record_le (PG_FUNCTION_ARGS)
 
Datum record_ge (PG_FUNCTION_ARGS)
 
Datum btrecordcmp (PG_FUNCTION_ARGS)
 
Datum record_larger (PG_FUNCTION_ARGS)
 
Datum record_smaller (PG_FUNCTION_ARGS)
 
static int record_image_cmp (FunctionCallInfo fcinfo)
 
Datum record_image_eq (PG_FUNCTION_ARGS)
 
Datum record_image_ne (PG_FUNCTION_ARGS)
 
Datum record_image_lt (PG_FUNCTION_ARGS)
 
Datum record_image_gt (PG_FUNCTION_ARGS)
 
Datum record_image_le (PG_FUNCTION_ARGS)
 
Datum record_image_ge (PG_FUNCTION_ARGS)
 
Datum btrecordimagecmp (PG_FUNCTION_ARGS)
 
Datum hash_record (PG_FUNCTION_ARGS)
 
Datum hash_record_extended (PG_FUNCTION_ARGS)
 

Typedef Documentation

◆ ColumnCompareData

◆ ColumnIOData

typedef struct ColumnIOData ColumnIOData

◆ RecordCompareData

◆ RecordIOData

typedef struct RecordIOData RecordIOData

Function Documentation

◆ btrecordcmp()

Datum btrecordcmp ( PG_FUNCTION_ARGS  )

Definition at line 1313 of file rowtypes.c.

1314 {
1315  PG_RETURN_INT32(record_cmp(fcinfo));
1316 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:823

References PG_RETURN_INT32, and record_cmp().

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1801 of file rowtypes.c.

1802 {
1804 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1349

References PG_RETURN_INT32, and record_image_cmp().

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1812 of file rowtypes.c.

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 }
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:150
unsigned int uint32
Definition: c.h:506
signed int int32
Definition: c.h:494
#define Assert(condition)
Definition: c.h:858
#define MemSet(start, val, len)
Definition: c.h:1020
#define OidIsValid(objectId)
Definition: c.h:775
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define PG_RETURN_UINT32(x)
Definition: fmgr.h:355
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
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
void check_stack_depth(void)
Definition: postgres.c:3530
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:222
uintptr_t Datum
Definition: postgres.h:64
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
TypeCacheEntry * typentry
Definition: rowtypes.c:56
Oid fn_oid
Definition: fmgr.h:59
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
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:66
int32 record1_typmod
Definition: rowtypes.c:63
FmgrInfo hash_proc_finfo
Definition: typcache.h:77
#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:1833
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:144

References Assert, check_stack_depth(), RecordCompareData::columns, DatumGetUInt32(), element_hash(), ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_oid, format_type_be(), FunctionCallInvoke, TypeCacheEntry::hash_proc_finfo, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, if(), InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid(), LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_UINT32, RecordCompareData::record1_type, RecordCompareData::record1_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, TypeCacheEntry::type_id, TYPECACHE_HASH_PROC_FINFO, ColumnCompareData::typentry, and values.

◆ hash_record_extended()

Datum hash_record_extended ( PG_FUNCTION_ARGS  )

Definition at line 1932 of file rowtypes.c.

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 }
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_UINT64(x)
Definition: fmgr.h:369
static uint64 DatumGetUInt64(Datum X)
Definition: postgres.h:419
FmgrInfo hash_extended_proc_finfo
Definition: typcache.h:78
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO
Definition: typcache.h:152

References Assert, check_stack_depth(), RecordCompareData::columns, DatumGetUInt64(), element_hash(), ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_oid, format_type_be(), FunctionCallInvoke, TypeCacheEntry::hash_extended_proc_finfo, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, if(), InitFunctionCallInfoData, Int64GetDatum(), InvalidOid, ItemPointerSetInvalid(), LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, PG_GETARG_INT64, PG_RETURN_UINT64, RecordCompareData::record1_type, RecordCompareData::record1_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, TypeCacheEntry::type_id, TYPECACHE_HASH_EXTENDED_PROC_FINFO, ColumnCompareData::typentry, and values.

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 823 of file rowtypes.c.

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 }
#define Max(x, y)
Definition: c.h:998
int j
Definition: isn.c:74
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:202
void * fn_extra
Definition: fmgr.h:64
MemoryContext fn_mcxt
Definition: fmgr.h:65
FmgrInfo * flinfo
Definition: fmgr.h:87
int32 record2_typmod
Definition: rowtypes.c:65
FmgrInfo cmp_proc_finfo
Definition: typcache.h:76
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:143

References Assert, check_stack_depth(), TypeCacheEntry::cmp_proc_finfo, RecordCompareData::columns, DatumGetInt32(), ereport, errcode(), errmsg(), ERROR, FunctionCallInfoBaseData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, FmgrInfo::fn_oid, format_type_be(), FunctionCallInvoke, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, if(), InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid(), j, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, TypeCacheEntry::type_id, TYPECACHE_CMP_PROC_FINFO, and ColumnCompareData::typentry.

Referenced by btrecordcmp(), record_ge(), record_gt(), record_larger(), record_le(), record_lt(), and record_smaller().

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1067 of file rowtypes.c.

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 }
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
FmgrInfo eq_opr_finfo
Definition: typcache.h:75
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:142

References check_stack_depth(), RecordCompareData::columns, DatumGetBool(), TypeCacheEntry::eq_opr_finfo, ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_oid, format_type_be(), FunctionCallInvoke, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, if(), InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid(), j, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BOOL, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, TypeCacheEntry::type_id, TYPECACHE_EQ_OPR_FINFO, and ColumnCompareData::typentry.

Referenced by record_ne().

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1307 of file rowtypes.c.

1308 {
1309  PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
1310 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1295 of file rowtypes.c.

1296 {
1297  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1298 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1349 of file rowtypes.c.

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 }
#define Min(x, y)
Definition: c.h:1004
char * Pointer
Definition: c.h:483
#define VARHDRSZ
Definition: c.h:692
size_t Size
Definition: c.h:605
Size toast_raw_datum_size(Datum value)
Definition: detoast.c:545
#define elog(elevel,...)
Definition: elog.h:224
#define PG_DETOAST_DATUM_PACKED(datum)
Definition: fmgr.h:248
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
Definition: c.h:687
#define VARDATA_ANY(PTR)
Definition: varatt.h:324

References Assert, RecordCompareData::columns, DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, FunctionCallInfoBaseData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, format_type_be(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, if(), InvalidOid, ItemPointerSetInvalid(), j, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, Min, TupleDescData::natts, RecordCompareData::ncolumns, palloc(), pfree(), PG_DETOAST_DATUM_PACKED, PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, toast_raw_datum_size(), TupleDescAttr, VARDATA_ANY, and VARHDRSZ.

Referenced by btrecordimagecmp(), record_image_ge(), record_image_gt(), record_image_le(), and record_image_lt().

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1595 of file rowtypes.c.

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 }
bool datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:266

References RecordCompareData::columns, datum_image_eq(), ereport, errcode(), errmsg(), ERROR, format_type_be(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, if(), InvalidOid, ItemPointerSetInvalid(), j, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BOOL, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, and TupleDescAttr.

Referenced by record_image_ne().

◆ record_image_ge()

Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1795 of file rowtypes.c.

1796 {
1797  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1798 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1783 of file rowtypes.c.

1784 {
1785  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1786 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1789 of file rowtypes.c.

1790 {
1791  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1792 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1777 of file rowtypes.c.

1778 {
1779  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1780 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1771 of file rowtypes.c.

1772 {
1774 }
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1595

References DatumGetBool(), PG_RETURN_BOOL, and record_image_eq().

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 74 of file rowtypes.c.

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 }
int errdetail(const char *fmt,...)
Definition: elog.c:1203
#define ereturn(context, dummy_value,...)
Definition: elog.h:276
#define errsave(context,...)
Definition: elog.h:260
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext, Datum *result)
Definition: fmgr.c:1585
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_HEAPTUPLEHEADER(x)
Definition: fmgr.h:375
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
static char * buf
Definition: pg_test_fsync.c:73
char string[11]
Definition: preproc-type.c:52
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
Oid typioparam
Definition: hstore_io.c:815
FmgrInfo proc
Definition: hstore_io.c:816
Oid column_type
Definition: hstore_io.c:813
Definition: nodes.h:129
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

References appendStringInfoChar(), buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, ereturn, errcode(), errdetail(), errmsg(), errsave, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeInputInfo(), heap_form_tuple(), heap_freetuple(), i, if(), initStringInfo(), InputFunctionCallSafe(), InvalidOid, lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, palloc(), pfree(), PG_GETARG_CSTRING, PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_HEAPTUPLEHEADER, PG_RETURN_NULL, ColumnIOData::proc, RecordIOData::record_type, RecordIOData::record_typmod, ReleaseTupleDesc, resetStringInfo(), HeapTupleData::t_data, HeapTupleData::t_len, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.

◆ record_larger()

Datum record_larger ( PG_FUNCTION_ARGS  )

Definition at line 1319 of file rowtypes.c.

1320 {
1321  if (record_cmp(fcinfo) > 0)
1323  else
1325 }
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353

References PG_GETARG_DATUM, PG_RETURN_DATUM, and record_cmp().

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1301 of file rowtypes.c.

1302 {
1303  PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
1304 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1289 of file rowtypes.c.

1290 {
1291  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1292 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1283 of file rowtypes.c.

1284 {
1286 }
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1067

References DatumGetBool(), PG_RETURN_BOOL, and record_eq().

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 329 of file rowtypes.c.

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 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
static struct @155 value
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:204
bool typisvarlena
Definition: rowtypes.c:39

References appendStringInfoChar(), appendStringInfoCharMacro, buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeOutputInfo(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, if(), initStringInfo(), InvalidOid, ItemPointerSetInvalid(), lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, OutputFunctionCall(), palloc(), pfree(), PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_CSTRING, ColumnIOData::proc, RecordIOData::record_type, RecordIOData::record_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typisvarlena, value, and values.

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 480 of file rowtypes.c.

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 }
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:125
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1697
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
char * format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
Definition: format_type.c:112
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2940
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:415
StringInfoData * StringInfo
Definition: stringinfo.h:54
static void initReadOnlyStringInfo(StringInfo str, char *data, int len)
Definition: stringinfo.h:130
#define FirstGenbkiObjectId
Definition: transam.h:195

References buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, StringInfoData::cursor, ereport, errcode(), errmsg(), ERROR, FirstGenbkiObjectId, fmgr_info_cxt(), FmgrInfo::fn_mcxt, FORMAT_TYPE_ALLOW_INVALID, format_type_extended(), getTypeBinaryInputInfo(), heap_form_tuple(), heap_freetuple(), i, if(), initReadOnlyStringInfo(), InvalidOid, lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, palloc(), pfree(), PG_GETARG_INT32, PG_GETARG_OID, PG_GETARG_POINTER, PG_RETURN_HEAPTUPLEHEADER, pq_getmsgint(), ColumnIOData::proc, ReceiveFunctionCall(), RecordIOData::record_type, RecordIOData::record_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 687 of file rowtypes.c.

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 }
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1744
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition: lsyscache.c:2973
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
#define VARDATA(PTR)
Definition: varatt.h:278
#define VARSIZE(PTR)
Definition: varatt.h:279

References buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeBinaryOutputInfo(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, if(), InvalidOid, ItemPointerSetInvalid(), lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, palloc(), pfree(), PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BYTEA_P, pq_begintypsend(), pq_endtypsend(), pq_sendbytes(), pq_sendint32(), ColumnIOData::proc, RecordIOData::record_type, RecordIOData::record_typmod, ReleaseTupleDesc, SendFunctionCall(), HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typisvarlena, values, VARDATA, VARHDRSZ, and VARSIZE.

◆ record_smaller()

Datum record_smaller ( PG_FUNCTION_ARGS  )

Definition at line 1328 of file rowtypes.c.

1329 {
1330  if (record_cmp(fcinfo) < 0)
1332  else
1334 }

References PG_GETARG_DATUM, PG_RETURN_DATUM, and record_cmp().