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 "common/hashfn.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)
 
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 1314 of file rowtypes.c.

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

References PG_RETURN_INT32, and record_cmp().

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1784 of file rowtypes.c.

1785 {
1787 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1332

References PG_RETURN_INT32, and record_image_cmp().

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1795 of file rowtypes.c.

1796 {
1798  uint32 result = 0;
1799  Oid tupType;
1800  int32 tupTypmod;
1801  TupleDesc tupdesc;
1802  HeapTupleData tuple;
1803  int ncolumns;
1804  RecordCompareData *my_extra;
1805  Datum *values;
1806  bool *nulls;
1807 
1808  check_stack_depth(); /* recurses for record-type columns */
1809 
1810  /* Extract type info from tuple */
1811  tupType = HeapTupleHeaderGetTypeId(record);
1812  tupTypmod = HeapTupleHeaderGetTypMod(record);
1813  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1814  ncolumns = tupdesc->natts;
1815 
1816  /* Build temporary HeapTuple control structure */
1817  tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1818  ItemPointerSetInvalid(&(tuple.t_self));
1819  tuple.t_tableOid = InvalidOid;
1820  tuple.t_data = record;
1821 
1822  /*
1823  * We arrange to look up the needed hashing info just once per series of
1824  * calls, assuming the record type doesn't change underneath us.
1825  */
1826  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1827  if (my_extra == NULL ||
1828  my_extra->ncolumns < ncolumns)
1829  {
1830  fcinfo->flinfo->fn_extra =
1831  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1832  offsetof(RecordCompareData, columns) +
1833  ncolumns * sizeof(ColumnCompareData));
1834  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1835  my_extra->ncolumns = ncolumns;
1836  my_extra->record1_type = InvalidOid;
1837  my_extra->record1_typmod = 0;
1838  }
1839 
1840  if (my_extra->record1_type != tupType ||
1841  my_extra->record1_typmod != tupTypmod)
1842  {
1843  MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1844  my_extra->record1_type = tupType;
1845  my_extra->record1_typmod = tupTypmod;
1846  }
1847 
1848  /* Break down the tuple into fields */
1849  values = (Datum *) palloc(ncolumns * sizeof(Datum));
1850  nulls = (bool *) palloc(ncolumns * sizeof(bool));
1851  heap_deform_tuple(&tuple, tupdesc, values, nulls);
1852 
1853  for (int i = 0; i < ncolumns; i++)
1854  {
1855  Form_pg_attribute att;
1856  TypeCacheEntry *typentry;
1858 
1859  att = TupleDescAttr(tupdesc, i);
1860 
1861  if (att->attisdropped)
1862  continue;
1863 
1864  /*
1865  * Lookup the hash function if not done already
1866  */
1867  typentry = my_extra->columns[i].typentry;
1868  if (typentry == NULL ||
1869  typentry->type_id != att->atttypid)
1870  {
1871  typentry = lookup_type_cache(att->atttypid,
1873  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
1874  ereport(ERROR,
1875  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1876  errmsg("could not identify a hash function for type %s",
1877  format_type_be(typentry->type_id))));
1878  my_extra->columns[i].typentry = typentry;
1879  }
1880 
1881  /* Compute hash of element */
1882  if (nulls[i])
1883  {
1884  element_hash = 0;
1885  }
1886  else
1887  {
1888  LOCAL_FCINFO(locfcinfo, 1);
1889 
1890  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1891  att->attcollation, NULL, NULL);
1892  locfcinfo->args[0].value = values[i];
1893  locfcinfo->args[0].isnull = false;
1895 
1896  /* We don't expect hash support functions to return null */
1897  Assert(!locfcinfo->isnull);
1898  }
1899 
1900  /* see hash_array() */
1901  result = (result << 5) - result + element_hash;
1902  }
1903 
1904  pfree(values);
1905  pfree(nulls);
1906  ReleaseTupleDesc(tupdesc);
1907 
1908  /* Avoid leaking memory when handed toasted input. */
1909  PG_FREE_IF_COPY(record, 0);
1910 
1911  PG_RETURN_UINT32(result);
1912 }
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:156
unsigned int uint32
Definition: c.h:495
signed int int32
Definition: c.h:483
#define MemSet(start, val, len)
Definition: c.h:1009
#define OidIsValid(objectId)
Definition: c.h:764
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#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:1346
#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
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1456
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1021
void * palloc(Size size)
Definition: mcxt.c:1226
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void check_stack_depth(void)
Definition: postgres.c:3520
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:57
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:67
int32 record1_typmod
Definition: rowtypes.c:64
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:1830
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:344
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:143

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 1915 of file rowtypes.c.

1916 {
1918  uint64 seed = PG_GETARG_INT64(1);
1919  uint64 result = 0;
1920  Oid tupType;
1921  int32 tupTypmod;
1922  TupleDesc tupdesc;
1923  HeapTupleData tuple;
1924  int ncolumns;
1925  RecordCompareData *my_extra;
1926  Datum *values;
1927  bool *nulls;
1928 
1929  check_stack_depth(); /* recurses for record-type columns */
1930 
1931  /* Extract type info from tuple */
1932  tupType = HeapTupleHeaderGetTypeId(record);
1933  tupTypmod = HeapTupleHeaderGetTypMod(record);
1934  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1935  ncolumns = tupdesc->natts;
1936 
1937  /* Build temporary HeapTuple control structure */
1938  tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1939  ItemPointerSetInvalid(&(tuple.t_self));
1940  tuple.t_tableOid = InvalidOid;
1941  tuple.t_data = record;
1942 
1943  /*
1944  * We arrange to look up the needed hashing info just once per series of
1945  * calls, assuming the record type doesn't change underneath us.
1946  */
1947  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1948  if (my_extra == NULL ||
1949  my_extra->ncolumns < ncolumns)
1950  {
1951  fcinfo->flinfo->fn_extra =
1952  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1953  offsetof(RecordCompareData, columns) +
1954  ncolumns * sizeof(ColumnCompareData));
1955  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1956  my_extra->ncolumns = ncolumns;
1957  my_extra->record1_type = InvalidOid;
1958  my_extra->record1_typmod = 0;
1959  }
1960 
1961  if (my_extra->record1_type != tupType ||
1962  my_extra->record1_typmod != tupTypmod)
1963  {
1964  MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1965  my_extra->record1_type = tupType;
1966  my_extra->record1_typmod = tupTypmod;
1967  }
1968 
1969  /* Break down the tuple into fields */
1970  values = (Datum *) palloc(ncolumns * sizeof(Datum));
1971  nulls = (bool *) palloc(ncolumns * sizeof(bool));
1972  heap_deform_tuple(&tuple, tupdesc, values, nulls);
1973 
1974  for (int i = 0; i < ncolumns; i++)
1975  {
1976  Form_pg_attribute att;
1977  TypeCacheEntry *typentry;
1978  uint64 element_hash;
1979 
1980  att = TupleDescAttr(tupdesc, i);
1981 
1982  if (att->attisdropped)
1983  continue;
1984 
1985  /*
1986  * Lookup the hash function if not done already
1987  */
1988  typentry = my_extra->columns[i].typentry;
1989  if (typentry == NULL ||
1990  typentry->type_id != att->atttypid)
1991  {
1992  typentry = lookup_type_cache(att->atttypid,
1994  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
1995  ereport(ERROR,
1996  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1997  errmsg("could not identify an extended hash function for type %s",
1998  format_type_be(typentry->type_id))));
1999  my_extra->columns[i].typentry = typentry;
2000  }
2001 
2002  /* Compute hash of element */
2003  if (nulls[i])
2004  {
2005  element_hash = 0;
2006  }
2007  else
2008  {
2009  LOCAL_FCINFO(locfcinfo, 2);
2010 
2011  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
2012  att->attcollation, NULL, NULL);
2013  locfcinfo->args[0].value = values[i];
2014  locfcinfo->args[0].isnull = false;
2015  locfcinfo->args[1].value = Int64GetDatum(seed);
2016  locfcinfo->args[0].isnull = false;
2018 
2019  /* We don't expect hash support functions to return null */
2020  Assert(!locfcinfo->isnull);
2021  }
2022 
2023  /* see hash_array_extended() */
2024  result = (result << 5) - result + element_hash;
2025  }
2026 
2027  pfree(values);
2028  pfree(nulls);
2029  ReleaseTupleDesc(tupdesc);
2030 
2031  /* Avoid leaking memory when handed toasted input. */
2032  PG_FREE_IF_COPY(record, 0);
2033 
2034  PG_RETURN_UINT64(result);
2035 }
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:151

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 824 of file rowtypes.c.

825 {
828  int result = 0;
829  Oid tupType1;
830  Oid tupType2;
831  int32 tupTypmod1;
832  int32 tupTypmod2;
833  TupleDesc tupdesc1;
834  TupleDesc tupdesc2;
835  HeapTupleData tuple1;
836  HeapTupleData tuple2;
837  int ncolumns1;
838  int ncolumns2;
839  RecordCompareData *my_extra;
840  int ncols;
841  Datum *values1;
842  Datum *values2;
843  bool *nulls1;
844  bool *nulls2;
845  int i1;
846  int i2;
847  int j;
848 
849  check_stack_depth(); /* recurses for record-type columns */
850 
851  /* Extract type info from the tuples */
852  tupType1 = HeapTupleHeaderGetTypeId(record1);
853  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
854  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
855  ncolumns1 = tupdesc1->natts;
856  tupType2 = HeapTupleHeaderGetTypeId(record2);
857  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
858  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
859  ncolumns2 = tupdesc2->natts;
860 
861  /* Build temporary HeapTuple control structures */
862  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
863  ItemPointerSetInvalid(&(tuple1.t_self));
864  tuple1.t_tableOid = InvalidOid;
865  tuple1.t_data = record1;
866  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
867  ItemPointerSetInvalid(&(tuple2.t_self));
868  tuple2.t_tableOid = InvalidOid;
869  tuple2.t_data = record2;
870 
871  /*
872  * We arrange to look up the needed comparison info just once per series
873  * of calls, assuming the record types don't change underneath us.
874  */
875  ncols = Max(ncolumns1, ncolumns2);
876  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
877  if (my_extra == NULL ||
878  my_extra->ncolumns < ncols)
879  {
880  fcinfo->flinfo->fn_extra =
882  offsetof(RecordCompareData, columns) +
883  ncols * sizeof(ColumnCompareData));
884  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
885  my_extra->ncolumns = ncols;
886  my_extra->record1_type = InvalidOid;
887  my_extra->record1_typmod = 0;
888  my_extra->record2_type = InvalidOid;
889  my_extra->record2_typmod = 0;
890  }
891 
892  if (my_extra->record1_type != tupType1 ||
893  my_extra->record1_typmod != tupTypmod1 ||
894  my_extra->record2_type != tupType2 ||
895  my_extra->record2_typmod != tupTypmod2)
896  {
897  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
898  my_extra->record1_type = tupType1;
899  my_extra->record1_typmod = tupTypmod1;
900  my_extra->record2_type = tupType2;
901  my_extra->record2_typmod = tupTypmod2;
902  }
903 
904  /* Break down the tuples into fields */
905  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
906  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
907  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
908  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
909  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
910  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
911 
912  /*
913  * Scan corresponding columns, allowing for dropped columns in different
914  * places in the two rows. i1 and i2 are physical column indexes, j is
915  * the logical column index.
916  */
917  i1 = i2 = j = 0;
918  while (i1 < ncolumns1 || i2 < ncolumns2)
919  {
920  Form_pg_attribute att1;
921  Form_pg_attribute att2;
922  TypeCacheEntry *typentry;
923  Oid collation;
924 
925  /*
926  * Skip dropped columns
927  */
928  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
929  {
930  i1++;
931  continue;
932  }
933  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
934  {
935  i2++;
936  continue;
937  }
938  if (i1 >= ncolumns1 || i2 >= ncolumns2)
939  break; /* we'll deal with mismatch below loop */
940 
941  att1 = TupleDescAttr(tupdesc1, i1);
942  att2 = TupleDescAttr(tupdesc2, i2);
943 
944  /*
945  * Have two matching columns, they must be same type
946  */
947  if (att1->atttypid != att2->atttypid)
948  ereport(ERROR,
949  (errcode(ERRCODE_DATATYPE_MISMATCH),
950  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
951  format_type_be(att1->atttypid),
952  format_type_be(att2->atttypid),
953  j + 1)));
954 
955  /*
956  * If they're not same collation, we don't complain here, but the
957  * comparison function might.
958  */
959  collation = att1->attcollation;
960  if (collation != att2->attcollation)
961  collation = InvalidOid;
962 
963  /*
964  * Lookup the comparison function if not done already
965  */
966  typentry = my_extra->columns[j].typentry;
967  if (typentry == NULL ||
968  typentry->type_id != att1->atttypid)
969  {
970  typentry = lookup_type_cache(att1->atttypid,
972  if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
973  ereport(ERROR,
974  (errcode(ERRCODE_UNDEFINED_FUNCTION),
975  errmsg("could not identify a comparison function for type %s",
976  format_type_be(typentry->type_id))));
977  my_extra->columns[j].typentry = typentry;
978  }
979 
980  /*
981  * We consider two NULLs equal; NULL > not-NULL.
982  */
983  if (!nulls1[i1] || !nulls2[i2])
984  {
985  LOCAL_FCINFO(locfcinfo, 2);
986  int32 cmpresult;
987 
988  if (nulls1[i1])
989  {
990  /* arg1 is greater than arg2 */
991  result = 1;
992  break;
993  }
994  if (nulls2[i2])
995  {
996  /* arg1 is less than arg2 */
997  result = -1;
998  break;
999  }
1000 
1001  /* Compare the pair of elements */
1002  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
1003  collation, NULL, NULL);
1004  locfcinfo->args[0].value = values1[i1];
1005  locfcinfo->args[0].isnull = false;
1006  locfcinfo->args[1].value = values2[i2];
1007  locfcinfo->args[1].isnull = false;
1008  cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
1009 
1010  /* We don't expect comparison support functions to return null */
1011  Assert(!locfcinfo->isnull);
1012 
1013  if (cmpresult < 0)
1014  {
1015  /* arg1 is less than arg2 */
1016  result = -1;
1017  break;
1018  }
1019  else if (cmpresult > 0)
1020  {
1021  /* arg1 is greater than arg2 */
1022  result = 1;
1023  break;
1024  }
1025  }
1026 
1027  /* equal, so continue to next column */
1028  i1++, i2++, j++;
1029  }
1030 
1031  /*
1032  * If we didn't break out of the loop early, check for column count
1033  * mismatch. (We do not report such mismatch if we found unequal column
1034  * values; is that a feature or a bug?)
1035  */
1036  if (result == 0)
1037  {
1038  if (i1 != ncolumns1 || i2 != ncolumns2)
1039  ereport(ERROR,
1040  (errcode(ERRCODE_DATATYPE_MISMATCH),
1041  errmsg("cannot compare record types with different numbers of columns")));
1042  }
1043 
1044  pfree(values1);
1045  pfree(nulls1);
1046  pfree(values2);
1047  pfree(nulls2);
1048  ReleaseTupleDesc(tupdesc1);
1049  ReleaseTupleDesc(tupdesc2);
1050 
1051  /* Avoid leaking memory when handed toasted input. */
1052  PG_FREE_IF_COPY(record1, 0);
1053  PG_FREE_IF_COPY(record2, 1);
1054 
1055  return result;
1056 }
#define Max(x, y)
Definition: c.h:987
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:66
FmgrInfo cmp_proc_finfo
Definition: typcache.h:76
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:142

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_le(), and record_lt().

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1068 of file rowtypes.c.

1069 {
1072  bool result = true;
1073  Oid tupType1;
1074  Oid tupType2;
1075  int32 tupTypmod1;
1076  int32 tupTypmod2;
1077  TupleDesc tupdesc1;
1078  TupleDesc tupdesc2;
1079  HeapTupleData tuple1;
1080  HeapTupleData tuple2;
1081  int ncolumns1;
1082  int ncolumns2;
1083  RecordCompareData *my_extra;
1084  int ncols;
1085  Datum *values1;
1086  Datum *values2;
1087  bool *nulls1;
1088  bool *nulls2;
1089  int i1;
1090  int i2;
1091  int j;
1092 
1093  check_stack_depth(); /* recurses for record-type columns */
1094 
1095  /* Extract type info from the tuples */
1096  tupType1 = HeapTupleHeaderGetTypeId(record1);
1097  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1098  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1099  ncolumns1 = tupdesc1->natts;
1100  tupType2 = HeapTupleHeaderGetTypeId(record2);
1101  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1102  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1103  ncolumns2 = tupdesc2->natts;
1104 
1105  /* Build temporary HeapTuple control structures */
1106  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1107  ItemPointerSetInvalid(&(tuple1.t_self));
1108  tuple1.t_tableOid = InvalidOid;
1109  tuple1.t_data = record1;
1110  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1111  ItemPointerSetInvalid(&(tuple2.t_self));
1112  tuple2.t_tableOid = InvalidOid;
1113  tuple2.t_data = record2;
1114 
1115  /*
1116  * We arrange to look up the needed comparison info just once per series
1117  * of calls, assuming the record types don't change underneath us.
1118  */
1119  ncols = Max(ncolumns1, ncolumns2);
1120  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1121  if (my_extra == NULL ||
1122  my_extra->ncolumns < ncols)
1123  {
1124  fcinfo->flinfo->fn_extra =
1125  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1126  offsetof(RecordCompareData, columns) +
1127  ncols * sizeof(ColumnCompareData));
1128  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1129  my_extra->ncolumns = ncols;
1130  my_extra->record1_type = InvalidOid;
1131  my_extra->record1_typmod = 0;
1132  my_extra->record2_type = InvalidOid;
1133  my_extra->record2_typmod = 0;
1134  }
1135 
1136  if (my_extra->record1_type != tupType1 ||
1137  my_extra->record1_typmod != tupTypmod1 ||
1138  my_extra->record2_type != tupType2 ||
1139  my_extra->record2_typmod != tupTypmod2)
1140  {
1141  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1142  my_extra->record1_type = tupType1;
1143  my_extra->record1_typmod = tupTypmod1;
1144  my_extra->record2_type = tupType2;
1145  my_extra->record2_typmod = tupTypmod2;
1146  }
1147 
1148  /* Break down the tuples into fields */
1149  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1150  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1151  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1152  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1153  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1154  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1155 
1156  /*
1157  * Scan corresponding columns, allowing for dropped columns in different
1158  * places in the two rows. i1 and i2 are physical column indexes, j is
1159  * the logical column index.
1160  */
1161  i1 = i2 = j = 0;
1162  while (i1 < ncolumns1 || i2 < ncolumns2)
1163  {
1164  LOCAL_FCINFO(locfcinfo, 2);
1165  Form_pg_attribute att1;
1166  Form_pg_attribute att2;
1167  TypeCacheEntry *typentry;
1168  Oid collation;
1169  bool oprresult;
1170 
1171  /*
1172  * Skip dropped columns
1173  */
1174  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1175  {
1176  i1++;
1177  continue;
1178  }
1179  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1180  {
1181  i2++;
1182  continue;
1183  }
1184  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1185  break; /* we'll deal with mismatch below loop */
1186 
1187  att1 = TupleDescAttr(tupdesc1, i1);
1188  att2 = TupleDescAttr(tupdesc2, i2);
1189 
1190  /*
1191  * Have two matching columns, they must be same type
1192  */
1193  if (att1->atttypid != att2->atttypid)
1194  ereport(ERROR,
1195  (errcode(ERRCODE_DATATYPE_MISMATCH),
1196  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1197  format_type_be(att1->atttypid),
1198  format_type_be(att2->atttypid),
1199  j + 1)));
1200 
1201  /*
1202  * If they're not same collation, we don't complain here, but the
1203  * equality function might.
1204  */
1205  collation = att1->attcollation;
1206  if (collation != att2->attcollation)
1207  collation = InvalidOid;
1208 
1209  /*
1210  * Lookup the equality function if not done already
1211  */
1212  typentry = my_extra->columns[j].typentry;
1213  if (typentry == NULL ||
1214  typentry->type_id != att1->atttypid)
1215  {
1216  typentry = lookup_type_cache(att1->atttypid,
1218  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1219  ereport(ERROR,
1220  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1221  errmsg("could not identify an equality operator for type %s",
1222  format_type_be(typentry->type_id))));
1223  my_extra->columns[j].typentry = typentry;
1224  }
1225 
1226  /*
1227  * We consider two NULLs equal; NULL > not-NULL.
1228  */
1229  if (!nulls1[i1] || !nulls2[i2])
1230  {
1231  if (nulls1[i1] || nulls2[i2])
1232  {
1233  result = false;
1234  break;
1235  }
1236 
1237  /* Compare the pair of elements */
1238  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
1239  collation, NULL, NULL);
1240  locfcinfo->args[0].value = values1[i1];
1241  locfcinfo->args[0].isnull = false;
1242  locfcinfo->args[1].value = values2[i2];
1243  locfcinfo->args[1].isnull = false;
1244  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1245  if (locfcinfo->isnull || !oprresult)
1246  {
1247  result = false;
1248  break;
1249  }
1250  }
1251 
1252  /* equal, so continue to next column */
1253  i1++, i2++, j++;
1254  }
1255 
1256  /*
1257  * If we didn't break out of the loop early, check for column count
1258  * mismatch. (We do not report such mismatch if we found unequal column
1259  * values; is that a feature or a bug?)
1260  */
1261  if (result)
1262  {
1263  if (i1 != ncolumns1 || i2 != ncolumns2)
1264  ereport(ERROR,
1265  (errcode(ERRCODE_DATATYPE_MISMATCH),
1266  errmsg("cannot compare record types with different numbers of columns")));
1267  }
1268 
1269  pfree(values1);
1270  pfree(nulls1);
1271  pfree(values2);
1272  pfree(nulls2);
1273  ReleaseTupleDesc(tupdesc1);
1274  ReleaseTupleDesc(tupdesc2);
1275 
1276  /* Avoid leaking memory when handed toasted input. */
1277  PG_FREE_IF_COPY(record1, 0);
1278  PG_FREE_IF_COPY(record2, 1);
1279 
1280  PG_RETURN_BOOL(result);
1281 }
#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:141

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 1308 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1296 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1332 of file rowtypes.c.

