*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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;
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;
/*
+ 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;
}
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 */
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;
/*
* 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,
}
}
+ /* The check for ')' here is redundant except when ncolumns == 0 */
if (*ptr++ != ')')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
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));
+ 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->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 */
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;
}
/*
- * 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,
}
}
+ /* And emit the string */
if (nq)
appendStringInfoChar(&buf, '"');
for (tmp = value; *tmp; tmp++)
char ch = *tmp;
if (ch == '"' || ch == '\\')
- appendStringInfoChar(&buf, '\\');
+ appendStringInfoChar(&buf, ch);
appendStringInfoChar(&buf, ch);
}
if (nq)
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);
}
/*
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;
+
+ /*
+ * 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_VOID(); /* keep compiler quiet */
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}