Add binary I/O support for composite types.

This commit is contained in:
Tom Lane 2004-06-06 18:06:25 +00:00
parent f24c5098fd
commit 62c3e61e50
1 changed files with 291 additions and 28 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.2 2004/06/06 04:50:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.3 2004/06/06 18:06:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -54,8 +54,9 @@ record_in(PG_FUNCTION_ARGS)
{
char *string = PG_GETARG_CSTRING(0);
Oid tupType = PG_GETARG_OID(1);
HeapTuple tuple;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
int ncolumns;
int i;
@ -74,7 +75,8 @@ record_in(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of anonymous composite types is not implemented")));
tupdesc = lookup_rowtype_tupdesc(tupType, -1);
tupTypmod = -1; /* for all non-anonymous types */
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/*
@ -91,17 +93,17 @@ record_in(PG_FUNCTION_ARGS)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = -1;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != -1)
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 = -1;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
@ -109,7 +111,8 @@ record_in(PG_FUNCTION_ARGS)
nulls = (char *) palloc(ncolumns * sizeof(char));
/*
* Scan the string.
* 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 */
@ -126,8 +129,9 @@ record_in(PG_FUNCTION_ARGS)
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
/* Check for null */
/* Check for null: completely empty input means null */
if (*ptr == ',' || *ptr == ')')
{
values[i] = (Datum) 0;
@ -179,14 +183,14 @@ record_in(PG_FUNCTION_ARGS)
/*
* Convert the column value
*/
if (column_info->column_type != tupdesc->attrs[i]->atttypid)
if (column_info->column_type != column_type)
{
getTypeInputInfo(tupdesc->attrs[i]->atttypid,
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 = tupdesc->attrs[i]->atttypid;
column_info->column_type = column_type;
}
values[i] = FunctionCall3(&column_info->proc,
@ -219,6 +223,7 @@ record_in(PG_FUNCTION_ARGS)
}
}
/* The check for ')' here is redundant except when ncolumns == 0 */
if (*ptr++ != ')')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
@ -274,6 +279,7 @@ record_out(PG_FUNCTION_ARGS)
tupTypmod = -1;
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
/* Build a temporary HeapTuple control structure */
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
@ -294,7 +300,7 @@ record_out(PG_FUNCTION_ARGS)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = -1;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
@ -308,9 +314,10 @@ record_out(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}
/* Break down the tuple into fields */
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (char *) palloc(ncolumns * sizeof(char));
/* Break down the tuple into fields */
heap_deformtuple(&tuple, tupdesc, values, nulls);
/* And build the result string */
@ -321,6 +328,7 @@ record_out(PG_FUNCTION_ARGS)
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
char *value;
char *tmp;
bool nq;
@ -335,19 +343,19 @@ record_out(PG_FUNCTION_ARGS)
}
/*
* Convert the column value
* Convert the column value to text
*/
if (column_info->column_type != tupdesc->attrs[i]->atttypid)
if (column_info->column_type != column_type)
{
bool typIsVarlena;
getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
getTypeOutputInfo(column_type,
&column_info->typiofunc,
&column_info->typioparam,
&typIsVarlena);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
column_info->column_type = tupdesc->attrs[i]->atttypid;
column_info->column_type = column_type;
}
value = DatumGetCString(FunctionCall3(&column_info->proc,
@ -370,6 +378,7 @@ record_out(PG_FUNCTION_ARGS)
}
}
/* And emit the string */
if (nq)
appendStringInfoChar(&buf, '"');
for (tmp = value; *tmp; tmp++)
@ -377,7 +386,7 @@ record_out(PG_FUNCTION_ARGS)
char ch = *tmp;
if (ch == '"' || ch == '\\')
appendStringInfoChar(&buf, '\\');
appendStringInfoChar(&buf, ch);
appendStringInfoChar(&buf, ch);
}
if (nq)
@ -398,12 +407,154 @@ record_out(PG_FUNCTION_ARGS)
Datum
record_recv(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("input of composite types not implemented yet")));
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid tupType = PG_GETARG_OID(1);
int32 tupTypmod;
TupleDesc tupdesc;
HeapTuple tuple;
RecordIOData *my_extra;
int ncolumns;
int i;
Datum *values;
char *nulls;
PG_RETURN_VOID(); /* keep compiler quiet */
/*
* 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 = (char *) palloc(ncolumns * sizeof(char));
/* Verify number of columns */
i = pq_getmsgint(buf, 4);
if (i != ncolumns)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("wrong number of columns: %d, expected %d",
i, ncolumns)));
/* 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;
/* 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 */
values[i] = (Datum) 0;
nulls[i] = 'n';
}
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.
*/
StringInfoData item_buf;
char csave;
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';
/* 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] = FunctionCall2(&column_info->proc,
PointerGetDatum(&item_buf),
ObjectIdGetDatum(column_info->typioparam));
nulls[i] = ' ';
/* 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_formtuple(tupdesc, values, nulls);
pfree(values);
pfree(nulls);
PG_RETURN_HEAPTUPLEHEADER(tuple->t_data);
}
/*
@ -412,10 +563,122 @@ record_recv(PG_FUNCTION_ARGS)
Datum
record_send(PG_FUNCTION_ARGS)
{
/* Need to decide on external format before we can write this */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("output of composite types not implemented yet")));
HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
Oid tupType = PG_GETARG_OID(1);
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
RecordIOData *my_extra;
int ncolumns;
int i;
Datum *values;
char *nulls;
StringInfoData buf;
PG_RETURN_VOID(); /* keep compiler quiet */
/*
* Use the passed type unless it's RECORD; in that case, we'd better
* get the type info out of the datum itself. Note that for RECORD,
* what we'll probably actually get is RECORD's typelem, ie, zero.
*/
if (tupType == InvalidOid || tupType == RECORDOID)
{
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
}
else
tupTypmod = -1;
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 = (char *) palloc(ncolumns * sizeof(char));
/* Break down the tuple into fields */
heap_deformtuple(&tuple, tupdesc, values, nulls);
/* And build the result string */
pq_begintypsend(&buf);
pq_sendint(&buf, ncolumns, 4);
for (i = 0; i < ncolumns; i++)
{
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
bytea *outputbytes;
pq_sendint(&buf, column_type, sizeof(Oid));
if (nulls[i] == 'n')
{
/* 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)
{
bool typIsVarlena;
getTypeBinaryOutputInfo(column_type,
&column_info->typiofunc,
&column_info->typioparam,
&typIsVarlena);
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
fcinfo->flinfo->fn_mcxt);
column_info->column_type = column_type;
}
outputbytes = DatumGetByteaP(FunctionCall2(&column_info->proc,
values[i],
ObjectIdGetDatum(column_info->typioparam)));
/* 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);
}
pfree(values);
pfree(nulls);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}