1333 {
1336  int result = 0;
1337  Oid tupType1;
1338  Oid tupType2;
1339  int32 tupTypmod1;
1340  int32 tupTypmod2;
1341  TupleDesc tupdesc1;
1342  TupleDesc tupdesc2;
1343  HeapTupleData tuple1;
1344  HeapTupleData tuple2;
1345  int ncolumns1;
1346  int ncolumns2;
1347  RecordCompareData *my_extra;
1348  int ncols;
1349  Datum *values1;
1350  Datum *values2;
1351  bool *nulls1;
1352  bool *nulls2;
1353  int i1;
1354  int i2;
1355  int j;
1356 
1357  /* Extract type info from the tuples */
1358  tupType1 = HeapTupleHeaderGetTypeId(record1);
1359  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1360  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1361  ncolumns1 = tupdesc1->natts;
1362  tupType2 = HeapTupleHeaderGetTypeId(record2);
1363  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1364  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1365  ncolumns2 = tupdesc2->natts;
1366 
1367  /* Build temporary HeapTuple control structures */
1368  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1369  ItemPointerSetInvalid(&(tuple1.t_self));
1370  tuple1.t_tableOid = InvalidOid;
1371  tuple1.t_data = record1;
1372  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1373  ItemPointerSetInvalid(&(tuple2.t_self));
1374  tuple2.t_tableOid = InvalidOid;
1375  tuple2.t_data = record2;
1376 
1377  /*
1378  * We arrange to look up the needed comparison info just once per series
1379  * of calls, assuming the record types don't change underneath us.
1380  */
1381  ncols = Max(ncolumns1, ncolumns2);
1382  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1383  if (my_extra == NULL ||
1384  my_extra->ncolumns < ncols)
1385  {
1386  fcinfo->flinfo->fn_extra =
1388  offsetof(RecordCompareData, columns) +
1389  ncols * sizeof(ColumnCompareData));
1390  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1391  my_extra->ncolumns = ncols;
1392  my_extra->record1_type = InvalidOid;
1393  my_extra->record1_typmod = 0;
1394  my_extra->record2_type = InvalidOid;
1395  my_extra->record2_typmod = 0;
1396  }
1397 
1398  if (my_extra->record1_type != tupType1 ||
1399  my_extra->record1_typmod != tupTypmod1 ||
1400  my_extra->record2_type != tupType2 ||
1401  my_extra->record2_typmod != tupTypmod2)
1402  {
1403  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1404  my_extra->record1_type = tupType1;
1405  my_extra->record1_typmod = tupTypmod1;
1406  my_extra->record2_type = tupType2;
1407  my_extra->record2_typmod = tupTypmod2;
1408  }
1409 
1410  /* Break down the tuples into fields */
1411  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1412  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1413  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1414  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1415  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1416  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1417 
1418  /*
1419  * Scan corresponding columns, allowing for dropped columns in different
1420  * places in the two rows. i1 and i2 are physical column indexes, j is
1421  * the logical column index.
1422  */
1423  i1 = i2 = j = 0;
1424  while (i1 < ncolumns1 || i2 < ncolumns2)
1425  {
1426  Form_pg_attribute att1;
1427  Form_pg_attribute att2;
1428 
1429  /*
1430  * Skip dropped columns
1431  */
1432  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1433  {
1434  i1++;
1435  continue;
1436  }
1437  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1438  {
1439  i2++;
1440  continue;
1441  }
1442  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1443  break; /* we'll deal with mismatch below loop */
1444 
1445  att1 = TupleDescAttr(tupdesc1, i1);
1446  att2 = TupleDescAttr(tupdesc2, i2);
1447 
1448  /*
1449  * Have two matching columns, they must be same type
1450  */
1451  if (att1->atttypid != att2->atttypid)
1452  ereport(ERROR,
1453  (errcode(ERRCODE_DATATYPE_MISMATCH),
1454  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1455  format_type_be(att1->atttypid),
1456  format_type_be(att2->atttypid),
1457  j + 1)));
1458 
1459  /*
1460  * The same type should have the same length (or both should be
1461  * variable).
1462  */
1463  Assert(att1->attlen == att2->attlen);
1464 
1465  /*
1466  * We consider two NULLs equal; NULL > not-NULL.
1467  */
1468  if (!nulls1[i1] || !nulls2[i2])
1469  {
1470  int cmpresult = 0;
1471 
1472  if (nulls1[i1])
1473  {
1474  /* arg1 is greater than arg2 */
1475  result = 1;
1476  break;
1477  }
1478  if (nulls2[i2])
1479  {
1480  /* arg1 is less than arg2 */
1481  result = -1;
1482  break;
1483  }
1484 
1485  /* Compare the pair of elements */
1486  if (att1->attbyval)
1487  {
1488  if (values1[i1] != values2[i2])
1489  cmpresult = (values1[i1] < values2[i2]) ? -1 : 1;
1490  }
1491  else if (att1->attlen > 0)
1492  {
1493  cmpresult = memcmp(DatumGetPointer(values1[i1]),
1494  DatumGetPointer(values2[i2]),
1495  att1->attlen);
1496  }
1497  else if (att1->attlen == -1)
1498  {
1499  Size len1,
1500  len2;
1501  struct varlena *arg1val;
1502  struct varlena *arg2val;
1503 
1504  len1 = toast_raw_datum_size(values1[i1]);
1505  len2 = toast_raw_datum_size(values2[i2]);
1506  arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
1507  arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
1508 
1509  cmpresult = memcmp(VARDATA_ANY(arg1val),
1510  VARDATA_ANY(arg2val),
1511  Min(len1, len2) - VARHDRSZ);
1512  if ((cmpresult == 0) && (len1 != len2))
1513  cmpresult = (len1 < len2) ? -1 : 1;
1514 
1515  if ((Pointer) arg1val != (Pointer) values1[i1])
1516  pfree(arg1val);
1517  if ((Pointer) arg2val != (Pointer) values2[i2])
1518  pfree(arg2val);
1519  }
1520  else
1521  elog(ERROR, "unexpected attlen: %d", att1->attlen);
1522 
1523  if (cmpresult < 0)
1524  {
1525  /* arg1 is less than arg2 */
1526  result = -1;
1527  break;
1528  }
1529  else if (cmpresult > 0)
1530  {
1531  /* arg1 is greater than arg2 */
1532  result = 1;
1533  break;
1534  }
1535  }
1536 
1537  /* equal, so continue to next column */
1538  i1++, i2++, j++;
1539  }
1540 
1541  /*
1542  * If we didn't break out of the loop early, check for column count
1543  * mismatch. (We do not report such mismatch if we found unequal column
1544  * values; is that a feature or a bug?)
1545  */
1546  if (result == 0)
1547  {
1548  if (i1 != ncolumns1 || i2 != ncolumns2)
1549  ereport(ERROR,
1550  (errcode(ERRCODE_DATATYPE_MISMATCH),
1551  errmsg("cannot compare record types with different numbers of columns")));
1552  }
1553 
1554  pfree(values1);
1555  pfree(nulls1);
1556  pfree(values2);
1557  pfree(nulls2);
1558  ReleaseTupleDesc(tupdesc1);
1559  ReleaseTupleDesc(tupdesc2);
1560 
1561  /* Avoid leaking memory when handed toasted input. */
1562  PG_FREE_IF_COPY(record1, 0);
1563  PG_FREE_IF_COPY(record2, 1);
1564 
1565  return result;
1566 }
#define Min(x, y)
Definition: c.h:993
char * Pointer
Definition: c.h:472
#define VARHDRSZ
Definition: c.h:681
size_t Size
Definition: c.h:594
Size toast_raw_datum_size(Datum value)
Definition: detoast.c:545
#define PG_DETOAST_DATUM_PACKED(datum)
Definition: fmgr.h:248
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
Definition: c.h:676
#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 1578 of file rowtypes.c.

1579 {
1582  bool result = true;
1583  Oid tupType1;
1584  Oid tupType2;
1585  int32 tupTypmod1;
1586  int32 tupTypmod2;
1587  TupleDesc tupdesc1;
1588  TupleDesc tupdesc2;
1589  HeapTupleData tuple1;
1590  HeapTupleData tuple2;
1591  int ncolumns1;
1592  int ncolumns2;
1593  RecordCompareData *my_extra;
1594  int ncols;
1595  Datum *values1;
1596  Datum *values2;
1597  bool *nulls1;
1598  bool *nulls2;
1599  int i1;
1600  int i2;
1601  int j;
1602 
1603  /* Extract type info from the tuples */
1604  tupType1 = HeapTupleHeaderGetTypeId(record1);
1605  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1606  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1607  ncolumns1 = tupdesc1->natts;
1608  tupType2 = HeapTupleHeaderGetTypeId(record2);
1609  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1610  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1611  ncolumns2 = tupdesc2->natts;
1612 
1613  /* Build temporary HeapTuple control structures */
1614  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1615  ItemPointerSetInvalid(&(tuple1.t_self));
1616  tuple1.t_tableOid = InvalidOid;
1617  tuple1.t_data = record1;
1618  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1619  ItemPointerSetInvalid(&(tuple2.t_self));
1620  tuple2.t_tableOid = InvalidOid;
1621  tuple2.t_data = record2;
1622 
1623  /*
1624  * We arrange to look up the needed comparison info just once per series
1625  * of calls, assuming the record types don't change underneath us.
1626  */
1627  ncols = Max(ncolumns1, ncolumns2);
1628  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1629  if (my_extra == NULL ||
1630  my_extra->ncolumns < ncols)
1631  {
1632  fcinfo->flinfo->fn_extra =
1633  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1634  offsetof(RecordCompareData, columns) +
1635  ncols * sizeof(ColumnCompareData));
1636  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1637  my_extra->ncolumns = ncols;
1638  my_extra->record1_type = InvalidOid;
1639  my_extra->record1_typmod = 0;
1640  my_extra->record2_type = InvalidOid;
1641  my_extra->record2_typmod = 0;
1642  }
1643 
1644  if (my_extra->record1_type != tupType1 ||
1645  my_extra->record1_typmod != tupTypmod1 ||
1646  my_extra->record2_type != tupType2 ||
1647  my_extra->record2_typmod != tupTypmod2)
1648  {
1649  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1650  my_extra->record1_type = tupType1;
1651  my_extra->record1_typmod = tupTypmod1;
1652  my_extra->record2_type = tupType2;
1653  my_extra->record2_typmod = tupTypmod2;
1654  }
1655 
1656  /* Break down the tuples into fields */
1657  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1658  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1659  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1660  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1661  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1662  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1663 
1664  /*
1665  * Scan corresponding columns, allowing for dropped columns in different
1666  * places in the two rows. i1 and i2 are physical column indexes, j is
1667  * the logical column index.
1668  */
1669  i1 = i2 = j = 0;
1670  while (i1 < ncolumns1 || i2 < ncolumns2)
1671  {
1672  Form_pg_attribute att1;
1673  Form_pg_attribute att2;
1674 
1675  /*
1676  * Skip dropped columns
1677  */
1678  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1679  {
1680  i1++;
1681  continue;
1682  }
1683  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1684  {
1685  i2++;
1686  continue;
1687  }
1688  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1689  break; /* we'll deal with mismatch below loop */
1690 
1691  att1 = TupleDescAttr(tupdesc1, i1);
1692  att2 = TupleDescAttr(tupdesc2, i2);
1693 
1694  /*
1695  * Have two matching columns, they must be same type
1696  */
1697  if (att1->atttypid != att2->atttypid)
1698  ereport(ERROR,
1699  (errcode(ERRCODE_DATATYPE_MISMATCH),
1700  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1701  format_type_be(att1->atttypid),
1702  format_type_be(att2->atttypid),
1703  j + 1)));
1704 
1705  /*
1706  * We consider two NULLs equal; NULL > not-NULL.
1707  */
1708  if (!nulls1[i1] || !nulls2[i2])
1709  {
1710  if (nulls1[i1] || nulls2[i2])
1711  {
1712  result = false;
1713  break;
1714  }
1715 
1716  /* Compare the pair of elements */
1717  result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen);
1718  if (!result)
1719  break;
1720  }
1721 
1722  /* equal, so continue to next column */
1723  i1++, i2++, j++;
1724  }
1725 
1726  /*
1727  * If we didn't break out of the loop early, check for column count
1728  * mismatch. (We do not report such mismatch if we found unequal column
1729  * values; is that a feature or a bug?)
1730  */
1731  if (result)
1732  {
1733  if (i1 != ncolumns1 || i2 != ncolumns2)
1734  ereport(ERROR,
1735  (errcode(ERRCODE_DATATYPE_MISMATCH),
1736  errmsg("cannot compare record types with different numbers of columns")));
1737  }
1738 
1739  pfree(values1);
1740  pfree(nulls1);
1741  pfree(values2);
1742  pfree(nulls2);
1743  ReleaseTupleDesc(tupdesc1);
1744  ReleaseTupleDesc(tupdesc2);
1745 
1746  /* Avoid leaking memory when handed toasted input. */
1747  PG_FREE_IF_COPY(record1, 0);
1748  PG_FREE_IF_COPY(record2, 1);
1749 
1750  PG_RETURN_BOOL(result);
1751 }
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 1778 of file rowtypes.c.

