Commit a3704d3d authored by Tom Lane's avatar Tom Lane

Preliminary support for composite type I/O; just text for now,

no binary yet.
parent c541bb86
......@@ -8,14 +8,42 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.1 2004/04/01 21:28:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.2 2004/06/06 04:50:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include "access/heapam.h"
#include "access/htup.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
/*
* structure to cache metadata needed for record I/O
*/
typedef struct ColumnIOData
{
Oid column_type;
Oid typiofunc;
Oid typioparam;
FmgrInfo proc;
} ColumnIOData;
typedef struct RecordIOData
{
Oid record_type;
int32 record_typmod;
int ncolumns;
ColumnIOData columns[1]; /* VARIABLE LENGTH ARRAY */
} RecordIOData;
/*
......@@ -24,12 +52,194 @@
Datum
record_in(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")));
char *string = PG_GETARG_CSTRING(0);
Oid tupType = PG_GETARG_OID(1);
HeapTuple tuple;
TupleDesc tupdesc;
RecordIOData *my_extra;
int ncolumns;
int i;
char *ptr;
Datum *values;
char *nulls;
StringInfoData buf;
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")));
tupdesc = lookup_rowtype_tupdesc(tupType, -1);
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 = -1;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != -1)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
my_extra->record_typmod = -1;
my_extra->ncolumns = ncolumns;
}
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (char *) palloc(ncolumns * sizeof(char));
/*
* Scan the string.
*/
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];
/* Check for null */
if (*ptr == ',' || *ptr == ')')
{
values[i] = (Datum) 0;
nulls[i] = 'n';
}
else
{
/* Extract string for this column */
bool inquote = false;
buf.len = 0;
buf.data[0] = '\0';
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);
}
/*
* Convert the column value
*/
if (column_info->column_type != tupdesc->attrs[i]->atttypid)
{
getTypeInputInfo(tupdesc->attrs[i]->atttypid,
&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;
}
values[i] = FunctionCall3(&column_info->proc,
CStringGetDatum(buf.data),
ObjectIdGetDatum(column_info->typioparam),
Int32GetDatum(tupdesc->attrs[i]->atttypmod));
nulls[i] = ' ';
}
/*
* Prep for next column
*/
if (*ptr == ',')
{
if (i == ncolumns-1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Too many columns.")));
ptr++;
}
else
{
/* *ptr must be ')' */
if (i < ncolumns-1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed record literal: \"%s\"", string),
errdetail("Too few columns.")));
}
}
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_formtuple(tupdesc, values, nulls);
pfree(buf.data);
pfree(values);
pfree(nulls);
PG_RETURN_HEAPTUPLEHEADER(tuple->t_data);
}
/*
......@@ -38,12 +248,148 @@ record_in(PG_FUNCTION_ARGS)
Datum
record_out(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 = -1;
}
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;
}
/* Break down the tuple into fields */
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (char *) palloc(ncolumns * sizeof(char));
heap_deformtuple(&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];
char *value;
char *tmp;
bool nq;
if (i > 0)
appendStringInfoChar(&buf, ',');
if (nulls[i] == 'n')
{
/* emit nothing... */
continue;
}
/*
* Convert the column value
*/
if (column_info->column_type != tupdesc->attrs[i]->atttypid)
{
bool typIsVarlena;
getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
&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;
}
value = DatumGetCString(FunctionCall3(&column_info->proc,
values[i],
ObjectIdGetDatum(column_info->typioparam),
Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
/* 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;
}
}
if (nq)
appendStringInfoChar(&buf, '"');
for (tmp = value; *tmp; tmp++)
{
char ch = *tmp;
if (ch == '"' || ch == '\\')
appendStringInfoChar(&buf, '\\');
appendStringInfoChar(&buf, ch);
}
if (nq)
appendStringInfoChar(&buf, '"');
}
appendStringInfoChar(&buf, ')');
pfree(values);
pfree(nulls);
PG_RETURN_CSTRING(buf.data);
}
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment