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

Go to the source code of this file.

Data Structures

struct  ColumnIOData
 
struct  RecordIOData
 
struct  ColumnCompareData
 
struct  RecordCompareData
 

Typedefs

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

Functions

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

Typedef Documentation

◆ ColumnCompareData

◆ ColumnIOData

typedef struct ColumnIOData ColumnIOData

◆ RecordCompareData

◆ RecordIOData

typedef struct RecordIOData RecordIOData

Function Documentation

◆ btrecordcmp()

Datum btrecordcmp ( PG_FUNCTION_ARGS  )

Definition at line 1313 of file rowtypes.c.

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

References PG_RETURN_INT32, and record_cmp().

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1801 of file rowtypes.c.

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

References PG_RETURN_INT32, and record_image_cmp().

◆ hash_record()

Datum hash_record ( PG_FUNCTION_ARGS  )

Definition at line 1812 of file rowtypes.c.

1813{
1815 uint32 result = 0;
1816 Oid tupType;
1817 int32 tupTypmod;
1818 TupleDesc tupdesc;
1819 HeapTupleData tuple;
1820 int ncolumns;
1821 RecordCompareData *my_extra;
1822 Datum *values;
1823 bool *nulls;
1824
1825 check_stack_depth(); /* recurses for record-type columns */
1826
1827 /* Extract type info from tuple */
1828 tupType = HeapTupleHeaderGetTypeId(record);
1829 tupTypmod = HeapTupleHeaderGetTypMod(record);
1830 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1831 ncolumns = tupdesc->natts;
1832
1833 /* Build temporary HeapTuple control structure */
1834 tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1835 ItemPointerSetInvalid(&(tuple.t_self));
1836 tuple.t_tableOid = InvalidOid;
1837 tuple.t_data = record;
1838
1839 /*
1840 * We arrange to look up the needed hashing info just once per series of
1841 * calls, assuming the record type doesn't change underneath us.
1842 */
1843 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1844 if (my_extra == NULL ||
1845 my_extra->ncolumns < ncolumns)
1846 {
1847 fcinfo->flinfo->fn_extra =
1848 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1849 offsetof(RecordCompareData, columns) +
1850 ncolumns * sizeof(ColumnCompareData));
1851 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1852 my_extra->ncolumns = ncolumns;
1853 my_extra->record1_type = InvalidOid;
1854 my_extra->record1_typmod = 0;
1855 }
1856
1857 if (my_extra->record1_type != tupType ||
1858 my_extra->record1_typmod != tupTypmod)
1859 {
1860 MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1861 my_extra->record1_type = tupType;
1862 my_extra->record1_typmod = tupTypmod;
1863 }
1864
1865 /* Break down the tuple into fields */
1866 values = (Datum *) palloc(ncolumns * sizeof(Datum));
1867 nulls = (bool *) palloc(ncolumns * sizeof(bool));
1868 heap_deform_tuple(&tuple, tupdesc, values, nulls);
1869
1870 for (int i = 0; i < ncolumns; i++)
1871 {
1873 TypeCacheEntry *typentry;
1875
1876 att = TupleDescAttr(tupdesc, i);
1877
1878 if (att->attisdropped)
1879 continue;
1880
1881 /*
1882 * Lookup the hash function if not done already
1883 */
1884 typentry = my_extra->columns[i].typentry;
1885 if (typentry == NULL ||
1886 typentry->type_id != att->atttypid)
1887 {
1888 typentry = lookup_type_cache(att->atttypid,
1890 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
1891 ereport(ERROR,
1892 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1893 errmsg("could not identify a hash function for type %s",
1894 format_type_be(typentry->type_id))));
1895 my_extra->columns[i].typentry = typentry;
1896 }
1897
1898 /* Compute hash of element */
1899 if (nulls[i])
1900 {
1901 element_hash = 0;
1902 }
1903 else
1904 {
1905 LOCAL_FCINFO(locfcinfo, 1);
1906
1907 InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1908 att->attcollation, NULL, NULL);
1909 locfcinfo->args[0].value = values[i];
1910 locfcinfo->args[0].isnull = false;
1912
1913 /* We don't expect hash support functions to return null */
1914 Assert(!locfcinfo->isnull);
1915 }
1916
1917 /* see hash_array() */
1918 result = (result << 5) - result + element_hash;
1919 }
1920
1921 pfree(values);
1922 pfree(nulls);
1923 ReleaseTupleDesc(tupdesc);
1924
1925 /* Avoid leaking memory when handed toasted input. */
1926 PG_FREE_IF_COPY(record, 0);
1927
1928 PG_RETURN_UINT32(result);
1929}
static uint32 element_hash(const void *key, Size keysize)
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define Assert(condition)
Definition: c.h:815
int32_t int32
Definition: c.h:484
uint32_t uint32
Definition: c.h:488
#define MemSet(start, val, len)
Definition: c.h:977
#define OidIsValid(objectId)
Definition: c.h:732
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define PG_RETURN_UINT32(x)
Definition: fmgr.h:355
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:312
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1346
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc(Size size)
Definition: mcxt.c:1317
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:227
uintptr_t Datum
Definition: postgres.h:69
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
void check_stack_depth(void)
Definition: stack_depth.c:95
TypeCacheEntry * typentry
Definition: rowtypes.c:56
Oid fn_oid
Definition: fmgr.h:59
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:66
int32 record1_typmod
Definition: rowtypes.c:63
FmgrInfo hash_proc_finfo
Definition: typcache.h:77
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:212
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:153
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1920
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:144

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

◆ hash_record_extended()

Datum hash_record_extended ( PG_FUNCTION_ARGS  )

Definition at line 1932 of file rowtypes.c.

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

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

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 823 of file rowtypes.c.

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

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

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

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1067 of file rowtypes.c.

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

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

Referenced by record_ne().

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1307 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1295 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1349 of file rowtypes.c.

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

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

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

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1595 of file rowtypes.c.

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

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

Referenced by record_image_ne().

◆ record_image_ge()

Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1795 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1783 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1789 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1777 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_image_cmp().

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1771 of file rowtypes.c.

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

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

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 74 of file rowtypes.c.

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

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

◆ record_larger()

Datum record_larger ( PG_FUNCTION_ARGS  )

Definition at line 1319 of file rowtypes.c.

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

References PG_GETARG_DATUM, PG_RETURN_DATUM, and record_cmp().

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1301 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1289 of file rowtypes.c.

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

References PG_RETURN_BOOL, and record_cmp().

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1283 of file rowtypes.c.

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

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

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 329 of file rowtypes.c.

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

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

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 480 of file rowtypes.c.

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

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

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 687 of file rowtypes.c.

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

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

◆ record_smaller()

Datum record_smaller ( PG_FUNCTION_ARGS  )

Definition at line 1328 of file rowtypes.c.

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

References PG_GETARG_DATUM, PG_RETURN_DATUM, and record_cmp().