1779 {
1780  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1781 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1766 of file rowtypes.c.

1767 {
1768  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1769 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1772 of file rowtypes.c.

1773 {
1774  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1775 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1760 of file rowtypes.c.

1761 {
1762  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1763 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1754 of file rowtypes.c.

1755 {
1757 }
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1578

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

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 75 of file rowtypes.c.

76 {
77  char *string = PG_GETARG_CSTRING(0);
78  Oid tupType = PG_GETARG_OID(1);
79  int32 tupTypmod = PG_GETARG_INT32(2);
80  Node *escontext = fcinfo->context;
81  HeapTupleHeader result;
82  TupleDesc tupdesc;
83  HeapTuple tuple;
84  RecordIOData *my_extra;
85  bool needComma = false;
86  int ncolumns;
87  int i;
88  char *ptr;
89  Datum *values;
90  bool *nulls;
92 
93  check_stack_depth(); /* recurses for record-type columns */
94 
95  /*
96  * Give a friendly error message if we did not get enough info to identify
97  * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
98  * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
99  * for typmod, since composite types and RECORD have no type modifiers at
100  * the SQL level, and thus must fail for RECORD. However some callers can
101  * supply a valid typmod, and then we can do something useful for RECORD.
102  */
103  if (tupType == RECORDOID && tupTypmod < 0)
104  ereturn(escontext, (Datum) 0,
105  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
106  errmsg("input of anonymous composite types is not implemented")));
107 
108  /*
109  * This comes from the composite type's pg_type.oid and stores system oids
110  * in user tables, specifically DatumTupleFields. This oid must be
111  * preserved by binary upgrades.
112  */
113  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
114  ncolumns = tupdesc->natts;
115 
116  /*
117  * We arrange to look up the needed I/O info just once per series of
118  * calls, assuming the record type doesn't change underneath us.
119  */
120  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
121  if (my_extra == NULL ||
122  my_extra->ncolumns != ncolumns)
123  {
124  fcinfo->flinfo->fn_extra =
125  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
126  offsetof(RecordIOData, columns) +
127  ncolumns * sizeof(ColumnIOData));
128  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
129  my_extra->record_type = InvalidOid;
130  my_extra->record_typmod = 0;
131  }
132 
133  if (my_extra->record_type != tupType ||
134  my_extra->record_typmod != tupTypmod)
135  {
136  MemSet(my_extra, 0,
137  offsetof(RecordIOData, columns) +
138  ncolumns * sizeof(ColumnIOData));
139  my_extra->record_type = tupType;
140  my_extra->record_typmod = tupTypmod;
141  my_extra->ncolumns = ncolumns;
142  }
143 
144  values = (Datum *) palloc(ncolumns * sizeof(Datum));
145  nulls = (bool *) palloc(ncolumns * sizeof(bool));
146 
147  /*
148  * Scan the string. We use "buf" to accumulate the de-quoted data for
149  * each column, which is then fed to the appropriate input converter.
150  */
151  ptr = string;
152  /* Allow leading whitespace */
153  while (*ptr && isspace((unsigned char) *ptr))
154  ptr++;
155  if (*ptr++ != '(')
156  {
157  errsave(escontext,
158  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
159  errmsg("malformed record literal: \"%s\"", string),
160  errdetail("Missing left parenthesis.")));
161  goto fail;
162  }
163 
165 
166  for (i = 0; i < ncolumns; i++)
167  {
168  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
169  ColumnIOData *column_info = &my_extra->columns[i];
170  Oid column_type = att->atttypid;
171  char *column_data;
172 
173  /* Ignore dropped columns in datatype, but fill with nulls */
174  if (att->attisdropped)
175  {
176  values[i] = (Datum) 0;
177  nulls[i] = true;
178  continue;
179  }
180 
181  if (needComma)
182  {
183  /* Skip comma that separates prior field from this one */
184  if (*ptr == ',')
185  ptr++;
186  else
187  /* *ptr must be ')' */
188  {
189  errsave(escontext,
190  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
191  errmsg("malformed record literal: \"%s\"", string),
192  errdetail("Too few columns.")));
193  goto fail;
194  }
195  }
196 
197  /* Check for null: completely empty input means null */
198  if (*ptr == ',' || *ptr == ')')
199  {
200  column_data = NULL;
201  nulls[i] = true;
202  }
203  else
204  {
205  /* Extract string for this column */
206  bool inquote = false;
207 
209  while (inquote || !(*ptr == ',' || *ptr == ')'))
210  {
211  char ch = *ptr++;
212 
213  if (ch == '\0')
214  {
215  errsave(escontext,
216  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
217  errmsg("malformed record literal: \"%s\"",
218  string),
219  errdetail("Unexpected end of input.")));
220  goto fail;
221  }
222  if (ch == '\\')
223  {
224  if (*ptr == '\0')
225  {
226  errsave(escontext,
227  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
228  errmsg("malformed record literal: \"%s\"",
229  string),
230  errdetail("Unexpected end of input.")));
231  goto fail;
232  }
233  appendStringInfoChar(&buf, *ptr++);
234  }
235  else if (ch == '"')
236  {
237  if (!inquote)
238  inquote = true;
239  else if (*ptr == '"')
240  {
241  /* doubled quote within quote sequence */
242  appendStringInfoChar(&buf, *ptr++);
243  }
244  else
245  inquote = false;
246  }
247  else
249  }
250 
251  column_data = buf.data;
252  nulls[i] = false;
253  }
254 
255  /*
256  * Convert the column value
257  */
258  if (column_info->column_type != column_type)
259  {
260  getTypeInputInfo(column_type,
261  &column_info->typiofunc,
262  &column_info->typioparam);
263  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
264  fcinfo->flinfo->fn_mcxt);
265  column_info->column_type = column_type;
266  }
267 
268  if (!InputFunctionCallSafe(&column_info->proc,
269  column_data,
270  column_info->typioparam,
271  att->atttypmod,
272  escontext,
273  &values[i]))
274  goto fail;
275 
276  /*
277  * Prep for next column
278  */
279  needComma = true;
280  }
281 
282  if (*ptr++ != ')')
283  {
284  errsave(escontext,
285  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
286  errmsg("malformed record literal: \"%s\"", string),
287  errdetail("Too many columns.")));
288  goto fail;
289  }
290  /* Allow trailing whitespace */
291  while (*ptr && isspace((unsigned char) *ptr))
292  ptr++;
293  if (*ptr)
294  {
295  errsave(escontext,
296  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
297  errmsg("malformed record literal: \"%s\"", string),
298  errdetail("Junk after right parenthesis.")));
299  goto fail;
300  }
301 
302  tuple = heap_form_tuple(tupdesc, values, nulls);
303 
304  /*
305  * We cannot return tuple->t_data because heap_form_tuple allocates it as
306  * part of a larger chunk, and our caller may expect to be able to pfree
307  * our result. So must copy the info into a new palloc chunk.
308  */
309  result = (HeapTupleHeader) palloc(tuple->t_len);
310  memcpy(result, tuple->t_data, tuple->t_len);
311 
312  heap_freetuple(tuple);
313  pfree(buf.data);
314  pfree(values);
315  pfree(nulls);
316  ReleaseTupleDesc(tupdesc);
317 
319 
320  /* exit here once we've done lookup_rowtype_tupdesc */
321 fail:
322  ReleaseTupleDesc(tupdesc);
323  PG_RETURN_NULL();
324 }
int errdetail(const char *fmt,...)
Definition: elog.c:1202
#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:1117
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2856
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_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1302 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1290 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1284 of file rowtypes.c.

1285 {
1287 }
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1068

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

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 330 of file rowtypes.c.

