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

Go to the source code of this file.

Data Structures

struct  ColumnIOData
 
struct  RecordIOData
 
struct  ColumnCompareData
 
struct  RecordCompareData
 

Typedefs

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

Functions

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

Typedef Documentation

◆ ColumnCompareData

◆ ColumnIOData

typedef struct ColumnIOData ColumnIOData

◆ RecordCompareData

◆ RecordIOData

typedef struct RecordIOData RecordIOData

Function Documentation

◆ btrecordcmp()

Datum btrecordcmp ( PG_FUNCTION_ARGS  )

Definition at line 1313 of file rowtypes.c.

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

References PG_RETURN_INT32, and record_cmp().

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1783 of file rowtypes.c.

1784 {
1786 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1331

References PG_RETURN_INT32, and record_image_cmp().

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1794 of file rowtypes.c.

1795 {
1797  uint32 result = 0;
1798  Oid tupType;
1799  int32 tupTypmod;
1800  TupleDesc tupdesc;
1801  HeapTupleData tuple;
1802  int ncolumns;
1803  RecordCompareData *my_extra;
1804  Datum *values;
1805  bool *nulls;
1806 
1807  check_stack_depth(); /* recurses for record-type columns */
1808 
1809  /* Extract type info from tuple */
1810  tupType = HeapTupleHeaderGetTypeId(record);
1811  tupTypmod = HeapTupleHeaderGetTypMod(record);
1812  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1813  ncolumns = tupdesc->natts;
1814 
1815  /* Build temporary HeapTuple control structure */
1816  tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1817  ItemPointerSetInvalid(&(tuple.t_self));
1818  tuple.t_tableOid = InvalidOid;
1819  tuple.t_data = record;
1820 
1821  /*
1822  * We arrange to look up the needed hashing info just once per series of
1823  * calls, assuming the record type doesn't change underneath us.
1824  */
1825  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1826  if (my_extra == NULL ||
1827  my_extra->ncolumns < ncolumns)
1828  {
1829  fcinfo->flinfo->fn_extra =
1830  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1831  offsetof(RecordCompareData, columns) +
1832  ncolumns * sizeof(ColumnCompareData));
1833  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1834  my_extra->ncolumns = ncolumns;
1835  my_extra->record1_type = InvalidOid;
1836  my_extra->record1_typmod = 0;
1837  }
1838 
1839  if (my_extra->record1_type != tupType ||
1840  my_extra->record1_typmod != tupTypmod)
1841  {
1842  MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1843  my_extra->record1_type = tupType;
1844  my_extra->record1_typmod = tupTypmod;
1845  }
1846 
1847  /* Break down the tuple into fields */
1848  values = (Datum *) palloc(ncolumns * sizeof(Datum));
1849  nulls = (bool *) palloc(ncolumns * sizeof(bool));
1850  heap_deform_tuple(&tuple, tupdesc, values, nulls);
1851 
1852  for (int i = 0; i < ncolumns; i++)
1853  {
1854  Form_pg_attribute att;
1855  TypeCacheEntry *typentry;
1857 
1858  att = TupleDescAttr(tupdesc, i);
1859 
1860  if (att->attisdropped)
1861  continue;
1862 
1863  /*
1864  * Lookup the hash function if not done already
1865  */
1866  typentry = my_extra->columns[i].typentry;
1867  if (typentry == NULL ||
1868  typentry->type_id != att->atttypid)
1869  {
1870  typentry = lookup_type_cache(att->atttypid,
1872  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
1873  ereport(ERROR,
1874  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1875  errmsg("could not identify a hash function for type %s",
1876  format_type_be(typentry->type_id))));
1877  my_extra->columns[i].typentry = typentry;
1878  }
1879 
1880  /* Compute hash of element */
1881  if (nulls[i])
1882  {
1883  element_hash = 0;
1884  }
1885  else
1886  {
1887  LOCAL_FCINFO(locfcinfo, 1);
1888 
1889  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1890  att->attcollation, NULL, NULL);
1891  locfcinfo->args[0].value = values[i];
1892  locfcinfo->args[0].isnull = false;
1894 
1895  /* We don't expect hash support functions to return null */
1896  Assert(!locfcinfo->isnull);
1897  }
1898 
1899  /* see hash_array() */
1900  result = (result << 5) - result + element_hash;
1901  }
1902 
1903  pfree(values);
1904  pfree(nulls);
1905  ReleaseTupleDesc(tupdesc);
1906 
1907  /* Avoid leaking memory when handed toasted input. */
1908  PG_FREE_IF_COPY(record, 0);
1909 
1910  PG_RETURN_UINT32(result);
1911 }
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:152
unsigned int uint32
Definition: c.h:506
signed int int32
Definition: c.h:494
#define Assert(condition)
Definition: c.h:858
#define MemSet(start, val, len)
Definition: c.h:1020
#define OidIsValid(objectId)
Definition: c.h:775
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define PG_RETURN_UINT32(x)
Definition: fmgr.h:355
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
void pfree(void *pointer)
Definition: mcxt.c:1520
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1180
void * palloc(Size size)
Definition: mcxt.c:1316
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void check_stack_depth(void)
Definition: postgres.c:3531
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:222
uintptr_t Datum
Definition: postgres.h:64
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
TypeCacheEntry * typentry
Definition: rowtypes.c:56
Oid fn_oid
Definition: fmgr.h:59
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:66
int32 record1_typmod
Definition: rowtypes.c:63
FmgrInfo hash_proc_finfo
Definition: typcache.h:77
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1833
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:144

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

◆ hash_record_extended()

Datum hash_record_extended ( PG_FUNCTION_ARGS  )

Definition at line 1914 of file rowtypes.c.

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

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

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 823 of file rowtypes.c.

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

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

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

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1067 of file rowtypes.c.

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

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

Referenced by record_ne().

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1307 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1295 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1331 of file rowtypes.c.

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

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

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

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1577 of file rowtypes.c.

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

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1765 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1771 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1759 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1753 of file rowtypes.c.

1754 {
1756 }
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1577

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

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 74 of file rowtypes.c.

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

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

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1301 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1289 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1283 of file rowtypes.c.

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

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

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 329 of file rowtypes.c.

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

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

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 480 of file rowtypes.c.

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

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

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 687 of file rowtypes.c.

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

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