Add binary I/O support for composite types.
This commit is contained in:
parent
f24c5098fd
commit
62c3e61e50
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue