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

References PG_RETURN_INT32, and record_cmp().

1297 {
1298  PG_RETURN_INT32(record_cmp(fcinfo));
1299 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:806
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1766 of file rowtypes.c.

References PG_RETURN_INT32, and record_image_cmp().

1767 {
1769 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1314

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1777 of file rowtypes.c.

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, InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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.

1778 {
1780  uint32 result = 0;
1781  Oid tupType;
1782  int32 tupTypmod;
1783  TupleDesc tupdesc;
1784  HeapTupleData tuple;
1785  int ncolumns;
1786  RecordCompareData *my_extra;
1787  Datum *values;
1788  bool *nulls;
1789 
1790  check_stack_depth(); /* recurses for record-type columns */
1791 
1792  /* Extract type info from tuple */
1793  tupType = HeapTupleHeaderGetTypeId(record);
1794  tupTypmod = HeapTupleHeaderGetTypMod(record);
1795  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1796  ncolumns = tupdesc->natts;
1797 
1798  /* Build temporary HeapTuple control structure */
1799  tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1800  ItemPointerSetInvalid(&(tuple.t_self));
1801  tuple.t_tableOid = InvalidOid;
1802  tuple.t_data = record;
1803 
1804  /*
1805  * We arrange to look up the needed hashing info just once per series of
1806  * calls, assuming the record type doesn't change underneath us.
1807  */
1808  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1809  if (my_extra == NULL ||
1810  my_extra->ncolumns < ncolumns)
1811  {
1812  fcinfo->flinfo->fn_extra =
1813  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1814  offsetof(RecordCompareData, columns) +
1815  ncolumns * sizeof(ColumnCompareData));
1816  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1817  my_extra->ncolumns = ncolumns;
1818  my_extra->record1_type = InvalidOid;
1819  my_extra->record1_typmod = 0;
1820  }
1821 
1822  if (my_extra->record1_type != tupType ||
1823  my_extra->record1_typmod != tupTypmod)
1824  {
1825  MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1826  my_extra->record1_type = tupType;
1827  my_extra->record1_typmod = tupTypmod;
1828  }
1829 
1830  /* Break down the tuple into fields */
1831  values = (Datum *) palloc(ncolumns * sizeof(Datum));
1832  nulls = (bool *) palloc(ncolumns * sizeof(bool));
1833  heap_deform_tuple(&tuple, tupdesc, values, nulls);
1834 
1835  for (int i = 0; i < ncolumns; i++)
1836  {
1837  Form_pg_attribute att;
1838  TypeCacheEntry *typentry;
1840 
1841  att = TupleDescAttr(tupdesc, i);
1842 
1843  if (att->attisdropped)
1844  continue;
1845 
1846  /*
1847  * Lookup the hash function if not done already
1848  */
1849  typentry = my_extra->columns[i].typentry;
1850  if (typentry == NULL ||
1851  typentry->type_id != att->atttypid)
1852  {
1853  typentry = lookup_type_cache(att->atttypid,
1855  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
1856  ereport(ERROR,
1857  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1858  errmsg("could not identify a hash function for type %s",
1859  format_type_be(typentry->type_id))));
1860  my_extra->columns[i].typentry = typentry;
1861  }
1862 
1863  /* Compute hash of element */
1864  if (nulls[i])
1865  {
1866  element_hash = 0;
1867  }
1868  else
1869  {
1870  LOCAL_FCINFO(locfcinfo, 1);
1871 
1872  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1873  att->attcollation, NULL, NULL);
1874  locfcinfo->args[0].value = values[i];
1875  locfcinfo->args[0].isnull = false;
1876  element_hash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
1877 
1878  /* We don't expect hash support functions to return null */
1879  Assert(!locfcinfo->isnull);
1880  }
1881 
1882  /* see hash_array() */
1883  result = (result << 5) - result + element_hash;
1884  }
1885 
1886  pfree(values);
1887  pfree(nulls);
1888  ReleaseTupleDesc(tupdesc);
1889 
1890  /* Avoid leaking memory when handed toasted input. */
1891  PG_FREE_IF_COPY(record, 0);
1892 
1893  PG_RETURN_UINT32(result);
1894 }
#define DatumGetUInt32(X)
Definition: postgres.h:530
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:143
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define PG_RETURN_UINT32(x)
Definition: fmgr.h:355
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
void check_stack_depth(void)
Definition: postgres.c:3469
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
unsigned int uint32
Definition: c.h:441
FmgrInfo hash_proc_finfo
Definition: typcache.h:77
Oid t_tableOid
Definition: htup.h:66
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:338
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
#define ereport(elevel,...)
Definition: elog.h:157
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define Assert(condition)
Definition: c.h:804
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
TypeCacheEntry * typentry
Definition: rowtypes.c:57
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ hash_record_extended()

Datum hash_record_extended ( PG_FUNCTION_ARGS  )

Definition at line 1897 of file rowtypes.c.

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, InitFunctionCallInfoData, Int64GetDatum(), InvalidOid, ItemPointerSetInvalid, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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.

1898 {
1900  uint64 seed = PG_GETARG_INT64(1);
1901  uint64 result = 0;
1902  Oid tupType;
1903  int32 tupTypmod;
1904  TupleDesc tupdesc;
1905  HeapTupleData tuple;
1906  int ncolumns;
1907  RecordCompareData *my_extra;
1908  Datum *values;
1909  bool *nulls;
1910 
1911  check_stack_depth(); /* recurses for record-type columns */
1912 
1913  /* Extract type info from tuple */
1914  tupType = HeapTupleHeaderGetTypeId(record);
1915  tupTypmod = HeapTupleHeaderGetTypMod(record);
1916  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1917  ncolumns = tupdesc->natts;
1918 
1919  /* Build temporary HeapTuple control structure */
1920  tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1921  ItemPointerSetInvalid(&(tuple.t_self));
1922  tuple.t_tableOid = InvalidOid;
1923  tuple.t_data = record;
1924 
1925  /*
1926  * We arrange to look up the needed hashing info just once per series of
1927  * calls, assuming the record type doesn't change underneath us.
1928  */
1929  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1930  if (my_extra == NULL ||
1931  my_extra->ncolumns < ncolumns)
1932  {
1933  fcinfo->flinfo->fn_extra =
1934  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1935  offsetof(RecordCompareData, columns) +
1936  ncolumns * sizeof(ColumnCompareData));
1937  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1938  my_extra->ncolumns = ncolumns;
1939  my_extra->record1_type = InvalidOid;
1940  my_extra->record1_typmod = 0;
1941  }
1942 
1943  if (my_extra->record1_type != tupType ||
1944  my_extra->record1_typmod != tupTypmod)
1945  {
1946  MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1947  my_extra->record1_type = tupType;
1948  my_extra->record1_typmod = tupTypmod;
1949  }
1950 
1951  /* Break down the tuple into fields */
1952  values = (Datum *) palloc(ncolumns * sizeof(Datum));
1953  nulls = (bool *) palloc(ncolumns * sizeof(bool));
1954  heap_deform_tuple(&tuple, tupdesc, values, nulls);
1955 
1956  for (int i = 0; i < ncolumns; i++)
1957  {
1958  Form_pg_attribute att;
1959  TypeCacheEntry *typentry;
1960  uint64 element_hash;
1961 
1962  att = TupleDescAttr(tupdesc, i);
1963 
1964  if (att->attisdropped)
1965  continue;
1966 
1967  /*
1968  * Lookup the hash function if not done already
1969  */
1970  typentry = my_extra->columns[i].typentry;
1971  if (typentry == NULL ||
1972  typentry->type_id != att->atttypid)
1973  {
1974  typentry = lookup_type_cache(att->atttypid,
1976  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
1977  ereport(ERROR,
1978  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1979  errmsg("could not identify an extended hash function for type %s",
1980  format_type_be(typentry->type_id))));
1981  my_extra->columns[i].typentry = typentry;
1982  }
1983 
1984  /* Compute hash of element */
1985  if (nulls[i])
1986  {
1987  element_hash = 0;
1988  }
1989  else
1990  {
1991  LOCAL_FCINFO(locfcinfo, 2);
1992 
1993  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
1994  att->attcollation, NULL, NULL);
1995  locfcinfo->args[0].value = values[i];
1996  locfcinfo->args[0].isnull = false;
1997  locfcinfo->args[1].value = Int64GetDatum(seed);
1998  locfcinfo->args[0].isnull = false;
1999  element_hash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
2000 
2001  /* We don't expect hash support functions to return null */
2002  Assert(!locfcinfo->isnull);
2003  }
2004 
2005  /* see hash_array_extended() */
2006  result = (result << 5) - result + element_hash;
2007  }
2008 
2009  pfree(values);
2010  pfree(nulls);
2011  ReleaseTupleDesc(tupdesc);
2012 
2013  /* Avoid leaking memory when handed toasted input. */
2014  PG_FREE_IF_COPY(record, 0);
2015 
2016  PG_RETURN_UINT64(result);
2017 }
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO
Definition: typcache.h:151
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
#define PG_RETURN_UINT64(x)
Definition: fmgr.h:369
#define OidIsValid(objectId)
Definition: c.h:710
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
void check_stack_depth(void)
Definition: postgres.c:3469
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1697
FmgrInfo hash_extended_proc_finfo
Definition: typcache.h:78
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:338
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
#define ereport(elevel,...)
Definition: elog.h:157
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define DatumGetUInt64(X)
Definition: postgres.h:678
#define Assert(condition)
Definition: c.h:804
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
TypeCacheEntry * typentry
Definition: rowtypes.c:57
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 806 of file rowtypes.c.

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, InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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().

