PostgreSQL Source Code  git master
rowtypes.c File Reference
#include "postgres.h"
#include <ctype.h>
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/builtins.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

◆ RecordCompareData

◆ RecordIOData

Function Documentation

◆ btrecordcmp()

Datum btrecordcmp ( PG_FUNCTION_ARGS  )

Definition at line 1273 of file rowtypes.c.

References PG_RETURN_INT32, and record_cmp().

1274 {
1275  PG_RETURN_INT32(record_cmp(fcinfo));
1276 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:784
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314

◆ btrecordimagecmp()

Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1779 of file rowtypes.c.

References PG_RETURN_INT32, and record_image_cmp().

1780 {
1782 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1291

◆ record_cmp()

static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 784 of file rowtypes.c.

References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, check_stack_depth(), TypeCacheEntry::cmp_proc_finfo, RecordCompareData::columns, DatumGetInt32, ereport, errcode(), errmsg(), ERROR, FunctionCallInfoData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, FmgrInfo::fn_oid, format_type_be(), FunctionCallInvoke, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, InitFunctionCallInfoData, InvalidOid, FunctionCallInfoData::isnull, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, tupleDesc::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().

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

◆ record_eq()

Datum record_eq ( PG_FUNCTION_ARGS  )

Definition at line 1026 of file rowtypes.c.

References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, 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, FunctionCallInfoData::isnull, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), lookup_type_cache(), Max, MemoryContextAlloc(), MemSet, tupleDesc::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().

1027 {
1030  bool result = true;
1031  Oid tupType1;
1032  Oid tupType2;
1033  int32 tupTypmod1;
1034  int32 tupTypmod2;
1035  TupleDesc tupdesc1;
1036  TupleDesc tupdesc2;
1037  HeapTupleData tuple1;
1038  HeapTupleData tuple2;
1039  int ncolumns1;
1040  int ncolumns2;
1041  RecordCompareData *my_extra;
1042  int ncols;
1043  Datum *values1;
1044  Datum *values2;
1045  bool *nulls1;
1046  bool *nulls2;
1047  int i1;
1048  int i2;
1049  int j;
1050 
1051  check_stack_depth(); /* recurses for record-type columns */
1052 
1053  /* Extract type info from the tuples */
1054  tupType1 = HeapTupleHeaderGetTypeId(record1);
1055  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1056  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1057  ncolumns1 = tupdesc1->natts;
1058  tupType2 = HeapTupleHeaderGetTypeId(record2);
1059  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1060  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1061  ncolumns2 = tupdesc2->natts;
1062 
1063  /* Build temporary HeapTuple control structures */
1064  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1065  ItemPointerSetInvalid(&(tuple1.t_self));
1066  tuple1.t_tableOid = InvalidOid;
1067  tuple1.t_data = record1;
1068  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1069  ItemPointerSetInvalid(&(tuple2.t_self));
1070  tuple2.t_tableOid = InvalidOid;
1071  tuple2.t_data = record2;
1072 
1073  /*
1074  * We arrange to look up the needed comparison info just once per series
1075  * of calls, assuming the record types don't change underneath us.
1076  */
1077  ncols = Max(ncolumns1, ncolumns2);
1078  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1079  if (my_extra == NULL ||
1080  my_extra->ncolumns < ncols)
1081  {
1082  fcinfo->flinfo->fn_extra =
1083  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1084  offsetof(RecordCompareData, columns) +
1085  ncols * sizeof(ColumnCompareData));
1086  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1087  my_extra->ncolumns = ncols;
1088  my_extra->record1_type = InvalidOid;
1089  my_extra->record1_typmod = 0;
1090  my_extra->record2_type = InvalidOid;
1091  my_extra->record2_typmod = 0;
1092  }
1093 
1094  if (my_extra->record1_type != tupType1 ||
1095  my_extra->record1_typmod != tupTypmod1 ||
1096  my_extra->record2_type != tupType2 ||
1097  my_extra->record2_typmod != tupTypmod2)
1098  {
1099  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1100  my_extra->record1_type = tupType1;
1101  my_extra->record1_typmod = tupTypmod1;
1102  my_extra->record2_type = tupType2;
1103  my_extra->record2_typmod = tupTypmod2;
1104  }
1105 
1106  /* Break down the tuples into fields */
1107  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1108  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1109  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1110  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1111  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1112  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1113 
1114  /*
1115  * Scan corresponding columns, allowing for dropped columns in different
1116  * places in the two rows. i1 and i2 are physical column indexes, j is
1117  * the logical column index.
1118  */
1119  i1 = i2 = j = 0;
1120  while (i1 < ncolumns1 || i2 < ncolumns2)
1121  {
1122  Form_pg_attribute att1;
1123  Form_pg_attribute att2;
1124  TypeCacheEntry *typentry;
1125  Oid collation;
1126  FunctionCallInfoData locfcinfo;
1127  bool oprresult;
1128 
1129  /*
1130  * Skip dropped columns
1131  */
1132  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1133  {
1134  i1++;
1135  continue;
1136  }
1137  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1138  {
1139  i2++;
1140  continue;
1141  }
1142  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1143  break; /* we'll deal with mismatch below loop */
1144 
1145  att1 = TupleDescAttr(tupdesc1, i1);
1146  att2 = TupleDescAttr(tupdesc2, i2);
1147 
1148  /*
1149  * Have two matching columns, they must be same type
1150  */
1151  if (att1->atttypid != att2->atttypid)
1152  ereport(ERROR,
1153  (errcode(ERRCODE_DATATYPE_MISMATCH),
1154  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1155  format_type_be(att1->atttypid),
1156  format_type_be(att2->atttypid),
1157  j + 1)));
1158 
1159  /*
1160  * If they're not same collation, we don't complain here, but the
1161  * equality function might.
1162  */
1163  collation = att1->attcollation;
1164  if (collation != att2->attcollation)
1165  collation = InvalidOid;
1166 
1167  /*
1168  * Lookup the equality function if not done already
1169  */
1170  typentry = my_extra->columns[j].typentry;
1171  if (typentry == NULL ||
1172  typentry->type_id != att1->atttypid)
1173  {
1174  typentry = lookup_type_cache(att1->atttypid,
1176  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1177  ereport(ERROR,
1178  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1179  errmsg("could not identify an equality operator for type %s",
1180  format_type_be(typentry->type_id))));
1181  my_extra->columns[j].typentry = typentry;
1182  }
1183 
1184  /*
1185  * We consider two NULLs equal; NULL > not-NULL.
1186  */
1187  if (!nulls1[i1] || !nulls2[i2])
1188  {
1189  if (nulls1[i1] || nulls2[i2])
1190  {
1191  result = false;
1192  break;
1193  }
1194 
1195  /* Compare the pair of elements */
1196  InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
1197  collation, NULL, NULL);
1198  locfcinfo.arg[0] = values1[i1];
1199  locfcinfo.arg[1] = values2[i2];
1200  locfcinfo.argnull[0] = false;
1201  locfcinfo.argnull[1] = false;
1202  locfcinfo.isnull = false;
1203  oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
1204  if (!oprresult)
1205  {
1206  result = false;
1207  break;
1208  }
1209  }
1210 
1211  /* equal, so continue to next column */
1212  i1++, i2++, j++;
1213  }
1214 
1215  /*
1216  * If we didn't break out of the loop early, check for column count
1217  * mismatch. (We do not report such mismatch if we found unequal column
1218  * values; is that a feature or a bug?)
1219  */
1220  if (result)
1221  {
1222  if (i1 != ncolumns1 || i2 != ncolumns2)
1223  ereport(ERROR,
1224  (errcode(ERRCODE_DATATYPE_MISMATCH),
1225  errmsg("cannot compare record types with different numbers of columns")));
1226  }
1227 
1228  pfree(values1);
1229  pfree(nulls1);
1230  pfree(values2);
1231  pfree(nulls2);
1232  ReleaseTupleDesc(tupdesc1);
1233  ReleaseTupleDesc(tupdesc2);
1234 
1235  /* Avoid leaking memory when handed toasted input. */
1236  PG_FREE_IF_COPY(record1, 0);
1237  PG_FREE_IF_COPY(record2, 1);
1238 
1239  PG_RETURN_BOOL(result);
1240 }
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1641
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:132
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:897
char * format_type_be(Oid type_oid)
Definition: format_type.c:320
int32 record1_typmod
Definition: rowtypes.c:62
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:276
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:594
int natts
Definition: tupdesc.h:79
signed int int32
Definition: c.h:302
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:460
void pfree(void *pointer)
Definition: mcxt.c:936
#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:137
void check_stack_depth(void)
Definition: postgres.c:3154
int32 record2_typmod
Definition: rowtypes.c:64
#define DatumGetBool(X)
Definition: postgres.h:376
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
Oid t_tableOid
Definition: htup.h:66
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:86
#define ereport(elevel, rest)
Definition: elog.h:122
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:365
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:450
FmgrInfo eq_opr_finfo
Definition: typcache.h:71
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:321
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
Datum arg[FUNC_MAX_ARGS]
Definition: fmgr.h:85
#define Max(x, y)
Definition: c.h:840
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:120
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:225
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:936
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
void * palloc(Size size)
Definition: mcxt.c:835
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:121
#define offsetof(type, field)
Definition: c.h:611
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:65
TypeCacheEntry * typentry
Definition: rowtypes.c:55
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:444

◆ record_ge()

Datum record_ge ( PG_FUNCTION_ARGS  )

Definition at line 1267 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1268 {
1269  PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
1270 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:784
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_gt()

Datum record_gt ( PG_FUNCTION_ARGS  )

Definition at line 1255 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1256 {
1257  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1258 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:784
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_image_cmp()

static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1291 of file rowtypes.c.

References Assert, RecordCompareData::columns, DatumGetPointer, ereport, errcode(), errmsg(), ERROR, FunctionCallInfoData::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, tupleDesc::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().

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

◆ record_image_eq()

Datum record_image_eq ( PG_FUNCTION_ARGS  )

Definition at line 1535 of file rowtypes.c.

References RecordCompareData::columns, DatumGetPointer, ereport, errcode(), errmsg(), ERROR, format_type_be(), heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, tupleDesc::natts, RecordCompareData::ncolumns, offsetof, palloc(), pfree(), PG_DETOAST_DATUM_PACKED, 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, toast_raw_datum_size(), TupleDescAttr, VARDATA_ANY, and VARHDRSZ.

Referenced by record_image_ne().

1536 {
1539  bool result = true;
1540  Oid tupType1;
1541  Oid tupType2;
1542  int32 tupTypmod1;
1543  int32 tupTypmod2;
1544  TupleDesc tupdesc1;
1545  TupleDesc tupdesc2;
1546  HeapTupleData tuple1;
1547  HeapTupleData tuple2;
1548  int ncolumns1;
1549  int ncolumns2;
1550  RecordCompareData *my_extra;
1551  int ncols;
1552  Datum *values1;
1553  Datum *values2;
1554  bool *nulls1;
1555  bool *nulls2;
1556  int i1;
1557  int i2;
1558  int j;
1559 
1560  /* Extract type info from the tuples */
1561  tupType1 = HeapTupleHeaderGetTypeId(record1);
1562  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1563  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1564  ncolumns1 = tupdesc1->natts;
1565  tupType2 = HeapTupleHeaderGetTypeId(record2);
1566  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1567  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1568  ncolumns2 = tupdesc2->natts;
1569 
1570  /* Build temporary HeapTuple control structures */
1571  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1572  ItemPointerSetInvalid(&(tuple1.t_self));
1573  tuple1.t_tableOid = InvalidOid;
1574  tuple1.t_data = record1;
1575  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1576  ItemPointerSetInvalid(&(tuple2.t_self));
1577  tuple2.t_tableOid = InvalidOid;
1578  tuple2.t_data = record2;
1579 
1580  /*
1581  * We arrange to look up the needed comparison info just once per series
1582  * of calls, assuming the record types don't change underneath us.
1583  */
1584  ncols = Max(ncolumns1, ncolumns2);
1585  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1586  if (my_extra == NULL ||
1587  my_extra->ncolumns < ncols)
1588  {
1589  fcinfo->flinfo->fn_extra =
1590  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1591  offsetof(RecordCompareData, columns) +
1592  ncols * sizeof(ColumnCompareData));
1593  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1594  my_extra->ncolumns = ncols;
1595  my_extra->record1_type = InvalidOid;
1596  my_extra->record1_typmod = 0;
1597  my_extra->record2_type = InvalidOid;
1598  my_extra->record2_typmod = 0;
1599  }
1600 
1601  if (my_extra->record1_type != tupType1 ||
1602  my_extra->record1_typmod != tupTypmod1 ||
1603  my_extra->record2_type != tupType2 ||
1604  my_extra->record2_typmod != tupTypmod2)
1605  {
1606  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1607  my_extra->record1_type = tupType1;
1608  my_extra->record1_typmod = tupTypmod1;
1609  my_extra->record2_type = tupType2;
1610  my_extra->record2_typmod = tupTypmod2;
1611  }
1612 
1613  /* Break down the tuples into fields */
1614  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1615  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1616  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1617  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1618  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1619  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1620 
1621  /*
1622  * Scan corresponding columns, allowing for dropped columns in different
1623  * places in the two rows. i1 and i2 are physical column indexes, j is
1624  * the logical column index.
1625  */
1626  i1 = i2 = j = 0;
1627  while (i1 < ncolumns1 || i2 < ncolumns2)
1628  {
1629  Form_pg_attribute att1;
1630  Form_pg_attribute att2;
1631 
1632  /*
1633  * Skip dropped columns
1634  */
1635  if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
1636  {
1637  i1++;
1638  continue;
1639  }
1640  if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
1641  {
1642  i2++;
1643  continue;
1644  }
1645  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1646  break; /* we'll deal with mismatch below loop */
1647 
1648  att1 = TupleDescAttr(tupdesc1, i1);
1649  att2 = TupleDescAttr(tupdesc2, i2);
1650 
1651  /*
1652  * Have two matching columns, they must be same type
1653  */
1654  if (att1->atttypid != att2->atttypid)
1655  ereport(ERROR,
1656  (errcode(ERRCODE_DATATYPE_MISMATCH),
1657  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1658  format_type_be(att1->atttypid),
1659  format_type_be(att2->atttypid),
1660  j + 1)));
1661 
1662  /*
1663  * We consider two NULLs equal; NULL > not-NULL.
1664  */
1665  if (!nulls1[i1] || !nulls2[i2])
1666  {
1667  if (nulls1[i1] || nulls2[i2])
1668  {
1669  result = false;
1670  break;
1671  }
1672 
1673  /* Compare the pair of elements */
1674  if (att1->attlen == -1)
1675  {
1676  Size len1,
1677  len2;
1678 
1679  len1 = toast_raw_datum_size(values1[i1]);
1680  len2 = toast_raw_datum_size(values2[i2]);
1681  /* No need to de-toast if lengths don't match. */
1682  if (len1 != len2)
1683  result = false;
1684  else
1685  {
1686  struct varlena *arg1val;
1687  struct varlena *arg2val;
1688 
1689  arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
1690  arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
1691 
1692  result = (memcmp(VARDATA_ANY(arg1val),
1693  VARDATA_ANY(arg2val),
1694  len1 - VARHDRSZ) == 0);
1695 
1696  /* Only free memory if it's a copy made here. */
1697  if ((Pointer) arg1val != (Pointer) values1[i1])
1698  pfree(arg1val);
1699  if ((Pointer) arg2val != (Pointer) values2[i2])
1700  pfree(arg2val);
1701  }
1702  }
1703  else if (att1->attbyval)
1704  {
1705  result = (values1[i1] == values2[i2]);
1706  }
1707  else
1708  {
1709  result = (memcmp(DatumGetPointer(values1[i1]),
1710  DatumGetPointer(values2[i2]),
1711  att1->attlen) == 0);
1712  }
1713  if (!result)
1714  break;
1715  }
1716 
1717  /* equal, so continue to next column */
1718  i1++, i2++, j++;
1719  }
1720 
1721  /*
1722  * If we didn't break out of the loop early, check for column count
1723  * mismatch. (We do not report such mismatch if we found unequal column
1724  * values; is that a feature or a bug?)
1725  */
1726  if (result)
1727  {
1728  if (i1 != ncolumns1 || i2 != ncolumns2)
1729  ereport(ERROR,
1730  (errcode(ERRCODE_DATATYPE_MISMATCH),
1731  errmsg("cannot compare record types with different numbers of columns")));
1732  }
1733 
1734  pfree(values1);
1735  pfree(nulls1);
1736  pfree(values2);
1737  pfree(nulls2);
1738  ReleaseTupleDesc(tupdesc1);
1739  ReleaseTupleDesc(tupdesc2);
1740 
1741  /* Avoid leaking memory when handed toasted input. */
1742  PG_FREE_IF_COPY(record1, 0);
1743  PG_FREE_IF_COPY(record2, 1);
1744 
1745  PG_RETURN_BOOL(result);
1746 }
#define VARDATA_ANY(PTR)
Definition: postgres.h:346
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1641
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
#define VARHDRSZ
Definition: c.h:511
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:897
char * format_type_be(Oid type_oid)
Definition: format_type.c:320
int32 record1_typmod
Definition: rowtypes.c:62
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:276
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:79
signed int int32
Definition: c.h:302
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:460
void pfree(void *pointer)
Definition: mcxt.c:936
Size toast_raw_datum_size(Datum value)
Definition: tuptoaster.c:353
char * Pointer
Definition: c.h:291
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define PG_DETOAST_DATUM_PACKED(datum)
Definition: fmgr.h:213
uint32 t_len
Definition: htup.h:64
int32 record2_typmod
Definition: rowtypes.c:64
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
Oid t_tableOid
Definition: htup.h:66
#define ereport(elevel, rest)
Definition: elog.h:122
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:365
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:450
#define InvalidOid
Definition: postgres_ext.h:36
#define Max(x, y)
Definition: c.h:840
size_t Size
Definition: c.h:422
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:225
#define DatumGetPointer(X)
Definition: postgres.h:532
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:936
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
void * palloc(Size size)
Definition: mcxt.c:835
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
Definition: c.h:505
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:121
#define offsetof(type, field)
Definition: c.h:611
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:65
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:444

◆ record_image_ge()

Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1773 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1774 {
1775  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1776 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1291
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_image_gt()

Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1761 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1762 {
1763  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1764 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1291
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_image_le()

Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1767 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1768 {
1769  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1770 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1291
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_image_lt()

Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1755 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1756 {
1757  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1758 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1291
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_image_ne()

Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1749 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_image_eq().

1750 {
1752 }
#define DatumGetBool(X)
Definition: postgres.h:376
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1535

◆ record_in()

Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 73 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, tupleDesc::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, RECORDOID, ReleaseTupleDesc, resetStringInfo(), HeapTupleData::t_data, HeapTupleData::t_len, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.

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

◆ record_le()

Datum record_le ( PG_FUNCTION_ARGS  )

Definition at line 1261 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1262 {
1263  PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
1264 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:784
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_lt()

Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1249 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1250 {
1251  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1252 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:784
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319

◆ record_ne()

Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1243 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_eq().

1244 {
1246 }
#define DatumGetBool(X)
Definition: postgres.h:376
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1026

◆ record_out()

Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 301 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, tupleDesc::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.

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

◆ record_recv()

Datum record_recv ( PG_FUNCTION_ARGS  )

Definition at line 452 of file rowtypes.c.

References buf, check_stack_depth(), ColumnIOData::column_type, RecordIOData::columns, StringInfoData::cursor, StringInfoData::data, ereport, errcode(), errmsg(), ERROR, fmgr_info_cxt(), FmgrInfo::fn_mcxt, getTypeBinaryInputInfo(), heap_form_tuple(), heap_freetuple(), i, InvalidOid, StringInfoData::len, lookup_rowtype_tupdesc(), StringInfoData::maxlen, MemoryContextAlloc(), MemSet, tupleDesc::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, RECORDOID, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, TupleDescAttr, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.

453 {
455  Oid tupType = PG_GETARG_OID(1);
456  int32 tupTypmod = PG_GETARG_INT32(2);
457  HeapTupleHeader result;
458  TupleDesc tupdesc;
459  HeapTuple tuple;
460  RecordIOData *my_extra;
461  int ncolumns;
462  int usercols;
463  int validcols;
464  int i;
465  Datum *values;
466  bool *nulls;
467 
468  check_stack_depth(); /* recurses for record-type columns */
469 
470  /*
471  * Give a friendly error message if we did not get enough info to identify
472  * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
473  * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
474  * for typmod, since composite types and RECORD have no type modifiers at
475  * the SQL level, and thus must fail for RECORD. However some callers can
476  * supply a valid typmod, and then we can do something useful for RECORD.
477  */
478  if (tupType == RECORDOID && tupTypmod < 0)
479  ereport(ERROR,
480  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
481  errmsg("input of anonymous composite types is not implemented")));
482 
483  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
484  ncolumns = tupdesc->natts;
485 
486  /*
487  * We arrange to look up the needed I/O info just once per series of
488  * calls, assuming the record type doesn't change underneath us.
489  */
490  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
491  if (my_extra == NULL ||
492  my_extra->ncolumns != ncolumns)
493  {
494  fcinfo->flinfo->fn_extra =
495  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
496  offsetof(RecordIOData, columns) +
497  ncolumns * sizeof(ColumnIOData));
498  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
499  my_extra->record_type = InvalidOid;
500  my_extra->record_typmod = 0;
501  }
502 
503  if (my_extra->record_type != tupType ||
504  my_extra->record_typmod != tupTypmod)
505  {
506  MemSet(my_extra, 0,
507  offsetof(RecordIOData, columns) +
508  ncolumns * sizeof(ColumnIOData));
509  my_extra->record_type = tupType;
510  my_extra->record_typmod = tupTypmod;
511  my_extra->ncolumns = ncolumns;
512  }
513 
514  values = (Datum *) palloc(ncolumns * sizeof(Datum));
515  nulls = (bool *) palloc(ncolumns * sizeof(bool));
516 
517  /* Fetch number of columns user thinks it has */
518  usercols = pq_getmsgint(buf, 4);
519 
520  /* Need to scan to count nondeleted columns */
521  validcols = 0;
522  for (i = 0; i < ncolumns; i++)
523  {
524  if (!TupleDescAttr(tupdesc, i)->attisdropped)
525  validcols++;
526  }
527  if (usercols != validcols)
528  ereport(ERROR,
529  (errcode(ERRCODE_DATATYPE_MISMATCH),
530  errmsg("wrong number of columns: %d, expected %d",
531  usercols, validcols)));
532 
533  /* Process each column */
534  for (i = 0; i < ncolumns; i++)
535  {
536  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
537  ColumnIOData *column_info = &my_extra->columns[i];
538  Oid column_type = att->atttypid;
539  Oid coltypoid;
540  int itemlen;
541  StringInfoData item_buf;
542  StringInfo bufptr;
543  char csave;
544 
545  /* Ignore dropped columns in datatype, but fill with nulls */
546  if (att->attisdropped)
547  {
548  values[i] = (Datum) 0;
549  nulls[i] = true;
550  continue;
551  }
552 
553  /* Verify column datatype */
554  coltypoid = pq_getmsgint(buf, sizeof(Oid));
555  if (coltypoid != column_type)
556  ereport(ERROR,
557  (errcode(ERRCODE_DATATYPE_MISMATCH),
558  errmsg("wrong data type: %u, expected %u",
559  coltypoid, column_type)));
560 
561  /* Get and check the item length */
562  itemlen = pq_getmsgint(buf, 4);
563  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
564  ereport(ERROR,
565  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
566  errmsg("insufficient data left in message")));
567 
568  if (itemlen == -1)
569  {
570  /* -1 length means NULL */
571  bufptr = NULL;
572  nulls[i] = true;
573  csave = 0; /* keep compiler quiet */
574  }
575  else
576  {
577  /*
578  * Rather than copying data around, we just set up a phony
579  * StringInfo pointing to the correct portion of the input buffer.
580  * We assume we can scribble on the input buffer so as to maintain
581  * the convention that StringInfos have a trailing null.
582  */
583  item_buf.data = &buf->data[buf->cursor];
584  item_buf.maxlen = itemlen + 1;
585  item_buf.len = itemlen;
586  item_buf.cursor = 0;
587 
588  buf->cursor += itemlen;
589 
590  csave = buf->data[buf->cursor];
591  buf->data[buf->cursor] = '\0';
592 
593  bufptr = &item_buf;
594  nulls[i] = false;
595  }
596 
597  /* Now call the column's receiveproc */
598  if (column_info->column_type != column_type)
599  {
600  getTypeBinaryInputInfo(column_type,
601  &column_info->typiofunc,
602  &column_info->typioparam);
603  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
604  fcinfo->flinfo->fn_mcxt);
605  column_info->column_type = column_type;
606  }
607 
608  values[i] = ReceiveFunctionCall(&column_info->proc,
609  bufptr,
610  column_info->typioparam,
611  att->atttypmod);
612 
613  if (bufptr)
614  {
615  /* Trouble if it didn't eat the whole buffer */
616  if (item_buf.cursor != itemlen)
617  ereport(ERROR,
618  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
619  errmsg("improper binary format in record column %d",
620  i + 1)));
621 
622  buf->data[buf->cursor] = csave;
623  }
624  }
625 
626  tuple = heap_form_tuple(tupdesc, values, nulls);
627 
628  /*
629  * We cannot return tuple->t_data because heap_form_tuple allocates it as
630  * part of a larger chunk, and our caller may expect to be able to pfree
631  * our result. So must copy the info into a new palloc chunk.
632  */
633  result = (HeapTupleHeader) palloc(tuple->t_len);
634  memcpy(result, tuple->t_data, tuple->t_len);
635 
636  heap_freetuple(tuple);
637  pfree(values);
638  pfree(nulls);
639  ReleaseTupleDesc(tupdesc);
640 
642 }
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
FmgrInfo proc
Definition: hstore_io.c:749
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1641
Oid record_type
Definition: hstore_io.c:754
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
StringInfoData * StringInfo
Definition: stringinfo.h:43
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:897
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:241
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:79
int32 record_typmod
Definition: hstore_io.c:755
signed int int32
Definition: c.h:302
HeapTupleHeader t_data
Definition: htup.h:67
void pfree(void *pointer)
Definition: mcxt.c:936
#define ERROR
Definition: elog.h:43
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1676
#define PG_RETURN_HEAPTUPLEHEADER(x)
Definition: fmgr.h:334
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:67
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
void check_stack_depth(void)
Definition: postgres.c:3154
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define RECORDOID
Definition: pg_type.h:680
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:132
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2683
#define ereport(elevel, rest)
Definition: elog.h:122
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:759
uintptr_t Datum
Definition: postgres.h:365
#define InvalidOid
Definition: postgres_ext.h:36
Oid typioparam
Definition: hstore_io.c:748
static Datum values[MAXATTR]
Definition: bootstrap.c:164
void * palloc(Size size)
Definition: mcxt.c:835
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
int i
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:417
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:121
#define offsetof(type, field)
Definition: c.h:611
Oid column_type
Definition: hstore_io.c:746

◆ record_send()

Datum record_send ( PG_FUNCTION_ARGS  )

Definition at line 648 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, tupleDesc::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.

649 {
651  Oid tupType;
652  int32 tupTypmod;
653  TupleDesc tupdesc;
654  HeapTupleData tuple;
655  RecordIOData *my_extra;
656  int ncolumns;
657  int validcols;
658  int i;
659  Datum *values;
660  bool *nulls;
662 
663  check_stack_depth(); /* recurses for record-type columns */
664 
665  /* Extract type info from the tuple itself */
666  tupType = HeapTupleHeaderGetTypeId(rec);
667  tupTypmod = HeapTupleHeaderGetTypMod(rec);
668  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
669  ncolumns = tupdesc->natts;
670 
671  /* Build a temporary HeapTuple control structure */
673  ItemPointerSetInvalid(&(tuple.t_self));
674  tuple.t_tableOid = InvalidOid;
675  tuple.t_data = rec;
676 
677  /*
678  * We arrange to look up the needed I/O info just once per series of
679  * calls, assuming the record type doesn't change underneath us.
680  */
681  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
682  if (my_extra == NULL ||
683  my_extra->ncolumns != ncolumns)
684  {
685  fcinfo->flinfo->fn_extra =
686  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
687  offsetof(RecordIOData, columns) +
688  ncolumns * sizeof(ColumnIOData));
689  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
690  my_extra->record_type = InvalidOid;
691  my_extra->record_typmod = 0;
692  }
693 
694  if (my_extra->record_type != tupType ||
695  my_extra->record_typmod != tupTypmod)
696  {
697  MemSet(my_extra, 0,
698  offsetof(RecordIOData, columns) +
699  ncolumns * sizeof(ColumnIOData));
700  my_extra->record_type = tupType;
701  my_extra->record_typmod = tupTypmod;
702  my_extra->ncolumns = ncolumns;
703  }
704 
705  values = (Datum *) palloc(ncolumns * sizeof(Datum));
706  nulls = (bool *) palloc(ncolumns * sizeof(bool));
707 
708  /* Break down the tuple into fields */
709  heap_deform_tuple(&tuple, tupdesc, values, nulls);
710 
711  /* And build the result string */
712  pq_begintypsend(&buf);
713 
714  /* Need to scan to count nondeleted columns */
715  validcols = 0;
716  for (i = 0; i < ncolumns; i++)
717  {
718  if (!TupleDescAttr(tupdesc, i)->attisdropped)
719  validcols++;
720  }
721  pq_sendint32(&buf, validcols);
722 
723  for (i = 0; i < ncolumns; i++)
724  {
725  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
726  ColumnIOData *column_info = &my_extra->columns[i];
727  Oid column_type = att->atttypid;
728  Datum attr;
729  bytea *outputbytes;
730 
731  /* Ignore dropped columns in datatype */
732  if (att->attisdropped)
733  continue;
734 
735  pq_sendint32(&buf, column_type);
736 
737  if (nulls[i])
738  {
739  /* emit -1 data length to signify a NULL */
740  pq_sendint32(&buf, -1);
741  continue;
742  }
743 
744  /*
745  * Convert the column value to binary
746  */
747  if (column_info->column_type != column_type)
748  {
749  getTypeBinaryOutputInfo(column_type,
750  &column_info->typiofunc,
751  &column_info->typisvarlena);
752  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
753  fcinfo->flinfo->fn_mcxt);
754  column_info->column_type = column_type;
755  }
756 
757  attr = values[i];
758  outputbytes = SendFunctionCall(&column_info->proc, attr);
759  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
760  pq_sendbytes(&buf, VARDATA(outputbytes),
761  VARSIZE(outputbytes) - VARHDRSZ);
762  }
763 
764  pfree(values);
765  pfree(nulls);
766  ReleaseTupleDesc(tupdesc);
767 
769 }
FmgrInfo proc
Definition: hstore_io.c:749
#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:1641
Oid record_type
Definition: hstore_io.c:754
#define VARSIZE(PTR)
Definition: postgres.h:303
static void pq_sendint32(StringInfo buf, int32 i)
Definition: pqformat.h:148
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
#define VARHDRSZ
Definition: c.h:511
#define MemSet(start, val, len)
Definition: c.h:897
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:330
#define PG_GETARG_HEAPTUPLEHEADER(n)
Definition: fmgr.h:276
unsigned int Oid
Definition: postgres_ext.h:31
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
int natts
Definition: tupdesc.h:79
int32 record_typmod
Definition: hstore_io.c:755
signed int int32
Definition: c.h:302
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:460
void pfree(void *pointer)
Definition: mcxt.c:936
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:67
void check_stack_depth(void)
Definition: postgres.c:3154
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1723
Oid t_tableOid
Definition: htup.h:66
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:132
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:759
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition: lsyscache.c:2716
uintptr_t Datum
Definition: postgres.h:365
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:450
#define InvalidOid
Definition: postgres_ext.h:36
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:936
static Datum values[MAXATTR]
Definition: bootstrap.c:164
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
void pq_sendbytes(StringInfo buf, const char *data, int datalen)
Definition: pqformat.c:125
void * palloc(Size size)
Definition: mcxt.c:835
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
int i
Definition: c.h:505
bool typisvarlena
Definition: rowtypes.c:38
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:121
#define offsetof(type, field)
Definition: c.h:611
Oid column_type
Definition: hstore_io.c:746
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:444