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

1324 {
1325  PG_RETURN_INT32(record_cmp(fcinfo));
1326 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:833

References PG_RETURN_INT32, and record_cmp().

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1793 of file rowtypes.c.

1794 {
1796 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1341

References PG_RETURN_INT32, and record_image_cmp().

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1804 of file rowtypes.c.

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

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

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

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

1318 {
1319  PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
1320 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1305 of file rowtypes.c.

1306 {
1307  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1308 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1341 of file rowtypes.c.

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

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

1788 {
1789  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1790 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1775 of file rowtypes.c.

1776 {
1777  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1778 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1781 of file rowtypes.c.

1782 {
1783  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1784 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1769 of file rowtypes.c.

1770 {
1771  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1772 }

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1763 of file rowtypes.c.

1764 {
1766 }
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1587

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:1556
#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, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2832
static char * buf
Definition: pg_test_fsync.c:67
char string[11]
Definition: preproc-type.c:52
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:75
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
Oid typioparam
Definition: hstore_io.c:814
FmgrInfo proc
Definition: hstore_io.c:815
Oid column_type
Definition: hstore_io.c:812
Definition: nodes.h:129
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:825
Oid record_type
Definition: hstore_io.c:820
int32 record_typmod
Definition: hstore_io.c:821

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

1312 {
1313  PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
1314 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1299 of file rowtypes.c.

1300 {
1301  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1302 }

References PG_RETURN_BOOL, and record_cmp().

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1293 of file rowtypes.c.

1294 {
1296 }
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1077

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:1654
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
static struct @147 value
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2865
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:128
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  char csave;
573 
574  /* Ignore dropped columns in datatype, but fill with nulls */
575  if (att->attisdropped)
576  {
577  values[i] = (Datum) 0;
578  nulls[i] = true;
579  continue;
580  }
581 
582  /* Check column type recorded in the data */
583  coltypoid = pq_getmsgint(buf, sizeof(Oid));
584 
585  /*
586  * From a security standpoint, it doesn't matter whether the input's
587  * column type matches what we expect: the column type's receive
588  * function has to be robust enough to cope with invalid data.
589  * However, from a user-friendliness standpoint, it's nicer to
590  * complain about type mismatches than to throw "improper binary
591  * format" errors. But there's a problem: only built-in types have
592  * OIDs that are stable enough to believe that a mismatch is a real
593  * issue. So complain only if both OIDs are in the built-in range.
594  * Otherwise, carry on with the column type we "should" be getting.
595  */
596  if (coltypoid != column_type &&
597  coltypoid < FirstGenbkiObjectId &&
598  column_type < FirstGenbkiObjectId)
599  ereport(ERROR,
600  (errcode(ERRCODE_DATATYPE_MISMATCH),
601  errmsg("binary data has type %u (%s) instead of expected %u (%s) in record column %d",
602  coltypoid,
603  format_type_extended(coltypoid, -1,
605  column_type,
606  format_type_extended(column_type, -1,
608  i + 1)));
609 
610  /* Get and check the item length */
611  itemlen = pq_getmsgint(buf, 4);
612  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
613  ereport(ERROR,
614  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
615  errmsg("insufficient data left in message")));
616 
617  if (itemlen == -1)
618  {
619  /* -1 length means NULL */
620  bufptr = NULL;
621  nulls[i] = true;
622  csave = 0; /* keep compiler quiet */
623  }
624  else
625  {
626  /*
627  * Rather than copying data around, we just set up a phony
628  * StringInfo pointing to the correct portion of the input buffer.
629  * We assume we can scribble on the input buffer so as to maintain
630  * the convention that StringInfos have a trailing null.
631  */
632  item_buf.data = &buf->data[buf->cursor];
633  item_buf.maxlen = itemlen + 1;
634  item_buf.len = itemlen;
635  item_buf.cursor = 0;
636 
637  buf->cursor += itemlen;
638 
639  csave = buf->data[buf->cursor];
640  buf->data[buf->cursor] = '\0';
641 
642  bufptr = &item_buf;
643  nulls[i] = false;
644  }
645 
646  /* Now call the column's receiveproc */
647  if (column_info->column_type != column_type)
648  {
649  getTypeBinaryInputInfo(column_type,
650  &column_info->typiofunc,
651  &column_info->typioparam);
652  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
653  fcinfo->flinfo->fn_mcxt);
654  column_info->column_type = column_type;
655  }
656 
657  values[i] = ReceiveFunctionCall(&column_info->proc,
658  bufptr,
659  column_info->typioparam,
660  att->atttypmod);
661 
662  if (bufptr)
663  {
664  /* Trouble if it didn't eat the whole buffer */
665  if (item_buf.cursor != itemlen)
666  ereport(ERROR,
667  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
668  errmsg("improper binary format in record column %d",
669  i + 1)));
670 
671  buf->data[buf->cursor] = csave;
672  }
673  }
674 
675  tuple = heap_form_tuple(tupdesc, values, nulls);
676 
677  /*
678  * We cannot return tuple->t_data because heap_form_tuple allocates it as
679  * part of a larger chunk, and our caller may expect to be able to pfree
680  * our result. So must copy the info into a new palloc chunk.
681  */
682  result = (HeapTupleHeader) palloc(tuple->t_len);
683  memcpy(result, tuple->t_data, tuple->t_len);
684 
685  heap_freetuple(tuple);
686  pfree(values);
687  pfree(nulls);
688  ReleaseTupleDesc(tupdesc);
689 
691 }
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:122
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1668
#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:2898
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:418
StringInfoData * StringInfo
Definition: stringinfo.h:44
#define FirstGenbkiObjectId
Definition: transam.h:195

References buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, StringInfoData::cursor, StringInfoData::data, 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(), InvalidOid, StringInfoData::len, lookup_rowtype_tupdesc(), StringInfoData::maxlen, 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 697 of file rowtypes.c.

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