331 {
333  Oid tupType;
334  int32 tupTypmod;
335  TupleDesc tupdesc;
336  HeapTupleData tuple;
337  RecordIOData *my_extra;
338  bool needComma = false;
339  int ncolumns;
340  int i;
341  Datum *values;
342  bool *nulls;
344 
345  check_stack_depth(); /* recurses for record-type columns */
346 
347  /* Extract type info from the tuple itself */
348  tupType = HeapTupleHeaderGetTypeId(rec);
349  tupTypmod = HeapTupleHeaderGetTypMod(rec);
350  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
351  ncolumns = tupdesc->natts;
352 
353  /* Build a temporary HeapTuple control structure */
355  ItemPointerSetInvalid(&(tuple.t_self));
356  tuple.t_tableOid = InvalidOid;
357  tuple.t_data = rec;
358 
359  /*
360  * We arrange to look up the needed I/O info just once per series of
361  * calls, assuming the record type doesn't change underneath us.
362  */
363  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
364  if (my_extra == NULL ||
365  my_extra->ncolumns != ncolumns)
366  {
367  fcinfo->flinfo->fn_extra =
368  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
369  offsetof(RecordIOData, columns) +
370  ncolumns * sizeof(ColumnIOData));
371  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
372  my_extra->record_type = InvalidOid;
373  my_extra->record_typmod = 0;
374  }
375 
376  if (my_extra->record_type != tupType ||
377  my_extra->record_typmod != tupTypmod)
378  {
379  MemSet(my_extra, 0,
380  offsetof(RecordIOData, columns) +
381  ncolumns * sizeof(ColumnIOData));
382  my_extra->record_type = tupType;
383  my_extra->record_typmod = tupTypmod;
384  my_extra->ncolumns = ncolumns;
385  }
386 
387  values = (Datum *) palloc(ncolumns * sizeof(Datum));
388  nulls = (bool *) palloc(ncolumns * sizeof(bool));
389 
390  /* Break down the tuple into fields */
391  heap_deform_tuple(&tuple, tupdesc, values, nulls);
392 
393  /* And build the result string */
395 
396  appendStringInfoChar(&buf, '(');
397 
398  for (i = 0; i < ncolumns; i++)
399  {
400  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
401  ColumnIOData *column_info = &my_extra->columns[i];
402  Oid column_type = att->atttypid;
403  Datum attr;
404  char *value;
405  char *tmp;
406  bool nq;
407 
408  /* Ignore dropped columns in datatype */
409  if (att->attisdropped)
410  continue;
411 
412  if (needComma)
413  appendStringInfoChar(&buf, ',');
414  needComma = true;
415 
416  if (nulls[i])
417  {
418  /* emit nothing... */
419  continue;
420  }
421 
422  /*
423  * Convert the column value to text
424  */
425  if (column_info->column_type != column_type)
426  {
427  getTypeOutputInfo(column_type,
428  &column_info->typiofunc,
429  &column_info->typisvarlena);
430  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
431  fcinfo->flinfo->fn_mcxt);
432  column_info->column_type = column_type;
433  }
434 
435  attr = values[i];
436  value = OutputFunctionCall(&column_info->proc, attr);
437 
438  /* Detect whether we need double quotes for this value */
439  nq = (value[0] == '\0'); /* force quotes for empty string */
440  for (tmp = value; *tmp; tmp++)
441  {
442  char ch = *tmp;
443 
444  if (ch == '"' || ch == '\\' ||
445  ch == '(' || ch == ')' || ch == ',' ||
446  isspace((unsigned char) ch))
447  {
448  nq = true;
449  break;
450  }
451  }
452 
453  /* And emit the string */
454  if (nq)
456  for (tmp = value; *tmp; tmp++)
457  {
458  char ch = *tmp;
459 
460  if (ch == '"' || ch == '\\')
463  }
464  if (nq)
466  }
467 
468  appendStringInfoChar(&buf, ')');
469 
470  pfree(values);
471  pfree(nulls);
472  ReleaseTupleDesc(tupdesc);
473 
474  PG_RETURN_CSTRING(buf.data);
475 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
static struct @148 value
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2889
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:203
bool typisvarlena
Definition: rowtypes.c:40

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 481 of file rowtypes.c.

482 {
484  Oid tupType = PG_GETARG_OID(1);
485  int32 tupTypmod = PG_GETARG_INT32(2);
486  HeapTupleHeader result;
487  TupleDesc tupdesc;
488  HeapTuple tuple;
489  RecordIOData *my_extra;
490  int ncolumns;
491  int usercols;
492  int validcols;
493  int i;
494  Datum *values;
495  bool *nulls;
496 
497  check_stack_depth(); /* recurses for record-type columns */
498 
499  /*
500  * Give a friendly error message if we did not get enough info to identify
501  * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
502  * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
503  * for typmod, since composite types and RECORD have no type modifiers at
504  * the SQL level, and thus must fail for RECORD. However some callers can
505  * supply a valid typmod, and then we can do something useful for RECORD.
506  */
507  if (tupType == RECORDOID && tupTypmod < 0)
508  ereport(ERROR,
509  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
510  errmsg("input of anonymous composite types is not implemented")));
511 
512  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
513  ncolumns = tupdesc->natts;
514 
515  /*
516  * We arrange to look up the needed I/O info just once per series of
517  * calls, assuming the record type doesn't change underneath us.
518  */
519  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
520  if (my_extra == NULL ||
521  my_extra->ncolumns != ncolumns)
522  {
523  fcinfo->flinfo->fn_extra =
524  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
525  offsetof(RecordIOData, columns) +
526  ncolumns * sizeof(ColumnIOData));
527  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
528  my_extra->record_type = InvalidOid;
529  my_extra->record_typmod = 0;
530  }
531 
532  if (my_extra->record_type != tupType ||
533  my_extra->record_typmod != tupTypmod)
534  {
535  MemSet(my_extra, 0,
536  offsetof(RecordIOData, columns) +
537  ncolumns * sizeof(ColumnIOData));
538  my_extra->record_type = tupType;
539  my_extra->record_typmod = tupTypmod;
540  my_extra->ncolumns = ncolumns;
541  }
542 
543  values = (Datum *) palloc(ncolumns * sizeof(Datum));
544  nulls = (bool *) palloc(ncolumns * sizeof(bool));
545 
546  /* Fetch number of columns user thinks it has */
547  usercols = pq_getmsgint(buf, 4);
548 
549  /* Need to scan to count nondeleted columns */
550  validcols = 0;
551  for (i = 0; i < ncolumns; i++)
552  {
553  if (!TupleDescAttr(tupdesc, i)->attisdropped)
554  validcols++;
555  }
556  if (usercols != validcols)
557  ereport(ERROR,
558  (errcode(ERRCODE_DATATYPE_MISMATCH),
559  errmsg("wrong number of columns: %d, expected %d",
560  usercols, validcols)));
561 
562  /* Process each column */
563  for (i = 0; i < ncolumns; i++)
564  {
565  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
566  ColumnIOData *column_info = &my_extra->columns[i];
567  Oid column_type = att->atttypid;
568  Oid coltypoid;
569  int itemlen;
570  StringInfoData item_buf;
571  StringInfo bufptr;
572 
573  /* Ignore dropped columns in datatype, but fill with nulls */
574  if (att->attisdropped)
575  {
576  values[i] = (Datum) 0;
577  nulls[i] = true;
578  continue;
579  }
580 
581  /* Check column type recorded in the data */
582  coltypoid = pq_getmsgint(buf, sizeof(Oid));
583 
584  /*
585  * From a security standpoint, it doesn't matter whether the input's
586  * column type matches what we expect: the column type's receive
587  * function has to be robust enough to cope with invalid data.
588  * However, from a user-friendliness standpoint, it's nicer to
589  * complain about type mismatches than to throw "improper binary
590  * format" errors. But there's a problem: only built-in types have
591  * OIDs that are stable enough to believe that a mismatch is a real
592  * issue. So complain only if both OIDs are in the built-in range.
593  * Otherwise, carry on with the column type we "should" be getting.
594  */
595  if (coltypoid != column_type &&
596  coltypoid < FirstGenbkiObjectId &&
597  column_type < FirstGenbkiObjectId)
598  ereport(ERROR,
599  (errcode(ERRCODE_DATATYPE_MISMATCH),
600  errmsg("binary data has type %u (%s) instead of expected %u (%s) in record column %d",
601  coltypoid,
602  format_type_extended(coltypoid, -1,
604  column_type,
605  format_type_extended(column_type, -1,
607  i + 1)));
608 
609  /* Get and check the item length */
610  itemlen = pq_getmsgint(buf, 4);
611  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
612  ereport(ERROR,
613  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
614  errmsg("insufficient data left in message")));
615 
616  if (itemlen == -1)
617  {
618  /* -1 length means NULL */
619  bufptr = NULL;
620  nulls[i] = true;
621  }
622  else
623  {
624  char *strbuff;
625 
626  /*
627  * Rather than copying data around, we just initialize a
628  * StringInfo pointing to the correct portion of the message
629  * buffer.
630  */
631  strbuff = &buf->data[buf->cursor];
632  buf->cursor += itemlen;
633  initReadOnlyStringInfo(&item_buf, strbuff, itemlen);
634 
635  bufptr = &item_buf;
636  nulls[i] = false;
637  }
638 
639  /* Now call the column's receiveproc */
640  if (column_info->column_type != column_type)
641  {
642  getTypeBinaryInputInfo(column_type,
643  &column_info->typiofunc,
644  &column_info->typioparam);
645  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
646  fcinfo->flinfo->fn_mcxt);
647  column_info->column_type = column_type;
648  }
649 
650  values[i] = ReceiveFunctionCall(&column_info->proc,
651  bufptr,
652  column_info->typioparam,
653  att->atttypmod);
654 
655  if (bufptr)
656  {
657  /* Trouble if it didn't eat the whole buffer */
658  if (item_buf.cursor != itemlen)
659  ereport(ERROR,
660  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
661  errmsg("improper binary format in record column %d",
662  i + 1)));
663  }
664  }
665 
666  tuple = heap_form_tuple(tupdesc, values, nulls);
667 
668  /*
669  * We cannot return tuple->t_data because heap_form_tuple allocates it as
670  * part of a larger chunk, and our caller may expect to be able to pfree
671  * our result. So must copy the info into a new palloc chunk.
672  */
673  result = (HeapTupleHeader) palloc(tuple->t_len);
674  memcpy(result, tuple->t_data, tuple->t_len);
675 
676  heap_freetuple(tuple);
677  pfree(values);
678  pfree(nulls);
679  ReleaseTupleDesc(tupdesc);
680 
682 }
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:122
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:2922
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:418
StringInfoData * StringInfo
Definition: stringinfo.h:54
static void initReadOnlyStringInfo(StringInfo str, char *data, int len)
Definition: stringinfo.h:129
#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 688 of file rowtypes.c.

689 {
691  Oid tupType;
692  int32 tupTypmod;
693  TupleDesc tupdesc;
694  HeapTupleData tuple;
695  RecordIOData *my_extra;
696  int ncolumns;
697  int validcols;
698  int i;
699  Datum *values;
700  bool *nulls;
702 
703  check_stack_depth(); /* recurses for record-type columns */
704 
705  /* Extract type info from the tuple itself */
706  tupType = HeapTupleHeaderGetTypeId(rec);
707  tupTypmod = HeapTupleHeaderGetTypMod(rec);
708  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
709  ncolumns = tupdesc->natts;
710 
711  /* Build a temporary HeapTuple control structure */
713  ItemPointerSetInvalid(&(tuple.t_self));
714  tuple.t_tableOid = InvalidOid;
715  tuple.t_data = rec;
716 
717  /*
718  * We arrange to look up the needed I/O info just once per series of
719  * calls, assuming the record type doesn't change underneath us.
720  */
721  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
722  if (my_extra == NULL ||
723  my_extra->ncolumns != ncolumns)
724  {
725  fcinfo->flinfo->fn_extra =
726  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
727  offsetof(RecordIOData, columns) +
728  ncolumns * sizeof(ColumnIOData));
729  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
730  my_extra->record_type = InvalidOid;
731  my_extra->record_typmod = 0;
732  }
733 
734  if (my_extra->record_type != tupType ||
735  my_extra->record_typmod != tupTypmod)
736  {
737  MemSet(my_extra, 0,
738  offsetof(RecordIOData, columns) +
739  ncolumns * sizeof(ColumnIOData));
740  my_extra->record_type = tupType;
741  my_extra->record_typmod = tupTypmod;
742  my_extra->ncolumns = ncolumns;
743  }
744 
745  values = (Datum *) palloc(ncolumns * sizeof(Datum));
746  nulls = (bool *) palloc(ncolumns * sizeof(bool));
747 
748  /* Break down the tuple into fields */
749  heap_deform_tuple(&tuple, tupdesc, values, nulls);
750 
751  /* And build the result string */
753 
754  /* Need to scan to count nondeleted columns */
755  validcols = 0;
756  for (i = 0; i < ncolumns; i++)
757  {
758  if (!TupleDescAttr(tupdesc, i)->attisdropped)
759  validcols++;
760  }
761  pq_sendint32(&buf, validcols);
762 
763  for (i = 0; i < ncolumns; i++)
764  {
765  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
766  ColumnIOData *column_info = &my_extra->columns[i];
767  Oid column_type = att->atttypid;
768  Datum attr;
769  bytea *outputbytes;
770 
771  /* Ignore dropped columns in datatype */
772  if (att->attisdropped)
773  continue;
774 
775  pq_sendint32(&buf, column_type);
776 
777  if (nulls[i])
778  {
779  /* emit -1 data length to signify a NULL */
780  pq_sendint32(&buf, -1);
781  continue;
782  }
783 
784  /*
785  * Convert the column value to binary
786  */
787  if (column_info->column_type != column_type)
788  {
789  getTypeBinaryOutputInfo(column_type,
790  &column_info->typiofunc,
791  &column_info->typisvarlena);
792  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
793  fcinfo->flinfo->fn_mcxt);
794  column_info->column_type = column_type;
795  }
796 
797  attr = values[i];
798  outputbytes = SendFunctionCall(&column_info->proc, attr);
799  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
800  pq_sendbytes(&buf, VARDATA(outputbytes),
801  VARSIZE(outputbytes) - VARHDRSZ);
802  }
803 
804  pfree(values);
805  pfree(nulls);
806  ReleaseTupleDesc(tupdesc);
807 
809 }
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:2955
void pq_sendbytes(StringInfo buf, const void *data, int datalen)
Definition: pqformat.c:126
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:329
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:349
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:145
#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.