807 {
810  int result = 0;
811  Oid tupType1;
812  Oid tupType2;
813  int32 tupTypmod1;
814  int32 tupTypmod2;
815  TupleDesc tupdesc1;
816  TupleDesc tupdesc2;
817  HeapTupleData tuple1;
818  HeapTupleData tuple2;
819  int ncolumns1;
820  int ncolumns2;
821  RecordCompareData *my_extra;
822  int ncols;
823  Datum *values1;
824  Datum *values2;
825  bool *nulls1;
826  bool *nulls2;
827  int i1;
828  int i2;
829  int j;
830 
831  check_stack_depth(); /* recurses for record-type columns */
832 
833  /* Extract type info from the tuples */
834  tupType1 = HeapTupleHeaderGetTypeId(record1);
835  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
836  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
837  ncolumns1 = tupdesc1->natts;
838  tupType2 = HeapTupleHeaderGetTypeId(record2);
839  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
840  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
841  ncolumns2 = tupdesc2->natts;
842 
843  /* Build temporary HeapTuple control structures */
844  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
845  ItemPointerSetInvalid(&(tuple1.t_self));
846  tuple1.t_tableOid = InvalidOid;
847  tuple1.t_data = record1;
848  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
849  ItemPointerSetInvalid(&(tuple2.t_self));
850  tuple2.t_tableOid = InvalidOid;
851  tuple2.t_data = record2;
852 
853  /*
854  * We arrange to look up the needed comparison info just once per series
855  * of calls, assuming the record types don't change underneath us.
856  */
857  ncols = Max(ncolumns1, ncolumns2);
858  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
859  if (my_extra == NULL ||
860  my_extra->ncolumns < ncols)
861  {
862  fcinfo->flinfo->fn_extra =
864  offsetof(RecordCompareData, columns) +
865  ncols * sizeof(ColumnCompareData));
866  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
867  my_extra->ncolumns = ncols;
868  my_extra->record1_type = InvalidOid;
869  my_extra->record1_typmod = 0;
870  my_extra->record2_type = InvalidOid;
871  my_extra->record2_typmod = 0;
872  }
873 
874  if (my_extra->record1_type != tupType1 ||
875  my_extra->record1_typmod != tupTypmod1 ||
876  my_extra->record2_type != tupType2 ||
877  my_extra->record2_typmod != tupTypmod2)
878  {
879  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
880  my_extra->record1_type = tupType1;
881  my_extra->record1_typmod = tupTypmod1;
882  my_extra->record2_type = tupType2;
883  my_extra->record2_typmod = tupTypmod2;
884  }
885 
886  /* Break down the tuples into fields */
887  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
888  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
889  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
890  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
891  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
892  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
893 
894  /*
895  * Scan corresponding columns, allowing for dropped columns in different
896  * places in the two rows. i1 and i2 are physical column indexes, j is
897  * the logical column index.
898  */
899  i1 = i2 = j = 0;
900  while (i1 < ncolumns1 || i2 < ncolumns2)
901  {
902  Form_pg_attribute att1;
903  Form_pg_attribute att2;
904  TypeCacheEntry *typentry;
905  Oid collation;
906 
907  /*
908  * Skip dropped columns
909  */
910  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
911  {
912  i1++;
913  continue;
914  }
915  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
916  {
917  i2++;
918  continue;
919  }
920  if (i1 >= ncolumns1 || i2 >= ncolumns2)
921  break; /* we'll deal with mismatch below loop */
922 
923  att1 = TupleDescAttr(tupdesc1, i1);
924  att2 = TupleDescAttr(tupdesc2, i2);
925 
926  /*
927  * Have two matching columns, they must be same type
928  */
929  if (att1->atttypid != att2->atttypid)
930  ereport(ERROR,
931  (errcode(ERRCODE_DATATYPE_MISMATCH),
932  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
933  format_type_be(att1->atttypid),
934  format_type_be(att2->atttypid),
935  j + 1)));
936 
937  /*
938  * If they're not same collation, we don't complain here, but the
939  * comparison function might.
940  */
941  collation = att1->attcollation;
942  if (collation != att2->attcollation)
943  collation = InvalidOid;
944 
945  /*
946  * Lookup the comparison function if not done already
947  */
948  typentry = my_extra->columns[j].typentry;
949  if (typentry == NULL ||
950  typentry->type_id != att1->atttypid)
951  {
952  typentry = lookup_type_cache(att1->atttypid,
954  if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
955  ereport(ERROR,
956  (errcode(ERRCODE_UNDEFINED_FUNCTION),
957  errmsg("could not identify a comparison function for type %s",
958  format_type_be(typentry->type_id))));
959  my_extra->columns[j].typentry = typentry;
960  }
961 
962  /*
963  * We consider two NULLs equal; NULL > not-NULL.
964  */
965  if (!nulls1[i1] || !nulls2[i2])
966  {
967  LOCAL_FCINFO(locfcinfo, 2);
968  int32 cmpresult;
969 
970  if (nulls1[i1])
971  {
972  /* arg1 is greater than arg2 */
973  result = 1;
974  break;
975  }
976  if (nulls2[i2])
977  {
978  /* arg1 is less than arg2 */
979  result = -1;
980  break;
981  }
982 
983  /* Compare the pair of elements */
984  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
985  collation, NULL, NULL);
986  locfcinfo->args[0].value = values1[i1];
987  locfcinfo->args[0].isnull = false;
988  locfcinfo->args[1].value = values2[i2];
989  locfcinfo->args[1].isnull = false;
990  cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
991 
992  /* We don't expect comparison support functions to return null */
993  Assert(!locfcinfo->isnull);
994 
995  if (cmpresult < 0)
996  {
997  /* arg1 is less than arg2 */
998  result = -1;
999  break;
1000  }
1001  else if (cmpresult > 0)
1002  {
1003  /* arg1 is greater than arg2 */
1004  result = 1;
1005  break;
1006  }
1007  }
1008 
1009  /* equal, so continue to next column */
1010  i1++, i2++, j++;
1011  }
1012 
1013  /*
1014  * If we didn't break out of the loop early, check for column count
1015  * mismatch. (We do not report such mismatch if we found unequal column
1016  * values; is that a feature or a bug?)
1017  */
1018  if (result == 0)
1019  {
1020  if (i1 != ncolumns1 || i2 != ncolumns2)
1021  ereport(ERROR,
1022  (errcode(ERRCODE_DATATYPE_MISMATCH),
1023  errmsg("cannot compare record types with different numbers of columns")));
1024  }
1025 
1026  pfree(values1);
1027  pfree(nulls1);
1028  pfree(values2);
1029  pfree(nulls2);
1030  ReleaseTupleDesc(tupdesc1);
1031  ReleaseTupleDesc(tupdesc2);
1032 
1033  /* Avoid leaking memory when handed toasted input. */
1034  PG_FREE_IF_COPY(record1, 0);
1035  PG_FREE_IF_COPY(record2, 1);
1036 
1037  return result;
1038 }
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define DatumGetInt32(X)
Definition: postgres.h:516
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
FmgrInfo cmp_proc_finfo
Definition: typcache.h:76
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
void check_stack_depth(void)
Definition: postgres.c:3469
int32 record2_typmod
Definition: rowtypes.c:66
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
uintptr_t Datum
Definition: postgres.h:411
FmgrInfo * flinfo
Definition: fmgr.h:87
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:338
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
#define ereport(elevel,...)
Definition: elog.h:157
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void * fn_extra
Definition: fmgr.h:64
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:142
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
TypeCacheEntry * typentry
Definition: rowtypes.c:57
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1050 of file rowtypes.c.

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, InitFunctionCallInfoData, InvalidOid, ItemPointerSetInvalid, LOCAL_FCINFO, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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().

1051 {
1054  bool result = true;
1055  Oid tupType1;
1056  Oid tupType2;
1057  int32 tupTypmod1;
1058  int32 tupTypmod2;
1059  TupleDesc tupdesc1;
1060  TupleDesc tupdesc2;
1061  HeapTupleData tuple1;
1062  HeapTupleData tuple2;
1063  int ncolumns1;
1064  int ncolumns2;
1065  RecordCompareData *my_extra;
1066  int ncols;
1067  Datum *values1;
1068  Datum *values2;
1069  bool *nulls1;
1070  bool *nulls2;
1071  int i1;
1072  int i2;
1073  int j;
1074 
1075  check_stack_depth(); /* recurses for record-type columns */
1076 
1077  /* Extract type info from the tuples */
1078  tupType1 = HeapTupleHeaderGetTypeId(record1);
1079  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1080  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1081  ncolumns1 = tupdesc1->natts;
1082  tupType2 = HeapTupleHeaderGetTypeId(record2);
1083  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1084  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1085  ncolumns2 = tupdesc2->natts;
1086 
1087  /* Build temporary HeapTuple control structures */
1088  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1089  ItemPointerSetInvalid(&(tuple1.t_self));
1090  tuple1.t_tableOid = InvalidOid;
1091  tuple1.t_data = record1;
1092  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1093  ItemPointerSetInvalid(&(tuple2.t_self));
1094  tuple2.t_tableOid = InvalidOid;
1095  tuple2.t_data = record2;
1096 
1097  /*
1098  * We arrange to look up the needed comparison info just once per series
1099  * of calls, assuming the record types don't change underneath us.
1100  */
1101  ncols = Max(ncolumns1, ncolumns2);
1102  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1103  if (my_extra == NULL ||
1104  my_extra->ncolumns < ncols)
1105  {
1106  fcinfo->flinfo->fn_extra =
1107  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1108  offsetof(RecordCompareData, columns) +
1109  ncols * sizeof(ColumnCompareData));
1110  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1111  my_extra->ncolumns = ncols;
1112  my_extra->record1_type = InvalidOid;
1113  my_extra->record1_typmod = 0;
1114  my_extra->record2_type = InvalidOid;
1115  my_extra->record2_typmod = 0;
1116  }
1117 
1118  if (my_extra->record1_type != tupType1 ||
1119  my_extra->record1_typmod != tupTypmod1 ||
1120  my_extra->record2_type != tupType2 ||
1121  my_extra->record2_typmod != tupTypmod2)
1122  {
1123  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1124  my_extra->record1_type = tupType1;
1125  my_extra->record1_typmod = tupTypmod1;
1126  my_extra->record2_type = tupType2;
1127  my_extra->record2_typmod = tupTypmod2;
1128  }
1129 
1130  /* Break down the tuples into fields */
1131  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1132  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1133  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1134  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1135  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1136  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1137 
1138  /*
1139  * Scan corresponding columns, allowing for dropped columns in different
1140  * places in the two rows. i1 and i2 are physical column indexes, j is
1141  * the logical column index.
1142  */
1143  i1 = i2 = j = 0;
1144  while (i1 < ncolumns1 || i2 < ncolumns2)
1145  {
1146  LOCAL_FCINFO(locfcinfo, 2);
1147  Form_pg_attribute att1;
1148  Form_pg_attribute att2;
1149  TypeCacheEntry *typentry;
1150  Oid collation;
1151  bool oprresult;
1152 
1153  /*
1154  * Skip dropped columns
1155  */
1156  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1157  {
1158  i1++;
1159  continue;
1160  }
1161  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1162  {
1163  i2++;
1164  continue;
1165  }
1166  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1167  break; /* we'll deal with mismatch below loop */
1168 
1169  att1 = TupleDescAttr(tupdesc1, i1);
1170  att2 = TupleDescAttr(tupdesc2, i2);
1171 
1172  /*
1173  * Have two matching columns, they must be same type
1174  */
1175  if (att1->atttypid != att2->atttypid)
1176  ereport(ERROR,
1177  (errcode(ERRCODE_DATATYPE_MISMATCH),
1178  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1179  format_type_be(att1->atttypid),
1180  format_type_be(att2->atttypid),
1181  j + 1)));
1182 
1183  /*
1184  * If they're not same collation, we don't complain here, but the
1185  * equality function might.
1186  */
1187  collation = att1->attcollation;
1188  if (collation != att2->attcollation)
1189  collation = InvalidOid;
1190 
1191  /*
1192  * Lookup the equality function if not done already
1193  */
1194  typentry = my_extra->columns[j].typentry;
1195  if (typentry == NULL ||
1196  typentry->type_id != att1->atttypid)
1197  {
1198  typentry = lookup_type_cache(att1->atttypid,
1200  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1201  ereport(ERROR,
1202  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1203  errmsg("could not identify an equality operator for type %s",
1204  format_type_be(typentry->type_id))));
1205  my_extra->columns[j].typentry = typentry;
1206  }
1207 
1208  /*
1209  * We consider two NULLs equal; NULL > not-NULL.
1210  */
1211  if (!nulls1[i1] || !nulls2[i2])
1212  {
1213  if (nulls1[i1] || nulls2[i2])
1214  {
1215  result = false;
1216  break;
1217  }
1218 
1219  /* Compare the pair of elements */
1220  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
1221  collation, NULL, NULL);
1222  locfcinfo->args[0].value = values1[i1];
1223  locfcinfo->args[0].isnull = false;
1224  locfcinfo->args[1].value = values2[i2];
1225  locfcinfo->args[1].isnull = false;
1226  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1227  if (locfcinfo->isnull || !oprresult)
1228  {
1229  result = false;
1230  break;
1231  }
1232  }
1233 
1234  /* equal, so continue to next column */
1235  i1++, i2++, j++;
1236  }
1237 
1238  /*
1239  * If we didn't break out of the loop early, check for column count
1240  * mismatch. (We do not report such mismatch if we found unequal column
1241  * values; is that a feature or a bug?)
1242  */
1243  if (result)
1244  {
1245  if (i1 != ncolumns1 || i2 != ncolumns2)
1246  ereport(ERROR,
1247  (errcode(ERRCODE_DATATYPE_MISMATCH),
1248  errmsg("cannot compare record types with different numbers of columns")));
1249  }
1250 
1251  pfree(values1);
1252  pfree(nulls1);
1253  pfree(values2);
1254  pfree(nulls2);
1255  ReleaseTupleDesc(tupdesc1);
1256  ReleaseTupleDesc(tupdesc2);
1257 
1258  /* Avoid leaking memory when handed toasted input. */
1259  PG_FREE_IF_COPY(record1, 0);
1260  PG_FREE_IF_COPY(record2, 1);
1261 
1262  PG_RETURN_BOOL(result);
1263 }
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:141
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
void check_stack_depth(void)
Definition: postgres.c:3469
int32 record2_typmod
Definition: rowtypes.c:66
#define DatumGetBool(X)
Definition: postgres.h:437
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
FmgrInfo eq_opr_finfo
Definition: typcache.h:75
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:338
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
#define ereport(elevel,...)
Definition: elog.h:157
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define Max(x, y)
Definition: c.h:980
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
TypeCacheEntry * typentry
Definition: rowtypes.c:57
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1290 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1291 {
1292  PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
1293 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:806
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1278 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1279 {
1280  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1281 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:806
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1314 of file rowtypes.c.

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, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, Min, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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().

1315 {
1318  int result = 0;
1319  Oid tupType1;
1320  Oid tupType2;
1321  int32 tupTypmod1;
1322  int32 tupTypmod2;
1323  TupleDesc tupdesc1;
1324  TupleDesc tupdesc2;
1325  HeapTupleData tuple1;
1326  HeapTupleData tuple2;
1327  int ncolumns1;
1328  int ncolumns2;
1329  RecordCompareData *my_extra;
1330  int ncols;
1331  Datum *values1;
1332  Datum *values2;
1333  bool *nulls1;
1334  bool *nulls2;
1335  int i1;
1336  int i2;
1337  int j;
1338 
1339  /* Extract type info from the tuples */
1340  tupType1 = HeapTupleHeaderGetTypeId(record1);
1341  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1342  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1343  ncolumns1 = tupdesc1->natts;
1344  tupType2 = HeapTupleHeaderGetTypeId(record2);
1345  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1346  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1347  ncolumns2 = tupdesc2->natts;
1348 
1349  /* Build temporary HeapTuple control structures */
1350  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1351  ItemPointerSetInvalid(&(tuple1.t_self));
1352  tuple1.t_tableOid = InvalidOid;
1353  tuple1.t_data = record1;
1354  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1355  ItemPointerSetInvalid(&(tuple2.t_self));
1356  tuple2.t_tableOid = InvalidOid;
1357  tuple2.t_data = record2;
1358 
1359  /*
1360  * We arrange to look up the needed comparison info just once per series
1361  * of calls, assuming the record types don't change underneath us.
1362  */
1363  ncols = Max(ncolumns1, ncolumns2);
1364  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1365  if (my_extra == NULL ||
1366  my_extra->ncolumns < ncols)
1367  {
1368  fcinfo->flinfo->fn_extra =
1370  offsetof(RecordCompareData, columns) +
1371  ncols * sizeof(ColumnCompareData));
1372  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1373  my_extra->ncolumns = ncols;
1374  my_extra->record1_type = InvalidOid;
1375  my_extra->record1_typmod = 0;
1376  my_extra->record2_type = InvalidOid;
1377  my_extra->record2_typmod = 0;
1378  }
1379 
1380  if (my_extra->record1_type != tupType1 ||
1381  my_extra->record1_typmod != tupTypmod1 ||
1382  my_extra->record2_type != tupType2 ||
1383  my_extra->record2_typmod != tupTypmod2)
1384  {
1385  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1386  my_extra->record1_type = tupType1;
1387  my_extra->record1_typmod = tupTypmod1;
1388  my_extra->record2_type = tupType2;
1389  my_extra->record2_typmod = tupTypmod2;
1390  }
1391 
1392  /* Break down the tuples into fields */
1393  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1394  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1395  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1396  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1397  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1398  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1399 
1400  /*
1401  * Scan corresponding columns, allowing for dropped columns in different
1402  * places in the two rows. i1 and i2 are physical column indexes, j is
1403  * the logical column index.
1404  */
1405  i1 = i2 = j = 0;
1406  while (i1 < ncolumns1 || i2 < ncolumns2)
1407  {
1408  Form_pg_attribute att1;
1409  Form_pg_attribute att2;
1410 
1411  /*
1412  * Skip dropped columns
1413  */
1414  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1415  {
1416  i1++;
1417  continue;
1418  }
1419  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1420  {
1421  i2++;
1422  continue;
1423  }
1424  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1425  break; /* we'll deal with mismatch below loop */
1426 
1427  att1 = TupleDescAttr(tupdesc1, i1);
1428  att2 = TupleDescAttr(tupdesc2, i2);
1429 
1430  /*
1431  * Have two matching columns, they must be same type
1432  */
1433  if (att1->atttypid != att2->atttypid)
1434  ereport(ERROR,
1435  (errcode(ERRCODE_DATATYPE_MISMATCH),
1436  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1437  format_type_be(att1->atttypid),
1438  format_type_be(att2->atttypid),
1439  j + 1)));
1440 
1441  /*
1442  * The same type should have the same length (or both should be
1443  * variable).
1444  */
1445  Assert(att1->attlen == att2->attlen);
1446 
1447  /*
1448  * We consider two NULLs equal; NULL > not-NULL.
1449  */
1450  if (!nulls1[i1] || !nulls2[i2])
1451  {
1452  int cmpresult = 0;
1453 
1454  if (nulls1[i1])
1455  {
1456  /* arg1 is greater than arg2 */
1457  result = 1;
1458  break;
1459  }
1460  if (nulls2[i2])
1461  {
1462  /* arg1 is less than arg2 */
1463  result = -1;
1464  break;
1465  }
1466 
1467  /* Compare the pair of elements */
1468  if (att1->attbyval)
1469  {
1470  if (values1[i1] != values2[i2])
1471  cmpresult = (values1[i1] < values2[i2]) ? -1 : 1;
1472  }
1473  else if (att1->attlen > 0)
1474  {
1475  cmpresult = memcmp(DatumGetPointer(values1[i1]),
1476  DatumGetPointer(values2[i2]),
1477  att1->attlen);
1478  }
1479  else if (att1->attlen == -1)
1480  {
1481  Size len1,
1482  len2;
1483  struct varlena *arg1val;
1484  struct varlena *arg2val;
1485 
1486  len1 = toast_raw_datum_size(values1[i1]);
1487  len2 = toast_raw_datum_size(values2[i2]);
1488  arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
1489  arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
1490 
1491  cmpresult = memcmp(VARDATA_ANY(arg1val),
1492  VARDATA_ANY(arg2val),
1493  Min(len1, len2) - VARHDRSZ);
1494  if ((cmpresult == 0) && (len1 != len2))
1495  cmpresult = (len1 < len2) ? -1 : 1;
1496 
1497  if ((Pointer) arg1val != (Pointer) values1[i1])
1498  pfree(arg1val);
1499  if ((Pointer) arg2val != (Pointer) values2[i2])
1500  pfree(arg2val);
1501  }
1502  else
1503  elog(ERROR, "unexpected attlen: %d", att1->attlen);
1504 
1505  if (cmpresult < 0)
1506  {
1507  /* arg1 is less than arg2 */
1508  result = -1;
1509  break;
1510  }
1511  else if (cmpresult > 0)
1512  {
1513  /* arg1 is greater than arg2 */
1514  result = 1;
1515  break;
1516  }
1517  }
1518 
1519  /* equal, so continue to next column */
1520  i1++, i2++, j++;
1521  }
1522 
1523  /*
1524  * If we didn't break out of the loop early, check for column count
1525  * mismatch. (We do not report such mismatch if we found unequal column
1526  * values; is that a feature or a bug?)
1527  */
1528  if (result == 0)
1529  {
1530  if (i1 != ncolumns1 || i2 != ncolumns2)
1531  ereport(ERROR,
1532  (errcode(ERRCODE_DATATYPE_MISMATCH),
1533  errmsg("cannot compare record types with different numbers of columns")));
1534  }
1535 
1536  pfree(values1);
1537  pfree(nulls1);
1538  pfree(values2);
1539  pfree(nulls2);
1540  ReleaseTupleDesc(tupdesc1);
1541  ReleaseTupleDesc(tupdesc2);
1542 
1543  /* Avoid leaking memory when handed toasted input. */
1544  PG_FREE_IF_COPY(record1, 0);
1545  PG_FREE_IF_COPY(record2, 1);
1546 
1547  return result;
1548 }
#define VARDATA_ANY(PTR)
Definition: postgres.h:361
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define VARHDRSZ
Definition: c.h:627
#define Min(x, y)
Definition: c.h:986
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
void pfree(void *pointer)
Definition: mcxt.c:1169
char * Pointer
Definition: c.h:418
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define PG_DETOAST_DATUM_PACKED(datum)
Definition: fmgr.h:248
uint32 t_len
Definition: htup.h:64
int32 record2_typmod
Definition: rowtypes.c:66
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
Size toast_raw_datum_size(Datum value)
Definition: detoast.c:545
uintptr_t Datum
Definition: postgres.h:411
FmgrInfo * flinfo
Definition: fmgr.h:87
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
size_t Size
Definition: c.h:540
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void * fn_extra
Definition: fmgr.h:64
#define DatumGetPointer(X)
Definition: postgres.h:593
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
#define elog(elevel,...)
Definition: elog.h:232
Definition: c.h:621
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1560 of file rowtypes.c.

