* rowtypes.c
* I/O and comparison functions for generic composite types.
*
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include <ctype.h>
+#include "access/detoast.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/datum.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
Oid record_type;
int32 record_typmod;
int ncolumns;
- ColumnIOData columns[1]; /* VARIABLE LENGTH ARRAY */
+ ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
} RecordIOData;
/*
int32 record1_typmod;
Oid record2_type;
int32 record2_typmod;
- ColumnCompareData columns[1]; /* VARIABLE LENGTH ARRAY */
+ ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER];
} RecordCompareData;
{
char *string = PG_GETARG_CSTRING(0);
Oid tupType = PG_GETARG_OID(1);
-
-#ifdef NOT_USED
- int32 typmod = PG_GETARG_INT32(2);
-#endif
+ int32 tupTypmod = PG_GETARG_INT32(2);
HeapTupleHeader result;
- int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
bool *nulls;
StringInfoData buf;
+ check_stack_depth(); /* recurses for record-type columns */
+
/*
- * Use the passed type unless it's RECORD; we can't support input of
- * anonymous types, mainly because there's no good way to figure out which
- * anonymous type is wanted. Note that for RECORD, what we'll probably
- * actually get is RECORD's typelem, ie, zero.
+ * Give a friendly error message if we did not get enough info to identify
+ * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
+ * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
+ * for typmod, since composite types and RECORD have no type modifiers at
+ * the SQL level, and thus must fail for RECORD. However some callers can
+ * supply a valid typmod, and then we can do something useful for RECORD.
*/
- if (tupType == InvalidOid || tupType == RECORDOID)
+ if (tupType == RECORDOID && tupTypmod < 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("input of anonymous composite types is not implemented")));
- tupTypmod = -1; /* for all non-anonymous types */
+ errmsg("input of anonymous composite types is not implemented")));
/*
* This comes from the composite type's pg_type.oid and stores system oids
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
for (i = 0; i < ncolumns; i++)
{
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
ColumnIOData *column_info = &my_extra->columns[i];
- Oid column_type = tupdesc->attrs[i]->atttypid;
+ Oid column_type = att->atttypid;
char *column_data;
/* Ignore dropped columns in datatype, but fill with nulls */
- if (tupdesc->attrs[i]->attisdropped)
+ if (att->attisdropped)
{
values[i] = (Datum) 0;
nulls[i] = true;
errdetail("Unexpected end of input.")));
appendStringInfoChar(&buf, *ptr++);
}
- else if (ch == '\"')
+ else if (ch == '"')
{
if (!inquote)
inquote = true;
- else if (*ptr == '\"')
+ else if (*ptr == '"')
{
/* doubled quote within quote sequence */
appendStringInfoChar(&buf, *ptr++);
values[i] = InputFunctionCall(&column_info->proc,
column_data,
column_info->typioparam,
- tupdesc->attrs[i]->atttypmod);
+ att->atttypmod);
/*
* Prep for next column
/*
* We cannot return tuple->t_data because heap_form_tuple allocates it as
* part of a larger chunk, and our caller may expect to be able to pfree
- * our result. So must copy the info into a new palloc chunk.
+ * our result. So must copy the info into a new palloc chunk.
*/
result = (HeapTupleHeader) palloc(tuple->t_len);
memcpy(result, tuple->t_data, tuple->t_len);
bool *nulls;
StringInfoData buf;
+ check_stack_depth(); /* recurses for record-type columns */
+
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
for (i = 0; i < ncolumns; i++)
{
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
ColumnIOData *column_info = &my_extra->columns[i];
- Oid column_type = tupdesc->attrs[i]->atttypid;
+ Oid column_type = att->atttypid;
Datum attr;
char *value;
char *tmp;
bool nq;
/* Ignore dropped columns in datatype */
- if (tupdesc->attrs[i]->attisdropped)
+ if (att->attisdropped)
continue;
if (needComma)
column_info->column_type = column_type;
}
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (column_info->typisvarlena)
- attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
- else
- attr = values[i];
-
+ attr = values[i];
value = OutputFunctionCall(&column_info->proc, attr);
/* Detect whether we need double quotes for this value */
}
if (nq)
appendStringInfoCharMacro(&buf, '"');
-
- pfree(value);
-
- /* Clean up detoasted copy, if any */
- if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
- pfree(DatumGetPointer(attr));
}
appendStringInfoChar(&buf, ')');
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid tupType = PG_GETARG_OID(1);
-
-#ifdef NOT_USED
- int32 typmod = PG_GETARG_INT32(2);
-#endif
+ int32 tupTypmod = PG_GETARG_INT32(2);
HeapTupleHeader result;
- int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
Datum *values;
bool *nulls;
+ check_stack_depth(); /* recurses for record-type columns */
+
/*
- * Use the passed type unless it's RECORD; we can't support input of
- * anonymous types, mainly because there's no good way to figure out which
- * anonymous type is wanted. Note that for RECORD, what we'll probably
- * actually get is RECORD's typelem, ie, zero.
+ * Give a friendly error message if we did not get enough info to identify
+ * the target record type. (lookup_rowtype_tupdesc would fail anyway, but
+ * with a non-user-friendly message.) In ordinary SQL usage, we'll get -1
+ * for typmod, since composite types and RECORD have no type modifiers at
+ * the SQL level, and thus must fail for RECORD. However some callers can
+ * supply a valid typmod, and then we can do something useful for RECORD.
*/
- if (tupType == InvalidOid || tupType == RECORDOID)
+ if (tupType == RECORDOID && tupTypmod < 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("input of anonymous composite types is not implemented")));
- tupTypmod = -1; /* for all non-anonymous types */
+ errmsg("input of anonymous composite types is not implemented")));
+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
validcols = 0;
for (i = 0; i < ncolumns; i++)
{
- if (!tupdesc->attrs[i]->attisdropped)
+ if (!TupleDescAttr(tupdesc, i)->attisdropped)
validcols++;
}
if (usercols != validcols)
/* Process each column */
for (i = 0; i < ncolumns; i++)
{
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
ColumnIOData *column_info = &my_extra->columns[i];
- Oid column_type = tupdesc->attrs[i]->atttypid;
+ Oid column_type = att->atttypid;
Oid coltypoid;
int itemlen;
StringInfoData item_buf;
char csave;
/* Ignore dropped columns in datatype, but fill with nulls */
- if (tupdesc->attrs[i]->attisdropped)
+ if (att->attisdropped)
{
values[i] = (Datum) 0;
nulls[i] = true;
values[i] = ReceiveFunctionCall(&column_info->proc,
bufptr,
column_info->typioparam,
- tupdesc->attrs[i]->atttypmod);
+ att->atttypmod);
if (bufptr)
{
/*
* We cannot return tuple->t_data because heap_form_tuple allocates it as
* part of a larger chunk, and our caller may expect to be able to pfree
- * our result. So must copy the info into a new palloc chunk.
+ * our result. So must copy the info into a new palloc chunk.
*/
result = (HeapTupleHeader) palloc(tuple->t_len);
memcpy(result, tuple->t_data, tuple->t_len);
bool *nulls;
StringInfoData buf;
+ check_stack_depth(); /* recurses for record-type columns */
+
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
- sizeof(RecordIOData) - sizeof(ColumnIOData)
- + ncolumns * sizeof(ColumnIOData));
+ offsetof(RecordIOData, columns) +
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
validcols = 0;
for (i = 0; i < ncolumns; i++)
{
- if (!tupdesc->attrs[i]->attisdropped)
+ if (!TupleDescAttr(tupdesc, i)->attisdropped)
validcols++;
}
- pq_sendint(&buf, validcols, 4);
+ pq_sendint32(&buf, validcols);
for (i = 0; i < ncolumns; i++)
{
+ Form_pg_attribute att = TupleDescAttr(tupdesc, i);
ColumnIOData *column_info = &my_extra->columns[i];
- Oid column_type = tupdesc->attrs[i]->atttypid;
+ Oid column_type = att->atttypid;
Datum attr;
bytea *outputbytes;
/* Ignore dropped columns in datatype */
- if (tupdesc->attrs[i]->attisdropped)
+ if (att->attisdropped)
continue;
- pq_sendint(&buf, column_type, sizeof(Oid));
+ pq_sendint32(&buf, column_type);
if (nulls[i])
{
/* emit -1 data length to signify a NULL */
- pq_sendint(&buf, -1, 4);
+ pq_sendint32(&buf, -1);
continue;
}
column_info->column_type = column_type;
}
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (column_info->typisvarlena)
- attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
- else
- attr = values[i];
-
+ attr = values[i];
outputbytes = SendFunctionCall(&column_info->proc, attr);
-
- /* We assume the result will not have been toasted */
- pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+ pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
pq_sendbytes(&buf, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
-
- pfree(outputbytes);
-
- /* Clean up detoasted copy, if any */
- if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
- pfree(DatumGetPointer(attr));
}
pfree(values);
int i2;
int j;
+ check_stack_depth(); /* recurses for record-type columns */
+
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordCompareData) - sizeof(ColumnCompareData)
- + ncols * sizeof(ColumnCompareData));
+ offsetof(RecordCompareData, columns) +
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
/*
* Scan corresponding columns, allowing for dropped columns in different
- * places in the two rows. i1 and i2 are physical column indexes, j is
+ * places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2)
{
+ Form_pg_attribute att1;
+ Form_pg_attribute att2;
TypeCacheEntry *typentry;
Oid collation;
/*
* Skip dropped columns
*/
- if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
+ if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
{
i1++;
continue;
}
- if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
+ if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
{
i2++;
continue;
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
+ att1 = TupleDescAttr(tupdesc1, i1);
+ att2 = TupleDescAttr(tupdesc2, i2);
+
/*
* Have two matching columns, they must be same type
*/
- if (tupdesc1->attrs[i1]->atttypid !=
- tupdesc2->attrs[i2]->atttypid)
+ if (att1->atttypid != att2->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
- format_type_be(tupdesc1->attrs[i1]->atttypid),
- format_type_be(tupdesc2->attrs[i2]->atttypid),
+ format_type_be(att1->atttypid),
+ format_type_be(att2->atttypid),
j + 1)));
/*
* If they're not same collation, we don't complain here, but the
* comparison function might.
*/
- collation = tupdesc1->attrs[i1]->attcollation;
- if (collation != tupdesc2->attrs[i2]->attcollation)
+ collation = att1->attcollation;
+ if (collation != att2->attcollation)
collation = InvalidOid;
/*
*/
typentry = my_extra->columns[j].typentry;
if (typentry == NULL ||
- typentry->type_id != tupdesc1->attrs[i1]->atttypid)
+ typentry->type_id != att1->atttypid)
{
- typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid,
+ typentry = lookup_type_cache(att1->atttypid,
TYPECACHE_CMP_PROC_FINFO);
if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify a comparison function for type %s",
- format_type_be(typentry->type_id))));
+ errmsg("could not identify a comparison function for type %s",
+ format_type_be(typentry->type_id))));
my_extra->columns[j].typentry = typentry;
}
*/
if (!nulls1[i1] || !nulls2[i2])
{
- FunctionCallInfoData locfcinfo;
+ LOCAL_FCINFO(locfcinfo, 2);
int32 cmpresult;
if (nulls1[i1])
}
/* Compare the pair of elements */
- InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
collation, NULL, NULL);
- locfcinfo.arg[0] = values1[i1];
- locfcinfo.arg[1] = values2[i2];
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].value = values1[i1];
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].value = values2[i2];
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
if (cmpresult < 0)
{
int i2;
int j;
+ check_stack_depth(); /* recurses for record-type columns */
+
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordCompareData) - sizeof(ColumnCompareData)
- + ncols * sizeof(ColumnCompareData));
+ offsetof(RecordCompareData, columns) +
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
/*
* Scan corresponding columns, allowing for dropped columns in different
- * places in the two rows. i1 and i2 are physical column indexes, j is
+ * places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2)
{
+ LOCAL_FCINFO(locfcinfo, 2);
+ Form_pg_attribute att1;
+ Form_pg_attribute att2;
TypeCacheEntry *typentry;
Oid collation;
- FunctionCallInfoData locfcinfo;
bool oprresult;
/*
* Skip dropped columns
*/
- if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
+ if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
{
i1++;
continue;
}
- if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
+ if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
{
i2++;
continue;
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
+ att1 = TupleDescAttr(tupdesc1, i1);
+ att2 = TupleDescAttr(tupdesc2, i2);
+
/*
* Have two matching columns, they must be same type
*/
- if (tupdesc1->attrs[i1]->atttypid !=
- tupdesc2->attrs[i2]->atttypid)
+ if (att1->atttypid != att2->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
- format_type_be(tupdesc1->attrs[i1]->atttypid),
- format_type_be(tupdesc2->attrs[i2]->atttypid),
+ format_type_be(att1->atttypid),
+ format_type_be(att2->atttypid),
j + 1)));
/*
* If they're not same collation, we don't complain here, but the
* equality function might.
*/
- collation = tupdesc1->attrs[i1]->attcollation;
- if (collation != tupdesc2->attrs[i2]->attcollation)
+ collation = att1->attcollation;
+ if (collation != att2->attcollation)
collation = InvalidOid;
/*
*/
typentry = my_extra->columns[j].typentry;
if (typentry == NULL ||
- typentry->type_id != tupdesc1->attrs[i1]->atttypid)
+ typentry->type_id != att1->atttypid)
{
- typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid,
+ typentry = lookup_type_cache(att1->atttypid,
TYPECACHE_EQ_OPR_FINFO);
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(typentry->type_id))));
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(typentry->type_id))));
my_extra->columns[j].typentry = typentry;
}
}
/* Compare the pair of elements */
- InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
collation, NULL, NULL);
- locfcinfo.arg[0] = values1[i1];
- locfcinfo.arg[1] = values2[i2];
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].value = values1[i1];
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].value = values2[i2];
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
if (!oprresult)
{
result = false;
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordCompareData) - sizeof(ColumnCompareData)
- + ncols * sizeof(ColumnCompareData));
+ offsetof(RecordCompareData, columns) +
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
/*
* Scan corresponding columns, allowing for dropped columns in different
- * places in the two rows. i1 and i2 are physical column indexes, j is
+ * places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2)
{
+ Form_pg_attribute att1;
+ Form_pg_attribute att2;
+
/*
* Skip dropped columns
*/
- if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
+ if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
{
i1++;
continue;
}
- if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
+ if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
{
i2++;
continue;
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
+ att1 = TupleDescAttr(tupdesc1, i1);
+ att2 = TupleDescAttr(tupdesc2, i2);
+
/*
* Have two matching columns, they must be same type
*/
- if (tupdesc1->attrs[i1]->atttypid !=
- tupdesc2->attrs[i2]->atttypid)
+ if (att1->atttypid != att2->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
- format_type_be(tupdesc1->attrs[i1]->atttypid),
- format_type_be(tupdesc2->attrs[i2]->atttypid),
+ format_type_be(att1->atttypid),
+ format_type_be(att2->atttypid),
j + 1)));
- /*
- * The same type should have the same length (or both should be variable).
- */
- Assert(tupdesc1->attrs[i1]->attlen ==
- tupdesc2->attrs[i2]->attlen);
+ /*
+ * The same type should have the same length (or both should be
+ * variable).
+ */
+ Assert(att1->attlen == att2->attlen);
/*
* We consider two NULLs equal; NULL > not-NULL.
}
/* Compare the pair of elements */
- if (tupdesc1->attrs[i1]->attlen == -1)
+ if (att1->attlen == -1)
{
Size len1,
len2;
- struct varlena *arg1val;
- struct varlena *arg2val;
+ struct varlena *arg1val;
+ struct varlena *arg2val;
len1 = toast_raw_datum_size(values1[i1]);
len2 = toast_raw_datum_size(values2[i2]);
if ((Pointer) arg2val != (Pointer) values2[i2])
pfree(arg2val);
}
- else if (tupdesc1->attrs[i1]->attbyval)
+ else if (att1->attbyval)
{
- switch (tupdesc1->attrs[i1]->attlen)
- {
- case 1:
- if (GET_1_BYTE(values1[i1]) !=
- GET_1_BYTE(values2[i2]))
- {
- cmpresult = (GET_1_BYTE(values1[i1]) <
- GET_1_BYTE(values2[i2])) ? -1 : 1;
- }
- break;
- case 2:
- if (GET_2_BYTES(values1[i1]) !=
- GET_2_BYTES(values2[i2]))
- {
- cmpresult = (GET_2_BYTES(values1[i1]) <
- GET_2_BYTES(values2[i2])) ? -1 : 1;
- }
- break;
- case 4:
- if (GET_4_BYTES(values1[i1]) !=
- GET_4_BYTES(values2[i2]))
- {
- cmpresult = (GET_4_BYTES(values1[i1]) <
- GET_4_BYTES(values2[i2])) ? -1 : 1;
- }
- break;
-#if SIZEOF_DATUM == 8
- case 8:
- if (GET_8_BYTES(values1[i1]) !=
- GET_8_BYTES(values2[i2]))
- {
- cmpresult = (GET_8_BYTES(values1[i1]) <
- GET_8_BYTES(values2[i2])) ? -1 : 1;
- }
- break;
-#endif
- default:
- Assert(false); /* cannot happen */
- }
+ if (values1[i1] != values2[i2])
+ cmpresult = (values1[i1] < values2[i2]) ? -1 : 1;
}
else
{
cmpresult = memcmp(DatumGetPointer(values1[i1]),
DatumGetPointer(values2[i2]),
- tupdesc1->attrs[i1]->attlen);
+ att1->attlen);
}
if (cmpresult < 0)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(RecordCompareData) - sizeof(ColumnCompareData)
- + ncols * sizeof(ColumnCompareData));
+ offsetof(RecordCompareData, columns) +
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
/*
* Scan corresponding columns, allowing for dropped columns in different
- * places in the two rows. i1 and i2 are physical column indexes, j is
+ * places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2)
{
+ Form_pg_attribute att1;
+ Form_pg_attribute att2;
+
/*
* Skip dropped columns
*/
- if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
+ if (i1 < ncolumns1 && TupleDescAttr(tupdesc1, i1)->attisdropped)
{
i1++;
continue;
}
- if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
+ if (i2 < ncolumns2 && TupleDescAttr(tupdesc2, i2)->attisdropped)
{
i2++;
continue;
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
+ att1 = TupleDescAttr(tupdesc1, i1);
+ att2 = TupleDescAttr(tupdesc2, i2);
+
/*
* Have two matching columns, they must be same type
*/
- if (tupdesc1->attrs[i1]->atttypid !=
- tupdesc2->attrs[i2]->atttypid)
+ if (att1->atttypid != att2->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
- format_type_be(tupdesc1->attrs[i1]->atttypid),
- format_type_be(tupdesc2->attrs[i2]->atttypid),
+ format_type_be(att1->atttypid),
+ format_type_be(att2->atttypid),
j + 1)));
/*
}
/* Compare the pair of elements */
- if (tupdesc1->attrs[i1]->attlen == -1)
- {
- Size len1,
- len2;
-
- len1 = toast_raw_datum_size(values1[i1]);
- len2 = toast_raw_datum_size(values2[i2]);
- /* No need to de-toast if lengths don't match. */
- if (len1 != len2)
- result = false;
- else
- {
- struct varlena *arg1val;
- struct varlena *arg2val;
-
- arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
- arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
-
- result = (memcmp(VARDATA_ANY(arg1val),
- VARDATA_ANY(arg2val),
- len1 - VARHDRSZ) == 0);
-
- /* Only free memory if it's a copy made here. */
- if ((Pointer) arg1val != (Pointer) values1[i1])
- pfree(arg1val);
- if ((Pointer) arg2val != (Pointer) values2[i2])
- pfree(arg2val);
- }
- }
- else if (tupdesc1->attrs[i1]->attbyval)
- {
- switch (tupdesc1->attrs[i1]->attlen)
- {
- case 1:
- result = (GET_1_BYTE(values1[i1]) ==
- GET_1_BYTE(values2[i2]));
- break;
- case 2:
- result = (GET_2_BYTES(values1[i1]) ==
- GET_2_BYTES(values2[i2]));
- break;
- case 4:
- result = (GET_4_BYTES(values1[i1]) ==
- GET_4_BYTES(values2[i2]));
- break;
-#if SIZEOF_DATUM == 8
- case 8:
- result = (GET_8_BYTES(values1[i1]) ==
- GET_8_BYTES(values2[i2]));
- break;
-#endif
- default:
- Assert(false); /* cannot happen */
- }
- }
- else
- {
- result = (memcmp(DatumGetPointer(values1[i1]),
- DatumGetPointer(values2[i2]),
- tupdesc1->attrs[i1]->attlen) == 0);
- }
+ result = datum_image_eq(values1[i1], values2[i2], att1->attbyval, att2->attlen);
if (!result)
break;
}