mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-02 06:51:22 +02:00
Add binary I/O support for composite types.
This commit is contained in:
parent
f24c5098fd
commit
62c3e61e50
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
char *string = PG_GETARG_CSTRING(0);
|
||||||
Oid tupType = PG_GETARG_OID(1);
|
Oid tupType = PG_GETARG_OID(1);
|
||||||
HeapTuple tuple;
|
int32 tupTypmod;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
|
HeapTuple tuple;
|
||||||
RecordIOData *my_extra;
|
RecordIOData *my_extra;
|
||||||
int ncolumns;
|
int ncolumns;
|
||||||
int i;
|
int i;
|
||||||
@ -74,7 +75,8 @@ record_in(PG_FUNCTION_ARGS)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("input of anonymous composite types is not implemented")));
|
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;
|
ncolumns = tupdesc->natts;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -91,17 +93,17 @@ record_in(PG_FUNCTION_ARGS)
|
|||||||
+ ncolumns * sizeof(ColumnIOData));
|
+ ncolumns * sizeof(ColumnIOData));
|
||||||
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
||||||
my_extra->record_type = InvalidOid;
|
my_extra->record_type = InvalidOid;
|
||||||
my_extra->record_typmod = -1;
|
my_extra->record_typmod = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (my_extra->record_type != tupType ||
|
if (my_extra->record_type != tupType ||
|
||||||
my_extra->record_typmod != -1)
|
my_extra->record_typmod != tupTypmod)
|
||||||
{
|
{
|
||||||
MemSet(my_extra, 0,
|
MemSet(my_extra, 0,
|
||||||
sizeof(RecordIOData) - sizeof(ColumnIOData)
|
sizeof(RecordIOData) - sizeof(ColumnIOData)
|
||||||
+ ncolumns * sizeof(ColumnIOData));
|
+ ncolumns * sizeof(ColumnIOData));
|
||||||
my_extra->record_type = tupType;
|
my_extra->record_type = tupType;
|
||||||
my_extra->record_typmod = -1;
|
my_extra->record_typmod = tupTypmod;
|
||||||
my_extra->ncolumns = ncolumns;
|
my_extra->ncolumns = ncolumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +111,8 @@ record_in(PG_FUNCTION_ARGS)
|
|||||||
nulls = (char *) palloc(ncolumns * sizeof(char));
|
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;
|
ptr = string;
|
||||||
/* Allow leading whitespace */
|
/* Allow leading whitespace */
|
||||||
@ -126,8 +129,9 @@ record_in(PG_FUNCTION_ARGS)
|
|||||||
for (i = 0; i < ncolumns; i++)
|
for (i = 0; i < ncolumns; i++)
|
||||||
{
|
{
|
||||||
ColumnIOData *column_info = &my_extra->columns[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 == ')')
|
if (*ptr == ',' || *ptr == ')')
|
||||||
{
|
{
|
||||||
values[i] = (Datum) 0;
|
values[i] = (Datum) 0;
|
||||||
@ -179,14 +183,14 @@ record_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert the column value
|
* 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->typiofunc,
|
||||||
&column_info->typioparam);
|
&column_info->typioparam);
|
||||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||||
fcinfo->flinfo->fn_mcxt);
|
fcinfo->flinfo->fn_mcxt);
|
||||||
column_info->column_type = tupdesc->attrs[i]->atttypid;
|
column_info->column_type = column_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
values[i] = FunctionCall3(&column_info->proc,
|
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++ != ')')
|
if (*ptr++ != ')')
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
@ -274,6 +279,7 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
tupTypmod = -1;
|
tupTypmod = -1;
|
||||||
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
|
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
|
||||||
ncolumns = tupdesc->natts;
|
ncolumns = tupdesc->natts;
|
||||||
|
|
||||||
/* Build a temporary HeapTuple control structure */
|
/* Build a temporary HeapTuple control structure */
|
||||||
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
||||||
ItemPointerSetInvalid(&(tuple.t_self));
|
ItemPointerSetInvalid(&(tuple.t_self));
|
||||||
@ -294,7 +300,7 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
+ ncolumns * sizeof(ColumnIOData));
|
+ ncolumns * sizeof(ColumnIOData));
|
||||||
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
||||||
my_extra->record_type = InvalidOid;
|
my_extra->record_type = InvalidOid;
|
||||||
my_extra->record_typmod = -1;
|
my_extra->record_typmod = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (my_extra->record_type != tupType ||
|
if (my_extra->record_type != tupType ||
|
||||||
@ -308,9 +314,10 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
my_extra->ncolumns = ncolumns;
|
my_extra->ncolumns = ncolumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Break down the tuple into fields */
|
|
||||||
values = (Datum *) palloc(ncolumns * sizeof(Datum));
|
values = (Datum *) palloc(ncolumns * sizeof(Datum));
|
||||||
nulls = (char *) palloc(ncolumns * sizeof(char));
|
nulls = (char *) palloc(ncolumns * sizeof(char));
|
||||||
|
|
||||||
|
/* Break down the tuple into fields */
|
||||||
heap_deformtuple(&tuple, tupdesc, values, nulls);
|
heap_deformtuple(&tuple, tupdesc, values, nulls);
|
||||||
|
|
||||||
/* And build the result string */
|
/* And build the result string */
|
||||||
@ -321,6 +328,7 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
for (i = 0; i < ncolumns; i++)
|
for (i = 0; i < ncolumns; i++)
|
||||||
{
|
{
|
||||||
ColumnIOData *column_info = &my_extra->columns[i];
|
ColumnIOData *column_info = &my_extra->columns[i];
|
||||||
|
Oid column_type = tupdesc->attrs[i]->atttypid;
|
||||||
char *value;
|
char *value;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
bool nq;
|
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;
|
bool typIsVarlena;
|
||||||
|
|
||||||
getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
|
getTypeOutputInfo(column_type,
|
||||||
&column_info->typiofunc,
|
&column_info->typiofunc,
|
||||||
&column_info->typioparam,
|
&column_info->typioparam,
|
||||||
&typIsVarlena);
|
&typIsVarlena);
|
||||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||||
fcinfo->flinfo->fn_mcxt);
|
fcinfo->flinfo->fn_mcxt);
|
||||||
column_info->column_type = tupdesc->attrs[i]->atttypid;
|
column_info->column_type = column_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = DatumGetCString(FunctionCall3(&column_info->proc,
|
value = DatumGetCString(FunctionCall3(&column_info->proc,
|
||||||
@ -370,6 +378,7 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* And emit the string */
|
||||||
if (nq)
|
if (nq)
|
||||||
appendStringInfoChar(&buf, '"');
|
appendStringInfoChar(&buf, '"');
|
||||||
for (tmp = value; *tmp; tmp++)
|
for (tmp = value; *tmp; tmp++)
|
||||||
@ -377,7 +386,7 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
char ch = *tmp;
|
char ch = *tmp;
|
||||||
|
|
||||||
if (ch == '"' || ch == '\\')
|
if (ch == '"' || ch == '\\')
|
||||||
appendStringInfoChar(&buf, '\\');
|
appendStringInfoChar(&buf, ch);
|
||||||
appendStringInfoChar(&buf, ch);
|
appendStringInfoChar(&buf, ch);
|
||||||
}
|
}
|
||||||
if (nq)
|
if (nq)
|
||||||
@ -398,12 +407,154 @@ record_out(PG_FUNCTION_ARGS)
|
|||||||
Datum
|
Datum
|
||||||
record_recv(PG_FUNCTION_ARGS)
|
record_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
/* Need to decide on external format before we can write this */
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("input of composite types not implemented yet")));
|
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;
|
||||||
|
|
||||||
PG_RETURN_VOID(); /* keep compiler quiet */
|
/*
|
||||||
|
* 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
|
Datum
|
||||||
record_send(PG_FUNCTION_ARGS)
|
record_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
/* Need to decide on external format before we can write this */
|
HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
|
||||||
ereport(ERROR,
|
Oid tupType = PG_GETARG_OID(1);
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
int32 tupTypmod;
|
||||||
errmsg("output of composite types not implemented yet")));
|
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
Block a user