PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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

Function Documentation

Datum btrecordcmp ( PG_FUNCTION_ARGS  )

Definition at line 1261 of file rowtypes.c.

References PG_RETURN_INT32, and record_cmp().

1262 {
1263  PG_RETURN_INT32(record_cmp(fcinfo));
1264 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:780
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314
Datum btrecordimagecmp ( PG_FUNCTION_ARGS  )

Definition at line 1817 of file rowtypes.c.

References PG_RETURN_INT32, and record_image_cmp().

1818 {
1820 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1279
static int record_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 780 of file rowtypes.c.

References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, tupleDesc::attrs, 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, NULL, offsetof, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, result, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TypeCacheEntry::type_id, TYPECACHE_CMP_PROC_FINFO, and ColumnCompareData::typentry.

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

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

Definition at line 1018 of file rowtypes.c.

References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, tupleDesc::attrs, 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, NULL, 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, result, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TypeCacheEntry::type_id, TYPECACHE_EQ_OPR_FINFO, and ColumnCompareData::typentry.

Referenced by record_ne().

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

Definition at line 1243 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1244 {
1245  PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
1246 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:780
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
static int record_image_cmp ( FunctionCallInfo  fcinfo)
static

Definition at line 1279 of file rowtypes.c.

References Assert, tupleDesc::attrs, RecordCompareData::columns, DatumGetPointer, ereport, errcode(), errmsg(), ERROR, FunctionCallInfoData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, format_type_be(), GET_1_BYTE, GET_2_BYTES, GET_4_BYTES, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, Min, tupleDesc::natts, RecordCompareData::ncolumns, NULL, 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, result, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, toast_raw_datum_size(), VARDATA_ANY, and VARHDRSZ.

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

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

Definition at line 1556 of file rowtypes.c.

References Assert, tupleDesc::attrs, RecordCompareData::columns, DatumGetPointer, ereport, errcode(), errmsg(), ERROR, format_type_be(), GET_1_BYTE, GET_2_BYTES, GET_4_BYTES, heap_deform_tuple(), HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, InvalidOid, ItemPointerSetInvalid, lookup_rowtype_tupdesc(), Max, MemoryContextAlloc(), MemSet, tupleDesc::natts, RecordCompareData::ncolumns, NULL, 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, result, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, toast_raw_datum_size(), VARDATA_ANY, and VARHDRSZ.

Referenced by record_image_ne().

1557 {
1560  bool result = true;
1561  Oid tupType1;
1562  Oid tupType2;
1563  int32 tupTypmod1;
1564  int32 tupTypmod2;
1565  TupleDesc tupdesc1;
1566  TupleDesc tupdesc2;
1567  HeapTupleData tuple1;
1568  HeapTupleData tuple2;
1569  int ncolumns1;
1570  int ncolumns2;
1571  RecordCompareData *my_extra;
1572  int ncols;
1573  Datum *values1;
1574  Datum *values2;
1575  bool *nulls1;
1576  bool *nulls2;
1577  int i1;
1578  int i2;
1579  int j;
1580 
1581  /* Extract type info from the tuples */
1582  tupType1 = HeapTupleHeaderGetTypeId(record1);
1583  tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
1584  tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
1585  ncolumns1 = tupdesc1->natts;
1586  tupType2 = HeapTupleHeaderGetTypeId(record2);
1587  tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
1588  tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
1589  ncolumns2 = tupdesc2->natts;
1590 
1591  /* Build temporary HeapTuple control structures */
1592  tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
1593  ItemPointerSetInvalid(&(tuple1.t_self));
1594  tuple1.t_tableOid = InvalidOid;
1595  tuple1.t_data = record1;
1596  tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
1597  ItemPointerSetInvalid(&(tuple2.t_self));
1598  tuple2.t_tableOid = InvalidOid;
1599  tuple2.t_data = record2;
1600 
1601  /*
1602  * We arrange to look up the needed comparison info just once per series
1603  * of calls, assuming the record types don't change underneath us.
1604  */
1605  ncols = Max(ncolumns1, ncolumns2);
1606  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1607  if (my_extra == NULL ||
1608  my_extra->ncolumns < ncols)
1609  {
1610  fcinfo->flinfo->fn_extra =
1611  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1612  offsetof(RecordCompareData, columns) +
1613  ncols * sizeof(ColumnCompareData));
1614  my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1615  my_extra->ncolumns = ncols;
1616  my_extra->record1_type = InvalidOid;
1617  my_extra->record1_typmod = 0;
1618  my_extra->record2_type = InvalidOid;
1619  my_extra->record2_typmod = 0;
1620  }
1621 
1622  if (my_extra->record1_type != tupType1 ||
1623  my_extra->record1_typmod != tupTypmod1 ||
1624  my_extra->record2_type != tupType2 ||
1625  my_extra->record2_typmod != tupTypmod2)
1626  {
1627  MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
1628  my_extra->record1_type = tupType1;
1629  my_extra->record1_typmod = tupTypmod1;
1630  my_extra->record2_type = tupType2;
1631  my_extra->record2_typmod = tupTypmod2;
1632  }
1633 
1634  /* Break down the tuples into fields */
1635  values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
1636  nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
1637  heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
1638  values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
1639  nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
1640  heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
1641 
1642  /*
1643  * Scan corresponding columns, allowing for dropped columns in different
1644  * places in the two rows. i1 and i2 are physical column indexes, j is
1645  * the logical column index.
1646  */
1647  i1 = i2 = j = 0;
1648  while (i1 < ncolumns1 || i2 < ncolumns2)
1649  {
1650  /*
1651  * Skip dropped columns
1652  */
1653  if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
1654  {
1655  i1++;
1656  continue;
1657  }
1658  if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
1659  {
1660  i2++;
1661  continue;
1662  }
1663  if (i1 >= ncolumns1 || i2 >= ncolumns2)
1664  break; /* we'll deal with mismatch below loop */
1665 
1666  /*
1667  * Have two matching columns, they must be same type
1668  */
1669  if (tupdesc1->attrs[i1]->atttypid !=
1670  tupdesc2->attrs[i2]->atttypid)
1671  ereport(ERROR,
1672  (errcode(ERRCODE_DATATYPE_MISMATCH),
1673  errmsg("cannot compare dissimilar column types %s and %s at record column %d",
1674  format_type_be(tupdesc1->attrs[i1]->atttypid),
1675  format_type_be(tupdesc2->attrs[i2]->atttypid),
1676  j + 1)));
1677 
1678  /*
1679  * We consider two NULLs equal; NULL > not-NULL.
1680  */
1681  if (!nulls1[i1] || !nulls2[i2])
1682  {
1683  if (nulls1[i1] || nulls2[i2])
1684  {
1685  result = false;
1686  break;
1687  }
1688 
1689  /* Compare the pair of elements */
1690  if (tupdesc1->attrs[i1]->attlen == -1)
1691  {
1692  Size len1,
1693  len2;
1694 
1695  len1 = toast_raw_datum_size(values1[i1]);
1696  len2 = toast_raw_datum_size(values2[i2]);
1697  /* No need to de-toast if lengths don't match. */
1698  if (len1 != len2)
1699  result = false;
1700  else
1701  {
1702  struct varlena *arg1val;
1703  struct varlena *arg2val;
1704 
1705  arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
1706  arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
1707 
1708  result = (memcmp(VARDATA_ANY(arg1val),
1709  VARDATA_ANY(arg2val),
1710  len1 - VARHDRSZ) == 0);
1711 
1712  /* Only free memory if it's a copy made here. */
1713  if ((Pointer) arg1val != (Pointer) values1[i1])
1714  pfree(arg1val);
1715  if ((Pointer) arg2val != (Pointer) values2[i2])
1716  pfree(arg2val);
1717  }
1718  }
1719  else if (tupdesc1->attrs[i1]->attbyval)
1720  {
1721  switch (tupdesc1->attrs[i1]->attlen)
1722  {
1723  case 1:
1724  result = (GET_1_BYTE(values1[i1]) ==
1725  GET_1_BYTE(values2[i2]));
1726  break;
1727  case 2:
1728  result = (GET_2_BYTES(values1[i1]) ==
1729  GET_2_BYTES(values2[i2]));
1730  break;
1731  case 4:
1732  result = (GET_4_BYTES(values1[i1]) ==
1733  GET_4_BYTES(values2[i2]));
1734  break;
1735 #if SIZEOF_DATUM == 8
1736  case 8:
1737  result = (GET_8_BYTES(values1[i1]) ==
1738  GET_8_BYTES(values2[i2]));
1739  break;
1740 #endif
1741  default:
1742  Assert(false); /* cannot happen */
1743  }
1744  }
1745  else
1746  {
1747  result = (memcmp(DatumGetPointer(values1[i1]),
1748  DatumGetPointer(values2[i2]),
1749  tupdesc1->attrs[i1]->attlen) == 0);
1750  }
1751  if (!result)
1752  break;
1753  }
1754 
1755  /* equal, so continue to next column */
1756  i1++, i2++, j++;
1757  }
1758 
1759  /*
1760  * If we didn't break out of the loop early, check for column count
1761  * mismatch. (We do not report such mismatch if we found unequal column
1762  * values; is that a feature or a bug?)
1763  */
1764  if (result)
1765  {
1766  if (i1 != ncolumns1 || i2 != ncolumns2)
1767  ereport(ERROR,
1768  (errcode(ERRCODE_DATATYPE_MISMATCH),
1769  errmsg("cannot compare record types with different numbers of columns")));
1770  }
1771 
1772  pfree(values1);
1773  pfree(nulls1);
1774  pfree(values2);
1775  pfree(nulls2);
1776  ReleaseTupleDesc(tupdesc1);
1777  ReleaseTupleDesc(tupdesc2);
1778 
1779  /* Avoid leaking memory when handed toasted input. */
1780  PG_FREE_IF_COPY(record1, 0);
1781  PG_FREE_IF_COPY(record2, 1);
1782 
1783  PG_RETURN_BOOL(result);
1784 }
#define VARDATA_ANY(PTR)
Definition: postgres.h:347
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1257
#define VARHDRSZ
Definition: c.h:445
Form_pg_attribute * attrs
Definition: tupdesc.h:74
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:857
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
return result
Definition: formatting.c:1632
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:73
signed int int32
Definition: c.h:256
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:455
#define GET_1_BYTE(datum)
Definition: postgres.h:378
void pfree(void *pointer)
Definition: mcxt.c:950
Size toast_raw_datum_size(Datum value)
Definition: tuptoaster.c:353
char * Pointer
Definition: c.h:245
#define GET_4_BYTES(datum)
Definition: postgres.h:380
#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
Oid t_tableOid
Definition: htup.h:66
#define ereport(elevel, rest)
Definition: elog.h:122
#define GET_2_BYTES(datum)
Definition: postgres.h:379
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:372
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:445
#define InvalidOid
Definition: postgres_ext.h:36
#define Max(x, y)
Definition: c.h:800
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
size_t Size
Definition: c.h:356
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:225
#define DatumGetPointer(X)
Definition: postgres.h:555
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:933
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:149
void * palloc(Size size)
Definition: mcxt.c:849
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:707
Definition: c.h:439
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:107
#define offsetof(type, field)
Definition: c.h:555
ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: rowtypes.c:65
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:439
Datum record_image_ge ( PG_FUNCTION_ARGS  )

Definition at line 1811 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1812 {
1813  PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
1814 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1279
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_gt ( PG_FUNCTION_ARGS  )

Definition at line 1799 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1800 {
1801  PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
1802 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1279
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_le ( PG_FUNCTION_ARGS  )

Definition at line 1805 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1806 {
1807  PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
1808 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1279
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_lt ( PG_FUNCTION_ARGS  )

Definition at line 1793 of file rowtypes.c.

References PG_RETURN_BOOL, and record_image_cmp().

1794 {
1795  PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
1796 }
static int record_image_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:1279
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_ne ( PG_FUNCTION_ARGS  )

Definition at line 1787 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_image_eq().

1788 {
1790 }
#define DatumGetBool(X)
Definition: postgres.h:399
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_image_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1556
Datum record_in ( PG_FUNCTION_ARGS  )

Definition at line 73 of file rowtypes.c.

References appendStringInfoChar(), tupleDesc::attrs, 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, NULL, 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(), result, HeapTupleData::t_data, HeapTupleData::t_len, 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);
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  ColumnIOData *column_info = &my_extra->columns[i];
163  Oid column_type = tupdesc->attrs[i]->atttypid;
164  char *column_data;
165 
166  /* Ignore dropped columns in datatype, but fill with nulls */
167  if (tupdesc->attrs[i]->attisdropped)
168  {
169  values[i] = (Datum) 0;
170  nulls[i] = true;
171  continue;
172  }
173 
174  if (needComma)
175  {
176  /* Skip comma that separates prior field from this one */
177  if (*ptr == ',')
178  ptr++;
179  else
180  /* *ptr must be ')' */
181  ereport(ERROR,
182  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
183  errmsg("malformed record literal: \"%s\"", string),
184  errdetail("Too few columns.")));
185  }
186 
187  /* Check for null: completely empty input means null */
188  if (*ptr == ',' || *ptr == ')')
189  {
190  column_data = NULL;
191  nulls[i] = true;
192  }
193  else
194  {
195  /* Extract string for this column */
196  bool inquote = false;
197 
198  resetStringInfo(&buf);
199  while (inquote || !(*ptr == ',' || *ptr == ')'))
200  {
201  char ch = *ptr++;
202 
203  if (ch == '\0')
204  ereport(ERROR,
205  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
206  errmsg("malformed record literal: \"%s\"",
207  string),
208  errdetail("Unexpected end of input.")));
209  if (ch == '\\')
210  {
211  if (*ptr == '\0')
212  ereport(ERROR,
213  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
214  errmsg("malformed record literal: \"%s\"",
215  string),
216  errdetail("Unexpected end of input.")));
217  appendStringInfoChar(&buf, *ptr++);
218  }
219  else if (ch == '"')
220  {
221  if (!inquote)
222  inquote = true;
223  else if (*ptr == '"')
224  {
225  /* doubled quote within quote sequence */
226  appendStringInfoChar(&buf, *ptr++);
227  }
228  else
229  inquote = false;
230  }
231  else
232  appendStringInfoChar(&buf, ch);
233  }
234 
235  column_data = buf.data;
236  nulls[i] = false;
237  }
238 
239  /*
240  * Convert the column value
241  */
242  if (column_info->column_type != column_type)
243  {
244  getTypeInputInfo(column_type,
245  &column_info->typiofunc,
246  &column_info->typioparam);
247  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
248  fcinfo->flinfo->fn_mcxt);
249  column_info->column_type = column_type;
250  }
251 
252  values[i] = InputFunctionCall(&column_info->proc,
253  column_data,
254  column_info->typioparam,
255  tupdesc->attrs[i]->atttypmod);
256 
257  /*
258  * Prep for next column
259  */
260  needComma = true;
261  }
262 
263  if (*ptr++ != ')')
264  ereport(ERROR,
265  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
266  errmsg("malformed record literal: \"%s\"", string),
267  errdetail("Too many columns.")));
268  /* Allow trailing whitespace */
269  while (*ptr && isspace((unsigned char) *ptr))
270  ptr++;
271  if (*ptr)
272  ereport(ERROR,
273  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
274  errmsg("malformed record literal: \"%s\"", string),
275  errdetail("Junk after right parenthesis.")));
276 
277  tuple = heap_form_tuple(tupdesc, values, nulls);
278 
279  /*
280  * We cannot return tuple->t_data because heap_form_tuple allocates it as
281  * part of a larger chunk, and our caller may expect to be able to pfree
282  * our result. So must copy the info into a new palloc chunk.
283  */
284  result = (HeapTupleHeader) palloc(tuple->t_len);
285  memcpy(result, tuple->t_data, tuple->t_len);
286 
287  heap_freetuple(tuple);
288  pfree(buf.data);
289  pfree(values);
290  pfree(nulls);
291  ReleaseTupleDesc(tupdesc);
292 
294 }
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
FmgrInfo proc
Definition: hstore_io.c:748
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1257
Oid record_type
Definition: hstore_io.c:753
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
Form_pg_attribute * attrs
Definition: tupdesc.h:74
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:857
return result
Definition: formatting.c:1632
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:692
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1372
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:73
int32 record_typmod
Definition: hstore_io.c:754
signed int int32
Definition: c.h:256
HeapTupleHeader t_data
Definition: htup.h:67
void pfree(void *pointer)
Definition: mcxt.c:950
#define ERROR
Definition: elog.h:43
#define PG_RETURN_HEAPTUPLEHEADER(x)
Definition: fmgr.h:333
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:66
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
void check_stack_depth(void)
Definition: postgres.c:3102
int errdetail(const char *fmt,...)
Definition: elog.c:873
char string[11]
Definition: preproc-type.c:46
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:137
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2599
#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:756
uintptr_t Datum
Definition: postgres.h:372
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1623
#define InvalidOid
Definition: postgres_ext.h:36
#define NULL
Definition: c.h:229
Oid typioparam
Definition: hstore_io.c:747
static Datum values[MAXATTR]
Definition: bootstrap.c:163
void * palloc(Size size)
Definition: mcxt.c:849
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:707
int i
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:242
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:107
#define offsetof(type, field)
Definition: c.h:555
Oid column_type
Definition: hstore_io.c:745
Datum record_le ( 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:780
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_lt ( PG_FUNCTION_ARGS  )

Definition at line 1237 of file rowtypes.c.

References PG_RETURN_BOOL, and record_cmp().

1238 {
1239  PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
1240 }
static int record_cmp(FunctionCallInfo fcinfo)
Definition: rowtypes.c:780
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_ne ( PG_FUNCTION_ARGS  )

Definition at line 1231 of file rowtypes.c.

References DatumGetBool, PG_RETURN_BOOL, and record_eq().

1232 {
1234 }
#define DatumGetBool(X)
Definition: postgres.h:399
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
Datum record_eq(PG_FUNCTION_ARGS)
Definition: rowtypes.c:1018
Datum record_out ( PG_FUNCTION_ARGS  )

Definition at line 300 of file rowtypes.c.

References appendStringInfoChar(), appendStringInfoCharMacro, tupleDesc::attrs, 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, NULL, 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, ColumnIOData::typiofunc, ColumnIOData::typisvarlena, value, and values.

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

Definition at line 450 of file rowtypes.c.

References tupleDesc::attrs, 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, NULL, 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, result, HeapTupleData::t_data, HeapTupleData::t_len, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.

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

Definition at line 645 of file rowtypes.c.

References tupleDesc::attrs, 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, NULL, offsetof, palloc(), pfree(), PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BYTEA_P, pq_begintypsend(), pq_endtypsend(), pq_sendbytes(), pq_sendint(), ColumnIOData::proc, RecordIOData::record_type, RecordIOData::record_typmod, ReleaseTupleDesc, SendFunctionCall(), HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, ColumnIOData::typiofunc, ColumnIOData::typisvarlena, values, VARDATA, VARHDRSZ, and VARSIZE.

646 {
648  Oid tupType;
649  int32 tupTypmod;
650  TupleDesc tupdesc;
651  HeapTupleData tuple;
652  RecordIOData *my_extra;
653  int ncolumns;
654  int validcols;
655  int i;
656  Datum *values;
657  bool *nulls;
659 
660  check_stack_depth(); /* recurses for record-type columns */
661 
662  /* Extract type info from the tuple itself */
663  tupType = HeapTupleHeaderGetTypeId(rec);
664  tupTypmod = HeapTupleHeaderGetTypMod(rec);
665  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
666  ncolumns = tupdesc->natts;
667 
668  /* Build a temporary HeapTuple control structure */
670  ItemPointerSetInvalid(&(tuple.t_self));
671  tuple.t_tableOid = InvalidOid;
672  tuple.t_data = rec;
673 
674  /*
675  * We arrange to look up the needed I/O info just once per series of
676  * calls, assuming the record type doesn't change underneath us.
677  */
678  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
679  if (my_extra == NULL ||
680  my_extra->ncolumns != ncolumns)
681  {
682  fcinfo->flinfo->fn_extra =
683  MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
684  offsetof(RecordIOData, columns) +
685  ncolumns * sizeof(ColumnIOData));
686  my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
687  my_extra->record_type = InvalidOid;
688  my_extra->record_typmod = 0;
689  }
690 
691  if (my_extra->record_type != tupType ||
692  my_extra->record_typmod != tupTypmod)
693  {
694  MemSet(my_extra, 0,
695  offsetof(RecordIOData, columns) +
696  ncolumns * sizeof(ColumnIOData));
697  my_extra->record_type = tupType;
698  my_extra->record_typmod = tupTypmod;
699  my_extra->ncolumns = ncolumns;
700  }
701 
702  values = (Datum *) palloc(ncolumns * sizeof(Datum));
703  nulls = (bool *) palloc(ncolumns * sizeof(bool));
704 
705  /* Break down the tuple into fields */
706  heap_deform_tuple(&tuple, tupdesc, values, nulls);
707 
708  /* And build the result string */
709  pq_begintypsend(&buf);
710 
711  /* Need to scan to count nondeleted columns */
712  validcols = 0;
713  for (i = 0; i < ncolumns; i++)
714  {
715  if (!tupdesc->attrs[i]->attisdropped)
716  validcols++;
717  }
718  pq_sendint(&buf, validcols, 4);
719 
720  for (i = 0; i < ncolumns; i++)
721  {
722  ColumnIOData *column_info = &my_extra->columns[i];
723  Oid column_type = tupdesc->attrs[i]->atttypid;
724  Datum attr;
725  bytea *outputbytes;
726 
727  /* Ignore dropped columns in datatype */
728  if (tupdesc->attrs[i]->attisdropped)
729  continue;
730 
731  pq_sendint(&buf, column_type, sizeof(Oid));
732 
733  if (nulls[i])
734  {
735  /* emit -1 data length to signify a NULL */
736  pq_sendint(&buf, -1, 4);
737  continue;
738  }
739 
740  /*
741  * Convert the column value to binary
742  */
743  if (column_info->column_type != column_type)
744  {
745  getTypeBinaryOutputInfo(column_type,
746  &column_info->typiofunc,
747  &column_info->typisvarlena);
748  fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
749  fcinfo->flinfo->fn_mcxt);
750  column_info->column_type = column_type;
751  }
752 
753  attr = values[i];
754  outputbytes = SendFunctionCall(&column_info->proc, attr);
755  pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
756  pq_sendbytes(&buf, VARDATA(outputbytes),
757  VARSIZE(outputbytes) - VARHDRSZ);
758  }
759 
760  pfree(values);
761  pfree(nulls);
762  ReleaseTupleDesc(tupdesc);
763 
765 }
FmgrInfo proc
Definition: hstore_io.c:748
#define VARDATA(PTR)
Definition: postgres.h:303
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1257
Oid record_type
Definition: hstore_io.c:753
#define VARSIZE(PTR)
Definition: postgres.h:304
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:359
#define VARHDRSZ
Definition: c.h:445
Form_pg_attribute * attrs
Definition: tupdesc.h:74
#define MemSet(start, val, len)
Definition: c.h:857
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:329
#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:379
int natts
Definition: tupdesc.h:73
int32 record_typmod
Definition: hstore_io.c:754
signed int int32
Definition: c.h:256
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:455
void pfree(void *pointer)
Definition: mcxt.c:950
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:66
void check_stack_depth(void)
Definition: postgres.c:3102
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1728
Oid t_tableOid
Definition: htup.h:66
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER]
Definition: hstore_io.c:756
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition: lsyscache.c:2698
uintptr_t Datum
Definition: postgres.h:372
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:445
#define InvalidOid
Definition: postgres_ext.h:36
#define NULL
Definition: c.h:229
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:933
static Datum values[MAXATTR]
Definition: bootstrap.c:163
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:149
void pq_sendbytes(StringInfo buf, const char *data, int datalen)
Definition: pqformat.c:115
void * palloc(Size size)
Definition: mcxt.c:849
void pq_sendint(StringInfo buf, int i, int b)
Definition: pqformat.c:236
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:707
int i
Definition: c.h:439
bool typisvarlena
Definition: rowtypes.c:38
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:107
#define offsetof(type, field)
Definition: c.h:555
Oid column_type
Definition: hstore_io.c:745
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:439