#include "postgres.h"#include <ctype.h>#include "access/htup_details.h"#include "catalog/pg_type.h"#include "libpq/pqformat.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/typcache.h"
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) |
| typedef struct ColumnCompareData ColumnCompareData |
| typedef struct ColumnIOData ColumnIOData |
| typedef struct RecordCompareData RecordCompareData |
| typedef struct RecordIOData RecordIOData |
| Datum btrecordcmp | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1280 of file rowtypes.c.
References PG_RETURN_INT32, and record_cmp().
{
PG_RETURN_INT32(record_cmp(fcinfo));
}
| static int record_cmp | ( | FunctionCallInfo | fcinfo | ) | [static] |
Definition at line 804 of file rowtypes.c.
References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, tupleDesc::attrs, 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, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TypeCacheEntry::type_id, TYPECACHE_CMP_PROC_FINFO, and ColumnCompareData::typentry.
Referenced by btrecordcmp(), record_ge(), record_gt(), record_le(), and record_lt().
{
HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
int result = 0;
Oid tupType1;
Oid tupType2;
int32 tupTypmod1;
int32 tupTypmod2;
TupleDesc tupdesc1;
TupleDesc tupdesc2;
HeapTupleData tuple1;
HeapTupleData tuple2;
int ncolumns1;
int ncolumns2;
RecordCompareData *my_extra;
int ncols;
Datum *values1;
Datum *values2;
bool *nulls1;
bool *nulls2;
int i1;
int i2;
int j;
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
ncolumns1 = tupdesc1->natts;
tupType2 = HeapTupleHeaderGetTypeId(record2);
tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
ncolumns2 = tupdesc2->natts;
/* Build temporary HeapTuple control structures */
tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
/*
* We arrange to look up the needed comparison info just once per series
* of calls, assuming the record types don't change underneath us.
*/
ncols = Max(ncolumns1, ncolumns2);
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns < ncols)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordCompareData) - sizeof(ColumnCompareData)
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
my_extra->record1_typmod = 0;
my_extra->record2_type = InvalidOid;
my_extra->record2_typmod = 0;
}
if (my_extra->record1_type != tupType1 ||
my_extra->record1_typmod != tupTypmod1 ||
my_extra->record2_type != tupType2 ||
my_extra->record2_typmod != tupTypmod2)
{
MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
my_extra->record1_type = tupType1;
my_extra->record1_typmod = tupTypmod1;
my_extra->record2_type = tupType2;
my_extra->record2_typmod = tupTypmod2;
}
/* Break down the tuples into fields */
values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
* Scan corresponding columns, allowing for dropped columns in different
* 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)
{
TypeCacheEntry *typentry;
Oid collation;
FunctionCallInfoData locfcinfo;
int32 cmpresult;
/*
* Skip dropped columns
*/
if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
{
i1++;
continue;
}
if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
{
i2++;
continue;
}
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
/*
* Have two matching columns, they must be same type
*/
if (tupdesc1->attrs[i1]->atttypid !=
tupdesc2->attrs[i2]->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),
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 = InvalidOid;
/*
* Lookup the comparison function if not done already
*/
typentry = my_extra->columns[j].typentry;
if (typentry == NULL ||
typentry->type_id != tupdesc1->attrs[i1]->atttypid)
{
typentry = lookup_type_cache(tupdesc1->attrs[i1]->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))));
my_extra->columns[j].typentry = typentry;
}
/*
* We consider two NULLs equal; NULL > not-NULL.
*/
if (!nulls1[i1] || !nulls2[i2])
{
if (nulls1[i1])
{
/* arg1 is greater than arg2 */
result = 1;
break;
}
if (nulls2[i2])
{
/* arg1 is less than arg2 */
result = -1;
break;
}
/* Compare the pair of elements */
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));
if (cmpresult < 0)
{
/* arg1 is less than arg2 */
result = -1;
break;
}
else if (cmpresult > 0)
{
/* arg1 is greater than arg2 */
result = 1;
break;
}
}
/* equal, so continue to next column */
i1++, i2++, j++;
}
/*
* If we didn't break out of the loop early, check for column count
* mismatch. (We do not report such mismatch if we found unequal column
* values; is that a feature or a bug?)
*/
if (result == 0)
{
if (i1 != ncolumns1 || i2 != ncolumns2)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare record types with different numbers of columns")));
}
pfree(values1);
pfree(nulls1);
pfree(values2);
pfree(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(record1, 0);
PG_FREE_IF_COPY(record2, 1);
return result;
}
| Datum record_eq | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1039 of file rowtypes.c.
References FunctionCallInfoData::arg, FunctionCallInfoData::argnull, tupleDesc::attrs, 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, OidIsValid, palloc(), pfree(), PG_FREE_IF_COPY, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BOOL, RecordCompareData::record1_type, RecordCompareData::record1_typmod, RecordCompareData::record2_type, RecordCompareData::record2_typmod, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TypeCacheEntry::type_id, TYPECACHE_EQ_OPR_FINFO, and ColumnCompareData::typentry.
Referenced by record_ne().
{
HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
bool result = true;
Oid tupType1;
Oid tupType2;
int32 tupTypmod1;
int32 tupTypmod2;
TupleDesc tupdesc1;
TupleDesc tupdesc2;
HeapTupleData tuple1;
HeapTupleData tuple2;
int ncolumns1;
int ncolumns2;
RecordCompareData *my_extra;
int ncols;
Datum *values1;
Datum *values2;
bool *nulls1;
bool *nulls2;
int i1;
int i2;
int j;
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
ncolumns1 = tupdesc1->natts;
tupType2 = HeapTupleHeaderGetTypeId(record2);
tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
ncolumns2 = tupdesc2->natts;
/* Build temporary HeapTuple control structures */
tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
/*
* We arrange to look up the needed comparison info just once per series
* of calls, assuming the record types don't change underneath us.
*/
ncols = Max(ncolumns1, ncolumns2);
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns < ncols)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordCompareData) - sizeof(ColumnCompareData)
+ ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
my_extra->record1_typmod = 0;
my_extra->record2_type = InvalidOid;
my_extra->record2_typmod = 0;
}
if (my_extra->record1_type != tupType1 ||
my_extra->record1_typmod != tupTypmod1 ||
my_extra->record2_type != tupType2 ||
my_extra->record2_typmod != tupTypmod2)
{
MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
my_extra->record1_type = tupType1;
my_extra->record1_typmod = tupTypmod1;
my_extra->record2_type = tupType2;
my_extra->record2_typmod = tupTypmod2;
}
/* Break down the tuples into fields */
values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
* Scan corresponding columns, allowing for dropped columns in different
* 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)
{
TypeCacheEntry *typentry;
Oid collation;
FunctionCallInfoData locfcinfo;
bool oprresult;
/*
* Skip dropped columns
*/
if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
{
i1++;
continue;
}
if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
{
i2++;
continue;
}
if (i1 >= ncolumns1 || i2 >= ncolumns2)
break; /* we'll deal with mismatch below loop */
/*
* Have two matching columns, they must be same type
*/
if (tupdesc1->attrs[i1]->atttypid !=
tupdesc2->attrs[i2]->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),
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 = InvalidOid;
/*
* Lookup the equality function if not done already
*/
typentry = my_extra->columns[j].typentry;
if (typentry == NULL ||
typentry->type_id != tupdesc1->attrs[i1]->atttypid)
{
typentry = lookup_type_cache(tupdesc1->attrs[i1]->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))));
my_extra->columns[j].typentry = typentry;
}
/*
* We consider two NULLs equal; NULL > not-NULL.
*/
if (!nulls1[i1] || !nulls2[i2])
{
if (nulls1[i1] || nulls2[i2])
{
result = false;
break;
}
/* Compare the pair of elements */
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));
if (!oprresult)
{
result = false;
break;
}
}
/* equal, so continue to next column */
i1++, i2++, j++;
}
/*
* If we didn't break out of the loop early, check for column count
* mismatch. (We do not report such mismatch if we found unequal column
* values; is that a feature or a bug?)
*/
if (result)
{
if (i1 != ncolumns1 || i2 != ncolumns2)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare record types with different numbers of columns")));
}
pfree(values1);
pfree(nulls1);
pfree(values2);
pfree(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(record1, 0);
PG_FREE_IF_COPY(record2, 1);
PG_RETURN_BOOL(result);
}
| Datum record_ge | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1274 of file rowtypes.c.
References PG_RETURN_BOOL, and record_cmp().
{
PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
}
| Datum record_gt | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1262 of file rowtypes.c.
References PG_RETURN_BOOL, and record_cmp().
{
PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
}
| Datum record_in | ( | PG_FUNCTION_ARGS | ) |
Definition at line 70 of file rowtypes.c.
References appendStringInfoChar(), tupleDesc::attrs, buf, 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, palloc(), pfree(), PG_GETARG_CSTRING, PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_HEAPTUPLEHEADER, ColumnIOData::proc, RecordIOData::record_type, RecordIOData::record_typmod, RECORDOID, ReleaseTupleDesc, resetStringInfo(), HeapTupleData::t_data, HeapTupleData::t_len, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.
{
char *string = PG_GETARG_CSTRING(0);
Oid tupType = PG_GETARG_OID(1);
#ifdef NOT_USED
int32 typmod = PG_GETARG_INT32(2);
#endif
HeapTupleHeader result;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
bool needComma = false;
int ncolumns;
int i;
char *ptr;
Datum *values;
bool *nulls;
StringInfoData buf;
/*
* 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.
*/
if (tupType == InvalidOid || tupType == RECORDOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of anonymous composite types is not implemented")));
tupTypmod = -1; /* for all non-anonymous types */
/*
* This comes from the composite type's pg_type.oid and stores system oids
* in user tables, specifically DatumTupleFields. This oid must be
* preserved by binary upgrades.
*/
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/*
* We arrange to look up the needed I/O info just once per series of
* calls, assuming the record type doesn't change underneath us.
*/
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns != ncolumns)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (bool *) palloc(ncolumns * sizeof(bool));
/*
* Scan the string. We use "buf" to accumulate the de-quoted data for
* each column, which is then fed to the appropriate input converter.
*/
ptr = string;
/* Allow leading whitespace */
while (*ptr && isspace((unsigned char) *ptr))
ptr++;
if (*ptr++ != '(')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Missing left parenthesis.")));
initStringInfo(&buf);
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
char *column_data;
/* Ignore dropped columns in datatype, but fill with nulls */
if (tupdesc->attrs[i]->attisdropped)
{
values[i] = (Datum) 0;
nulls[i] = true;
continue;
}
if (needComma)
{
/* Skip comma that separates prior field from this one */
if (*ptr == ',')
ptr++;
else
/* *ptr must be ')' */
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Too few columns.")));
}
/* Check for null: completely empty input means null */
if (*ptr == ',' || *ptr == ')')
{
column_data = NULL;
nulls[i] = true;
}
else
{
/* Extract string for this column */
bool inquote = false;
resetStringInfo(&buf);
while (inquote || !(*ptr == ',' || *ptr == ')'))
{
char ch = *ptr++;
if (ch == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"",
string),
errdetail("Unexpected end of input.")));
if (ch == '\\')
{
if (*ptr == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"",
string),
errdetail("Unexpected end of input.")));
appendStringInfoChar(&buf, *ptr++);
}
else if (ch == '\"')
{
if (!inquote)
inquote = true;
else if (*ptr == '\"')
{
/* doubled quote within quote sequence */
appendStringInfoChar(&buf, *ptr++);
}
else
inquote = false;
}
else
appendStringInfoChar(&buf, ch);
}
column_data = buf.data;
nulls[i] = false;
}
/*
* Convert the column value
*/
if (column_info->column_type != column_type)
{
getTypeInputInfo(column_type,
&column_info->typiofunc,
&column_info->typioparam);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
column_info->column_type = column_type;
}
values[i] = InputFunctionCall(&column_info->proc,
column_data,
column_info->typioparam,
tupdesc->attrs[i]->atttypmod);
/*
* Prep for next column
*/
needComma = true;
}
if (*ptr++ != ')')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Too many columns.")));
/* Allow trailing whitespace */
while (*ptr && isspace((unsigned char) *ptr))
ptr++;
if (*ptr)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Junk after right parenthesis.")));
tuple = heap_form_tuple(tupdesc, values, nulls);
/*
* 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.
*/
result = (HeapTupleHeader) palloc(tuple->t_len);
memcpy(result, tuple->t_data, tuple->t_len);
heap_freetuple(tuple);
pfree(buf.data);
pfree(values);
pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result);
}
| Datum record_le | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1268 of file rowtypes.c.
References PG_RETURN_BOOL, and record_cmp().
{
PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
}
| Datum record_lt | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1256 of file rowtypes.c.
References PG_RETURN_BOOL, and record_cmp().
{
PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
}
| Datum record_ne | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1250 of file rowtypes.c.
References DatumGetBool, PG_RETURN_BOOL, and record_eq().
{
PG_RETURN_BOOL(!DatumGetBool(record_eq(fcinfo)));
}
| Datum record_out | ( | PG_FUNCTION_ARGS | ) |
Definition at line 298 of file rowtypes.c.
References appendStringInfoChar(), appendStringInfoCharMacro, tupleDesc::attrs, buf, ColumnIOData::column_type, RecordIOData::columns, StringInfoData::data, DatumGetPointer, 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, OutputFunctionCall(), palloc(), pfree(), PG_DETOAST_DATUM, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_CSTRING, PointerGetDatum, 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.
{
HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
RecordIOData *my_extra;
bool needComma = false;
int ncolumns;
int i;
Datum *values;
bool *nulls;
StringInfoData buf;
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/* Build a temporary HeapTuple control structure */
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = rec;
/*
* We arrange to look up the needed I/O info just once per series of
* calls, assuming the record type doesn't change underneath us.
*/
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns != ncolumns)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (bool *) palloc(ncolumns * sizeof(bool));
/* Break down the tuple into fields */
heap_deform_tuple(&tuple, tupdesc, values, nulls);
/* And build the result string */
initStringInfo(&buf);
appendStringInfoChar(&buf, '(');
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
Datum attr;
char *value;
char *tmp;
bool nq;
/* Ignore dropped columns in datatype */
if (tupdesc->attrs[i]->attisdropped)
continue;
if (needComma)
appendStringInfoChar(&buf, ',');
needComma = true;
if (nulls[i])
{
/* emit nothing... */
continue;
}
/*
* Convert the column value to text
*/
if (column_info->column_type != column_type)
{
getTypeOutputInfo(column_type,
&column_info->typiofunc,
&column_info->typisvarlena);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
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];
value = OutputFunctionCall(&column_info->proc, attr);
/* Detect whether we need double quotes for this value */
nq = (value[0] == '\0'); /* force quotes for empty string */
for (tmp = value; *tmp; tmp++)
{
char ch = *tmp;
if (ch == '"' || ch == '\\' ||
ch == '(' || ch == ')' || ch == ',' ||
isspace((unsigned char) ch))
{
nq = true;
break;
}
}
/* And emit the string */
if (nq)
appendStringInfoCharMacro(&buf, '"');
for (tmp = value; *tmp; tmp++)
{
char ch = *tmp;
if (ch == '"' || ch == '\\')
appendStringInfoCharMacro(&buf, ch);
appendStringInfoCharMacro(&buf, ch);
}
if (nq)
appendStringInfoCharMacro(&buf, '"');
pfree(value);
/* Clean up detoasted copy, if any */
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
pfree(DatumGetPointer(attr));
}
appendStringInfoChar(&buf, ')');
pfree(values);
pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_CSTRING(buf.data);
}
| Datum record_recv | ( | PG_FUNCTION_ARGS | ) |
Definition at line 460 of file rowtypes.c.
References tupleDesc::attrs, buf, 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, palloc(), pfree(), PG_GETARG_INT32, PG_GETARG_OID, PG_GETARG_POINTER, PG_RETURN_HEAPTUPLEHEADER, pq_getmsgint(), ColumnIOData::proc, ReceiveFunctionCall(), RecordIOData::record_type, RecordIOData::record_typmod, RECORDOID, ReleaseTupleDesc, HeapTupleData::t_data, HeapTupleData::t_len, ColumnIOData::typiofunc, ColumnIOData::typioparam, and values.
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid tupType = PG_GETARG_OID(1);
#ifdef NOT_USED
int32 typmod = PG_GETARG_INT32(2);
#endif
HeapTupleHeader result;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
int ncolumns;
int usercols;
int validcols;
int i;
Datum *values;
bool *nulls;
/*
* 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.
*/
if (tupType == InvalidOid || tupType == RECORDOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of anonymous composite types is not implemented")));
tupTypmod = -1; /* for all non-anonymous types */
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/*
* We arrange to look up the needed I/O info just once per series of
* calls, assuming the record type doesn't change underneath us.
*/
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns != ncolumns)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (bool *) palloc(ncolumns * sizeof(bool));
/* Fetch number of columns user thinks it has */
usercols = pq_getmsgint(buf, 4);
/* Need to scan to count nondeleted columns */
validcols = 0;
for (i = 0; i < ncolumns; i++)
{
if (!tupdesc->attrs[i]->attisdropped)
validcols++;
}
if (usercols != validcols)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("wrong number of columns: %d, expected %d",
usercols, validcols)));
/* Process each column */
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
Oid coltypoid;
int itemlen;
StringInfoData item_buf;
StringInfo bufptr;
char csave;
/* Ignore dropped columns in datatype, but fill with nulls */
if (tupdesc->attrs[i]->attisdropped)
{
values[i] = (Datum) 0;
nulls[i] = true;
continue;
}
/* Verify column datatype */
coltypoid = pq_getmsgint(buf, sizeof(Oid));
if (coltypoid != column_type)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("wrong data type: %u, expected %u",
coltypoid, column_type)));
/* Get and check the item length */
itemlen = pq_getmsgint(buf, 4);
if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("insufficient data left in message")));
if (itemlen == -1)
{
/* -1 length means NULL */
bufptr = NULL;
nulls[i] = true;
csave = 0; /* keep compiler quiet */
}
else
{
/*
* Rather than copying data around, we just set up a phony
* StringInfo pointing to the correct portion of the input buffer.
* We assume we can scribble on the input buffer so as to maintain
* the convention that StringInfos have a trailing null.
*/
item_buf.data = &buf->data[buf->cursor];
item_buf.maxlen = itemlen + 1;
item_buf.len = itemlen;
item_buf.cursor = 0;
buf->cursor += itemlen;
csave = buf->data[buf->cursor];
buf->data[buf->cursor] = '\0';
bufptr = &item_buf;
nulls[i] = false;
}
/* Now call the column's receiveproc */
if (column_info->column_type != column_type)
{
getTypeBinaryInputInfo(column_type,
&column_info->typiofunc,
&column_info->typioparam);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
column_info->column_type = column_type;
}
values[i] = ReceiveFunctionCall(&column_info->proc,
bufptr,
column_info->typioparam,
tupdesc->attrs[i]->atttypmod);
if (bufptr)
{
/* Trouble if it didn't eat the whole buffer */
if (item_buf.cursor != itemlen)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("improper binary format in record column %d",
i + 1)));
buf->data[buf->cursor] = csave;
}
}
tuple = heap_form_tuple(tupdesc, values, nulls);
/*
* 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.
*/
result = (HeapTupleHeader) palloc(tuple->t_len);
memcpy(result, tuple->t_data, tuple->t_len);
heap_freetuple(tuple);
pfree(values);
pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result);
}
| Datum record_send | ( | PG_FUNCTION_ARGS | ) |
Definition at line 655 of file rowtypes.c.
References tupleDesc::attrs, buf, ColumnIOData::column_type, RecordIOData::columns, DatumGetPointer, 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, palloc(), pfree(), PG_DETOAST_DATUM, PG_GETARG_HEAPTUPLEHEADER, PG_RETURN_BYTEA_P, PointerGetDatum, 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.
{
HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
RecordIOData *my_extra;
int ncolumns;
int validcols;
int i;
Datum *values;
bool *nulls;
StringInfoData buf;
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/* Build a temporary HeapTuple control structure */
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = rec;
/*
* We arrange to look up the needed I/O info just once per series of
* calls, assuming the record type doesn't change underneath us.
*/
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns != ncolumns)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (bool *) palloc(ncolumns * sizeof(bool));
/* Break down the tuple into fields */
heap_deform_tuple(&tuple, tupdesc, values, nulls);
/* And build the result string */
pq_begintypsend(&buf);
/* Need to scan to count nondeleted columns */
validcols = 0;
for (i = 0; i < ncolumns; i++)
{
if (!tupdesc->attrs[i]->attisdropped)
validcols++;
}
pq_sendint(&buf, validcols, 4);
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
Datum attr;
bytea *outputbytes;
/* Ignore dropped columns in datatype */
if (tupdesc->attrs[i]->attisdropped)
continue;
pq_sendint(&buf, column_type, sizeof(Oid));
if (nulls[i])
{
/* emit -1 data length to signify a NULL */
pq_sendint(&buf, -1, 4);
continue;
}
/*
* Convert the column value to binary
*/
if (column_info->column_type != column_type)
{
getTypeBinaryOutputInfo(column_type,
&column_info->typiofunc,
&column_info->typisvarlena);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
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];
outputbytes = SendFunctionCall(&column_info->proc, attr);
/* We assume the result will not have been toasted */
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
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);
pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
1.7.1