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

Go to the source code of this file.

Data Structures

struct  ColumnIOData
 
struct  RecordIOData
 
struct  ColumnCompareData
 
struct  RecordCompareData
 

Typedefs

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

Functions

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

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

References PG_RETURN_INT32, and record_cmp().

1296 {
1297  PG_RETURN_INT32(record_cmp(fcinfo));
1298 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:805
#define PG_RETURN_INT32(x)
Definition: fmgr.h:353

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1765 of file rowtypes.c.

References PG_RETURN_INT32, and record_image_cmp().

1766 {
1768 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:353
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1313

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 805 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().

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

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1049 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().

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

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1289 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

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

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1277 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

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

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1313 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().

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

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1559 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().

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

◆ record_image_ge()

Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1759 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

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

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1747 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

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

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1753 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

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

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1741 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

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

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1735 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_image_eq().

1736 {
1738 }
#define DatumGetBool(X)
Definition: postgres.h:393
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:358
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1559

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 74 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.

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

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1283 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

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

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1271 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

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

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1265 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_eq().

1266 {
1268 }
#define DatumGetBool(X)
Definition: postgres.h:393
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:358
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1049

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 302 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.

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

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 453 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.

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

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 669 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.

670 {
672  Oid tupType;
673  int32 tupTypmod;
674  TupleDesc tupdesc;
675  HeapTupleData tuple;
676  RecordIOData *my_extra;
677  int ncolumns;
678  int validcols;
679  int i;
680  Datum *values;
681  bool *nulls;
683 
684  check_stack_depth(); /* recurses for record-type columns */
685 
686  /* Extract type info from the tuple itself */
687  tupType = HeapTupleHeaderGetTypeId(rec);
688  tupTypmod = HeapTupleHeaderGetTypMod(rec);
689  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
690  ncolumns = tupdesc->natts;
691 
692  /* Build a temporary HeapTuple control structure */
694  ItemPointerSetInvalid(&(tuple.t_self));
695  tuple.t_tableOid = InvalidOid;
696  tuple.t_data = rec;
697 
698  /*
699  * We arrange to look up the needed I/O info just once per series of
700  * calls, assuming the record type doesn't change underneath us.
701  */
702  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
703  if (my_extra == NULL ||
704  my_extra->ncolumns != ncolumns)
705  {
706  fcinfo->flinfo->fn_extra =
707  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
708  offsetof(RecordIOData, columns) +
709  ncolumns * sizeof(ColumnIOData));
710  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
711  my_extra->record_type = InvalidOid;
712  my_extra->record_typmod = 0;
713  }
714 
715  if (my_extra->record_type != tupType ||
716  my_extra->record_typmod != tupTypmod)
717  {
718  MemSet(my_extra, 0,
719  offsetof(RecordIOData, columns) +
720  ncolumns * sizeof(ColumnIOData));
721  my_extra->record_type = tupType;
722  my_extra->record_typmod = tupTypmod;
723  my_extra->ncolumns = ncolumns;
724  }
725 
726  values = (Datum *) palloc(ncolumns * sizeof(Datum));
727  nulls = (bool *) palloc(ncolumns * sizeof(bool));
728 
729  /* Break down the tuple into fields */
730  heap_deform_tuple(&tuple, tupdesc, values, nulls);
731 
732  /* And build the result string */
733  pq_begintypsend(&buf);
734 
735  /* Need to scan to count nondeleted columns */
736  validcols = 0;
737  for (i = 0; i < ncolumns; i++)
738  {
739  if (!TupleDescAttr(tupdesc, i)->attisdropped)
740  validcols++;
741  }
742  pq_sendint32(&buf, validcols);
743 
744  for (i = 0; i < ncolumns; i++)
745  {
746  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
747  ColumnIOData *column_info = &my_extra->columns[i];
748  Oid column_type = att->atttypid;
749  Datum attr;
750  bytea *outputbytes;
751 
752  /* Ignore dropped columns in datatype */
753  if (att->attisdropped)
754  continue;
755 
756  pq_sendint32(&buf, column_type);
757 
758  if (nulls[i])
759  {
760  /* emit -1 data length to signify a NULL */
761  pq_sendint32(&buf, -1);
762  continue;
763  }
764 
765  /*
766  * Convert the column value to binary
767  */
768  if (column_info->column_type != column_type)
769  {
770  getTypeBinaryOutputInfo(column_type,
771  &column_info->typiofunc,
772  &column_info->typisvarlena);
773  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
774  fcinfo->flinfo->fn_mcxt);
775  column_info->column_type = column_type;
776  }
777 
778  attr = values[i];
779  outputbytes = SendFunctionCall(&column_info->proc, attr);
780  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
781  pq_sendbytes(&buf, VARDATA(outputbytes),
782  VARSIZE(outputbytes) - VARHDRSZ);
783  }
784 
785  pfree(values);
786  pfree(nulls);
787  ReleaseTupleDesc(tupdesc);
788 
790 }
FmgrInfo proc
Definition: hstore_io.c:761
#define VARDATA(PTR)
Definition: postgres.h:302
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1710
Oid record_type
Definition: hstore_io.c:766
#define VARSIZE(PTR)
Definition: postgres.h:303
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
#define VARHDRSZ
Definition: c.h:569
#define MemSet(start, val, len)
Definition: c.h:950
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:369
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:311
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:363
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:468
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:145
void pfree(void *pointer)
Definition: mcxt.c:1057
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:3312
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1638
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:2850
uintptr_t Datum
Definition: postgres.h:367
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:458
#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:165
#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:950
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
int i
Definition: c.h:563
bool typisvarlena
Definition: rowtypes.c:39
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define offsetof(type, field)
Definition: c.h:669
Oid column_type
Definition: hstore_io.c:758
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:452