aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/rowtypes.c368
1 files changed, 357 insertions, 11 deletions
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index b487dfc9047..96cbac992c5 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -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);
}
/*