References RecordCompareData::columns, datum_image_eq(), ereport, errcode(), errmsg(), ERROR, format_type_be(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordCompareData::ncolumns, offsetof, 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().

1561 {
1564  bool result = true;
1565  Oid tupType1;
1566  Oid tupType2;
1567  int32 tupTypmod1;
1568  int32 tupTypmod2;
1569  TupleDesc tupdesc1;
1570  TupleDesc tupdesc2;
1571  HeapTupleData tuple1;
1572  HeapTupleData tuple2;
1573  int ncolumns1;
1574  int ncolumns2;
1575  RecordCompareData *my_extra;
1576  int ncols;
1577  Datum *values1;
1578  Datum *values2;
1579  bool *nulls1;
1580  bool *nulls2;
1581  int i1;
1582  int i2;
1583  int j;
1584 
1585  /* Extract type info from the tuples */
1586  tupType1 = HeapTupleHeaderGetTypeId(record1);
1587  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1588  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1589  ncolumns1 = tupdesc1->natts;
1590  tupType2 = HeapTupleHeaderGetTypeId(record2);
1591  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1592  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1593  ncolumns2 = tupdesc2->natts;
1594 
1595  /* Build temporary HeapTuple control structures */
1596  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1597  ItemPointerSetInvalid(&(tuple1.t_self));
1598  tuple1.t_tableOid = InvalidOid;
1599  tuple1.t_data = record1;
1600  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1601  ItemPointerSetInvalid(&(tuple2.t_self));
1602  tuple2.t_tableOid = InvalidOid;
1603  tuple2.t_data = record2;
1604 
1605  /*
1606  * We arrange to look up the needed comparison info just once per series
1607  * of calls, assuming the record types don't change underneath us.
1608  */
1609  ncols = Max(ncolumns1, ncolumns2);
1610  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1611  if (my_extra == NULL ||
1612  my_extra->ncolumns < ncols)
1613  {
1614  fcinfo->flinfo->fn_extra =
1615  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1616  offsetof(RecordCompareData, columns) +
1617  ncols * sizeof(ColumnCompareData));
1618  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1619  my_extra->ncolumns = ncols;
1620  my_extra->record1_type = InvalidOid;
1621  my_extra->record1_typmod = 0;
1622  my_extra->record2_type = InvalidOid;
1623  my_extra->record2_typmod = 0;
1624  }
1625 
1626  if (my_extra->record1_type != tupType1 ||
1627  my_extra->record1_typmod != tupTypmod1 ||
1628  my_extra->record2_type != tupType2 ||
1629  my_extra->record2_typmod != tupTypmod2)
1630  {
1631  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1632  my_extra->record1_type = tupType1;
1633  my_extra->record1_typmod = tupTypmod1;
1634  my_extra->record2_type = tupType2;
1635  my_extra->record2_typmod = tupTypmod2;
1636  }
1637 
1638  /* Break down the tuples into fields */
1639  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1640  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1641  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1642  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1643  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1644  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1645 
1646  /*
1647  * Scan corresponding columns, allowing for dropped columns in different
1648  * places in the two rows. i1 and i2 are physical column indexes, j is
1649  * the logical column index.
1650  */
1651  i1 = i2 = j = 0;
1652  while (i1 < ncolumns1 || i2 < ncolumns2)
1653  {
1654  Form_pg_attribute att1;
1655  Form_pg_attribute att2;
1656 
1657  /*
1658  * Skip dropped columns
1659  */
1660  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1661  {
1662  i1++;
1663  continue;
1664  }
1665  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1666  {
1667  i2++;
1668  continue;
1669  }
1670  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1671  break; /* we'll deal with mismatch below loop */
1672 
1673  att1 = TupleDescAttr(tupdesc1, i1);
1674  att2 = TupleDescAttr(tupdesc2, i2);
1675 
1676  /*
1677  * Have two matching columns, they must be same type
1678  */
1679  if (att1->atttypid != att2->atttypid)
1680  ereport(ERROR,
1681  (errcode(ERRCODE_DATATYPE_MISMATCH),
1682  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1683  format_type_be(att1->atttypid),
1684  format_type_be(att2->atttypid),
1685  j + 1)));
1686 
1687  /*
1688  * We consider two NULLs equal; NULL > not-NULL.
1689  */
1690  if (!nulls1[i1] || !nulls2[i2])
1691  {
1692  if (nulls1[i1] || nulls2[i2])
1693  {
1694  result = false;
1695  break;
1696  }
1697 
1698  /* Compare the pair of elements */
1699  result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen);
1700  if (!result)
1701  break;
1702  }
1703 
1704  /* equal, so continue to next column */
1705  i1++, i2++, j++;
1706  }
1707 
1708  /*
1709  * If we didn't break out of the loop early, check for column count
1710  * mismatch. (We do not report such mismatch if we found unequal column
1711  * values; is that a feature or a bug?)
1712  */
1713  if (result)
1714  {
1715  if (i1 != ncolumns1 || i2 != ncolumns2)
1716  ereport(ERROR,
1717  (errcode(ERRCODE_DATATYPE_MISMATCH),
1718  errmsg("cannot compare record types with different numbers of columns")));
1719  }
1720 
1721  pfree(values1);
1722  pfree(nulls1);
1723  pfree(values2);
1724  pfree(nulls2);
1725  ReleaseTupleDesc(tupdesc1);
1726  ReleaseTupleDesc(tupdesc2);
1727 
1728  /* Avoid leaking memory when handed toasted input. */
1729  PG_FREE_IF_COPY(record1, 0);
1730  PG_FREE_IF_COPY(record2, 1);
1731 
1732  PG_RETURN_BOOL(result);
1733 }
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
int32 record1_typmod
Definition: rowtypes.c:64
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
int32 record2_typmod
Definition: rowtypes.c:66
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define Max(x, y)
Definition: c.h:980
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
bool datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:265
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:67
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_image_ge()

Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1760 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1761 {
1762  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1763 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1314
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1748 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1749 {
1750  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1751 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1314
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1754 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1755 {
1756  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1757 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1314
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1742 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1743 {
1744  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1745 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1314
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1736 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_image_eq().

1737 {
1739 }
#define DatumGetBool(X)
Definition: postgres.h:437
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1560

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 75 of file rowtypes.c.

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

76 {
77  char *string = PG_GETARG_CSTRING(0);
78  Oid tupType = PG_GETARG_OID(1);
79  int32 tupTypmod = PG_GETARG_INT32(2);
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  ereport(ERROR,
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  ereport(ERROR,
156  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
157  errmsg("malformed record literal: \"%s\"", string),
158  errdetail("Missing left parenthesis.")));
159 
160  initStringInfo(&buf);
161 
162  for (i = 0; i < ncolumns; i++)
163  {
164  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
165  ColumnIOData *column_info = &my_extra->columns[i];
166  Oid column_type = att->atttypid;
167  char *column_data;
168 
169  /* Ignore dropped columns in datatype, but fill with nulls */
170  if (att->attisdropped)
171  {
172  values[i] = (Datum) 0;
173  nulls[i] = true;
174  continue;
175  }
176 
177  if (needComma)
178  {
179  /* Skip comma that separates prior field from this one */
180  if (*ptr == ',')
181  ptr++;
182  else
183  /* *ptr must be ')' */
184  ereport(ERROR,
185  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
186  errmsg("malformed record literal: \"%s\"", string),
187  errdetail("Too few columns.")));
188  }
189 
190  /* Check for null: completely empty input means null */
191  if (*ptr == ',' || *ptr == ')')
192  {
193  column_data = NULL;
194  nulls[i] = true;
195  }
196  else
197  {
198  /* Extract string for this column */
199  bool inquote = false;
200 
201  resetStringInfo(&buf);
202  while (inquote || !(*ptr == ',' || *ptr == ')'))
203  {
204  char ch = *ptr++;
205 
206  if (ch == '\0')
207  ereport(ERROR,
208  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
209  errmsg("malformed record literal: \"%s\"",
210  string),
211  errdetail("Unexpected end of input.")));
212  if (ch == '\\')
213  {
214  if (*ptr == '\0')
215  ereport(ERROR,
216  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
217  errmsg("malformed record literal: \"%s\"",
218  string),
219  errdetail("Unexpected end of input.")));
220  appendStringInfoChar(&buf, *ptr++);
221  }
222  else if (ch == '"')
223  {
224  if (!inquote)
225  inquote = true;
226  else if (*ptr == '"')
227  {
228  /* doubled quote within quote sequence */
229  appendStringInfoChar(&buf, *ptr++);
230  }
231  else
232  inquote = false;
233  }
234  else
235  appendStringInfoChar(&buf, ch);
236  }
237 
238  column_data = buf.data;
239  nulls[i] = false;
240  }
241 
242  /*
243  * Convert the column value
244  */
245  if (column_info->column_type != column_type)
246  {
247  getTypeInputInfo(column_type,
248  &column_info->typiofunc,
249  &column_info->typioparam);
250  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
251  fcinfo->flinfo->fn_mcxt);
252  column_info->column_type = column_type;
253  }
254 
255  values[i] = InputFunctionCall(&column_info->proc,
256  column_data,
257  column_info->typioparam,
258  att->atttypmod);
259 
260  /*
261  * Prep for next column
262  */
263  needComma = true;
264  }
265 
266  if (*ptr++ != ')')
267  ereport(ERROR,
268  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
269  errmsg("malformed record literal: \"%s\"", string),
270  errdetail("Too many columns.")));
271  /* Allow trailing whitespace */
272  while (*ptr && isspace((unsigned char) *ptr))
273  ptr++;
274  if (*ptr)
275  ereport(ERROR,
276  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277  errmsg("malformed record literal: \"%s\"", string),
278  errdetail("Junk after right parenthesis.")));
279 
280  tuple = heap_form_tuple(tupdesc, values, nulls);
281 
282  /*
283  * We cannot return tuple->t_data because heap_form_tuple allocates it as
284  * part of a larger chunk, and our caller may expect to be able to pfree
285  * our result. So must copy the info into a new palloc chunk.
286  */
287  result = (HeapTupleHeader) palloc(tuple->t_len);
288  memcpy(result, tuple->t_data, tuple->t_len);
289 
290  heap_freetuple(tuple);
291  pfree(buf.data);
292  pfree(values);
293  pfree(nulls);
294  ReleaseTupleDesc(tupdesc);
295 
297 }
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
FmgrInfo proc
Definition: hstore_io.c:761
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
Oid record_type
Definition: hstore_io.c:766
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
int32 record_typmod
Definition: hstore_io.c:767
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
#define PG_RETURN_HEAPTUPLEHEADER(x)
Definition: fmgr.h:375
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:68
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
void check_stack_depth(void)
Definition: postgres.c:3469
int errdetail(const char *fmt,...)
Definition: elog.c:1042
char string[11]
Definition: preproc-type.c:46
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:75
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2821
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:771
uintptr_t Datum
Definition: postgres.h:411
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1529
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
Oid typioparam
Definition: hstore_io.c:760
static Datum values[MAXATTR]
Definition: bootstrap.c:166
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
Oid column_type
Definition: hstore_io.c:758

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1284 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1285 {
1286  PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
1287 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:806
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1272 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1273 {
1274  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1275 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:806
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1266 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_eq().

1267 {
1269 }
#define DatumGetBool(X)
Definition: postgres.h:437
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1050

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 303 of file rowtypes.c.

References appendStringInfoChar(), appendStringInfoCharMacro, buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, StringInfoData::data, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeOutputInfo(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, initStringInfo(), InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, offsetof, 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.

304 {
306  Oid tupType;
307  int32 tupTypmod;
308  TupleDesc tupdesc;
309  HeapTupleData tuple;
310  RecordIOData *my_extra;
311  bool needComma = false;
312  int ncolumns;
313  int i;
314  Datum *values;
315  bool *nulls;
317 
318  check_stack_depth(); /* recurses for record-type columns */
319 
320  /* Extract type info from the tuple itself */
321  tupType = HeapTupleHeaderGetTypeId(rec);
322  tupTypmod = HeapTupleHeaderGetTypMod(rec);
323  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
324  ncolumns = tupdesc->natts;
325 
326  /* Build a temporary HeapTuple control structure */
328  ItemPointerSetInvalid(&(tuple.t_self));
329  tuple.t_tableOid = InvalidOid;
330  tuple.t_data = rec;
331 
332  /*
333  * We arrange to look up the needed I/O info just once per series of
334  * calls, assuming the record type doesn't change underneath us.
335  */
336  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
337  if (my_extra == NULL ||
338  my_extra->ncolumns != ncolumns)
339  {
340  fcinfo->flinfo->fn_extra =
341  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
342  offsetof(RecordIOData, columns) +
343  ncolumns * sizeof(ColumnIOData));
344  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
345  my_extra->record_type = InvalidOid;
346  my_extra->record_typmod = 0;
347  }
348 
349  if (my_extra->record_type != tupType ||
350  my_extra->record_typmod != tupTypmod)
351  {
352  MemSet(my_extra, 0,
353  offsetof(RecordIOData, columns) +
354  ncolumns * sizeof(ColumnIOData));
355  my_extra->record_type = tupType;
356  my_extra->record_typmod = tupTypmod;
357  my_extra->ncolumns = ncolumns;
358  }
359 
360  values = (Datum *) palloc(ncolumns * sizeof(Datum));
361  nulls = (bool *) palloc(ncolumns * sizeof(bool));
362 
363  /* Break down the tuple into fields */
364  heap_deform_tuple(&tuple, tupdesc, values, nulls);
365 
366  /* And build the result string */
367  initStringInfo(&buf);
368 
369  appendStringInfoChar(&buf, '(');
370 
371  for (i = 0; i < ncolumns; i++)
372  {
373  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
374  ColumnIOData *column_info = &my_extra->columns[i];
375  Oid column_type = att->atttypid;
376  Datum attr;
377  char *value;
378  char *tmp;
379  bool nq;
380 
381  /* Ignore dropped columns in datatype */
382  if (att->attisdropped)
383  continue;
384 
385  if (needComma)
386  appendStringInfoChar(&buf, ',');
387  needComma = true;
388 
389  if (nulls[i])
390  {
391  /* emit nothing... */
392  continue;
393  }
394 
395  /*
396  * Convert the column value to text
397  */
398  if (column_info->column_type != column_type)
399  {
400  getTypeOutputInfo(column_type,
401  &column_info->typiofunc,
402  &column_info->typisvarlena);
403  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
404  fcinfo->flinfo->fn_mcxt);
405  column_info->column_type = column_type;
406  }
407 
408  attr = values[i];
409  value = OutputFunctionCall(&column_info->proc, attr);
410 
411  /* Detect whether we need double quotes for this value */
412  nq = (value[0] == '\0'); /* force quotes for empty string */
413  for (tmp = value; *tmp; tmp++)
414  {
415  char ch = *tmp;
416 
417  if (ch == '"' || ch == '\\' ||
418  ch == '(' || ch == ')' || ch == ',' ||
419  isspace((unsigned char) ch))
420  {
421  nq = true;
422  break;
423  }
424  }
425 
426  /* And emit the string */
427  if (nq)
428  appendStringInfoCharMacro(&buf, '"');
429  for (tmp = value; *tmp; tmp++)
430  {
431  char ch = *tmp;
432 
433  if (ch == '"' || ch == '\\')
434  appendStringInfoCharMacro(&buf, ch);
435  appendStringInfoCharMacro(&buf, ch);
436  }
437  if (nq)
438  appendStringInfoCharMacro(&buf, '"');
439  }
440 
441  appendStringInfoChar(&buf, ')');
442 
443  pfree(values);
444  pfree(nulls);
445  ReleaseTupleDesc(tupdesc);
446 
447  PG_RETURN_CSTRING(buf.data);
448 }
FmgrInfo proc
Definition: hstore_io.c:761
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2854
MemoryContext fn_mcxt
Definition: fmgr.h:65
static struct @142 value
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
Oid record_type
Definition: hstore_io.c:766
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define MemSet(start, val, len)
Definition: c.h:1008
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
int32 record_typmod
Definition: hstore_io.c:767
signed int int32
Definition: c.h:429
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1573
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:128
void pfree(void *pointer)
Definition: mcxt.c:1169
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:68
void check_stack_depth(void)
Definition: postgres.c:3469
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Oid t_tableOid
Definition: htup.h:66
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:771
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
#define InvalidOid
Definition: postgres_ext.h:36
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void * palloc(Size size)
Definition: mcxt.c:1062
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
bool typisvarlena
Definition: rowtypes.c:40
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
Oid column_type
Definition: hstore_io.c:758
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 454 of file rowtypes.c.

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, InvalidOid, StringInfoData::len, lookup_rowtype_tupdesc(), StringInfoData::maxlen, MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, offsetof, 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.

455 {
457  Oid tupType = PG_GETARG_OID(1);
458  int32 tupTypmod = PG_GETARG_INT32(2);
459  HeapTupleHeader result;
460  TupleDesc tupdesc;
461  HeapTuple tuple;
462  RecordIOData *my_extra;
463  int ncolumns;
464  int usercols;
465  int validcols;
466  int i;
467  Datum *values;
468  bool *nulls;
469 
470  check_stack_depth(); /* recurses for record-type columns */
471 
472  /*
473  * Give a friendly error message if we did not get enough info to identify
474  * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
475  * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
476  * for typmod, since composite types and RECORD have no type modifiers at
477  * the SQL level, and thus must fail for RECORD. However some callers can
478  * supply a valid typmod, and then we can do something useful for RECORD.
479  */
480  if (tupType == RECORDOID && tupTypmod < 0)
481  ereport(ERROR,
482  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
483  errmsg("input of anonymous composite types is not implemented")));
484 
485  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
486  ncolumns = tupdesc->natts;
487 
488  /*
489  * We arrange to look up the needed I/O info just once per series of
490  * calls, assuming the record type doesn't change underneath us.
491  */
492  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
493  if (my_extra == NULL ||
494  my_extra->ncolumns != ncolumns)
495  {
496  fcinfo->flinfo->fn_extra =
497  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
498  offsetof(RecordIOData, columns) +
499  ncolumns * sizeof(ColumnIOData));
500  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
501  my_extra->record_type = InvalidOid;
502  my_extra->record_typmod = 0;
503  }
504 
505  if (my_extra->record_type != tupType ||
506  my_extra->record_typmod != tupTypmod)
507  {
508  MemSet(my_extra, 0,
509  offsetof(RecordIOData, columns) +
510  ncolumns * sizeof(ColumnIOData));
511  my_extra->record_type = tupType;
512  my_extra->record_typmod = tupTypmod;
513  my_extra->ncolumns = ncolumns;
514  }
515 
516  values = (Datum *) palloc(ncolumns * sizeof(Datum));
517  nulls = (bool *) palloc(ncolumns * sizeof(bool));
518 
519  /* Fetch number of columns user thinks it has */
520  usercols = pq_getmsgint(buf, 4);
521 
522  /* Need to scan to count nondeleted columns */
523  validcols = 0;
524  for (i = 0; i < ncolumns; i++)
525  {
526  if (!TupleDescAttr(tupdesc, i)->attisdropped)
527  validcols++;
528  }
529  if (usercols != validcols)
530  ereport(ERROR,
531  (errcode(ERRCODE_DATATYPE_MISMATCH),
532  errmsg("wrong number of columns: %d, expected %d",
533  usercols, validcols)));
534 
535  /* Process each column */
536  for (i = 0; i < ncolumns; i++)
537  {
538  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
539  ColumnIOData *column_info = &my_extra->columns[i];
540  Oid column_type = att->atttypid;
541  Oid coltypoid;
542  int itemlen;
543  StringInfoData item_buf;
544  StringInfo bufptr;
545  char csave;
546 
547  /* Ignore dropped columns in datatype, but fill with nulls */
548  if (att->attisdropped)
549  {
550  values[i] = (Datum) 0;
551  nulls[i] = true;
552  continue;
553  }
554 
555  /* Check column type recorded in the data */
556  coltypoid = pq_getmsgint(buf, sizeof(Oid));
557 
558  /*
559  * From a security standpoint, it doesn't matter whether the input's
560  * column type matches what we expect: the column type's receive
561  * function has to be robust enough to cope with invalid data.
562  * However, from a user-friendliness standpoint, it's nicer to
563  * complain about type mismatches than to throw "improper binary
564  * format" errors. But there's a problem: only built-in types have
565  * OIDs that are stable enough to believe that a mismatch is a real
566  * issue. So complain only if both OIDs are in the built-in range.
567  * Otherwise, carry on with the column type we "should" be getting.
568  */
569  if (coltypoid != column_type &&
570  coltypoid < FirstGenbkiObjectId &&
571  column_type < FirstGenbkiObjectId)
572  ereport(ERROR,
573  (errcode(ERRCODE_DATATYPE_MISMATCH),
574  errmsg("binary data has type %u (%s) instead of expected %u (%s) in record column %d",
575  coltypoid,
576  format_type_extended(coltypoid, -1,
578  column_type,
579  format_type_extended(column_type, -1,
581  i + 1)));
582 
583  /* Get and check the item length */
584  itemlen = pq_getmsgint(buf, 4);
585  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
586  ereport(ERROR,
587  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
588  errmsg("insufficient data left in message")));
589 
590  if (itemlen == -1)
591  {
592  /* -1 length means NULL */
593  bufptr = NULL;
594  nulls[i] = true;
595  csave = 0; /* keep compiler quiet */
596  }
597  else
598  {
599  /*
600  * Rather than copying data around, we just set up a phony
601  * StringInfo pointing to the correct portion of the input buffer.
602  * We assume we can scribble on the input buffer so as to maintain
603  * the convention that StringInfos have a trailing null.
604  */
605  item_buf.data = &buf->data[buf->cursor];
606  item_buf.maxlen = itemlen + 1;
607  item_buf.len = itemlen;
608  item_buf.cursor = 0;
609 
610  buf->cursor += itemlen;
611 
612  csave = buf->data[buf->cursor];
613  buf->data[buf->cursor] = '\0';
614 
615  bufptr = &item_buf;
616  nulls[i] = false;
617  }
618 
619  /* Now call the column's receiveproc */
620  if (column_info->column_type != column_type)
621  {
622  getTypeBinaryInputInfo(column_type,
623  &column_info->typiofunc,
624  &column_info->typioparam);
625  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
626  fcinfo->flinfo->fn_mcxt);
627  column_info->column_type = column_type;
628  }
629 
630  values[i] = ReceiveFunctionCall(&column_info->proc,
631  bufptr,
632  column_info->typioparam,
633  att->atttypmod);
634 
635  if (bufptr)
636  {
637  /* Trouble if it didn't eat the whole buffer */
638  if (item_buf.cursor != itemlen)
639  ereport(ERROR,
640  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
641  errmsg("improper binary format in record column %d",
642  i + 1)));
643 
644  buf->data[buf->cursor] = csave;
645  }
646  }
647 
648  tuple = heap_form_tuple(tupdesc, values, nulls);
649 
650  /*
651  * We cannot return tuple->t_data because heap_form_tuple allocates it as
652  * part of a larger chunk, and our caller may expect to be able to pfree
653  * our result. So must copy the info into a new palloc chunk.
654  */
655  result = (HeapTupleHeader) palloc(tuple->t_len);
656  memcpy(result, tuple->t_data, tuple->t_len);
657 
658  heap_freetuple(tuple);
659  pfree(values);
660  pfree(nulls);
661  ReleaseTupleDesc(tupdesc);
662 
664 }
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
FmgrInfo proc
Definition: hstore_io.c:761
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
Oid record_type
Definition: hstore_io.c:766
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
StringInfoData * StringInfo
Definition: stringinfo.h:44
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
int32 record_typmod
Definition: hstore_io.c:767
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1587
#define PG_RETURN_HEAPTUPLEHEADER(x)
Definition: fmgr.h:375
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:68
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
void check_stack_depth(void)
Definition: postgres.c:3469
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2887
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:771
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:109
uintptr_t Datum
Definition: postgres.h:411
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
char * format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
Definition: format_type.c:112
Oid typioparam
Definition: hstore_io.c:760
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define FirstGenbkiObjectId
Definition: transam.h:188
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:417
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
Oid column_type
Definition: hstore_io.c:758

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 670 of file rowtypes.c.

References buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeBinaryOutputInfo(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), MemoryContextAlloc(), MemSet, TupleDescData::natts, RecordIOData::ncolumns, offsetof, 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.

671 {
673  Oid tupType;
674  int32 tupTypmod;
675  TupleDesc tupdesc;
676  HeapTupleData tuple;
677  RecordIOData *my_extra;
678  int ncolumns;
679  int validcols;
680  int i;
681  Datum *values;
682  bool *nulls;
684 
685  check_stack_depth(); /* recurses for record-type columns */
686 
687  /* Extract type info from the tuple itself */
688  tupType = HeapTupleHeaderGetTypeId(rec);
689  tupTypmod = HeapTupleHeaderGetTypMod(rec);
690  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
691  ncolumns = tupdesc->natts;
692 
693  /* Build a temporary HeapTuple control structure */
695  ItemPointerSetInvalid(&(tuple.t_self));
696  tuple.t_tableOid = InvalidOid;
697  tuple.t_data = rec;
698 
699  /*
700  * We arrange to look up the needed I/O info just once per series of
701  * calls, assuming the record type doesn't change underneath us.
702  */
703  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
704  if (my_extra == NULL ||
705  my_extra->ncolumns != ncolumns)
706  {
707  fcinfo->flinfo->fn_extra =
708  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
709  offsetof(RecordIOData, columns) +
710  ncolumns * sizeof(ColumnIOData));
711  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
712  my_extra->record_type = InvalidOid;
713  my_extra->record_typmod = 0;
714  }
715 
716  if (my_extra->record_type != tupType ||
717  my_extra->record_typmod != tupTypmod)
718  {
719  MemSet(my_extra, 0,
720  offsetof(RecordIOData, columns) +
721  ncolumns * sizeof(ColumnIOData));
722  my_extra->record_type = tupType;
723  my_extra->record_typmod = tupTypmod;
724  my_extra->ncolumns = ncolumns;
725  }
726 
727  values = (Datum *) palloc(ncolumns * sizeof(Datum));
728  nulls = (bool *) palloc(ncolumns * sizeof(bool));
729 
730  /* Break down the tuple into fields */
731  heap_deform_tuple(&tuple, tupdesc, values, nulls);
732 
733  /* And build the result string */
734  pq_begintypsend(&buf);
735 
736  /* Need to scan to count nondeleted columns */
737  validcols = 0;
738  for (i = 0; i < ncolumns; i++)
739  {
740  if (!TupleDescAttr(tupdesc, i)->attisdropped)
741  validcols++;
742  }
743  pq_sendint32(&buf, validcols);
744 
745  for (i = 0; i < ncolumns; i++)
746  {
747  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
748  ColumnIOData *column_info = &my_extra->columns[i];
749  Oid column_type = att->atttypid;
750  Datum attr;
751  bytea *outputbytes;
752 
753  /* Ignore dropped columns in datatype */
754  if (att->attisdropped)
755  continue;
756 
757  pq_sendint32(&buf, column_type);
758 
759  if (nulls[i])
760  {
761  /* emit -1 data length to signify a NULL */
762  pq_sendint32(&buf, -1);
763  continue;
764  }
765 
766  /*
767  * Convert the column value to binary
768  */
769  if (column_info->column_type != column_type)
770  {
771  getTypeBinaryOutputInfo(column_type,
772  &column_info->typiofunc,
773  &column_info->typisvarlena);
774  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
775  fcinfo->flinfo->fn_mcxt);
776  column_info->column_type = column_type;
777  }
778 
779  attr = values[i];
780  outputbytes = SendFunctionCall(&column_info->proc, attr);
781  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
782  pq_sendbytes(&buf, VARDATA(outputbytes),
783  VARSIZE(outputbytes) - VARHDRSZ);
784  }
785 
786  pfree(values);
787  pfree(nulls);
788  ReleaseTupleDesc(tupdesc);
789 
791 }
FmgrInfo proc
Definition: hstore_io.c:761
#define VARDATA(PTR)
Definition: postgres.h:315
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1826
Oid record_type
Definition: hstore_io.c:766
#define VARSIZE(PTR)
Definition: postgres.h:316
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
#define VARHDRSZ
Definition: c.h:627
#define MemSet(start, val, len)
Definition: c.h:1008
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
unsigned int Oid
Definition: postgres_ext.h:31
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
int32 record_typmod
Definition: hstore_io.c:767
signed int int32
Definition: c.h:429
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:467
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:145
void pfree(void *pointer)
Definition: mcxt.c:1169
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:68
void check_stack_depth(void)
Definition: postgres.c:3469
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1634
Oid t_tableOid
Definition: htup.h:66
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:771
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition: lsyscache.c:2920
uintptr_t Datum
Definition: postgres.h:411
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:457
#define InvalidOid
Definition: postgres_ext.h:36
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
void pq_sendbytes(StringInfo buf, const char *data, int datalen)
Definition: pqformat.c:125
void * palloc(Size size)
Definition: mcxt.c:1062
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
int i
Definition: c.h:621
bool typisvarlena
Definition: rowtypes.c:40
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:727
Oid column_type
Definition: hstore_io.c:758
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451