aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/dblink/dblink.c45
-rw-r--r--contrib/intagg/int_aggregate.c1
-rw-r--r--contrib/pgstattuple/pgstattuple.c10
-rw-r--r--contrib/tablefunc/tablefunc.c20
-rw-r--r--contrib/tsearch2/ts_stat.c3
-rw-r--r--contrib/tsearch2/wparser.c6
-rw-r--r--doc/src/sgml/catalogs.sgml20
-rw-r--r--doc/src/sgml/spi.sgml91
-rw-r--r--doc/src/sgml/xfunc.sgml155
-rw-r--r--src/backend/access/common/heaptuple.c74
-rw-r--r--src/backend/access/common/tupdesc.c198
-rw-r--r--src/backend/access/heap/heapam.c4
-rw-r--r--src/backend/bootstrap/bootstrap.c126
-rw-r--r--src/backend/catalog/heap.c43
-rw-r--r--src/backend/catalog/pg_proc.c41
-rw-r--r--src/backend/commands/explain.c4
-rw-r--r--src/backend/commands/tablecmds.c9
-rw-r--r--src/backend/executor/execQual.c276
-rw-r--r--src/backend/executor/execTuples.c61
-rw-r--r--src/backend/executor/functions.c124
-rw-r--r--src/backend/executor/nodeFunctionscan.c39
-rw-r--r--src/backend/executor/spi.c55
-rw-r--r--src/backend/parser/parse_func.c12
-rw-r--r--src/backend/rewrite/rewriteHandler.c13
-rw-r--r--src/backend/utils/adt/Makefile6
-rw-r--r--src/backend/utils/adt/lockfuncs.c23
-rw-r--r--src/backend/utils/adt/pseudotypes.c55
-rw-r--r--src/backend/utils/adt/rowtypes.c75
-rw-r--r--src/backend/utils/adt/sets.c213
-rw-r--r--src/backend/utils/cache/relcache.c30
-rw-r--r--src/backend/utils/cache/typcache.c268
-rw-r--r--src/backend/utils/fmgr/funcapi.c15
-rw-r--r--src/backend/utils/misc/guc.c44
-rw-r--r--src/include/access/heapam.h7
-rw-r--r--src/include/access/htup.h291
-rw-r--r--src/include/access/tupdesc.h16
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_attribute.h490
-rw-r--r--src/include/catalog/pg_class.h4
-rw-r--r--src/include/catalog/pg_proc.h12
-rw-r--r--src/include/catalog/pg_type.h22
-rw-r--r--src/include/executor/executor.h6
-rw-r--r--src/include/executor/spi.h6
-rw-r--r--src/include/fmgr.h7
-rw-r--r--src/include/funcapi.h71
-rw-r--r--src/include/nodes/execnodes.h13
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/primnodes.h7
-rw-r--r--src/include/utils/builtins.h12
-rw-r--r--src/include/utils/sets.h27
-rw-r--r--src/include/utils/typcache.h19
-rw-r--r--src/pl/plperl/plperl.c69
-rw-r--r--src/pl/plpgsql/src/pl_exec.c36
-rw-r--r--src/pl/plpython/plpython.c98
-rw-r--r--src/pl/tcl/pltcl.c81
-rw-r--r--src/test/regress/input/misc.source4
-rw-r--r--src/test/regress/output/misc.source18
-rw-r--r--src/test/regress/regress.c6
-rw-r--r--src/tutorial/funcs.c4
-rw-r--r--src/tutorial/funcs_new.c2
60 files changed, 1770 insertions, 1724 deletions
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ac941598be7..4a521a0fac7 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -447,7 +447,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
TupleDesc tupdesc = NULL;
int call_cntr;
int max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
char *msg;
PGresult *res = NULL;
@@ -566,9 +565,10 @@ dblink_fetch(PG_FUNCTION_ARGS)
if (functyptype == 'c')
tupdesc = TypeGetTupleDesc(functypeid, NIL);
- else if (functyptype == 'p' && functypeid == RECORDOID)
+ else if (functypeid == RECORDOID)
{
- if (!rsinfo || !IsA(rsinfo, ReturnSetInfo))
+ if (!rsinfo || !IsA(rsinfo, ReturnSetInfo) ||
+ rsinfo->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context "
@@ -582,8 +582,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
elog(ERROR, "return type must be a row type");
/* store needed metadata for subsequent calls */
- slot = TupleDescGetSlot(tupdesc);
- funcctx->slot = slot;
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
@@ -599,8 +597,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- slot = funcctx->slot;
-
res = (PGresult *) funcctx->user_fctx;
attinmeta = funcctx->attinmeta;
tupdesc = attinmeta->tupdesc;
@@ -626,7 +622,7 @@ dblink_fetch(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
@@ -649,7 +645,6 @@ dblink_record(PG_FUNCTION_ARGS)
TupleDesc tupdesc = NULL;
int call_cntr;
int max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
char *msg;
PGresult *res = NULL;
@@ -741,7 +736,7 @@ dblink_record(PG_FUNCTION_ARGS)
/* need a tuple descriptor representing one TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/*
* and save a copy of the command status string to return as
@@ -776,9 +771,10 @@ dblink_record(PG_FUNCTION_ARGS)
{
if (functyptype == 'c')
tupdesc = TypeGetTupleDesc(functypeid, NIL);
- else if (functyptype == 'p' && functypeid == RECORDOID)
+ else if (functypeid == RECORDOID)
{
- if (!rsinfo || !IsA(rsinfo, ReturnSetInfo))
+ if (!rsinfo || !IsA(rsinfo, ReturnSetInfo) ||
+ rsinfo->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context "
@@ -793,8 +789,6 @@ dblink_record(PG_FUNCTION_ARGS)
}
/* store needed metadata for subsequent calls */
- slot = TupleDescGetSlot(tupdesc);
- funcctx->slot = slot;
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
@@ -810,8 +804,6 @@ dblink_record(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- slot = funcctx->slot;
-
res = (PGresult *) funcctx->user_fctx;
attinmeta = funcctx->attinmeta;
tupdesc = attinmeta->tupdesc;
@@ -846,7 +838,7 @@ dblink_record(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
@@ -925,7 +917,7 @@ dblink_exec(PG_FUNCTION_ARGS)
/* need a tuple descriptor representing one TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/*
* and save a copy of the command status string to return as our
@@ -939,7 +931,7 @@ dblink_exec(PG_FUNCTION_ARGS)
/* need a tuple descriptor representing one TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/*
* and save a copy of the command status string to return as our
@@ -978,7 +970,6 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
FuncCallContext *funcctx;
int32 call_cntr;
int32 max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
MemoryContext oldcontext;
@@ -1010,15 +1001,9 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
*/
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "position",
- INT4OID, -1, 0, false);
+ INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "colname",
- TEXTOID, -1, 0, false);
-
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
- /* assign slot to function context */
- funcctx->slot = slot;
+ TEXTOID, -1, 0);
/*
* Generate attribute metadata needed later to produce tuples from
@@ -1053,8 +1038,6 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- slot = funcctx->slot;
-
results = (char **) funcctx->user_fctx;
attinmeta = funcctx->attinmeta;
@@ -1075,7 +1058,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
diff --git a/contrib/intagg/int_aggregate.c b/contrib/intagg/int_aggregate.c
index 2bb06ff73a4..bd03f5c0c31 100644
--- a/contrib/intagg/int_aggregate.c
+++ b/contrib/intagg/int_aggregate.c
@@ -25,7 +25,6 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
-#include "utils/sets.h"
#include "utils/syscache.h"
#include "access/tupmacs.h"
#include "access/xact.h"
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index abc2249aedf..ca082618568 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
/*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.13 2003/11/29 19:51:35 pgsql Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.14 2004/04/01 21:28:43 tgl Exp $
*
* Copyright (c) 2001,2002 Tatsuo Ishii
*
@@ -111,7 +111,6 @@ pgstattuple_real(Relation rel)
uint64 free_space = 0; /* free/reusable space in bytes */
double free_percent; /* free/reusable space in % */
TupleDesc tupdesc;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
char **values;
int i;
@@ -122,9 +121,6 @@ pgstattuple_real(Relation rel)
*/
tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
/*
* Generate attribute metadata needed later to produce tuples from raw
* C strings
@@ -192,7 +188,7 @@ pgstattuple_real(Relation rel)
}
/*
- * Prepare a values array for storage in our slot. This should be an
+ * Prepare a values array for constructing the tuple. This should be an
* array of C strings which will be processed later by the appropriate
* "in" functions.
*/
@@ -214,7 +210,7 @@ pgstattuple_real(Relation rel)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
/* Clean up */
for (i = 0; i < NCOLUMNS; i++)
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 622164b91b5..3eccebf476f 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -351,7 +351,6 @@ crosstab(PG_FUNCTION_ARGS)
TupleDesc ret_tupdesc;
int call_cntr;
int max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
SPITupleTable *spi_tuptable = NULL;
TupleDesc spi_tupdesc;
@@ -429,10 +428,10 @@ crosstab(PG_FUNCTION_ARGS)
if (functyptype == 'c')
{
- /* Build a tuple description for a functypeid tuple */
+ /* Build a tuple description for a named composite type */
tupdesc = TypeGetTupleDesc(functypeid, NIL);
}
- else if (functyptype == 'p' && functypeid == RECORDOID)
+ else if (functypeid == RECORDOID)
{
if (fcinfo->nargs != 2)
ereport(ERROR,
@@ -461,12 +460,6 @@ crosstab(PG_FUNCTION_ARGS)
errmsg("return and sql tuple descriptions are " \
"incompatible")));
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
- /* assign slot to function context */
- funcctx->slot = slot;
-
/*
* Generate attribute metadata needed later to produce tuples from
* raw C strings
@@ -499,9 +492,6 @@ crosstab(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- /* return slot for our tuple */
- slot = funcctx->slot;
-
/* user context info */
fctx = (crosstab_fctx *) funcctx->user_fctx;
lastrowid = fctx->lastrowid;
@@ -621,7 +611,7 @@ crosstab(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
/* Clean up */
for (i = 0; i < num_categories + 1; i++)
@@ -1675,7 +1665,7 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_categories)
strcpy(attname, "rowname");
TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid,
- -1, 0, false);
+ -1, 0);
/* now the category values columns */
sql_attr = spi_tupdesc->attrs[2];
@@ -1687,7 +1677,7 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_categories)
sprintf(attname, "category_%d", i + 1);
TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid,
- -1, 0, false);
+ -1, 0);
}
return tupdesc;
diff --git a/contrib/tsearch2/ts_stat.c b/contrib/tsearch2/ts_stat.c
index 732f25b6bd1..a6518e34396 100644
--- a/contrib/tsearch2/ts_stat.c
+++ b/contrib/tsearch2/ts_stat.c
@@ -303,7 +303,6 @@ ts_setup_firstcall(FuncCallContext *funcctx, tsstat * stat)
memcpy(st->stat, stat, stat->len);
funcctx->user_fctx = (void *) st;
tupdesc = RelationNameGetTupleDesc("statinfo");
- funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
MemoryContextSwitchTo(oldcontext);
}
@@ -334,7 +333,7 @@ ts_process_call(FuncCallContext *funcctx)
(values[0])[entry->len] = '\0';
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
- result = TupleGetDatum(funcctx->slot, tuple);
+ result = HeapTupleGetDatum(tuple);
pfree(values[0]);
st->cur++;
diff --git a/contrib/tsearch2/wparser.c b/contrib/tsearch2/wparser.c
index b7e45e51885..9c3c4430480 100644
--- a/contrib/tsearch2/wparser.c
+++ b/contrib/tsearch2/wparser.c
@@ -187,7 +187,6 @@ setup_firstcall(FuncCallContext *funcctx, Oid prsid)
);
funcctx->user_fctx = (void *) st;
tupdesc = RelationNameGetTupleDesc("tokentype");
- funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
MemoryContextSwitchTo(oldcontext);
}
@@ -211,7 +210,7 @@ process_call(FuncCallContext *funcctx)
values[2] = st->list[st->cur].descr;
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
- result = TupleGetDatum(funcctx->slot, tuple);
+ result = HeapTupleGetDatum(tuple);
pfree(values[1]);
pfree(values[2]);
@@ -391,7 +390,6 @@ prs_setup_firstcall(FuncCallContext *funcctx, int prsid, text *txt)
funcctx->user_fctx = (void *) st;
tupdesc = RelationNameGetTupleDesc("tokenout");
- funcctx->slot = TupleDescGetSlot(tupdesc);
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
MemoryContextSwitchTo(oldcontext);
}
@@ -413,7 +411,7 @@ prs_process_call(FuncCallContext *funcctx)
sprintf(tid, "%d", st->list[st->cur].type);
values[1] = st->list[st->cur].lexem;
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
- result = TupleGetDatum(funcctx->slot, tuple);
+ result = HeapTupleGetDatum(tuple);
pfree(values[1]);
st->cur++;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 196fdc6efdd..0cdbdcfb7b5 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.84 2004/02/15 21:01:38 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.85 2004/04/01 21:28:43 tgl Exp $
-->
<chapter id="catalogs">
@@ -810,24 +810,6 @@
</row>
<row>
- <entry><structfield>attisset</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>
- If true, this attribute is a set. In that case, what is really
- stored in the attribute is the OID of a row in the
- <structname>pg_proc</structname> catalog. The
- <structname>pg_proc</structname> row contains the query
- string that defines this set, i.e., the query to run to get
- the set. So the <structfield>atttypid</structfield> (see
- above) refers to the type returned by this query, but the
- actual length of this attribute is the length (size) of an
- <type>oid</type>. --- At least this is the theory. All this
- is probably quite broken these days.
- </entry>
- </row>
-
- <row>
<entry><structfield>attalign</structfield></entry>
<entry><type>char</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml
index c87a136bdd4..4018c2e3e1b 100644
--- a/doc/src/sgml/spi.sgml
+++ b/doc/src/sgml/spi.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.33 2004/03/17 01:05:10 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.34 2004/04/01 21:28:43 tgl Exp $
-->
<chapter id="spi">
@@ -1953,8 +1953,7 @@ char * SPI_getrelname(Relation <parameter>rel</parameter>)
allocations made by <function>palloc</function>,
<function>repalloc</function>, or SPI utility functions (except for
<function>SPI_copytuple</function>,
- <function>SPI_copytupledesc</function>,
- <function>SPI_copytupleintoslot</function>,
+ <function>SPI_returntuple</function>,
<function>SPI_modifytuple</function>, and
<function>SPI_palloc</function>) are made in this context. When a
procedure disconnects from the SPI manager (via
@@ -2169,7 +2168,9 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
<para>
<function>SPI_copytuple</function> makes a copy of a row in the
- upper executor context.
+ upper executor context. This is normally used to return a modified
+ row from a trigger. In a function declared to return a composite
+ type, use <function>SPI_returntuple</function> instead.
</para>
</refsect1>
@@ -2200,21 +2201,21 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
<!-- *********************************************** -->
-<refentry id="spi-spi-copytupledesc">
+<refentry id="spi-spi-returntuple">
<refmeta>
- <refentrytitle>SPI_copytupledesc</refentrytitle>
+ <refentrytitle>SPI_returntuple</refentrytitle>
</refmeta>
<refnamediv>
- <refname>SPI_copytupledesc</refname>
- <refpurpose>make a copy of a row descriptor in the upper executor context</refpurpose>
+ <refname>SPI_returntuple</refname>
+ <refpurpose>prepare to return a tuple as a Datum</refpurpose>
</refnamediv>
- <indexterm><primary>SPI_copytupledesc</primary></indexterm>
+ <indexterm><primary>SPI_returntuple</primary></indexterm>
<refsynopsisdiv>
<synopsis>
-TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
+HeapTupleHeader SPI_returntuple(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
</synopsis>
</refsynopsisdiv>
@@ -2222,63 +2223,16 @@ TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
<title>Description</title>
<para>
- <function>SPI_copytupledesc</function> makes a copy of a row
- descriptor in the upper executor context.
+ <function>SPI_returntuple</function> makes a copy of a row in
+ the upper executor context, returning it in the form of a rowtype Datum.
+ The returned pointer need only be converted to Datum via PointerGetDatum
+ before returning.
</para>
- </refsect1>
-
- <refsect1>
- <title>Arguments</title>
-
- <variablelist>
- <varlistentry>
- <term><literal>TupleDesc <parameter>tupdesc</parameter></literal></term>
- <listitem>
- <para>
- row descriptor to be copied
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Return Value</title>
-
- <para>
- the copied row descriptor; <symbol>NULL</symbol> only if
- <parameter>tupdesc</parameter> is <symbol>NULL</symbol>
- </para>
- </refsect1>
-</refentry>
-
-<!-- *********************************************** -->
-
-<refentry id="spi-spi-copytupleintoslot">
- <refmeta>
- <refentrytitle>SPI_copytupleintoslot</refentrytitle>
- </refmeta>
-
- <refnamediv>
- <refname>SPI_copytupleintoslot</refname>
- <refpurpose>make a copy of a row and descriptor in the upper executor context</refpurpose>
- </refnamediv>
-
- <indexterm><primary>SPI_copytupleintoslot</primary></indexterm>
-
- <refsynopsisdiv>
-<synopsis>
-TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
-</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
<para>
- <function>SPI_copytupleintoslot</function> makes a copy of a row in
- the upper executor context, returning it in the form of a filled-in
- <type>TupleTableSlot</type> structure.
+ Note that this should be used for functions that are declared to return
+ composite types. It is not used for triggers; use
+ <function>SPI_copytuple</> for returning a modified row in a trigger.
</para>
</refsect1>
@@ -2299,7 +2253,8 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
<term><literal>TupleDesc <parameter>rowdesc</parameter></literal></term>
<listitem>
<para>
- row descriptor to be copied
+ descriptor for row (pass the same descriptor each time for most
+ effective caching)
</para>
</listitem>
</varlistentry>
@@ -2310,9 +2265,9 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
<title>Return Value</title>
<para>
- <type>TupleTableSlot</type> containing the copied row and
- descriptor; <symbol>NULL</symbol> only if
- <parameter>row</parameter> or <parameter>rowdesc</parameter> are
+ <type>HeapTupleHeader</type> pointing to copied row;
+ <symbol>NULL</symbol> only if
+ <parameter>row</parameter> or <parameter>rowdesc</parameter> is
<symbol>NULL</symbol>
</para>
</refsect1>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index c8f71296857..4b06aefd362 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.80 2004/03/09 16:57:47 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.81 2004/04/01 21:28:43 tgl Exp $
-->
<sect1 id="xfunc">
@@ -1623,7 +1623,7 @@ SELECT name, c_overpaid(emp, 1500) AS overpaid
#include "executor/executor.h" /* for GetAttributeByName() */
bool
-c_overpaid(TupleTableSlot *t, /* the current row of emp */
+c_overpaid(HeapTupleHeader t, /* the current row of emp */
int32 limit)
{
bool isnull;
@@ -1647,7 +1647,7 @@ PG_FUNCTION_INFO_V1(c_overpaid);
Datum
c_overpaid(PG_FUNCTION_ARGS)
{
- TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
+ HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
int32 limit = PG_GETARG_INT32(1);
bool isnull;
int32 salary;
@@ -1666,7 +1666,8 @@ c_overpaid(PG_FUNCTION_ARGS)
<function>GetAttributeByName</function> is the
<productname>PostgreSQL</productname> system function that
returns attributes out of the specified row. It has
- three arguments: the argument of type <type>TupleTableSlot*</type> passed into
+ three arguments: the argument of type <type>HeapTupleHeader</type> passed
+ into
the function, the name of the desired attribute, and a
return parameter that tells whether the attribute
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
@@ -1675,14 +1676,22 @@ c_overpaid(PG_FUNCTION_ARGS)
</para>
<para>
+ There is also <function>GetAttributeByNum</function>, which selects
+ the target attribute by column number instead of name.
+ </para>
+
+ <para>
The following command declares the function
<function>c_overpaid</function> in SQL:
<programlisting>
CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_overpaid'
- LANGUAGE C;
+ LANGUAGE C STRICT;
</programlisting>
+
+ Notice we have used <literal>STRICT</> so that we did not have to
+ check whether the input arguments were NULL.
</para>
</sect2>
@@ -1700,38 +1709,25 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
</para>
<para>
- The support for returning composite data types (or rows) starts
- with the <structname>AttInMetadata</> structure. This structure
- holds arrays of individual attribute information needed to create
- a row from raw C strings. The information contained in the
- structure is derived from a <structname>TupleDesc</> structure,
- but it is stored to avoid redundant computations on each call to
- a set-returning function (see next section). In the case of a
- function returning a set, the <structname>AttInMetadata</>
- structure should be computed once during the first call and saved
- for reuse in later calls. <structname>AttInMetadata</> also
- saves a pointer to the original <structname>TupleDesc</>.
-<programlisting>
-typedef struct AttInMetadata
-{
- /* full TupleDesc */
- TupleDesc tupdesc;
-
- /* array of attribute type input function finfo */
- FmgrInfo *attinfuncs;
-
- /* array of attribute type typelem */
- Oid *attelems;
-
- /* array of attribute typmod */
- int32 *atttypmods;
-} AttInMetadata;
-</programlisting>
+ There are two ways you can build a composite data value (henceforth
+ a <quote>tuple</>): you can build it from an array of Datum values,
+ or from an array of C strings that can be passed to the input
+ conversion functions of the tuple's column datatypes. In either
+ case, you first need to obtain or construct a <structname>TupleDesc</>
+ descriptor for the tuple structure. When working with Datums, you
+ pass the <structname>TupleDesc</> to <function>BlessTupleDesc</>,
+ and then call <function>heap_formtuple</> for each row. When working
+ with C strings, you pass the <structname>TupleDesc</> to
+ <function>TupleDescGetAttInMetadata</>, and then call
+ <function>BuildTupleFromCStrings</> for each row. In the case of a
+ function returning a set of tuples, the setup steps can all be done
+ once during the first call of the function.
</para>
<para>
- To assist you in populating this structure, several functions and a macro
- are available. Use
+ Several helper functions are available for setting up the initial
+ <structname>TupleDesc</>. If you want to use a named composite type,
+ you can fetch the information from the system catalogs. Use
<programlisting>
TupleDesc RelationNameGetTupleDesc(const char *relname)
</programlisting>
@@ -1741,36 +1737,43 @@ TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
</programlisting>
to get a <structname>TupleDesc</> based on a type OID. This can
be used to get a <structname>TupleDesc</> for a base or
- composite type. Then
+ composite type. When writing a function that returns
+ <structname>record</>, the expected <structname>TupleDesc</>
+ must be passed in by the caller.
+ </para>
+
+ <para>
+ Once you have a <structname>TupleDesc</>, call
+<programlisting>
+TupleDesc BlessTupleDesc(TupleDesc tupdesc)
+</programlisting>
+ if you plan to work with Datums, or
<programlisting>
AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
</programlisting>
- will return a pointer to an <structname>AttInMetadata</>,
- initialized based on the given
- <structname>TupleDesc</>. <structname>AttInMetadata</> can be
- used in conjunction with C strings to produce a properly formed
- row value (internally called tuple).
+ if you plan to work with C strings. If you are writing a function
+ returning set, you can save the results of these functions in the
+ <structname>FuncCallContext</> structure --- use the
+ <structfield>tuple_desc</> or <structfield>attinmeta</> field
+ respectively.
</para>
<para>
- To return a tuple you must create a tuple slot based on the
- <structname>TupleDesc</>. You can use
+ When working with Datums, use
<programlisting>
-TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
+HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls)
</programlisting>
- to initialize this tuple slot, or obtain one through other (user provided)
- means. The tuple slot is needed to create a <type>Datum</> for return by the
- function. The same slot can (and should) be reused on each call.
+ to build a <structname>HeapTuple</> given user data in Datum form.
</para>
<para>
- After constructing an <structname>AttInMetadata</> structure,
+ When working with C strings, use
<programlisting>
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
</programlisting>
- can be used to build a <structname>HeapTuple</> given user data
- in C string form. <literal>values</literal> is an array of C strings, one for
- each attribute of the return row. Each C string should be in
+ to build a <structname>HeapTuple</> given user data
+ in C string form. <literal>values</literal> is an array of C strings,
+ one for each attribute of the return row. Each C string should be in
the form expected by the input function of the attribute data
type. In order to return a null value for one of the attributes,
the corresponding pointer in the <parameter>values</> array
@@ -1779,24 +1782,12 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
</para>
<para>
- Building a tuple via <function>TupleDescGetAttInMetadata</> and
- <function>BuildTupleFromCStrings</> is only convenient if your
- function naturally computes the values to be returned as text
- strings. If your code naturally computes the values as a set of
- <type>Datum</> values, you should instead use the underlying
- function <function>heap_formtuple</> to convert the
- <type>Datum</type> values directly into a tuple. You will still need
- the <structname>TupleDesc</> and a <structname>TupleTableSlot</>,
- but not <structname>AttInMetadata</>.
- </para>
-
- <para>
Once you have built a tuple to return from your function, it
must be converted into a <type>Datum</>. Use
<programlisting>
-TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
+HeapTupleGetDatum(HeapTuple tuple)
</programlisting>
- to get a <type>Datum</> given a tuple and a slot. This
+ to convert a <structname>HeapTuple</> into a valid Datum. This
<type>Datum</> can be returned directly if you intend to return
just a single row, or it can be used as the current return value
in a set-returning function.
@@ -1851,8 +1842,8 @@ typedef struct
/*
* OPTIONAL pointer to result slot
*
- * slot is for use when returning tuples (i.e., composite data types)
- * and is not needed when returning base data types.
+ * This is obsolete and only present for backwards compatibility, viz,
+ * user-defined SRFs that use the deprecated TupleDescGetSlot().
*/
TupleTableSlot *slot;
@@ -1868,9 +1859,9 @@ typedef struct
* OPTIONAL pointer to struct containing attribute type input metadata
*
* attinmeta is for use when returning tuples (i.e., composite data types)
- * and is not needed when returning base data types. It
- * is only needed if you intend to use BuildTupleFromCStrings() to create
- * the return tuple.
+ * and is not used when returning base data types. It is only needed
+ * if you intend to use BuildTupleFromCStrings() to create the return
+ * tuple.
*/
AttInMetadata *attinmeta;
@@ -1883,6 +1874,18 @@ typedef struct
* of the SRF.
*/
MemoryContext multi_call_memory_ctx;
+
+ /*
+ * OPTIONAL pointer to struct containing tuple description
+ *
+ * tuple_desc is for use when returning tuples (i.e. composite data types)
+ * and is only needed if you are going to build the tuples with
+ * heap_formtuple() rather than with BuildTupleFromCStrings(). Note that
+ * the TupleDesc pointer stored here should usually have been run through
+ * BlessTupleDesc() first.
+ */
+ TupleDesc tuple_desc;
+
} FuncCallContext;
</programlisting>
</para>
@@ -1956,8 +1959,6 @@ my_set_returning_function(PG_FUNCTION_ARGS)
<replaceable>user code</replaceable>
<replaceable>if returning composite</replaceable>
<replaceable>build TupleDesc, and perhaps AttInMetadata</replaceable>
- <replaceable>obtain slot</replaceable>
- funcctx-&gt;slot = slot;
<replaceable>endif returning composite</replaceable>
<replaceable>user code</replaceable>
MemoryContextSwitchTo(oldcontext);
@@ -1998,7 +1999,6 @@ testpassbyval(PG_FUNCTION_ARGS)
int call_cntr;
int max_calls;
TupleDesc tupdesc;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
/* stuff done only on the first call of the function */
@@ -2018,12 +2018,6 @@ testpassbyval(PG_FUNCTION_ARGS)
/* Build a tuple description for a __testpassbyval tuple */
tupdesc = RelationNameGetTupleDesc("__testpassbyval");
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
- /* assign slot to function context */
- funcctx-&gt;slot = slot;
-
/*
* generate attribute metadata needed later to produce tuples from raw
* C strings
@@ -2039,7 +2033,6 @@ testpassbyval(PG_FUNCTION_ARGS)
call_cntr = funcctx-&gt;call_cntr;
max_calls = funcctx-&gt;max_calls;
- slot = funcctx-&gt;slot;
attinmeta = funcctx-&gt;attinmeta;
if (call_cntr &lt; max_calls) /* do when there is more left to send */
@@ -2049,7 +2042,7 @@ testpassbyval(PG_FUNCTION_ARGS)
Datum result;
/*
- * Prepare a values array for storage in our slot.
+ * Prepare a values array for building the returned tuple.
* This should be an array of C strings which will
* be processed later by the type input functions.
*/
@@ -2066,7 +2059,7 @@ testpassbyval(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
/* clean up (this is not really necessary) */
pfree(values[0]);
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index f1b20ff2732..4355c0df4c7 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.89 2004/01/16 20:51:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $
*
* NOTES
* The old interface functions have been converted to macros
@@ -31,6 +31,8 @@
/* ----------------
* ComputeDataSize
+ *
+ * Determine size of the data area of a tuple to be constructed
* ----------------
*/
Size
@@ -417,7 +419,7 @@ nocachegetattr(HeapTuple tuple,
* ----------------
*/
Datum
-heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
+heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
{
Datum result;
@@ -451,6 +453,31 @@ heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
case TableOidAttributeNumber:
result = ObjectIdGetDatum(tup->t_tableOid);
break;
+
+ /*
+ * If the attribute number is 0, then we are supposed to return
+ * the entire tuple as a row-type Datum. (Using zero for this
+ * purpose is unclean since it risks confusion with "invalid attr"
+ * result codes, but it's not worth changing now.)
+ *
+ * We have to make a copy of the tuple so we can safely insert the
+ * Datum overhead fields, which are not set in on-disk tuples.
+ */
+ case InvalidAttrNumber:
+ {
+ HeapTupleHeader dtup;
+
+ dtup = (HeapTupleHeader) palloc(tup->t_len);
+ memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
+
+ HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
+ HeapTupleHeaderSetTypeId(dtup, tupleDesc->tdtypeid);
+ HeapTupleHeaderSetTypMod(dtup, tupleDesc->tdtypmod);
+
+ result = PointerGetDatum(dtup);
+ }
+ break;
+
default:
elog(ERROR, "invalid attnum: %d", attnum);
result = 0; /* keep compiler quiet */
@@ -547,19 +574,10 @@ heap_deformtuple(HeapTuple tuple,
/* ----------------
* heap_formtuple
*
- * constructs a tuple from the given *value and *null arrays
- *
- * old comments
- * Handles alignment by aligning 2 byte attributes on short boundries
- * and 3 or 4 byte attributes on long word boundries on a vax; and
- * aligning non-byte attributes on short boundries on a sun. Does
- * not properly align fixed length arrays of 1 or 2 byte types (yet).
+ * constructs a tuple from the given *value and *nulls arrays
*
* Null attributes are indicated by a 'n' in the appropriate byte
- * of the *null. Non-null attributes are indicated by a ' ' (space).
- *
- * Fix me. (Figure that must keep context if debug--allow give oid.)
- * Assumes in order.
+ * of *nulls. Non-null attributes are indicated by a ' ' (space).
* ----------------
*/
HeapTuple
@@ -581,6 +599,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
errmsg("number of columns (%d) exceeds limit (%d)",
numberOfAttributes, MaxTupleAttributeNumber)));
+ /*
+ * Determine total space needed
+ */
for (i = 0; i < numberOfAttributes; i++)
{
if (nulls[i] != ' ')
@@ -602,15 +623,26 @@ heap_formtuple(TupleDesc tupleDescriptor,
len += ComputeDataSize(tupleDescriptor, value, nulls);
- tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+ /*
+ * Allocate and zero the space needed. Note that the tuple body and
+ * HeapTupleData management structure are allocated in one chunk.
+ */
+ tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
tuple->t_datamcxt = CurrentMemoryContext;
- td = tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
-
- MemSet((char *) td, 0, len);
+ tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
+ /*
+ * And fill in the information. Note we fill the Datum fields even
+ * though this tuple may never become a Datum.
+ */
tuple->t_len = len;
ItemPointerSetInvalid(&(tuple->t_self));
tuple->t_tableOid = InvalidOid;
+
+ HeapTupleHeaderSetDatumLength(td, len);
+ HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
+ HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
+
td->t_natts = numberOfAttributes;
td->t_hoff = hoff;
@@ -759,15 +791,15 @@ heap_addheader(int natts, /* max domain index */
hoff = MAXALIGN(hoff);
len = hoff + structlen;
- tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+ tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
+ tuple->t_datamcxt = CurrentMemoryContext;
+ tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
tuple->t_len = len;
ItemPointerSetInvalid(&(tuple->t_self));
tuple->t_tableOid = InvalidOid;
- tuple->t_datamcxt = CurrentMemoryContext;
- tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
- MemSet((char *) td, 0, hoff);
+ /* we don't bother to fill the Datum fields */
td->t_natts = natts;
td->t_hoff = hoff;
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 614cb00f5c2..1a016d86f80 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.101 2003/11/29 19:51:39 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.102 2004/04/01 21:28:43 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@@ -28,12 +28,16 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/* ----------------------------------------------------------------
* CreateTemplateTupleDesc
*
* This function allocates and zeros a tuple descriptor structure.
+ *
+ * Tuple type ID information is initially set for an anonymous record type;
+ * caller can overwrite this if needed.
* ----------------------------------------------------------------
*/
TupleDesc
@@ -47,24 +51,26 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
AssertArg(natts >= 0);
/*
- * allocate enough memory for the tuple descriptor and zero it as
- * TupleDescInitEntry assumes that the descriptor is filled with NULL
- * pointers.
+ * Allocate enough memory for the tuple descriptor, and zero the
+ * attrs[] array since TupleDescInitEntry assumes that the array
+ * is filled with NULL pointers.
*/
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
- desc->natts = natts;
- desc->tdhasoid = hasoid;
-
if (natts > 0)
- {
- uint32 size = natts * sizeof(Form_pg_attribute);
-
- desc->attrs = (Form_pg_attribute *) palloc0(size);
- }
+ desc->attrs = (Form_pg_attribute *)
+ palloc0(natts * sizeof(Form_pg_attribute));
else
desc->attrs = NULL;
+
+ /*
+ * Initialize other fields of the tupdesc.
+ */
+ desc->natts = natts;
desc->constr = NULL;
+ desc->tdtypeid = RECORDOID;
+ desc->tdtypmod = -1;
+ desc->tdhasoid = hasoid;
return desc;
}
@@ -74,6 +80,9 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
*
* This function allocates a new TupleDesc pointing to a given
* Form_pg_attribute array
+ *
+ * Tuple type ID information is initially set for an anonymous record type;
+ * caller can overwrite this if needed.
* ----------------------------------------------------------------
*/
TupleDesc
@@ -90,6 +99,8 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
+ desc->tdtypeid = RECORDOID;
+ desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
return desc;
@@ -101,22 +112,21 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
*
- * !!! Constraints are not copied !!!
+ * !!! Constraints and defaults are not copied !!!
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDescCopy(TupleDesc tupdesc)
{
TupleDesc desc;
- int i,
- size;
+ int i;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
if (desc->natts > 0)
{
- size = desc->natts * sizeof(Form_pg_attribute);
- desc->attrs = (Form_pg_attribute *) palloc(size);
+ desc->attrs = (Form_pg_attribute *)
+ palloc(desc->natts * sizeof(Form_pg_attribute));
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -127,7 +137,11 @@ CreateTupleDescCopy(TupleDesc tupdesc)
}
else
desc->attrs = NULL;
+
desc->constr = NULL;
+
+ desc->tdtypeid = tupdesc->tdtypeid;
+ desc->tdtypmod = tupdesc->tdtypmod;
desc->tdhasoid = tupdesc->tdhasoid;
return desc;
@@ -137,7 +151,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
* CreateTupleDescCopyConstr
*
* This function creates a new TupleDesc by copying from an existing
- * TupleDesc (with Constraints)
+ * TupleDesc (including its constraints and defaults)
* ----------------------------------------------------------------
*/
TupleDesc
@@ -145,15 +159,14 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
{
TupleDesc desc;
TupleConstr *constr = tupdesc->constr;
- int i,
- size;
+ int i;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
if (desc->natts > 0)
{
- size = desc->natts * sizeof(Form_pg_attribute);
- desc->attrs = (Form_pg_attribute *) palloc(size);
+ desc->attrs = (Form_pg_attribute *)
+ palloc(desc->natts * sizeof(Form_pg_attribute));
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -162,9 +175,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
}
else
desc->attrs = NULL;
+
if (constr)
{
- TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
+ TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null;
@@ -197,10 +211,16 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
else
desc->constr = NULL;
+ desc->tdtypeid = tupdesc->tdtypeid;
+ desc->tdtypmod = tupdesc->tdtypmod;
desc->tdhasoid = tupdesc->tdhasoid;
+
return desc;
}
+/*
+ * Free a TupleDesc including all substructure
+ */
void
FreeTupleDesc(TupleDesc tupdesc)
{
@@ -244,6 +264,10 @@ FreeTupleDesc(TupleDesc tupdesc)
/*
* Compare two TupleDesc structures for logical equality
+ *
+ * Note: we deliberately do not check the attrelid and tdtypmod fields.
+ * This allows typcache.c to use this routine to see if a cached record type
+ * matches a requested type, and is harmless for relcache.c's uses.
*/
bool
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
@@ -254,8 +278,11 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
if (tupdesc1->natts != tupdesc2->natts)
return false;
+ if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
+ return false;
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
return false;
+
for (i = 0; i < tupdesc1->natts; i++)
{
Form_pg_attribute attr1 = tupdesc1->attrs[i];
@@ -265,6 +292,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
* We do not need to check every single field here: we can
* disregard attrelid, attnum (it was used to place the row in the
* attrs array) and everything derived from the column datatype.
+ * Also, attcacheoff must NOT be checked since it's possibly not
+ * set in both copies.
*/
if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
return false;
@@ -272,6 +301,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attstattarget != attr2->attstattarget)
return false;
+ if (attr1->attndims != attr2->attndims)
+ return false;
if (attr1->atttypmod != attr2->atttypmod)
return false;
if (attr1->attstorage != attr2->attstorage)
@@ -287,6 +318,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
if (attr1->attinhcount != attr2->attinhcount)
return false;
}
+
if (tupdesc1->constr != NULL)
{
TupleConstr *constr1 = tupdesc1->constr;
@@ -360,8 +392,7 @@ TupleDescInitEntry(TupleDesc desc,
const char *attributeName,
Oid oidtypeid,
int32 typmod,
- int attdim,
- bool attisset)
+ int attdim)
{
HeapTuple tuple;
Form_pg_type typeForm;
@@ -403,7 +434,6 @@ TupleDescInitEntry(TupleDesc desc,
att->attnum = attributeNumber;
att->attndims = attdim;
- att->attisset = attisset;
att->attnotnull = false;
att->atthasdef = false;
@@ -416,70 +446,13 @@ TupleDescInitEntry(TupleDesc desc,
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", oidtypeid);
-
- /*
- * type info exists so we initialize our attribute information from
- * the type tuple we found..
- */
typeForm = (Form_pg_type) GETSTRUCT(tuple);
- att->atttypid = HeapTupleGetOid(tuple);
-
- /*
- * There are a couple of cases where we must override the information
- * stored in pg_type.
- *
- * First: if this attribute is a set, what is really stored in the
- * attribute is the OID of a tuple in the pg_proc catalog. The pg_proc
- * tuple contains the query string which defines this set - i.e., the
- * query to run to get the set. So the atttypid (just assigned above)
- * refers to the type returned by this query, but the actual length of
- * this attribute is the length (size) of an OID.
- *
- * (Why not just make the atttypid point to the OID type, instead of the
- * type the query returns? Because the executor uses the atttypid to
- * tell the front end what type will be returned, and in the end the
- * type returned will be the result of the query, not an OID.)
- *
- * (Why not wait until the return type of the set is known (i.e., the
- * recursive call to the executor to execute the set has returned)
- * before telling the front end what the return type will be? Because
- * the executor is a delicate thing, and making sure that the correct
- * order of front-end commands is maintained is messy, especially
- * considering that target lists may change as inherited attributes
- * are considered, etc. Ugh.)
- *
- * Second: if we are dealing with a complex type (a tuple type), then
- * pg_type will say that the representation is the same as Oid. But
- * if typmod is sizeof(Pointer) then the internal representation is
- * actually a pointer to a TupleTableSlot, and we have to substitute
- * that information.
- *
- * A set of complex type is first and foremost a set, so its
- * representation is Oid not pointer. So, test that case first.
- */
- if (attisset)
- {
- att->attlen = sizeof(Oid);
- att->attbyval = true;
- att->attalign = 'i';
- att->attstorage = 'p';
- }
- else if (typeForm->typtype == 'c' && typmod == sizeof(Pointer))
- {
- att->attlen = sizeof(Pointer);
- att->attbyval = true;
- att->attalign = 'd'; /* kluge to work with 8-byte pointers */
- /* XXX ought to have a separate attalign value for pointers ... */
- att->attstorage = 'p';
- }
- else
- {
- att->attlen = typeForm->typlen;
- att->attbyval = typeForm->typbyval;
- att->attalign = typeForm->typalign;
- att->attstorage = typeForm->typstorage;
- }
+ att->atttypid = oidtypeid;
+ att->attlen = typeForm->typlen;
+ att->attbyval = typeForm->typbyval;
+ att->attalign = typeForm->typalign;
+ att->attstorage = typeForm->typstorage;
ReleaseSysCache(tuple);
}
@@ -491,7 +464,8 @@ TupleDescInitEntry(TupleDesc desc,
* Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
*
* Note: the default assumption is no OIDs; caller may modify the returned
- * TupleDesc if it wants OIDs.
+ * TupleDesc if it wants OIDs. Also, tdtypeid will need to be filled in
+ * later on.
*/
TupleDesc
BuildDescForRelation(List *schema)
@@ -501,12 +475,11 @@ BuildDescForRelation(List *schema)
List *p;
TupleDesc desc;
AttrDefault *attrdef = NULL;
- TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+ TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
char *attname;
int32 atttypmod;
int attdim;
int ndef = 0;
- bool attisset;
/*
* allocate a new tuple descriptor
@@ -529,13 +502,18 @@ BuildDescForRelation(List *schema)
attnum++;
attname = entry->colname;
- attisset = entry->typename->setof;
atttypmod = entry->typename->typmod;
attdim = length(entry->typename->arrayBounds);
+ if (entry->typename->setof)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("column \"%s\" cannot be declared SETOF",
+ attname)));
+
TupleDescInitEntry(desc, attnum, attname,
typenameTypeId(entry->typename),
- atttypmod, attdim, attisset);
+ atttypmod, attdim);
/* Fill in additional stuff not handled by TupleDescInitEntry */
if (entry->is_not_null)
@@ -586,6 +564,7 @@ BuildDescForRelation(List *schema)
pfree(constr);
desc->constr = NULL;
}
+
return desc;
}
@@ -603,7 +582,7 @@ RelationNameGetTupleDesc(const char *relname)
TupleDesc tupdesc;
List *relname_list;
- /* Open relation and get the tuple description */
+ /* Open relation and copy the tuple description */
relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
relvar = makeRangeVarFromNameList(relname_list);
rel = relation_openrv(relvar, AccessShareLock);
@@ -620,7 +599,8 @@ RelationNameGetTupleDesc(const char *relname)
*
* If the type is composite, *and* a colaliases List is provided, *and*
* the List is of natts length, use the aliases instead of the relation
- * attnames.
+ * attnames. (NB: this usage is deprecated since it may result in
+ * creation of unnecessary transient record types.)
*
* If the type is a base type, a single item alias List is required.
*/
@@ -635,22 +615,12 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
*/
if (functyptype == 'c')
{
- /* Composite data type, i.e. a table's row type */
- Oid relid = typeidTypeRelid(typeoid);
- Relation rel;
- int natts;
-
- if (!OidIsValid(relid))
- elog(ERROR, "invalid typrelid for complex type %u", typeoid);
-
- rel = relation_open(relid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- natts = tupdesc->natts;
- relation_close(rel, AccessShareLock);
- /* XXX should we hold the lock to ensure table doesn't change? */
+ /* Composite data type, e.g. a table's row type */
+ tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
if (colaliases != NIL)
{
+ int natts = tupdesc->natts;
int varattno;
/* does the list length match the number of attributes? */
@@ -667,6 +637,10 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
if (label != NULL)
namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
}
+
+ /* The tuple type is now an anonymous record type */
+ tupdesc->tdtypeid = RECORDOID;
+ tupdesc->tdtypmod = -1;
}
}
else if (functyptype == 'b' || functyptype == 'd')
@@ -695,13 +669,15 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
attname,
typeoid,
-1,
- 0,
- false);
+ 0);
}
- else if (functyptype == 'p' && typeoid == RECORDOID)
+ else if (typeoid == RECORDOID)
+ {
+ /* XXX can't support this because typmod wasn't passed in ... */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine row description for function returning record")));
+ }
else
{
/* crummy error message, but parser should have caught this */
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 902f0621377..695ef36b509 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.163 2004/03/11 01:47:35 ishii Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.164 2004/04/01 21:28:43 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1116,6 +1116,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId());
HeapTupleHeaderSetCmin(tup->t_data, cid);
+ HeapTupleHeaderSetCmax(tup->t_data, 0); /* zero out Datum fields */
tup->t_tableOid = relation->rd_id;
/*
@@ -1576,6 +1577,7 @@ l2:
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
HeapTupleHeaderSetXmin(newtup->t_data, GetCurrentTransactionId());
HeapTupleHeaderSetCmin(newtup->t_data, cid);
+ HeapTupleHeaderSetCmax(newtup->t_data, 0); /* zero out Datum fields */
/*
* If the toaster needs to be activated, OR if the new tuple will not
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index f18fece3982..c7fcb400873 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.177 2004/02/25 19:41:22 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.178 2004/04/01 21:28:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -107,34 +107,55 @@ struct typinfo
Oid oid;
Oid elem;
int16 len;
+ bool byval;
+ char align;
+ char storage;
Oid inproc;
Oid outproc;
};
-static struct typinfo Procid[] = {
- {"bool", BOOLOID, 0, 1, F_BOOLIN, F_BOOLOUT},
- {"bytea", BYTEAOID, 0, -1, F_BYTEAIN, F_BYTEAOUT},
- {"char", CHAROID, 0, 1, F_CHARIN, F_CHAROUT},
- {"name", NAMEOID, 0, NAMEDATALEN, F_NAMEIN, F_NAMEOUT},
- {"int2", INT2OID, 0, 2, F_INT2IN, F_INT2OUT},
- {"int2vector", INT2VECTOROID, 0, INDEX_MAX_KEYS * 2, F_INT2VECTORIN, F_INT2VECTOROUT},
- {"int4", INT4OID, 0, 4, F_INT4IN, F_INT4OUT},
- {"regproc", REGPROCOID, 0, 4, F_REGPROCIN, F_REGPROCOUT},
- {"regclass", REGCLASSOID, 0, 4, F_REGCLASSIN, F_REGCLASSOUT},
- {"regtype", REGTYPEOID, 0, 4, F_REGTYPEIN, F_REGTYPEOUT},
- {"text", TEXTOID, 0, -1, F_TEXTIN, F_TEXTOUT},
- {"oid", OIDOID, 0, 4, F_OIDIN, F_OIDOUT},
- {"tid", TIDOID, 0, 6, F_TIDIN, F_TIDOUT},
- {"xid", XIDOID, 0, 4, F_XIDIN, F_XIDOUT},
- {"cid", CIDOID, 0, 4, F_CIDIN, F_CIDOUT},
- {"oidvector", OIDVECTOROID, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT},
- {"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT},
- {"_int4", 1007, INT4OID, -1, F_ARRAY_IN, F_ARRAY_OUT},
- {"_text", 1009, TEXTOID, -1, F_ARRAY_IN, F_ARRAY_OUT},
- {"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT}
+static const struct typinfo TypInfo[] = {
+ {"bool", BOOLOID, 0, 1, true, 'c', 'p',
+ F_BOOLIN, F_BOOLOUT},
+ {"bytea", BYTEAOID, 0, -1, false, 'i', 'x',
+ F_BYTEAIN, F_BYTEAOUT},
+ {"char", CHAROID, 0, 1, true, 'c', 'p',
+ F_CHARIN, F_CHAROUT},
+ {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'i', 'p',
+ F_NAMEIN, F_NAMEOUT},
+ {"int2", INT2OID, 0, 2, true, 's', 'p',
+ F_INT2IN, F_INT2OUT},
+ {"int4", INT4OID, 0, 4, true, 'i', 'p',
+ F_INT4IN, F_INT4OUT},
+ {"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
+ F_REGPROCIN, F_REGPROCOUT},
+ {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p',
+ F_REGCLASSIN, F_REGCLASSOUT},
+ {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p',
+ F_REGTYPEIN, F_REGTYPEOUT},
+ {"text", TEXTOID, 0, -1, false, 'i', 'x',
+ F_TEXTIN, F_TEXTOUT},
+ {"oid", OIDOID, 0, 4, true, 'i', 'p',
+ F_OIDIN, F_OIDOUT},
+ {"tid", TIDOID, 0, 6, false, 's', 'p',
+ F_TIDIN, F_TIDOUT},
+ {"xid", XIDOID, 0, 4, true, 'i', 'p',
+ F_XIDIN, F_XIDOUT},
+ {"cid", CIDOID, 0, 4, true, 'i', 'p',
+ F_CIDIN, F_CIDOUT},
+ {"int2vector", INT2VECTOROID, INT2OID, INDEX_MAX_KEYS * 2, false, 's', 'p',
+ F_INT2VECTORIN, F_INT2VECTOROUT},
+ {"oidvector", OIDVECTOROID, OIDOID, INDEX_MAX_KEYS * 4, false, 'i', 'p',
+ F_OIDVECTORIN, F_OIDVECTOROUT},
+ {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x',
+ F_ARRAY_IN, F_ARRAY_OUT},
+ {"_text", 1009, TEXTOID, -1, false, 'i', 'x',
+ F_ARRAY_IN, F_ARRAY_OUT},
+ {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x',
+ F_ARRAY_IN, F_ARRAY_OUT}
};
-static int n_types = sizeof(Procid) / sizeof(struct typinfo);
+static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo);
struct typmap
{ /* a hack */
@@ -697,44 +718,13 @@ DefineAttr(char *name, char *type, int attnum)
}
else
{
- attrtypes[attnum]->atttypid = Procid[typeoid].oid;
- attlen = attrtypes[attnum]->attlen = Procid[typeoid].len;
-
- /*
- * Cheat like mad to fill in these items from the length only.
- * This only has to work for types that appear in Procid[].
- */
- switch (attlen)
- {
- case 1:
- attrtypes[attnum]->attbyval = true;
- attrtypes[attnum]->attstorage = 'p';
- attrtypes[attnum]->attalign = 'c';
- break;
- case 2:
- attrtypes[attnum]->attbyval = true;
- attrtypes[attnum]->attstorage = 'p';
- attrtypes[attnum]->attalign = 's';
- break;
- case 4:
- attrtypes[attnum]->attbyval = true;
- attrtypes[attnum]->attstorage = 'p';
- attrtypes[attnum]->attalign = 'i';
- break;
- case -1:
- attrtypes[attnum]->attbyval = false;
- attrtypes[attnum]->attstorage = 'x';
- attrtypes[attnum]->attalign = 'i';
- break;
- default:
- /* TID and fixed-length arrays, such as oidvector */
- attrtypes[attnum]->attbyval = false;
- attrtypes[attnum]->attstorage = 'p';
- attrtypes[attnum]->attalign = 'i';
- break;
- }
+ attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
+ attlen = attrtypes[attnum]->attlen = TypInfo[typeoid].len;
+ attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
+ attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
+ attrtypes[attnum]->attalign = TypInfo[typeoid].align;
/* if an array type, assume 1-dimensional attribute */
- if (Procid[typeoid].elem != InvalidOid && attlen < 0)
+ if (TypInfo[typeoid].elem != InvalidOid && attlen < 0)
attrtypes[attnum]->attndims = 1;
else
attrtypes[attnum]->attndims = 0;
@@ -844,19 +834,19 @@ InsertOneValue(char *value, int i)
{
for (typeindex = 0; typeindex < n_types; typeindex++)
{
- if (Procid[typeindex].oid == attrtypes[i]->atttypid)
+ if (TypInfo[typeindex].oid == attrtypes[i]->atttypid)
break;
}
if (typeindex >= n_types)
elog(ERROR, "type oid %u not found", attrtypes[i]->atttypid);
elog(DEBUG4, "Typ == NULL, typeindex = %u", typeindex);
- values[i] = OidFunctionCall3(Procid[typeindex].inproc,
+ values[i] = OidFunctionCall3(TypInfo[typeindex].inproc,
CStringGetDatum(value),
- ObjectIdGetDatum(Procid[typeindex].elem),
+ ObjectIdGetDatum(TypInfo[typeindex].elem),
Int32GetDatum(-1));
- prt = DatumGetCString(OidFunctionCall3(Procid[typeindex].outproc,
+ prt = DatumGetCString(OidFunctionCall3(TypInfo[typeindex].outproc,
values[i],
- ObjectIdGetDatum(Procid[typeindex].elem),
+ ObjectIdGetDatum(TypInfo[typeindex].elem),
Int32GetDatum(-1)));
elog(DEBUG4, " -> %s", prt);
pfree(prt);
@@ -930,9 +920,9 @@ cleanup(void)
/* ----------------
* gettype
*
- * NB: this is really ugly; it will return an integer index into Procid[],
+ * NB: this is really ugly; it will return an integer index into TypInfo[],
* and not an OID at all, until the first reference to a type not known in
- * Procid[]. At that point it will read and cache pg_type in the Typ array,
+ * TypInfo[]. At that point it will read and cache pg_type in the Typ array,
* and subsequently return a real OID (and set the global pointer Ap to
* point at the found row in Typ). So caller must check whether Typ is
* still NULL to determine what the return value is!
@@ -962,7 +952,7 @@ gettype(char *type)
{
for (i = 0; i < n_types; i++)
{
- if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0)
+ if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0)
return i;
}
elog(DEBUG4, "external type: %s", type);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ed68f9124d5..087e2d7f23c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.261 2004/03/23 19:35:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.262 2004/04/01 21:28:43 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -97,37 +97,37 @@ static void SetRelationNumChecks(Relation rel, int numchecks);
static FormData_pg_attribute a1 = {
0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
- false, 'p', false, 'i', true, false, false, true, 0
+ false, 'p', 's', true, false, false, true, 0
};
static FormData_pg_attribute a2 = {
0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a3 = {
0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a4 = {
0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a5 = {
0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a6 = {
0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
/*
@@ -139,7 +139,7 @@ static FormData_pg_attribute a6 = {
static FormData_pg_attribute a7 = {
0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
- true, 'p', false, 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0
};
static Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -633,6 +633,8 @@ AddNewRelationTuple(Relation pg_class_desc,
new_rel_reltup->reltype = new_type_oid;
new_rel_reltup->relkind = relkind;
+ new_rel_desc->rd_att->tdtypeid = new_type_oid;
+
/* ----------------
* now form a tuple to add to pg_class
* XXX Natts_pg_class_fixed is a hack - see pg_class.h
@@ -660,7 +662,7 @@ AddNewRelationTuple(Relation pg_class_desc,
/* --------------------------------
* AddNewRelationType -
*
- * define a complex type corresponding to the new relation
+ * define a composite type corresponding to the new relation
* --------------------------------
*/
static void
@@ -670,27 +672,12 @@ AddNewRelationType(const char *typeName,
char new_rel_kind,
Oid new_type_oid)
{
- /*
- * We set the I/O procedures of a complex type to record_in and
- * record_out, so that a user will get an error message not a weird
- * number if he tries to SELECT a complex type.
- *
- * OLD and probably obsolete comments:
- *
- * The sizes are set to oid size because it makes implementing sets MUCH
- * easier, and no one (we hope) uses these fields to figure out how
- * much space to allocate for the type. An oid is the type used for a
- * set definition. When a user requests a set, what they actually get
- * is the oid of a tuple in the pg_proc catalog, so the size of the
- * "set" is the size of an oid. Similarly, byval being true makes sets
- * much easier, and it isn't used by anything else.
- */
TypeCreate(typeName, /* type name */
typeNamespace, /* type namespace */
new_type_oid, /* preassigned oid for type */
new_rel_oid, /* relation oid */
new_rel_kind, /* relation kind */
- sizeof(Oid), /* internal size */
+ -1, /* internal size (varlena) */
'c', /* type-type (complex) */
',', /* default array delimiter */
F_RECORD_IN, /* input procedure */
@@ -702,9 +689,9 @@ AddNewRelationType(const char *typeName,
InvalidOid, /* domain base type - irrelevant */
NULL, /* default type value - none */
NULL, /* default type binary representation */
- true, /* passed by value */
- 'i', /* default alignment - same as for OID */
- 'p', /* Not TOASTable */
+ false, /* passed by reference */
+ 'd', /* alignment - must be the largest! */
+ 'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false); /* Type NOT NULL */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 6fe64eadd0d..0640aacbe5a 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.113 2004/03/21 22:29:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.114 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,7 +32,6 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-#include "utils/sets.h"
#include "utils/syscache.h"
@@ -137,44 +136,6 @@ ProcedureCreate(const char *procedureName,
/* Process param names, if given */
namesarray = create_parameternames_array(parameterCount, parameterNames);
- if (languageObjectId == SQLlanguageId)
- {
- /*
- * If this call is defining a set, check if the set is already
- * defined by looking to see whether this call's function text
- * matches a function already in pg_proc. If so just return the
- * OID of the existing set.
- */
- if (strcmp(procedureName, GENERICSETNAME) == 0)
- {
-#ifdef SETS_FIXED
-
- /*
- * The code below doesn't work any more because the PROSRC
- * system cache and the pg_proc_prosrc_index have been
- * removed. Instead a sequential heap scan or something better
- * must get implemented. The reason for removing is that
- * nbtree index crashes if sources exceed 2K --- what's likely
- * for procedural languages.
- *
- * 1999/09/30 Jan
- */
- text *prosrctext;
-
- prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
- CStringGetDatum(prosrc)));
- retval = GetSysCacheOid(PROSRC,
- PointerGetDatum(prosrctext),
- 0, 0, 0);
- pfree(prosrctext);
- if (OidIsValid(retval))
- return retval;
-#else
- elog(ERROR, "lookup for procedure by source needs fix (Jan)");
-#endif /* SETS_FIXED */
- }
- }
-
/*
* don't allow functions of complex types that have the same name as
* existing attributes of the type
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 3c134a5c166..e2f3d4aa813 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.119 2004/01/31 05:09:40 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.120 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -125,7 +125,7 @@ ExplainResultDesc(ExplainStmt *stmt)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
return tupdesc;
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1a6bd2fdfc0..a6e3a93d349 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.101 2004/03/23 19:35:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.102 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1796,7 +1796,6 @@ AlterTableAddColumn(Oid myrelid,
attribute->attnum = i;
attribute->attbyval = tform->typbyval;
attribute->attndims = attndims;
- attribute->attisset = (bool) (tform->typtype == 'c');
attribute->attstorage = tform->typstorage;
attribute->attalign = tform->typalign;
attribute->attnotnull = colDef->is_not_null;
@@ -4084,15 +4083,15 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
TupleDescInitEntry(tupdesc, (AttrNumber) 1,
"chunk_id",
OIDOID,
- -1, 0, false);
+ -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2,
"chunk_seq",
INT4OID,
- -1, 0, false);
+ -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3,
"chunk_data",
BYTEAOID,
- -1, 0, false);
+ -1, 0);
/*
* Ensure that the toast table doesn't itself get toasted, or we'll be
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 4c6f95a9a6f..b27e86122bc 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.157 2004/03/24 22:40:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,6 +49,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/typcache.h"
/* static function decls */
@@ -110,7 +111,7 @@ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalFieldSelect(GenericExprState *fstate,
+static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalRelabelType(GenericExprState *exprstate,
@@ -420,16 +421,25 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
*isDone = ExprSingleResult;
/*
- * get the slot we want
+ * Get the slot and attribute number we want
+ *
+ * The asserts check that references to system attributes only appear
+ * at the level of a relation scan; at higher levels, system attributes
+ * must be treated as ordinary variables (since we no longer have access
+ * to the original tuple).
*/
+ attnum = variable->varattno;
+
switch (variable->varno)
{
case INNER: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple;
+ Assert(attnum > 0);
break;
case OUTER: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple;
+ Assert(attnum > 0);
break;
default: /* get the tuple from the relation being
@@ -444,8 +454,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
heapTuple = slot->val;
tuple_type = slot->ttc_tupleDescriptor;
- attnum = variable->varattno;
-
/*
* Some checks that are only applied for user attribute numbers
* (bogus system attnums will be caught inside heap_getattr).
@@ -481,38 +489,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid);
}
- /*
- * If the attribute number is invalid, then we are supposed to return
- * the entire tuple; we give back a whole slot so that callers know
- * what the tuple looks like.
- *
- * XXX this is a horrid crock: since the pointer to the slot might live
- * longer than the current evaluation context, we are forced to copy
- * the tuple and slot into a long-lived context --- we use the
- * econtext's per-query memory which should be safe enough. This
- * represents a serious memory leak if many such tuples are processed
- * in one command, however. We ought to redesign the representation
- * of whole-tuple datums so that this is not necessary.
- *
- * We assume it's OK to point to the existing tupleDescriptor, rather
- * than copy that too.
- */
- if (attnum == InvalidAttrNumber)
- {
- MemoryContext oldContext;
- TupleTableSlot *tempSlot;
- HeapTuple tup;
-
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tempSlot = MakeTupleTableSlot();
- tup = heap_copytuple(heapTuple);
- ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
- ExecSetSlotDescriptor(tempSlot, tuple_type, false);
- MemoryContextSwitchTo(oldContext);
- *isNull = false;
- return PointerGetDatum(tempSlot);
- }
-
result = heap_getattr(heapTuple, /* tuple containing attribute */
attnum, /* attribute number of desired
* attribute */
@@ -656,17 +632,23 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
* GetAttributeByName
* GetAttributeByNum
*
- * These are functions which return the value of the
- * named attribute out of the tuple from the arg slot. User defined
+ * These functions return the value of the requested attribute
+ * out of the given tuple Datum.
* C functions which take a tuple as an argument are expected
- * to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
+ * to use these. Ex: overpaid(EMP) might call GetAttributeByNum().
+ * Note: these are actually rather slow because they do a typcache
+ * lookup on each call.
*/
Datum
-GetAttributeByNum(TupleTableSlot *slot,
+GetAttributeByNum(HeapTupleHeader tuple,
AttrNumber attrno,
bool *isNull)
{
- Datum retval;
+ Datum result;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
if (!AttributeNumberIsValid(attrno))
elog(ERROR, "invalid attribute number %d", attrno);
@@ -674,29 +656,43 @@ GetAttributeByNum(TupleTableSlot *slot,
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
- if (TupIsNull(slot))
+ if (tuple == NULL)
{
+ /* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
- retval = heap_getattr(slot->val,
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+ /*
+ * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
+ * all the fields in the struct just in case user tries to inspect
+ * system columns.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = tuple;
+
+ result = heap_getattr(&tmptup,
attrno,
- slot->ttc_tupleDescriptor,
+ tupDesc,
isNull);
- if (*isNull)
- return (Datum) 0;
-
- return retval;
+ return result;
}
Datum
-GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
+GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
{
AttrNumber attrno;
- TupleDesc tupdesc;
- Datum retval;
- int natts;
+ Datum result;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
int i;
if (attname == NULL)
@@ -705,21 +701,23 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
- if (TupIsNull(slot))
+ if (tuple == NULL)
{
+ /* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
- tupdesc = slot->ttc_tupleDescriptor;
- natts = slot->val->t_data->t_natts;
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
attrno = InvalidAttrNumber;
- for (i = 0; i < tupdesc->natts; i++)
+ for (i = 0; i < tupDesc->natts; i++)
{
- if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0)
+ if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
{
- attrno = tupdesc->attrs[i]->attnum;
+ attrno = tupDesc->attrs[i]->attnum;
break;
}
}
@@ -727,14 +725,21 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
if (attrno == InvalidAttrNumber)
elog(ERROR, "attribute \"%s\" does not exist", attname);
- retval = heap_getattr(slot->val,
+ /*
+ * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
+ * all the fields in the struct just in case user tries to inspect
+ * system columns.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = tuple;
+
+ result = heap_getattr(&tmptup,
attrno,
- tupdesc,
+ tupDesc,
isNull);
- if (*isNull)
- return (Datum) 0;
-
- return retval;
+ return result;
}
/*
@@ -1133,14 +1138,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
Tuplestorestate *tupstore = NULL;
TupleDesc tupdesc = NULL;
Oid funcrettype;
+ bool returnsTuple;
FunctionCallInfoData fcinfo;
ReturnSetInfo rsinfo;
+ HeapTupleData tmptup;
MemoryContext callerContext;
MemoryContext oldcontext;
- TupleTableSlot *slot;
bool direct_function_call;
bool first_time = true;
- bool returnsTuple = false;
/*
* Normally the passed expression tree will be a FuncExprState, since
@@ -1216,6 +1221,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
funcrettype = exprType((Node *) funcexpr->expr);
+ returnsTuple = (funcrettype == RECORDOID ||
+ get_typtype(funcrettype) == 'c');
+
/*
* Prepare a resultinfo node for communication. We always do this
* even if not expecting a set result, so that we can pass
@@ -1282,31 +1290,34 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
break;
/*
+ * Can't do anything useful with NULL rowtype values. Currently
+ * we raise an error, but another alternative is to just ignore
+ * the result and "continue" to get another row.
+ */
+ if (returnsTuple && fcinfo.isnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("function returning row cannot return null value")));
+
+ /*
* If first time through, build tupdesc and tuplestore for
* result
*/
if (first_time)
{
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- if (funcrettype == RECORDOID ||
- get_typtype(funcrettype) == 'c')
+ if (returnsTuple)
{
/*
- * Composite type, so function should have returned a
- * TupleTableSlot; use its descriptor
+ * Use the type info embedded in the rowtype Datum to
+ * look up the needed tupdesc. Make a copy for the query.
*/
- slot = (TupleTableSlot *) DatumGetPointer(result);
- if (fcinfo.isnull || !slot)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("function returning row cannot return null value")));
- if (!IsA(slot, TupleTableSlot) ||
- !slot->ttc_tupleDescriptor)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("function returning row did not return a valid tuple slot")));
- tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
- returnsTuple = true;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(result);
+ tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
+ HeapTupleHeaderGetTypMod(td));
+ tupdesc = CreateTupleDescCopy(tupdesc);
}
else
{
@@ -1319,8 +1330,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
"column",
funcrettype,
-1,
- 0,
- false);
+ 0);
}
tupstore = tuplestore_begin_heap(true, false, work_mem);
MemoryContextSwitchTo(oldcontext);
@@ -1333,15 +1343,17 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
*/
if (returnsTuple)
{
- slot = (TupleTableSlot *) DatumGetPointer(result);
- if (fcinfo.isnull ||
- !slot ||
- !IsA(slot, TupleTableSlot) ||
- TupIsNull(slot))
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("function returning row cannot return null value")));
- tuple = slot->val;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(result);
+
+ /*
+ * tuplestore_puttuple needs a HeapTuple not a bare
+ * HeapTupleHeader, but it doesn't need all the fields.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ tmptup.t_data = td;
+ tuple = &tmptup;
}
else
{
@@ -2415,26 +2427,62 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate,
* ----------------------------------------------------------------
*/
static Datum
-ExecEvalFieldSelect(GenericExprState *fstate,
+ExecEvalFieldSelect(FieldSelectState *fstate,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
Datum result;
- TupleTableSlot *resSlot;
+ Datum tupDatum;
+ HeapTupleHeader tuple;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
- result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+ tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
/* this test covers the isDone exception too: */
if (*isNull)
- return result;
+ return tupDatum;
+
+ tuple = DatumGetHeapTupleHeader(tupDatum);
+
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+ /* Lookup tupdesc if first time through or if type changes */
+ tupDesc = fstate->argdesc;
+ if (tupDesc == NULL ||
+ tupType != tupDesc->tdtypeid ||
+ tupTypmod != tupDesc->tdtypmod)
+ {
+ MemoryContext oldcontext;
+
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Copy the tupdesc into query storage for safety */
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ tupDesc = CreateTupleDescCopy(tupDesc);
+ if (fstate->argdesc)
+ FreeTupleDesc(fstate->argdesc);
+ fstate->argdesc = tupDesc;
+ MemoryContextSwitchTo(oldcontext);
+ }
- resSlot = (TupleTableSlot *) DatumGetPointer(result);
- Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
- result = heap_getattr(resSlot->val,
+ /*
+ * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set
+ * all the fields in the struct just in case user tries to inspect
+ * system columns.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = tuple;
+
+ result = heap_getattr(&tmptup,
fselect->fieldnum,
- resSlot->ttc_tupleDescriptor,
+ tupDesc,
isNull);
return result;
}
@@ -2703,11 +2751,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
- GenericExprState *gstate = makeNode(GenericExprState);
+ FieldSelectState *fstate = makeNode(FieldSelectState);
- gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
- gstate->arg = ExecInitExpr(fselect->arg, parent);
- state = (ExprState *) gstate;
+ fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
+ fstate->arg = ExecInitExpr(fselect->arg, parent);
+ fstate->argdesc = NULL;
+ state = (ExprState *) fstate;
}
break;
case T_RelabelType:
@@ -3088,8 +3137,6 @@ ExecTargetList(List *targetlist,
List *tl;
bool isNull;
bool haveDoneSets;
- static struct tupleDesc NullTupleDesc; /* we assume this inits to
- * zeroes */
/*
* debugging stuff
@@ -3106,13 +3153,8 @@ ExecTargetList(List *targetlist,
/*
* There used to be some klugy and demonstrably broken code here that
* special-cased the situation where targetlist == NIL. Now we just
- * fall through and return an empty-but-valid tuple. We do, however,
- * have to cope with the possibility that targettype is NULL ---
- * heap_formtuple won't like that, so pass a dummy descriptor with
- * natts = 0 to deal with it.
+ * fall through and return an empty-but-valid tuple.
*/
- if (targettype == NULL)
- targettype = &NullTupleDesc;
/*
* evaluate all the expressions in the target list
@@ -3285,8 +3327,8 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
/*
* store the tuple in the projection slot and return the slot.
*/
- return ExecStoreTuple(newTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* tuple has no buffer */
+ return ExecStoreTuple(newTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* tuple has no buffer */
true);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 0bb59d26b11..faf910b736f 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.75 2004/01/07 18:56:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -109,8 +109,11 @@
#include "funcapi.h"
#include "access/heapam.h"
+#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
@@ -144,16 +147,11 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in
/*
* Now allocate our new table along with space for the pointers to the
- * tuples.
+ * tuples. Zero out the slots.
*/
newtable = (TupleTable) palloc(sizeof(TupleTableData));
- array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
-
- /*
- * clean out the slots we just allocated
- */
- MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
+ array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
/*
* initialize the new table and return it to the caller.
@@ -514,6 +512,10 @@ TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
+ struct tupleDesc nullTupleDesc;
+ HeapTuple nullTuple;
+ Datum values[1];
+ char nulls[1];
/*
* Since heap_getattr() will treat attributes beyond a tuple's t_natts
@@ -521,15 +523,12 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
* of zero length. However, the slot descriptor must match the real
* tupType.
*/
- HeapTuple nullTuple;
- Datum values[1];
- char nulls[1];
- static struct tupleDesc NullTupleDesc; /* we assume this inits to
- * zeroes */
+ nullTupleDesc = *tupType;
+ nullTupleDesc.natts = 0;
- ExecSetSlotDescriptor(slot, tupType, false);
+ nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
- nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
+ ExecSetSlotDescriptor(slot, tupType, false);
return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
}
@@ -590,21 +589,45 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
resdom->resname,
resdom->restype,
resdom->restypmod,
- 0,
- false);
+ 0);
}
return typeInfo;
}
/*
+ * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
+ *
+ * Rowtype Datums returned by a function must contain valid type information.
+ * This happens "for free" if the tupdesc came from a relcache entry, but
+ * not if we have manufactured a tupdesc for a transient RECORD datatype.
+ * In that case we have to notify typcache.c of the existence of the type.
+ */
+TupleDesc
+BlessTupleDesc(TupleDesc tupdesc)
+{
+ if (tupdesc->tdtypeid == RECORDOID &&
+ tupdesc->tdtypmod < 0)
+ assign_record_type_typmod(tupdesc);
+
+ return tupdesc; /* just for notational convenience */
+}
+
+/*
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ *
+ * Note: this is obsolete; it is sufficient to call BlessTupleDesc on
+ * the tupdesc. We keep it around just for backwards compatibility with
+ * existing user-written SRFs.
*/
TupleTableSlot *
TupleDescGetSlot(TupleDesc tupdesc)
{
TupleTableSlot *slot;
+ /* The useful work is here */
+ BlessTupleDesc(tupdesc);
+
/* Make a standalone slot */
slot = MakeTupleTableSlot();
@@ -634,6 +657,9 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
+ /* "Bless" the tupledesc so that we can make rowtype datums with it */
+ attinmeta->tupdesc = BlessTupleDesc(tupdesc);
+
/*
* Gather info needed later to call the "in" function for each
* attribute
@@ -653,7 +679,6 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
atttypmods[i] = tupdesc->attrs[i]->atttypmod;
}
}
- attinmeta->tupdesc = tupdesc;
attinmeta->attinfuncs = attinfuncinfo;
attinmeta->attelems = attelems;
attinmeta->atttypmods = atttypmods;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 8dec6131fb4..aa7652e07ef 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.78 2004/03/21 22:29:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.79 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,8 +24,10 @@
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/*
@@ -61,10 +63,6 @@ typedef struct
bool returnsTuple; /* true if return type is a tuple */
bool shutdown_reg; /* true if registered shutdown callback */
- TupleTableSlot *funcSlot; /* if one result we need to copy it before
- * we end execution of the function and
- * free stuff */
-
ParamListInfo paramLI; /* Param list representing current args */
/* head of linked list of execution_state records */
@@ -196,34 +194,9 @@ init_sql_fcache(FmgrInfo *finfo)
* get the type length and by-value flag from the type tuple
*/
fcache->typlen = typeStruct->typlen;
-
- if (typeStruct->typtype != 'c' && rettype != RECORDOID)
- {
- /* The return type is not a composite type, so just use byval */
- fcache->typbyval = typeStruct->typbyval;
- fcache->returnsTuple = false;
- }
- else
- {
- /*
- * This is a hack. We assume here that any function returning a
- * tuple returns it by reference. This needs to be fixed, since
- * actually the mechanism isn't quite like return-by-reference.
- */
- fcache->typbyval = false;
- fcache->returnsTuple = true;
- }
-
- /*
- * If we are returning exactly one result then we have to copy tuples
- * and by reference results because we have to end the execution
- * before we return the results. When you do this everything
- * allocated by the executor (i.e. slots and tuples) is freed.
- */
- if (!finfo->fn_retset && !fcache->typbyval)
- fcache->funcSlot = MakeTupleTableSlot();
- else
- fcache->funcSlot = NULL;
+ fcache->typbyval = typeStruct->typbyval;
+ fcache->returnsTuple = (typeStruct->typtype == 'c' ||
+ rettype == RECORDOID);
/*
* Parse and plan the queries. We need the argument type info to pass
@@ -366,39 +339,6 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
fcache->paramLI = paramLI;
}
-static TupleTableSlot *
-copy_function_result(SQLFunctionCachePtr fcache,
- TupleTableSlot *resultSlot)
-{
- TupleTableSlot *funcSlot;
- TupleDesc resultTd;
- HeapTuple resultTuple;
- HeapTuple newTuple;
-
- Assert(!TupIsNull(resultSlot));
- resultTuple = resultSlot->val;
-
- funcSlot = fcache->funcSlot;
-
- if (funcSlot == NULL)
- return resultSlot; /* no need to copy result */
-
- /*
- * If first time through, we have to initialize the funcSlot's tuple
- * descriptor.
- */
- if (funcSlot->ttc_tupleDescriptor == NULL)
- {
- resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
- ExecSetSlotDescriptor(funcSlot, resultTd, true);
- ExecSetSlotDescriptorIsNew(funcSlot, true);
- }
-
- newTuple = heap_copytuple(resultTuple);
-
- return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
-}
-
static Datum
postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
@@ -429,43 +369,51 @@ postquel_execute(execution_state *es,
if (LAST_POSTQUEL_COMMAND(es))
{
- TupleTableSlot *resSlot;
-
/*
- * Copy the result. copy_function_result is smart enough to do
- * nothing when no action is called for. This helps reduce the
- * logic and code redundancy here.
+ * Set up to return the function value.
*/
- resSlot = copy_function_result(fcache, slot);
+ HeapTuple tup = slot->val;
+ TupleDesc tupDesc = slot->ttc_tupleDescriptor;
- /*
- * If we are supposed to return a tuple, we return the tuple slot
- * pointer converted to Datum. If we are supposed to return a
- * simple value, then project out the first attribute of the
- * result tuple (ie, take the first result column of the final
- * SELECT).
- */
if (fcache->returnsTuple)
{
/*
+ * We are returning the whole tuple, so copy it into current
+ * execution context and make sure it is a valid Datum.
+ *
* XXX do we need to remove junk attrs from the result tuple?
* Probably OK to leave them, as long as they are at the end.
*/
- value = PointerGetDatum(resSlot);
+ HeapTupleHeader dtup;
+
+ dtup = (HeapTupleHeader) palloc(tup->t_len);
+ memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
+
+ /*
+ * For RECORD results, make sure a typmod has been assigned.
+ */
+ if (tupDesc->tdtypeid == RECORDOID &&
+ tupDesc->tdtypmod < 0)
+ assign_record_type_typmod(tupDesc);
+
+ HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
+ HeapTupleHeaderSetTypeId(dtup, tupDesc->tdtypeid);
+ HeapTupleHeaderSetTypMod(dtup, tupDesc->tdtypmod);
+
+ value = PointerGetDatum(dtup);
fcinfo->isnull = false;
}
else
{
- value = heap_getattr(resSlot->val,
- 1,
- resSlot->ttc_tupleDescriptor,
- &(fcinfo->isnull));
-
/*
- * Note: if result type is pass-by-reference then we are
- * returning a pointer into the tuple copied by
- * copy_function_result. This is OK.
+ * Returning a scalar, which we have to extract from the
+ * first column of the SELECT result, and then copy into current
+ * execution context if needed.
*/
+ value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
+
+ if (!fcinfo->isnull)
+ value = datumCopy(value, fcache->typbyval, fcache->typlen);
}
/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index f3fa17c8881..7847b24ffe2 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.23 2003/11/29 19:51:48 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.24 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
+#include "utils/typcache.h"
static TupleTableSlot *FunctionNext(FunctionScanState *node);
@@ -194,25 +195,12 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
if (functyptype == 'c')
{
- /*
- * Composite data type, i.e. a table's row type
- */
- Oid funcrelid;
- Relation rel;
-
- funcrelid = typeidTypeRelid(funcrettype);
- if (!OidIsValid(funcrelid))
- elog(ERROR, "invalid typrelid for complex type %u",
- funcrettype);
- rel = relation_open(funcrelid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- relation_close(rel, AccessShareLock);
+ /* Composite data type, e.g. a table's row type */
+ tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
}
else if (functyptype == 'b' || functyptype == 'd')
{
- /*
- * Must be a base data type, i.e. scalar
- */
+ /* Must be a base data type, i.e. scalar */
char *attname = strVal(lfirst(rte->eref->colnames));
tupdesc = CreateTemplateTupleDesc(1, false);
@@ -221,14 +209,11 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
attname,
funcrettype,
-1,
- 0,
- false);
+ 0);
}
- else if (functyptype == 'p' && funcrettype == RECORDOID)
+ else if (funcrettype == RECORDOID)
{
- /*
- * Must be a pseudo type, i.e. record
- */
+ /* Must be a pseudo type, i.e. record */
tupdesc = BuildDescForRelation(rte->coldeflist);
}
else
@@ -237,6 +222,14 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
elog(ERROR, "function in FROM has unsupported return type");
}
+ /*
+ * For RECORD results, make sure a typmod has been assigned. (The
+ * function should do this for itself, but let's cover things in case
+ * it doesn't.)
+ */
+ if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0)
+ assign_record_type_typmod(tupdesc);
+
scanstate->tupdesc = tupdesc;
ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
tupdesc, false);
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 1fb60fff02d..bcf7f8d52b6 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.112 2004/03/21 22:29:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.113 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,6 +19,7 @@
#include "executor/spi_priv.h"
#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
+#include "utils/typcache.h"
uint32 SPI_processed = 0;
@@ -380,40 +381,11 @@ SPI_copytuple(HeapTuple tuple)
return ctuple;
}
-TupleDesc
-SPI_copytupledesc(TupleDesc tupdesc)
+HeapTupleHeader
+SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt = NULL;
- TupleDesc ctupdesc;
-
- if (tupdesc == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(ERROR, "SPI stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
-
- ctupdesc = CreateTupleDescCopy(tupdesc);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return ctupdesc;
-}
-
-TupleTableSlot *
-SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
-{
- MemoryContext oldcxt = NULL;
- TupleTableSlot *cslot;
- HeapTuple ctuple;
- TupleDesc ctupdesc;
+ HeapTupleHeader dtup;
if (tuple == NULL || tupdesc == NULL)
{
@@ -421,6 +393,11 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
return NULL;
}
+ /* For RECORD results, make sure a typmod has been assigned */
+ if (tupdesc->tdtypeid == RECORDOID &&
+ tupdesc->tdtypmod < 0)
+ assign_record_type_typmod(tupdesc);
+
if (_SPI_curid + 1 == _SPI_connected) /* connected */
{
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
@@ -428,17 +405,17 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
}
- ctuple = heap_copytuple(tuple);
- ctupdesc = CreateTupleDescCopy(tupdesc);
+ dtup = (HeapTupleHeader) palloc(tuple->t_len);
+ memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
- cslot = MakeTupleTableSlot();
- ExecSetSlotDescriptor(cslot, ctupdesc, true);
- cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
+ HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
+ HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
+ HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
if (oldcxt)
MemoryContextSwitchTo(oldcxt);
- return cslot;
+ return dtup;
}
HeapTuple
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 24b2fefe16a..1677493abbc 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.165 2004/01/07 18:56:27 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -170,9 +170,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* tuple from the relation. We build a special VarNode to
* reflect this -- it has varno set to the correct range table
* entry, but has varattno == 0 to signal that the whole tuple
- * is the argument. Also, it has typmod set to
- * sizeof(Pointer) to signal that the runtime representation
- * will be a pointer not an Oid.
+ * is the argument.
*/
switch (rte->rtekind)
{
@@ -185,7 +183,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,
toid,
- sizeof(Pointer),
+ -1,
sublevels_up);
break;
case RTE_FUNCTION:
@@ -196,7 +194,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,
toid,
- sizeof(Pointer),
+ -1,
sublevels_up);
}
else
@@ -214,6 +212,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
+ *
+ * XXX FIXME
*/
if (is_column)
unknown_attribute(schemaname, relname,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index e66eb905f56..d83e8ac4580 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.133 2004/01/14 23:01:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.134 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -523,16 +523,7 @@ build_column_default(Relation rel, int attrno)
* No per-column default, so look for a default for the type
* itself.
*/
- if (att_tup->attisset)
- {
- /*
- * Set attributes are represented as OIDs no matter what the
- * set element type is, and the element type's default is
- * irrelevant too.
- */
- }
- else
- expr = get_typdefault(atttype);
+ expr = get_typdefault(atttype);
}
if (expr == NULL)
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 5223e33816e..427d6334cd4 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for utils/adt
#
-# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.56 2003/11/29 19:51:57 pgsql Exp $
+# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.57 2004/04/01 21:28:45 tgl Exp $
#
subdir = src/backend/utils/adt
@@ -19,8 +19,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
- oid.o oracle_compat.o pseudotypes.o \
- regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
+ oid.o oracle_compat.o pseudotypes.o rowtypes.o \
+ regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_net_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index b009e167a3f..bc4901aa546 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.12 2003/11/29 19:51:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,22 +52,22 @@ pg_lock_status(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* build tupdesc for result tuples */
- /* this had better match pg_locks view in initdb.sh */
+ /* this had better match pg_locks view in system_views.sql */
tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
- OIDOID, -1, 0, false);
+ OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
- OIDOID, -1, 0, false);
+ OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
- XIDOID, -1, 0, false);
+ XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
- INT4OID, -1, 0, false);
+ INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
- BOOLOID, -1, 0, false);
+ BOOLOID, -1, 0);
- funcctx->slot = TupleDescGetSlot(tupdesc);
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/*
* Collect all the locking information that we will format and
@@ -173,9 +173,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
CStringGetDatum(GetLockmodeName(mode)));
values[5] = BoolGetDatum(granted);
- tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
- values, nulls);
- result = TupleGetDatum(funcctx->slot, tuple);
+ tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 71b99553bc1..0526f52e677 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.12 2003/11/29 19:51:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,59 +28,6 @@
/*
- * record_in - input routine for pseudo-type RECORD.
- */
-Datum
-record_in(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot accept a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_out - output routine for pseudo-type RECORD.
- */
-Datum
-record_out(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot display a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_recv - binary input routine for pseudo-type RECORD.
- */
-Datum
-record_recv(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot accept a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_send - binary output routine for pseudo-type RECORD.
- */
-Datum
-record_send(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot display a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-
-/*
* cstring_in - input routine for pseudo-type CSTRING.
*
* We might as well allow this to support constructs like "foo_in('blah')".
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
new file mode 100644
index 00000000000..b487dfc9047
--- /dev/null
+++ b/src/backend/utils/adt/rowtypes.c
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * rowtypes.c
+ * I/O functions for generic composite types.
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.1 2004/04/01 21:28:45 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+
+
+/*
+ * record_in - input routine for any composite type.
+ */
+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")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_out - output routine for any composite type.
+ */
+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")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_recv - binary input routine for any composite type.
+ */
+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")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_send - binary output routine for any composite type.
+ */
+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")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
deleted file mode 100644
index 33f1ed1a4a8..00000000000
--- a/src/backend/utils/adt/sets.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sets.c
- * Functions for sets, which are defined by queries.
- * Example: a set is defined as being the result of the query
- * retrieve (X.all)
- *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/sets.c,v 1.62 2004/01/06 23:55:18 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/indexing.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_proc.h"
-#include "executor/executor.h"
-#include "utils/fmgroids.h"
-#include "utils/sets.h"
-#include "utils/syscache.h"
-
-
-/*
- * SetDefine - converts query string defining set to an oid
- *
- * We create an SQL function having the given querystring as its body.
- * The name of the function is then changed to use the OID of its tuple
- * in pg_proc.
- */
-Oid
-SetDefine(char *querystr, Oid elemType)
-{
- Oid setoid;
- char *procname = GENERICSETNAME;
- char *fileName = "-";
- char realprocname[NAMEDATALEN];
- HeapTuple tup,
- newtup = NULL;
- Form_pg_proc proc;
- Relation procrel;
- int i;
- Datum replValue[Natts_pg_proc];
- char replNull[Natts_pg_proc];
- char repl[Natts_pg_proc];
-
- setoid = ProcedureCreate(procname, /* changed below, after oid known */
- PG_CATALOG_NAMESPACE, /* XXX wrong */
- false, /* don't replace */
- true, /* returnsSet */
- elemType, /* returnType */
- SQLlanguageId, /* language */
- F_FMGR_SQL_VALIDATOR,
- querystr, /* prosrc */
- fileName, /* probin */
- false, /* not aggregate */
- false, /* security invoker */
- false, /* isStrict (irrelevant, no args) */
- PROVOLATILE_VOLATILE, /* assume unsafe */
- 0, /* parameterCount */
- NULL, /* parameterTypes */
- NULL); /* parameterNames */
-
- /*
- * Since we're still inside this command of the transaction, we can't
- * see the results of the procedure definition unless we pretend we've
- * started the next command. (Postgres's solution to the Halloween
- * problem is to not allow you to see the results of your command
- * until you start the next command.)
- */
- CommandCounterIncrement();
-
- procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
- tup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(setoid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for function %u", setoid);
-
- /*
- * We can tell whether the set was already defined by checking the
- * name. If it's GENERICSETNAME, the set is new. If it's "set<some
- * oid>" it's already defined.
- */
- proc = (Form_pg_proc) GETSTRUCT(tup);
- if (strcmp(procname, NameStr(proc->proname)) == 0)
- {
- /* make the real proc name */
- snprintf(realprocname, sizeof(realprocname), "set%u", setoid);
-
- /* set up the attributes to be modified or kept the same */
- repl[0] = 'r';
- for (i = 1; i < Natts_pg_proc; i++)
- repl[i] = ' ';
- replValue[0] = (Datum) realprocname;
- for (i = 1; i < Natts_pg_proc; i++)
- replValue[i] = (Datum) 0;
- for (i = 0; i < Natts_pg_proc; i++)
- replNull[i] = ' ';
-
- /* change the pg_proc tuple */
- newtup = heap_modifytuple(tup,
- procrel,
- replValue,
- replNull,
- repl);
-
- simple_heap_update(procrel, &newtup->t_self, newtup);
-
- setoid = HeapTupleGetOid(newtup);
-
- CatalogUpdateIndexes(procrel, newtup);
-
- heap_freetuple(newtup);
- }
-
- ReleaseSysCache(tup);
-
- heap_close(procrel, RowExclusiveLock);
-
- return setoid;
-}
-
-/*
- * This function executes set evaluation. The parser sets up a set reference
- * as a call to this function with the OID of the set to evaluate as argument.
- *
- * We build a new fcache for execution of the set's function and run the
- * function until it says "no mas". The fn_extra field of the call's
- * FmgrInfo record is a handy place to hold onto the fcache. (Since this
- * is a built-in function, there is no competing use of fn_extra.)
- */
-Datum
-seteval(PG_FUNCTION_ARGS)
-{
- Oid funcoid = PG_GETARG_OID(0);
- FuncExprState *fcache;
- Datum result;
- bool isNull;
- ExprDoneCond isDone;
-
- /*
- * If this is the first call, we need to set up the fcache for the
- * target set's function.
- */
- fcache = (FuncExprState *) fcinfo->flinfo->fn_extra;
- if (fcache == NULL)
- {
- MemoryContext oldcontext;
- FuncExpr *func;
-
- oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-
- func = makeNode(FuncExpr);
- func->funcid = funcoid;
- func->funcresulttype = InvalidOid; /* nothing will look at
- * this */
- func->funcretset = true;
- func->funcformat = COERCE_EXPLICIT_CALL;
- func->args = NIL; /* there are no arguments */
-
- fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL);
-
- MemoryContextSwitchTo(oldcontext);
-
- init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt);
-
- fcinfo->flinfo->fn_extra = (void *) fcache;
- }
-
- /*
- * Evaluate the function. NOTE: we need no econtext because there are
- * no arguments to evaluate.
- */
-
- /* ExecMakeFunctionResult assumes these are initialized at call: */
- isNull = false;
- isDone = ExprSingleResult;
-
- result = ExecMakeFunctionResult(fcache,
- NULL, /* no econtext, see above */
- &isNull,
- &isDone);
-
- /*
- * Return isNull/isDone status.
- */
- fcinfo->isnull = isNull;
-
- if (isDone != ExprSingleResult)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = isDone;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
- }
-
- PG_RETURN_DATUM(result);
-}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 5c302e18d39..85ad6ffe788 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.200 2004/03/16 05:05:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.201 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/*
@@ -432,7 +433,10 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
AttrDefault *attrdef = NULL;
int ndef = 0;
- relation->rd_att->tdhasoid = RelationGetForm(relation)->relhasoids;
+ /* copy some fields from pg_class row to rd_att */
+ relation->rd_att->tdtypeid = relation->rd_rel->reltype;
+ relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
+ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
@@ -1312,9 +1316,12 @@ formrdesc(const char *relationName,
* Unlike the case with the relation tuple, this data had better be right
* because it will never be replaced. The input values must be
* correctly defined by macros in src/include/catalog/ headers.
+ *
+ * Note however that rd_att's tdtypeid, tdtypmod, tdhasoid fields are
+ * not right at this point. They will be fixed later when the real
+ * pg_class row is loaded.
*/
- relation->rd_att = CreateTemplateTupleDesc(natts,
- relation->rd_rel->relhasoids);
+ relation->rd_att = CreateTemplateTupleDesc(natts, false);
/*
* initialize tuple desc info
@@ -1595,6 +1602,7 @@ RelationReloadClassinfo(Relation relation)
static void
RelationClearRelation(Relation relation, bool rebuild)
{
+ Oid old_reltype = relation->rd_rel->reltype;
MemoryContext oldcxt;
/*
@@ -1679,6 +1687,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (!rebuild)
{
/* ok to zap remaining substructure */
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(relation->rd_att);
if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt);
@@ -1704,6 +1713,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (RelationBuildDesc(buildinfo, relation) != relation)
{
/* Should only get here if relation was deleted */
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
@@ -1715,11 +1725,15 @@ RelationClearRelation(Relation relation, bool rebuild)
relation->rd_isnew = old_isnew;
if (equalTupleDescs(old_att, relation->rd_att))
{
+ /* needn't flush typcache here */
FreeTupleDesc(relation->rd_att);
relation->rd_att = old_att;
}
else
+ {
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
+ }
if (equalRuleLocks(old_rules, relation->rd_rules))
{
if (relation->rd_rulescxt)
@@ -2329,6 +2343,12 @@ RelationCacheInitializePhase2(void)
*/
Assert(relation->rd_rel != NULL);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
+
+ /*
+ * Also update the derived fields in rd_att.
+ */
+ relation->rd_att->tdtypeid = relp->reltype;
+ relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
relation->rd_att->tdhasoid = relp->relhasoids;
ReleaseSysCache(htup);
@@ -2918,6 +2938,8 @@ load_relcache_init_file(void)
/* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
relform->relhasoids);
+ rel->rd_att->tdtypeid = relform->reltype;
+ rel->rd_att->tdtypmod = -1; /* unnecessary, but... */
/* next read all the attribute tuple form data entries */
has_not_null = false;
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index d6e560c34c5..7a8e67c83c8 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -28,12 +28,15 @@
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
+ * We do support clearing the tuple descriptor part of a rowtype's cache
+ * entry, since that may need to change as a consequence of ALTER TABLE.
+ *
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.4 2003/11/29 19:52:00 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.5 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,11 +56,42 @@
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/typcache.h"
+/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
+/*
+ * We use a separate table for storing the definitions of non-anonymous
+ * record types. Once defined, a record type will be remembered for the
+ * life of the backend. Subsequent uses of the "same" record type (where
+ * sameness means equalTupleDescs) will refer to the existing table entry.
+ *
+ * Stored record types are remembered in a linear array of TupleDescs,
+ * which can be indexed quickly with the assigned typmod. There is also
+ * a hash table to speed searches for matching TupleDescs. The hash key
+ * uses just the first N columns' type OIDs, and so we may have multiple
+ * entries with the same hash key.
+ */
+#define REC_HASH_KEYS 16 /* use this many columns in hash key */
+
+typedef struct RecordCacheEntry
+{
+ /* the hash lookup key MUST BE FIRST */
+ Oid hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
+
+ /* list of TupleDescs for record types with this hashkey */
+ List *tupdescs;
+} RecordCacheEntry;
+
+static HTAB *RecordCacheHash = NULL;
+
+static TupleDesc *RecordCacheArray = NULL;
+static int32 RecordCacheArrayLen = 0; /* allocated length of array */
+static int32 NextRecordTypmod = 0; /* number of entries used */
+
static Oid lookup_default_opclass(Oid type_id, Oid am_id);
@@ -102,16 +136,26 @@ lookup_type_cache(Oid type_id, int flags)
if (typentry == NULL)
{
/*
- * If we didn't find one, we want to make one. But first get the
- * required info from the pg_type row, just to make sure we don't
- * make a cache entry for an invalid type OID.
+ * If we didn't find one, we want to make one. But first look up
+ * the pg_type row, just to make sure we don't make a cache entry
+ * for an invalid type OID.
*/
- int16 typlen;
- bool typbyval;
- char typalign;
+ HeapTuple tp;
+ Form_pg_type typtup;
- get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
+ tp = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for type %u", type_id);
+ typtup = (Form_pg_type) GETSTRUCT(tp);
+ if (!typtup->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" is only a shell",
+ NameStr(typtup->typname))));
+ /* Now make the typcache entry */
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_ENTER, &found);
@@ -123,13 +167,20 @@ lookup_type_cache(Oid type_id, int flags)
MemSet(typentry, 0, sizeof(TypeCacheEntry));
typentry->type_id = type_id;
- typentry->typlen = typlen;
- typentry->typbyval = typbyval;
- typentry->typalign = typalign;
+ typentry->typlen = typtup->typlen;
+ typentry->typbyval = typtup->typbyval;
+ typentry->typalign = typtup->typalign;
+ typentry->typtype = typtup->typtype;
+ typentry->typrelid = typtup->typrelid;
+
+ ReleaseSysCache(tp);
}
/* If we haven't already found the opclass, try to do so */
- if (flags != 0 && typentry->btree_opc == InvalidOid)
+ if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
+ TYPECACHE_CMP_PROC |
+ TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
+ typentry->btree_opc == InvalidOid)
{
typentry->btree_opc = lookup_default_opclass(type_id,
BTREE_AM_OID);
@@ -215,6 +266,30 @@ lookup_type_cache(Oid type_id, int flags)
CacheMemoryContext);
}
+ /*
+ * If it's a composite type (row type), get tupdesc if requested
+ */
+ if ((flags & TYPECACHE_TUPDESC) &&
+ typentry->tupDesc == NULL &&
+ typentry->typtype == 'c')
+ {
+ Relation rel;
+
+ if (!OidIsValid(typentry->typrelid)) /* should not happen */
+ elog(ERROR, "invalid typrelid for composite type %u",
+ typentry->type_id);
+ rel = relation_open(typentry->typrelid, AccessShareLock);
+ Assert(rel->rd_rel->reltype == typentry->type_id);
+ /*
+ * Notice that we simply store a link to the relcache's tupdesc.
+ * Since we are relying on relcache to detect cache flush events,
+ * there's not a lot of point to maintaining an independent copy.
+ */
+ typentry->tupDesc = RelationGetDescr(rel);
+
+ relation_close(rel, AccessShareLock);
+ }
+
return typentry;
}
@@ -296,3 +371,172 @@ lookup_default_opclass(Oid type_id, Oid am_id)
return InvalidOid;
}
+
+
+/*
+ * lookup_rowtype_tupdesc
+ *
+ * Given a typeid/typmod that should describe a known composite type,
+ * return the tuple descriptor for the type. Will ereport on failure.
+ *
+ * Note: returned TupleDesc points to cached copy; caller must copy it
+ * if intending to scribble on it or keep a reference for a long time.
+ */
+TupleDesc
+lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
+{
+ if (type_id != RECORDOID)
+ {
+ /*
+ * It's a named composite type, so use the regular typcache.
+ */
+ TypeCacheEntry *typentry;
+
+ typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
+ /* this should not happen unless caller messed up: */
+ if (typentry->tupDesc == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("type %u is not composite",
+ type_id)));
+ return typentry->tupDesc;
+ }
+ else
+ {
+ /*
+ * It's a transient record type, so look in our record-type table.
+ */
+ if (typmod < 0 || typmod >= NextRecordTypmod)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("record type has not been registered")));
+ }
+ return RecordCacheArray[typmod];
+ }
+}
+
+
+/*
+ * assign_record_type_typmod
+ *
+ * Given a tuple descriptor for a RECORD type, find or create a cache entry
+ * for the type, and set the tupdesc's tdtypmod field to a value that will
+ * identify this cache entry to lookup_rowtype_tupdesc.
+ */
+void
+assign_record_type_typmod(TupleDesc tupDesc)
+{
+ RecordCacheEntry *recentry;
+ TupleDesc entDesc;
+ Oid hashkey[REC_HASH_KEYS];
+ bool found;
+ int i;
+ List *l;
+ int32 newtypmod;
+ MemoryContext oldcxt;
+
+ Assert(tupDesc->tdtypeid == RECORDOID);
+
+ if (RecordCacheHash == NULL)
+ {
+ /* First time through: initialize the hash table */
+ HASHCTL ctl;
+
+ if (!CacheMemoryContext)
+ CreateCacheMemoryContext();
+
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
+ ctl.entrysize = sizeof(RecordCacheEntry);
+ ctl.hash = tag_hash;
+ RecordCacheHash = hash_create("Record information cache", 64,
+ &ctl, HASH_ELEM | HASH_FUNCTION);
+ }
+
+ /* Find or create a hashtable entry for this hash class */
+ MemSet(hashkey, 0, sizeof(hashkey));
+ for (i = 0; i < tupDesc->natts; i++)
+ {
+ if (i >= REC_HASH_KEYS)
+ break;
+ hashkey[i] = tupDesc->attrs[i]->atttypid;
+ }
+ recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
+ (void *) hashkey,
+ HASH_ENTER, &found);
+ if (recentry == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+ if (!found)
+ {
+ /* New entry ... hash_search initialized only the hash key */
+ recentry->tupdescs = NIL;
+ }
+
+ /* Look for existing record cache entry */
+ foreach(l, recentry->tupdescs)
+ {
+ entDesc = (TupleDesc) lfirst(l);
+ if (equalTupleDescs(tupDesc, entDesc))
+ {
+ tupDesc->tdtypmod = entDesc->tdtypmod;
+ return;
+ }
+ }
+
+ /* Not present, so need to manufacture an entry */
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ if (RecordCacheArray == NULL)
+ {
+ RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
+ RecordCacheArrayLen = 64;
+ }
+ else if (NextRecordTypmod >= RecordCacheArrayLen)
+ {
+ int32 newlen = RecordCacheArrayLen * 2;
+
+ RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
+ newlen * sizeof(TupleDesc));
+ RecordCacheArrayLen = newlen;
+ }
+
+ /* if fail in subrs, no damage except possibly some wasted memory... */
+ entDesc = CreateTupleDescCopy(tupDesc);
+ recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+ /* now it's safe to advance NextRecordTypmod */
+ newtypmod = NextRecordTypmod++;
+ entDesc->tdtypmod = newtypmod;
+ RecordCacheArray[newtypmod] = entDesc;
+
+ /* report to caller as well */
+ tupDesc->tdtypmod = newtypmod;
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * flush_rowtype_cache
+ *
+ * If a typcache entry exists for a rowtype, delete the entry's cached
+ * tuple descriptor link. This is called from relcache.c when a cached
+ * relation tupdesc is about to be dropped.
+ */
+void
+flush_rowtype_cache(Oid type_id)
+{
+ TypeCacheEntry *typentry;
+
+ if (TypeCacheHash == NULL)
+ return; /* no table, so certainly no entry */
+
+ typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
+ (void *) &type_id,
+ HASH_FIND, NULL);
+ if (typentry == NULL)
+ return; /* no matching entry */
+
+ typentry->tupDesc = NULL;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e6ef3fcec3c..dac4a8916e1 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.13 2003/12/19 00:02:11 joe Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.14 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,9 +49,8 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
* Allocate suitably long-lived space and zero it
*/
retval = (FuncCallContext *)
- MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(FuncCallContext));
- MemSet(retval, 0, sizeof(FuncCallContext));
+ MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
+ sizeof(FuncCallContext));
/*
* initialize the elements
@@ -61,6 +60,7 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
retval->slot = NULL;
retval->user_fctx = NULL;
retval->attinmeta = NULL;
+ retval->tuple_desc = NULL;
retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
/*
@@ -104,8 +104,11 @@ per_MultiFuncCall(PG_FUNCTION_ARGS)
* FuncCallContext is pointing to it), but in most usage patterns the
* tuples stored in it will be in the function's per-tuple context. So
* at the beginning of each call, the Slot will hold a dangling
- * pointer to an already-recycled tuple. We clear it out here. (See
- * also the definition of TupleGetDatum() in funcapi.h!)
+ * pointer to an already-recycled tuple. We clear it out here.
+ *
+ * Note: use of retval->slot is obsolete as of 7.5, and we expect that
+ * it will always be NULL. This is just here for backwards compatibility
+ * in case someone creates a slot anyway.
*/
if (retval->slot != NULL)
ExecClearTuple(retval->slot);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bc2499ed3af..26f4210ad0e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.194 2004/04/01 14:25:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.195 2004/04/01 21:28:45 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -3453,9 +3453,9 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
}
else
{
@@ -3467,7 +3467,7 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
}
return tupdesc;
}
@@ -3507,7 +3507,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3532,9 +3532,9 @@ ShowAllGUCConfig(DestReceiver *dest)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3740,7 +3740,6 @@ show_all_settings(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
int call_cntr;
int max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
MemoryContext oldcontext;
@@ -3762,31 +3761,25 @@ show_all_settings(PG_FUNCTION_ARGS)
*/
tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "category",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "short_desc",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "extra_desc",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "context",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "vartype",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "source",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "min_val",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "max_val",
- TEXTOID, -1, 0, false);
-
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
- /* assign slot to function context */
- funcctx->slot = slot;
+ TEXTOID, -1, 0);
/*
* Generate attribute metadata needed later to produce tuples from
@@ -3806,7 +3799,6 @@ show_all_settings(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- slot = funcctx->slot;
attinmeta = funcctx->attinmeta;
if (call_cntr < max_calls) /* do when there is more left to send */
@@ -3837,7 +3829,7 @@ show_all_settings(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index c6579ea2462..9f91d81107d 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.87 2004/03/11 01:47:41 ishii Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.88 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -111,11 +111,12 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
fastgetattr((tup), (attnum), (tupleDesc), (isnull)) \
) \
: \
- heap_getsysattr((tup), (attnum), (isnull)) \
+ heap_getsysattr((tup), (attnum), (tupleDesc), (isnull)) \
) \
)
-extern Datum heap_getsysattr(HeapTuple tup, int attnum, bool *isnull);
+extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
+ bool *isnull);
/* ----------------
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index a47e668f9b2..3d48b5f45a3 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.64 2004/01/16 20:51:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.65 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,10 +47,19 @@
#define MaxHeapAttributeNumber 1600 /* 8 * 200 */
/*----------
- * On-disk heap tuple header. Currently this is also used as the header
- * format for tuples formed in memory, although in principle they could
- * be different. To avoid wasting space, the fields should be layed out
- * in such a way to avoid structure padding.
+ * Heap tuple header. To avoid wasting space, the fields should be
+ * layed out in such a way to avoid structure padding.
+ *
+ * Datums of composite types (row types) share the same general structure
+ * as on-disk tuples, so that the same routines can be used to build and
+ * examine them. However the requirements are slightly different: a Datum
+ * does not need any transaction visibility information, and it does need
+ * a length word and some embedded type information. We can achieve this
+ * by overlaying the xmin/cmin/xmax/cmax/xvac fields of a heap tuple
+ * with the fields needed in the Datum case. Typically, all tuples built
+ * in-memory will be initialized with the Datum fields; but when a tuple is
+ * about to be inserted in a table, the transaction fields will be filled,
+ * overwriting the datum fields.
*
* The overall structure of a heap tuple looks like:
* fixed fields (HeapTupleHeaderData struct)
@@ -96,7 +105,8 @@
* MAXALIGN.
*----------
*/
-typedef struct HeapTupleHeaderData
+
+typedef struct HeapTupleFields
{
TransactionId t_xmin; /* inserting xact ID */
@@ -111,6 +121,28 @@ typedef struct HeapTupleHeaderData
CommandId t_cmax; /* deleting command ID */
TransactionId t_xvac; /* VACUUM FULL xact ID */
} t_field3;
+} HeapTupleFields;
+
+typedef struct DatumTupleFields
+{
+ int32 datum_len; /* required to be a varlena type */
+
+ int32 datum_typmod; /* -1, or identifier of a record type */
+
+ Oid datum_typeid; /* composite type OID, or RECORDOID */
+ /*
+ * Note: field ordering is chosen with thought that Oid might someday
+ * widen to 64 bits.
+ */
+} DatumTupleFields;
+
+typedef struct HeapTupleHeaderData
+{
+ union
+ {
+ HeapTupleFields t_heap;
+ DatumTupleFields t_datum;
+ } t_choice;
ItemPointerData t_ctid; /* current TID of this or newer tuple */
@@ -169,31 +201,31 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
#define HeapTupleHeaderGetXmin(tup) \
( \
- (tup)->t_xmin \
+ (tup)->t_choice.t_heap.t_xmin \
)
#define HeapTupleHeaderSetXmin(tup, xid) \
( \
- TransactionIdStore((xid), &(tup)->t_xmin) \
+ TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmin) \
)
#define HeapTupleHeaderGetXmax(tup) \
( \
((tup)->t_infomask & HEAP_XMAX_IS_XMIN) ? \
- (tup)->t_xmin \
+ (tup)->t_choice.t_heap.t_xmin \
: \
- (tup)->t_field2.t_xmax \
+ (tup)->t_choice.t_heap.t_field2.t_xmax \
)
#define HeapTupleHeaderSetXmax(tup, xid) \
do { \
TransactionId _newxid = (xid); \
- if (TransactionIdEquals((tup)->t_xmin, _newxid)) \
+ if (TransactionIdEquals((tup)->t_choice.t_heap.t_xmin, _newxid)) \
(tup)->t_infomask |= HEAP_XMAX_IS_XMIN; \
else \
{ \
(tup)->t_infomask &= ~HEAP_XMAX_IS_XMIN; \
- TransactionIdStore(_newxid, &(tup)->t_field2.t_xmax); \
+ TransactionIdStore(_newxid, &(tup)->t_choice.t_heap.t_field2.t_xmax); \
} \
} while (0)
@@ -207,13 +239,13 @@ do { \
*/
#define HeapTupleHeaderGetCmin(tup) \
( \
- (tup)->t_field2.t_cmin \
+ (tup)->t_choice.t_heap.t_field2.t_cmin \
)
#define HeapTupleHeaderSetCmin(tup, cid) \
do { \
Assert((tup)->t_infomask & HEAP_XMAX_INVALID); \
- (tup)->t_field2.t_cmin = (cid); \
+ (tup)->t_choice.t_heap.t_field2.t_cmin = (cid); \
} while (0)
/*
@@ -222,19 +254,19 @@ do { \
*/
#define HeapTupleHeaderGetCmax(tup) \
( \
- (tup)->t_field3.t_cmax \
+ (tup)->t_choice.t_heap.t_field3.t_cmax \
)
#define HeapTupleHeaderSetCmax(tup, cid) \
do { \
Assert(!((tup)->t_infomask & HEAP_MOVED)); \
- (tup)->t_field3.t_cmax = (cid); \
+ (tup)->t_choice.t_heap.t_field3.t_cmax = (cid); \
} while (0)
#define HeapTupleHeaderGetXvac(tup) \
( \
((tup)->t_infomask & HEAP_MOVED) ? \
- (tup)->t_field3.t_xvac \
+ (tup)->t_choice.t_heap.t_field3.t_xvac \
: \
InvalidTransactionId \
)
@@ -242,9 +274,39 @@ do { \
#define HeapTupleHeaderSetXvac(tup, xid) \
do { \
Assert((tup)->t_infomask & HEAP_MOVED); \
- TransactionIdStore((xid), &(tup)->t_field3.t_xvac); \
+ TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_field3.t_xvac); \
} while (0)
+#define HeapTupleHeaderGetDatumLength(tup) \
+( \
+ (tup)->t_choice.t_datum.datum_len \
+)
+
+#define HeapTupleHeaderSetDatumLength(tup, len) \
+( \
+ (tup)->t_choice.t_datum.datum_len = (len) \
+)
+
+#define HeapTupleHeaderGetTypeId(tup) \
+( \
+ (tup)->t_choice.t_datum.datum_typeid \
+)
+
+#define HeapTupleHeaderSetTypeId(tup, typeid) \
+( \
+ (tup)->t_choice.t_datum.datum_typeid = (typeid) \
+)
+
+#define HeapTupleHeaderGetTypMod(tup) \
+( \
+ (tup)->t_choice.t_datum.datum_typmod \
+)
+
+#define HeapTupleHeaderSetTypMod(tup, typmod) \
+( \
+ (tup)->t_choice.t_datum.datum_typmod = (typmod) \
+)
+
#define HeapTupleHeaderGetOid(tup) \
( \
((tup)->t_infomask & HEAP_HASOID) ? \
@@ -261,95 +323,10 @@ do { \
/*
- * WAL record definitions for heapam.c's WAL operations
- *
- * XLOG allows to store some information in high 4 bits of log
- * record xl_info field
- */
-#define XLOG_HEAP_INSERT 0x00
-#define XLOG_HEAP_DELETE 0x10
-#define XLOG_HEAP_UPDATE 0x20
-#define XLOG_HEAP_MOVE 0x30
-#define XLOG_HEAP_CLEAN 0x40
-#define XLOG_HEAP_OPMASK 0x70
-/*
- * When we insert 1st item on new page in INSERT/UPDATE
- * we can (and we do) restore entire page in redo
- */
-#define XLOG_HEAP_INIT_PAGE 0x80
-
-/*
- * All what we need to find changed tuple (14 bytes)
- *
- * NB: on most machines, sizeof(xl_heaptid) will include some trailing pad
- * bytes for alignment. We don't want to store the pad space in the XLOG,
- * so use SizeOfHeapTid for space calculations. Similar comments apply for
- * the other xl_FOO structs.
- */
-typedef struct xl_heaptid
-{
- RelFileNode node;
- ItemPointerData tid; /* changed tuple id */
-} xl_heaptid;
-
-#define SizeOfHeapTid (offsetof(xl_heaptid, tid) + SizeOfIptrData)
-
-/* This is what we need to know about delete */
-typedef struct xl_heap_delete
-{
- xl_heaptid target; /* deleted tuple id */
-} xl_heap_delete;
-
-#define SizeOfHeapDelete (offsetof(xl_heap_delete, target) + SizeOfHeapTid)
-
-/*
- * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
- * or updated tuple in WAL; we can save a few bytes by reconstructing the
- * fields that are available elsewhere in the WAL record, or perhaps just
- * plain needn't be reconstructed. These are the fields we must store.
- * NOTE: t_hoff could be recomputed, but we may as well store it because
- * it will come for free due to alignment considerations.
+ * BITMAPLEN(NATTS) -
+ * Computes size of null bitmap given number of data columns.
*/
-typedef struct xl_heap_header
-{
- int16 t_natts;
- uint16 t_infomask;
- uint8 t_hoff;
-} xl_heap_header;
-
-#define SizeOfHeapHeader (offsetof(xl_heap_header, t_hoff) + sizeof(uint8))
-
-/* This is what we need to know about insert */
-typedef struct xl_heap_insert
-{
- xl_heaptid target; /* inserted tuple id */
- /* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */
-} xl_heap_insert;
-
-#define SizeOfHeapInsert (offsetof(xl_heap_insert, target) + SizeOfHeapTid)
-
-/* This is what we need to know about update|move */
-typedef struct xl_heap_update
-{
- xl_heaptid target; /* deleted tuple id */
- ItemPointerData newtid; /* new inserted tuple id */
- /* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */
- /* and TUPLE DATA FOLLOWS AT END OF STRUCT */
-} xl_heap_update;
-
-#define SizeOfHeapUpdate (offsetof(xl_heap_update, newtid) + SizeOfIptrData)
-
-/* This is what we need to know about page cleanup */
-typedef struct xl_heap_clean
-{
- RelFileNode node;
- BlockNumber block;
- /* UNUSED OFFSET NUMBERS FOLLOW AT THE END */
-} xl_heap_clean;
-
-#define SizeOfHeapClean (offsetof(xl_heap_clean, block) + sizeof(BlockNumber))
-
-
+#define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
/*
* MaxTupleSize is the maximum allowed size of a tuple, including header and
@@ -388,6 +365,7 @@ typedef struct xl_heap_clean
#define TableOidAttributeNumber (-7)
#define FirstLowInvalidHeapAttributeNumber (-8)
+
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
*
@@ -417,22 +395,13 @@ typedef HeapTupleData *HeapTuple;
#define HEAPTUPLESIZE MAXALIGN(sizeof(HeapTupleData))
-
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
-
/*
- * BITMAPLEN(NATTS) -
- * Computes size of null bitmap given number of data columns.
- */
-#define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
-
-/*
- * HeapTupleIsValid
- * True iff the heap tuple is valid.
+ * Accessor macros to be used with HeapTuple pointers.
*/
#define HeapTupleIsValid(tuple) PointerIsValid(tuple)
@@ -463,4 +432,94 @@ typedef HeapTupleData *HeapTuple;
#define HeapTupleSetOid(tuple, oid) \
HeapTupleHeaderSetOid((tuple)->t_data, (oid))
+
+/*
+ * WAL record definitions for heapam.c's WAL operations
+ *
+ * XLOG allows to store some information in high 4 bits of log
+ * record xl_info field
+ */
+#define XLOG_HEAP_INSERT 0x00
+#define XLOG_HEAP_DELETE 0x10
+#define XLOG_HEAP_UPDATE 0x20
+#define XLOG_HEAP_MOVE 0x30
+#define XLOG_HEAP_CLEAN 0x40
+#define XLOG_HEAP_OPMASK 0x70
+/*
+ * When we insert 1st item on new page in INSERT/UPDATE
+ * we can (and we do) restore entire page in redo
+ */
+#define XLOG_HEAP_INIT_PAGE 0x80
+
+/*
+ * All what we need to find changed tuple (14 bytes)
+ *
+ * NB: on most machines, sizeof(xl_heaptid) will include some trailing pad
+ * bytes for alignment. We don't want to store the pad space in the XLOG,
+ * so use SizeOfHeapTid for space calculations. Similar comments apply for
+ * the other xl_FOO structs.
+ */
+typedef struct xl_heaptid
+{
+ RelFileNode node;
+ ItemPointerData tid; /* changed tuple id */
+} xl_heaptid;
+
+#define SizeOfHeapTid (offsetof(xl_heaptid, tid) + SizeOfIptrData)
+
+/* This is what we need to know about delete */
+typedef struct xl_heap_delete
+{
+ xl_heaptid target; /* deleted tuple id */
+} xl_heap_delete;
+
+#define SizeOfHeapDelete (offsetof(xl_heap_delete, target) + SizeOfHeapTid)
+
+/*
+ * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
+ * or updated tuple in WAL; we can save a few bytes by reconstructing the
+ * fields that are available elsewhere in the WAL record, or perhaps just
+ * plain needn't be reconstructed. These are the fields we must store.
+ * NOTE: t_hoff could be recomputed, but we may as well store it because
+ * it will come for free due to alignment considerations.
+ */
+typedef struct xl_heap_header
+{
+ int16 t_natts;
+ uint16 t_infomask;
+ uint8 t_hoff;
+} xl_heap_header;
+
+#define SizeOfHeapHeader (offsetof(xl_heap_header, t_hoff) + sizeof(uint8))
+
+/* This is what we need to know about insert */
+typedef struct xl_heap_insert
+{
+ xl_heaptid target; /* inserted tuple id */
+ /* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */
+} xl_heap_insert;
+
+#define SizeOfHeapInsert (offsetof(xl_heap_insert, target) + SizeOfHeapTid)
+
+/* This is what we need to know about update|move */
+typedef struct xl_heap_update
+{
+ xl_heaptid target; /* deleted tuple id */
+ ItemPointerData newtid; /* new inserted tuple id */
+ /* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */
+ /* and TUPLE DATA FOLLOWS AT END OF STRUCT */
+} xl_heap_update;
+
+#define SizeOfHeapUpdate (offsetof(xl_heap_update, newtid) + SizeOfIptrData)
+
+/* This is what we need to know about page cleanup */
+typedef struct xl_heap_clean
+{
+ RelFileNode node;
+ BlockNumber block;
+ /* UNUSED OFFSET NUMBERS FOLLOW AT THE END */
+} xl_heap_clean;
+
+#define SizeOfHeapClean (offsetof(xl_heap_clean, block) + sizeof(BlockNumber))
+
#endif /* HTUP_H */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index bf4e3aa1055..c7e560c0c65 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.42 2003/11/29 22:40:55 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.43 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -44,6 +44,15 @@ typedef struct tupleConstr
/*
* This structure contains all information (i.e. from Classes
* pg_attribute, pg_attrdef, pg_constraint) for the structure of a tuple.
+ *
+ * Note that only user attributes, not system attributes, are mentioned in
+ * TupleDesc; with the exception that tdhasoid indicates if OID is present.
+ *
+ * If the tuple is known to correspond to a named rowtype (such as a table's
+ * rowtype) then tdtypeid identifies that type and tdtypmod is -1. Otherwise
+ * tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous
+ * row type, or a value >= 0 to allow the rowtype to be looked up in the
+ * typcache.c type cache.
*/
typedef struct tupleDesc
{
@@ -51,6 +60,8 @@ typedef struct tupleDesc
Form_pg_attribute *attrs;
/* attrs[N] is a pointer to the description of Attribute Number N+1. */
TupleConstr *constr;
+ Oid tdtypeid; /* composite type ID for tuple type */
+ int32 tdtypmod; /* typmod for tuple type */
bool tdhasoid; /* Tuple has oid attribute in its header */
} *TupleDesc;
@@ -73,8 +84,7 @@ extern void TupleDescInitEntry(TupleDesc desc,
const char *attributeName,
Oid oidtypeid,
int32 typmod,
- int attdim,
- bool attisset);
+ int attdim);
extern TupleDesc BuildDescForRelation(List *schema);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 5344d7afe86..82900e55f34 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.222 2004/03/22 01:38:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.223 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200403211
+#define CATALOG_VERSION_NO 200403291
#endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 754878f0148..896a06ada86 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.108 2004/02/12 23:41:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.109 2004/04/01 21:28:45 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -129,9 +129,6 @@ CATALOG(pg_attribute) BOOTSTRAP BKI_WITHOUT_OIDS
*/
char attstorage;
- /* This flag indicates that the attribute is really a set */
- bool attisset;
-
/*
* attalign is a copy of the typalign field from pg_type for this
* attribute. See atttypid comments above.
@@ -174,7 +171,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 18
+#define Natts_pg_attribute 17
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
@@ -186,13 +183,12 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_atttypmod 9
#define Anum_pg_attribute_attbyval 10
#define Anum_pg_attribute_attstorage 11
-#define Anum_pg_attribute_attisset 12
-#define Anum_pg_attribute_attalign 13
-#define Anum_pg_attribute_attnotnull 14
-#define Anum_pg_attribute_atthasdef 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attisdropped 15
+#define Anum_pg_attribute_attislocal 16
+#define Anum_pg_attribute_attinhcount 17
@@ -226,278 +222,276 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
#define Schema_pg_type \
-{ 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typowner"}, 23, -1, 4, 3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typisdefined"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typdelim"}, 18, -1, 1, 8, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typrelid"}, 26, -1, 4, 9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typelem"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typinput"}, 24, -1, 4, 11, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typoutput"}, 24, -1, 4, 12, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typreceive"}, 24, -1, 4, 13, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typsend"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typanalyze"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typalign"}, 18, -1, 1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typstorage"}, 18, -1, 1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnotnull"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typbasetype"}, 26, -1, 4, 19, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typtypmod"}, 23, -1, 4, 20, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typndims"}, 23, -1, 4, 21, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1247, {"typdefault"}, 25, -1, -1, 23, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-
-DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typowner 23 -1 4 3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typisdefined 16 -1 1 7 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typdelim 18 -1 1 8 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typrelid 26 -1 4 9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typelem 26 -1 4 10 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typinput 24 -1 4 11 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typoutput 24 -1 4 12 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typreceive 24 -1 4 13 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typsend 24 -1 4 14 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typanalyze 24 -1 4 15 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typalign 18 -1 1 16 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typstorage 18 -1 1 17 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typnotnull 16 -1 1 18 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typbasetype 26 -1 4 19 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typtypmod 23 -1 4 20 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typndims 23 -1 4 21 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typdefaultbin 25 -1 -1 22 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1247 typdefault 25 -1 -1 23 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+{ 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typowner"}, 23, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typisdefined"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typdelim"}, 18, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typrelid"}, 26, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typelem"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typinput"}, 24, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typoutput"}, 24, -1, 4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typreceive"}, 24, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typsend"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typanalyze"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typalign"}, 18, -1, 1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typstorage"}, 18, -1, 1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typnotnull"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typbasetype"}, 26, -1, 4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typtypmod"}, 23, -1, 4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typndims"}, 23, -1, 4, 21, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1247, {"typdefault"}, 25, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+
+DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typowner 23 -1 4 3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typisdefined 16 -1 1 7 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typdelim 18 -1 1 8 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typrelid 26 -1 4 9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typelem 26 -1 4 10 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typinput 24 -1 4 11 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typoutput 24 -1 4 12 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typreceive 24 -1 4 13 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typsend 24 -1 4 14 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typanalyze 24 -1 4 15 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typalign 18 -1 1 16 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typstorage 18 -1 1 17 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typnotnull 16 -1 1 18 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typbasetype 26 -1 4 19 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typtypmod 23 -1 4 20 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typndims 23 -1 4 21 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typdefaultbin 25 -1 -1 22 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 typdefault 25 -1 -1 23 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_database
* ----------------
*/
-DATA(insert ( 1262 datname 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 datdba 23 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 encoding 23 -1 4 3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datistemplate 16 -1 1 4 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1262 datallowconn 16 -1 1 5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1262 datlastsysoid 26 -1 4 6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datvacuumxid 28 -1 4 7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datfrozenxid 28 -1 4 8 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1262 datname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1262 datdba 23 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 encoding 23 -1 4 3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datistemplate 16 -1 1 4 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1262 datallowconn 16 -1 1 5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1262 datlastsysoid 26 -1 4 6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datvacuumxid 28 -1 4 7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datfrozenxid 28 -1 4 8 0 -1 -1 t p i t f f t 0));
/* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */
-DATA(insert ( 1262 datpath 25 -1 -1 9 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 datconfig 1009 -1 -1 10 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1262 datacl 1034 -1 -1 11 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 oid 26 0 4 -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1262 datpath 25 -1 -1 9 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1262 datconfig 1009 -1 -1 10 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1262 datacl 1034 -1 -1 11 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1262 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_proc
* ----------------
*/
#define Schema_pg_proc \
-{ 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proowner"}, 23, -1, 4, 3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proisagg"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"prosecdef"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"proisstrict"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"proretset"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"provolatile"}, 18, -1, 1, 9, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"pronargs"}, 21, -1, 2, 10, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1255, {"prorettype"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proargtypes"}, 30, -1, INDEX_MAX_KEYS*4, 12, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proargnames"}, 1009, -1, -1, 13, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"prosrc"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"probin"}, 17, -1, -1, 15, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"proacl"}, 1034, -1, -1, 16, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proowner 23 -1 4 3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proisagg 16 -1 1 5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 prosecdef 16 -1 1 6 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 proisstrict 16 -1 1 7 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 proretset 16 -1 1 8 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 provolatile 18 -1 1 9 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 pronargs 21 -1 2 10 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1255 prorettype 26 -1 4 11 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proargtypes 30 -1 INDEX_MAX_KEYS*4 12 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 proargnames 1009 -1 -1 13 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 prosrc 25 -1 -1 14 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 probin 17 -1 -1 15 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 proacl 1034 -1 -1 16 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+{ 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proowner"}, 23, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proisagg"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"prosecdef"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"proisstrict"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"proretset"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"provolatile"}, 18, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"pronargs"}, 21, -1, 2, 10, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1255, {"prorettype"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proargtypes"}, 30, -1, INDEX_MAX_KEYS*4, 12, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proargnames"}, 1009, -1, -1, 13, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"prosrc"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"probin"}, 17, -1, -1, 15, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"proacl"}, 1034, -1, -1, 16, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proowner 23 -1 4 3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proisagg 16 -1 1 5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 prosecdef 16 -1 1 6 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 proisstrict 16 -1 1 7 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 proretset 16 -1 1 8 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 provolatile 18 -1 1 9 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 pronargs 21 -1 2 10 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1255 prorettype 26 -1 4 11 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proargtypes 30 -1 INDEX_MAX_KEYS*4 12 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1255 proargnames 1009 -1 -1 13 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 prosrc 25 -1 -1 14 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 probin 17 -1 -1 15 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 proacl 1034 -1 -1 16 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_shadow
* ----------------
*/
-DATA(insert ( 1260 usename 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1260 usesysid 23 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 usecreatedb 16 -1 1 3 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 usesuper 16 -1 1 4 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 usecatupd 16 -1 1 5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 passwd 25 -1 -1 6 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1260 valuntil 702 -1 4 7 0 -1 -1 t p f i f f f t 0));
-DATA(insert ( 1260 useconfig 1009 -1 -1 8 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1260 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
+DATA(insert ( 1260 usename 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1260 usesysid 23 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 usecreatedb 16 -1 1 3 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 usesuper 16 -1 1 4 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 usecatupd 16 -1 1 5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 passwd 25 -1 -1 6 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1260 valuntil 702 -1 4 7 0 -1 -1 t p i f f f t 0));
+DATA(insert ( 1260 useconfig 1009 -1 -1 8 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1260 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
/* no OIDs in pg_shadow */
-DATA(insert ( 1260 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1260 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_group
* ----------------
*/
-DATA(insert ( 1261 groname 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1261 grosysid 23 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 grolist 1007 -1 -1 3 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1261 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
+DATA(insert ( 1261 groname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1261 grosysid 23 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 grolist 1007 -1 -1 3 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1261 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
/* no OIDs in pg_group */
-DATA(insert ( 1261 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1261 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_attribute
* ----------------
*/
#define Schema_pg_attribute \
-{ 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attisset"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attalign"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attnotnull"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"atthasdef"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attisdropped"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attislocal"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attinhcount"}, 23, -1, 4, 18, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }
-
-DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attisset 16 -1 1 12 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attalign 18 -1 1 13 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attnotnull 16 -1 1 14 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 atthasdef 16 -1 1 15 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attisdropped 16 -1 1 16 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attislocal 16 -1 1 17 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attinhcount 23 -1 4 18 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
+{ 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attalign"}, 18, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attnotnull"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"atthasdef"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attisdropped"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attislocal"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attinhcount"}, 23, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }
+
+DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attalign 18 -1 1 12 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attnotnull 16 -1 1 13 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 atthasdef 16 -1 1 14 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attisdropped 16 -1 1 15 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attislocal 16 -1 1 16 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attinhcount 23 -1 4 17 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
/* no OIDs in pg_attribute */
-DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_class
* ----------------
*/
#define Schema_pg_class \
-{ 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relowner"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relpages"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltuples"}, 700, -1, 4, 8, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastrelid"}, 26, -1, 4, 9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastidxid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relhasindex"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relisshared"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relkind"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relnatts"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relchecks"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"reltriggers"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relukeys"}, 21, -1, 2, 17, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relfkeys"}, 21, -1, 2, 18, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relrefs"}, 21, -1, 2, 19, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relhasoids"}, 16, -1, 1, 20, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhaspkey"}, 16, -1, 1, 21, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhasrules"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhassubclass"},16, -1, 1, 23, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relacl"}, 1034, -1, -1, 24, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relowner 23 -1 4 4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relpages 23 -1 4 7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltuples 700 -1 4 8 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 reltoastrelid 26 -1 4 9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltoastidxid 26 -1 4 10 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relhasindex 16 -1 1 11 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relisshared 16 -1 1 12 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relkind 18 -1 1 13 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relnatts 21 -1 2 14 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relchecks 21 -1 2 15 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 reltriggers 21 -1 2 16 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relukeys 21 -1 2 17 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relfkeys 21 -1 2 18 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relrefs 21 -1 2 19 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relhasoids 16 -1 1 20 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhaspkey 16 -1 1 21 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhasrules 16 -1 1 22 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhassubclass 16 -1 1 23 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relacl 1034 -1 -1 24 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
+{ 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relowner"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relpages"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltuples"}, 700, -1, 4, 8, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastrelid"}, 26, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastidxid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relhasindex"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relisshared"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relkind"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relnatts"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relchecks"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"reltriggers"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relukeys"}, 21, -1, 2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relfkeys"}, 21, -1, 2, 18, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relrefs"}, 21, -1, 2, 19, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relhasoids"}, 16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhaspkey"}, 16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhasrules"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhassubclass"},16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relacl"}, 1034, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relowner 23 -1 4 4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relpages 23 -1 4 7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltuples 700 -1 4 8 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1259 reltoastrelid 26 -1 4 9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltoastidxid 26 -1 4 10 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relhasindex 16 -1 1 11 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relisshared 16 -1 1 12 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relkind 18 -1 1 13 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relnatts 21 -1 2 14 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relchecks 21 -1 2 15 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 reltriggers 21 -1 2 16 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relukeys 21 -1 2 17 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relfkeys 21 -1 2 18 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relrefs 21 -1 2 19 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relhasoids 16 -1 1 20 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhaspkey 16 -1 1 21 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhasrules 16 -1 1 22 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhassubclass 16 -1 1 23 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relacl 1034 -1 -1 24 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
/* ----------------
* pg_xactlock - this is not a real relation, but is a placeholder
@@ -507,6 +501,6 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p f i t f f t 0));
* table; and this entry is just to link to that one.
* ----------------
*/
-DATA(insert ( 376 xactlockfoo 26 0 4 1 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 376 xactlockfoo 26 0 4 1 0 -1 -1 t p i t f f t 0));
#endif /* PG_ATTRIBUTE_H */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index eafe5ceb328..8f044b0459a 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.80 2004/02/12 23:41:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.81 2004/04/01 21:28:45 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -136,7 +136,7 @@ typedef FormData_pg_class *Form_pg_class;
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 18 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 f f r 16 0 0 0 0 0 t f f f _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 739e1b0da64..7da13b33c90 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.322 2004/03/22 01:38:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.323 2004/04/01 21:28:45 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -1334,8 +1334,6 @@ DATA(insert OID = 1062 ( aclitemeq PGNSP PGUID 12 f f t f i 2 16 "1033 1033
DESCR("equality operator for ACL items");
DATA(insert OID = 1365 ( makeaclitem PGNSP PGUID 12 f f t f i 5 1033 "23 23 23 25 16" _null_ makeaclitem - _null_ ));
DESCR("make ACL item");
-DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" _null_ seteval - _null_ ));
-DESCR("internal function supporting PostQuel-style sets");
DATA(insert OID = 1044 ( bpcharin PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" _null_ bpcharin - _null_ ));
DESCR("I/O");
DATA(insert OID = 1045 ( bpcharout PGNSP PGUID 12 f f t f i 1 2275 "1042" _null_ bpcharout - _null_ ));
@@ -3155,9 +3153,9 @@ DATA(insert OID = 2273 ( has_schema_privilege PGNSP PGUID 12 f f t f s 2 16
DESCR("current user privilege on schema by schema oid");
-DATA(insert OID = 2290 ( record_in PGNSP PGUID 12 f f t f i 1 2249 "2275" _null_ record_in - _null_ ));
+DATA(insert OID = 2290 ( record_in PGNSP PGUID 12 f f t f v 1 2249 "2275" _null_ record_in - _null_ ));
DESCR("I/O");
-DATA(insert OID = 2291 ( record_out PGNSP PGUID 12 f f t f i 1 2275 "2249" _null_ record_out - _null_ ));
+DATA(insert OID = 2291 ( record_out PGNSP PGUID 12 f f t f v 1 2275 "2249" _null_ record_out - _null_ ));
DESCR("I/O");
DATA(insert OID = 2292 ( cstring_in PGNSP PGUID 12 f f t f i 1 2275 "2275" _null_ cstring_in - _null_ ));
DESCR("I/O");
@@ -3298,9 +3296,9 @@ DATA(insert OID = 2400 ( array_recv PGNSP PGUID 12 f f t f s 2 2277 "2281 2
DESCR("I/O");
DATA(insert OID = 2401 ( array_send PGNSP PGUID 12 f f t f s 2 17 "2277 26" _null_ array_send - _null_ ));
DESCR("I/O");
-DATA(insert OID = 2402 ( record_recv PGNSP PGUID 12 f f t f i 1 2249 "2281" _null_ record_recv - _null_ ));
+DATA(insert OID = 2402 ( record_recv PGNSP PGUID 12 f f t f v 1 2249 "2281" _null_ record_recv - _null_ ));
DESCR("I/O");
-DATA(insert OID = 2403 ( record_send PGNSP PGUID 12 f f t f i 1 17 "2249" _null_ record_send - _null_ ));
+DATA(insert OID = 2403 ( record_send PGNSP PGUID 12 f f t f v 1 17 "2249" _null_ record_send - _null_ ));
DESCR("I/O");
DATA(insert OID = 2404 ( int2recv PGNSP PGUID 12 f f t f i 1 21 "2281" _null_ int2recv - _null_ ));
DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 8cbc0aa8e67..9f68e22fdb8 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.151 2004/03/19 18:58:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.152 2004/04/01 21:28:45 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -268,7 +268,7 @@ DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b t \054 0 0 int2in int2out int2
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0 21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0 21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - s p f 0 -1 0 _null_ _null_ ));
DESCR("array of INDEX_MAX_KEYS int2 integers, used in system tables");
#define INT2VECTOROID 22
@@ -288,7 +288,7 @@ DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b t \054 0 0 oidin oidout oidrec
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b t \054 0 0 tidin tidout tidrecv tidsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b t \054 0 0 tidin tidout tidrecv tidsend - s p f 0 -1 0 _null_ _null_ ));
DESCR("(Block, offset), physical location of tuple");
#define TIDOID 27
@@ -307,13 +307,13 @@ DESCR("array of INDEX_MAX_KEYS oids, used in system tables");
DATA(insert OID = 32 ( SET PGNSP PGUID -1 f b t \054 0 0 unknownin unknownout - - - i p f 0 -1 0 _null_ _null_ ));
DESCR("set of tuples");
-DATA(insert OID = 71 ( pg_type PGNSP PGUID 4 t c t \054 1247 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID 4 t c t \054 1249 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID 4 t c t \054 1255 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID 4 t c t \054 1259 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 86 ( pg_shadow PGNSP PGUID 4 t c t \054 1260 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 87 ( pg_group PGNSP PGUID 4 t c t \054 1261 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 88 ( pg_database PGNSP PGUID 4 t c t \054 1262 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 86 ( pg_shadow PGNSP PGUID -1 f c t \054 1260 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 87 ( pg_group PGNSP PGUID -1 f c t \054 1261 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 88 ( pg_database PGNSP PGUID -1 f c t \054 1262 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
@@ -526,7 +526,7 @@ DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b t \054 0 2206 array_in a
* argument and result types (if supported by the function's implementation
* language).
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID 4 t p t \054 0 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p t \054 0 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - c p f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 1eb158440b1..a3088ca7f62 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.108 2004/03/17 01:02:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.109 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -120,9 +120,9 @@ extern void ExecEndNode(PlanState *node);
/*
* prototypes from functions in execQual.c
*/
-extern Datum GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
+extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
bool *isNull);
-extern Datum GetAttributeByName(TupleTableSlot *slot, char *attname,
+extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
bool *isNull);
extern void init_fcache(Oid foid, FuncExprState *fcache,
MemoryContext fcacheCxt);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index ac2b494b532..2e477e70f87 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -2,7 +2,7 @@
*
* spi.h
*
- * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.43 2004/03/17 01:05:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.44 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -95,9 +95,7 @@ extern int SPI_getargcount(void *plan);
extern bool SPI_is_cursor_plan(void *plan);
extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern TupleDesc SPI_copytupledesc(TupleDesc tupdesc);
-extern TupleTableSlot *SPI_copytupleintoslot(HeapTuple tuple,
- TupleDesc tupdesc);
+extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 6ed389e8f95..acdcb4ff475 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.33 2004/01/19 02:06:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.34 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -192,11 +192,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
#define DatumGetTextP(X) ((text *) PG_DETOAST_DATUM(X))
#define DatumGetBpCharP(X) ((BpChar *) PG_DETOAST_DATUM(X))
#define DatumGetVarCharP(X) ((VarChar *) PG_DETOAST_DATUM(X))
+#define DatumGetHeapTupleHeader(X) ((HeapTupleHeader) PG_DETOAST_DATUM(X))
/* And we also offer variants that return an OK-to-write copy */
#define DatumGetByteaPCopy(X) ((bytea *) PG_DETOAST_DATUM_COPY(X))
#define DatumGetTextPCopy(X) ((text *) PG_DETOAST_DATUM_COPY(X))
#define DatumGetBpCharPCopy(X) ((BpChar *) PG_DETOAST_DATUM_COPY(X))
#define DatumGetVarCharPCopy(X) ((VarChar *) PG_DETOAST_DATUM_COPY(X))
+#define DatumGetHeapTupleHeaderCopy(X) ((HeapTupleHeader) PG_DETOAST_DATUM_COPY(X))
/* Variants which return n bytes starting at pos. m */
#define DatumGetByteaPSlice(X,m,n) ((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n))
#define DatumGetTextPSlice(X,m,n) ((text *) PG_DETOAST_DATUM_SLICE(X,m,n))
@@ -207,11 +209,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
#define PG_GETARG_TEXT_P(n) DatumGetTextP(PG_GETARG_DATUM(n))
#define PG_GETARG_BPCHAR_P(n) DatumGetBpCharP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARCHAR_P(n) DatumGetVarCharP(PG_GETARG_DATUM(n))
+#define PG_GETARG_HEAPTUPLEHEADER(n) DatumGetHeapTupleHeader(PG_GETARG_DATUM(n))
/* And we also offer variants that return an OK-to-write copy */
#define PG_GETARG_BYTEA_P_COPY(n) DatumGetByteaPCopy(PG_GETARG_DATUM(n))
#define PG_GETARG_TEXT_P_COPY(n) DatumGetTextPCopy(PG_GETARG_DATUM(n))
#define PG_GETARG_BPCHAR_P_COPY(n) DatumGetBpCharPCopy(PG_GETARG_DATUM(n))
#define PG_GETARG_VARCHAR_P_COPY(n) DatumGetVarCharPCopy(PG_GETARG_DATUM(n))
+#define PG_GETARG_HEAPTUPLEHEADER_COPY(n) DatumGetHeapTupleHeaderCopy(PG_GETARG_DATUM(n))
/* And a b-byte slice from position a -also OK to write */
#define PG_GETARG_BYTEA_P_SLICE(n,a,b) DatumGetByteaPSlice(PG_GETARG_DATUM(n),a,b)
#define PG_GETARG_TEXT_P_SLICE(n,a,b) DatumGetTextPSlice(PG_GETARG_DATUM(n),a,b)
@@ -246,6 +250,7 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
#define PG_RETURN_TEXT_P(x) PG_RETURN_POINTER(x)
#define PG_RETURN_BPCHAR_P(x) PG_RETURN_POINTER(x)
#define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
+#define PG_RETURN_HEAPTUPLEHEADER(x) PG_RETURN_POINTER(x)
/*-------------------------------------------------------------------------
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index a8051fc27f0..fd1660423e1 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,7 +9,7 @@
*
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.10 2003/11/29 22:40:53 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.11 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -57,7 +57,7 @@ typedef struct AttInMetadata
typedef struct FuncCallContext
{
/*
- * Number of times we've been called before.
+ * Number of times we've been called before
*
* call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
* incremented for you every time SRF_RETURN_NEXT() is called.
@@ -67,7 +67,7 @@ typedef struct FuncCallContext
/*
* OPTIONAL maximum number of calls
*
- * max_calls is here for convenience ONLY and setting it is OPTIONAL. If
+ * max_calls is here for convenience only and setting it is optional. If
* not set, you must provide alternative means to know when the
* function is done.
*/
@@ -76,41 +76,50 @@ typedef struct FuncCallContext
/*
* OPTIONAL pointer to result slot
*
- * slot is for use when returning tuples (i.e. composite data types) and
- * is not needed when returning base (i.e. scalar) data types.
+ * This is obsolete and only present for backwards compatibility, viz,
+ * user-defined SRFs that use the deprecated TupleDescGetSlot().
*/
TupleTableSlot *slot;
/*
- * OPTIONAL pointer to misc user provided context info
+ * OPTIONAL pointer to miscellaneous user-provided context information
*
* user_fctx is for use as a pointer to your own struct to retain
- * arbitrary context information between calls for your function.
+ * arbitrary context information between calls of your function.
*/
void *user_fctx;
/*
- * OPTIONAL pointer to struct containing arrays of attribute type
- * input metainfo
+ * OPTIONAL pointer to struct containing attribute type input metadata
*
* attinmeta is for use when returning tuples (i.e. composite data types)
- * and is not needed when returning base (i.e. scalar) data types. It
- * is ONLY needed if you intend to use BuildTupleFromCStrings() to
- * create the return tuple.
+ * and is not used when returning base data types. It is only needed
+ * if you intend to use BuildTupleFromCStrings() to create the return
+ * tuple.
*/
AttInMetadata *attinmeta;
/*
- * memory context used for structures which must live for multiple
- * calls
+ * memory context used for structures that must live for multiple calls
*
* multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
* by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
- * context for any memory that is to be re-used across multiple calls
+ * context for any memory that is to be reused across multiple calls
* of the SRF.
*/
MemoryContext multi_call_memory_ctx;
+ /*
+ * OPTIONAL pointer to struct containing tuple description
+ *
+ * tuple_desc is for use when returning tuples (i.e. composite data types)
+ * and is only needed if you are going to build the tuples with
+ * heap_formtuple() rather than with BuildTupleFromCStrings(). Note that
+ * the TupleDesc pointer stored here should usually have been run through
+ * BlessTupleDesc() first.
+ */
+ TupleDesc tuple_desc;
+
} FuncCallContext;
/*----------
@@ -122,38 +131,43 @@ typedef struct FuncCallContext
* TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use to get a
* TupleDesc based on a type OID. This can be used to get
* a TupleDesc for a base (scalar) or composite (relation) type.
- * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
- * given a TupleDesc.
+ * TupleDesc BlessTupleDesc(TupleDesc tupdesc) - "Bless" a completed tuple
+ * descriptor so that it can be used to return properly labeled tuples.
+ * You need to call this if you are going to use heap_formtuple directly.
+ * TupleDescGetAttInMetadata does it for you, however, so no need to call
+ * it if you call TupleDescGetAttInMetadata.
* AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Build an
* AttInMetadata struct based on the given TupleDesc. AttInMetadata can
* be used in conjunction with C strings to produce a properly formed
- * tuple. Store the metadata here for use across calls to avoid redundant
- * work.
+ * tuple.
* HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -
* build a HeapTuple given user data in C string form. values is an array
* of C strings, one for each attribute of the return tuple.
*
* Macro declarations:
+ * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
+ *
+ * Obsolete routines and macros:
+ * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Builds a
+ * TupleTableSlot, which is not needed anymore.
* TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
* given a tuple and a slot.
*----------
*/
+#define HeapTupleGetDatum(_tuple) PointerGetDatum((_tuple)->t_data)
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) PointerGetDatum((_tuple)->t_data)
+
/* from tupdesc.c */
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
-extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
+extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
-
-/*
- * Note we pass shouldFree = false; this is needed because the tuple will
- * typically be in a shorter-lived memory context than the TupleTableSlot.
- */
-#define TupleGetDatum(_slot, _tuple) \
- PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, false))
+extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
/*----------
@@ -176,8 +190,7 @@ extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
* oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
* <user defined code>
* <if returning composite>
- * <obtain slot>
- * funcctx->slot = slot;
+ * <build TupleDesc, and perhaps AttInMetaData>
* <endif returning composite>
* <user defined code>
* // return to original context when allocating transient memory
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 440fcc4d576..8a0fbf7be0f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.114 2004/03/17 20:48:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.115 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -564,6 +564,17 @@ typedef struct SubPlanState
} SubPlanState;
/* ----------------
+ * FieldSelectState node
+ * ----------------
+ */
+typedef struct FieldSelectState
+{
+ ExprState xprstate;
+ ExprState *arg; /* input expression */
+ TupleDesc argdesc; /* tupdesc for most recent input */
+} FieldSelectState;
+
+/* ----------------
* CaseExprState node
* ----------------
*/
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5c34b8d8559..d5d4f0832a7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.151 2004/03/17 20:48:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.152 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -141,6 +141,7 @@ typedef enum NodeTag
T_ScalarArrayOpExprState,
T_BoolExprState,
T_SubPlanState,
+ T_FieldSelectState,
T_CaseExprState,
T_CaseWhenState,
T_ArrayExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index d94196d7400..567310fa1c3 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.96 2004/03/17 20:48:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.97 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -525,9 +525,8 @@ typedef struct SubPlan
* FieldSelect
*
* FieldSelect represents the operation of extracting one field from a tuple
- * value. At runtime, the input expression is expected to yield a Datum
- * that contains a pointer-to-TupleTableSlot. The specified field number
- * is extracted and returned as a Datum.
+ * value. At runtime, the input expression is expected to yield a rowtype
+ * Datum. The specified field number is extracted and returned as a Datum.
* ----------------
*/
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b7c1f5fea77..02e6cbca49d 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.235 2004/03/15 03:29:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.236 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -384,10 +384,6 @@ extern Datum oidvectorge(PG_FUNCTION_ARGS);
extern Datum oidvectorgt(PG_FUNCTION_ARGS);
/* pseudotypes.c */
-extern Datum record_in(PG_FUNCTION_ARGS);
-extern Datum record_out(PG_FUNCTION_ARGS);
-extern Datum record_recv(PG_FUNCTION_ARGS);
-extern Datum record_send(PG_FUNCTION_ARGS);
extern Datum cstring_in(PG_FUNCTION_ARGS);
extern Datum cstring_out(PG_FUNCTION_ARGS);
extern Datum cstring_recv(PG_FUNCTION_ARGS);
@@ -452,6 +448,12 @@ extern List *stringToQualifiedNameList(const char *string, const char *caller);
extern char *format_procedure(Oid procedure_oid);
extern char *format_operator(Oid operator_oid);
+/* rowtypes.c */
+extern Datum record_in(PG_FUNCTION_ARGS);
+extern Datum record_out(PG_FUNCTION_ARGS);
+extern Datum record_recv(PG_FUNCTION_ARGS);
+extern Datum record_send(PG_FUNCTION_ARGS);
+
/* ruleutils.c */
extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
extern Datum pg_get_ruledef_ext(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/sets.h b/src/include/utils/sets.h
deleted file mode 100644
index 82350f2806a..00000000000
--- a/src/include/utils/sets.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sets.h
- *
- *
- *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $PostgreSQL: pgsql/src/include/utils/sets.h,v 1.16 2003/11/29 22:41:16 pgsql Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef SETS_H
-#define SETS_H
-
-#include "fmgr.h"
-
-
-/* Temporary name of a set function, before SetDefine changes it. */
-#define GENERICSETNAME "ZYX#Set#ZYX"
-
-extern Oid SetDefine(char *querystr, Oid elemType);
-
-extern Datum seteval(PG_FUNCTION_ARGS);
-
-#endif /* SETS_H */
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 93f3f163c46..8b85f517000 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -9,13 +9,14 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.2 2003/11/29 22:41:16 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.3 2004/04/01 21:28:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef TYPCACHE_H
#define TYPCACHE_H
+#include "access/tupdesc.h"
#include "fmgr.h"
@@ -28,6 +29,8 @@ typedef struct TypeCacheEntry
int16 typlen;
bool typbyval;
char typalign;
+ char typtype;
+ Oid typrelid;
/*
* Information obtained from opclass entries
@@ -51,6 +54,13 @@ typedef struct TypeCacheEntry
*/
FmgrInfo eq_opr_finfo;
FmgrInfo cmp_proc_finfo;
+
+ /*
+ * Tuple descriptor if it's a composite type (row type). NULL if not
+ * composite or information hasn't yet been requested. (NOTE: this
+ * is actually just a link to information maintained by relcache.c.)
+ */
+ TupleDesc tupDesc;
} TypeCacheEntry;
/* Bit flags to indicate which fields a given caller needs to have set */
@@ -60,7 +70,14 @@ typedef struct TypeCacheEntry
#define TYPECACHE_CMP_PROC 0x0008
#define TYPECACHE_EQ_OPR_FINFO 0x0010
#define TYPECACHE_CMP_PROC_FINFO 0x0020
+#define TYPECACHE_TUPDESC 0x0040
extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);
+extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
+
+extern void assign_record_type_typmod(TupleDesc tupDesc);
+
+extern void flush_rowtype_cache(Oid type_id);
+
#endif /* TYPCACHE_H */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index f7b690f71ce..8c3680ae19c 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -33,7 +33,7 @@
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.42 2004/01/06 23:55:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.43 2004/04/01 21:28:46 tgl Exp $
*
**********************************************************************/
@@ -45,15 +45,16 @@
#include <setjmp.h>
/* postgreSQL stuff */
-#include "executor/spi.h"
-#include "commands/trigger.h"
-#include "fmgr.h"
#include "access/heapam.h"
-#include "tcop/tcopprot.h"
-#include "utils/syscache.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+#include "commands/trigger.h"
+#include "executor/spi.h"
+#include "fmgr.h"
+#include "tcop/tcopprot.h"
+#include "utils/syscache.h"
+#include "utils/typcache.h"
/* perl stuff */
#include "EXTERN.h"
@@ -82,7 +83,7 @@ typedef struct plperl_proc_desc
int nargs;
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
Oid arg_out_elem[FUNC_MAX_ARGS];
- int arg_is_rel[FUNC_MAX_ARGS];
+ bool arg_is_rowtype[FUNC_MAX_ARGS];
SV *reference;
} plperl_proc_desc;
@@ -388,19 +389,34 @@ plperl_call_perl_func(plperl_proc_desc * desc, FunctionCallInfo fcinfo)
PUSHMARK(SP);
for (i = 0; i < desc->nargs; i++)
{
- if (desc->arg_is_rel[i])
+ if (desc->arg_is_rowtype[i])
{
- TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
- SV *hashref;
-
- Assert(slot != NULL && !fcinfo->argnull[i]);
-
- /*
- * plperl_build_tuple_argument better return a mortal SV.
- */
- hashref = plperl_build_tuple_argument(slot->val,
- slot->ttc_tupleDescriptor);
- XPUSHs(hashref);
+ if (fcinfo->argnull[i])
+ XPUSHs(&PL_sv_undef);
+ else
+ {
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+ SV *hashref;
+
+ td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ tmptup.t_data = td;
+
+ /*
+ * plperl_build_tuple_argument better return a mortal SV.
+ */
+ hashref = plperl_build_tuple_argument(&tmptup, tupdesc);
+ XPUSHs(hashref);
+ }
}
else
{
@@ -645,7 +661,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
}
}
- if (typeStruct->typrelid != InvalidOid)
+ if (typeStruct->typtype == 'c')
{
free(prodesc->proname);
free(prodesc);
@@ -692,13 +708,16 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
format_type_be(procStruct->proargtypes[i]))));
}
- if (typeStruct->typrelid != InvalidOid)
- prodesc->arg_is_rel[i] = 1;
+ if (typeStruct->typtype == 'c')
+ prodesc->arg_is_rowtype[i] = true;
else
- prodesc->arg_is_rel[i] = 0;
+ {
+ prodesc->arg_is_rowtype[i] = false;
+ perm_fmgr_info(typeStruct->typoutput,
+ &(prodesc->arg_out_func[i]));
+ prodesc->arg_out_elem[i] = typeStruct->typelem;
+ }
- perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
- prodesc->arg_out_elem[i] = typeStruct->typelem;
ReleaseSysCache(typeTup);
}
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b984f0932bb..b71a71bbaac 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.98 2004/03/17 20:48:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.99 2004/04/01 21:28:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -52,6 +52,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/typcache.h"
static const char *const raise_skip_msg = "RAISE";
@@ -258,16 +259,26 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
case PLPGSQL_DTYPE_ROW:
{
PLpgSQL_row *row = (PLpgSQL_row *) estate.datums[n];
- TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
- HeapTuple tup;
- TupleDesc tupdesc;
if (!fcinfo->argnull[i])
{
- Assert(slot != NULL);
- tup = slot->val;
- tupdesc = slot->ttc_tupleDescriptor;
- exec_move_row(&estate, NULL, row, tup, tupdesc);
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+
+ td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = td;
+ exec_move_row(&estate, NULL, row, &tmptup, tupdesc);
}
else
{
@@ -371,11 +382,10 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
{
if (estate.retistuple)
{
- /* Copy tuple to upper executor memory */
- /* Here we need to return a TupleTableSlot not just a tuple */
- estate.retval = (Datum)
- SPI_copytupleintoslot((HeapTuple) (estate.retval),
- estate.rettupdesc);
+ /* Copy tuple to upper executor memory, as a tuple Datum */
+ estate.retval =
+ PointerGetDatum(SPI_returntuple((HeapTuple) (estate.retval),
+ estate.rettupdesc));
}
else
{
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 0da1399d836..010f1e1645e 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -29,26 +29,19 @@
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.45 2004/01/07 18:56:30 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.46 2004/04/01 21:28:46 tgl Exp $
*
*********************************************************************
*/
#include "postgres.h"
-/* system stuff
- */
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
+/* system stuff */
#include <unistd.h>
#include <fcntl.h>
-#include <string.h>
#include <setjmp.h>
-/* postgreSQL stuff
- */
+/* postgreSQL stuff */
#include "access/heapam.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -59,6 +52,7 @@
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
#include <Python.h>
#include <compile.h>
@@ -121,7 +115,14 @@ typedef struct PLyTypeInfo
{
PLyTypeInput in;
PLyTypeOutput out;
- int is_rel;
+ int is_rowtype;
+ /*
+ * is_rowtype can be:
+ * -1 not known yet (initial state)
+ * 0 scalar datatype
+ * 1 rowtype
+ * 2 rowtype, but I/O functions not set up yet
+ */
} PLyTypeInfo;
@@ -916,16 +917,40 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
args = PyList_New(proc->nargs);
for (i = 0; i < proc->nargs; i++)
{
- if (proc->args[i].is_rel == 1)
+ if (proc->args[i].is_rowtype > 0)
{
- TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
- arg = PLyDict_FromTuple(&(proc->args[i]), slot->val,
- slot->ttc_tupleDescriptor);
+ if (fcinfo->argnull[i])
+ arg = NULL;
+ else
+ {
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+
+ td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+ /* Set up I/O funcs if not done yet */
+ if (proc->args[i].is_rowtype != 1)
+ PLy_input_tuple_funcs(&(proc->args[i]), tupdesc);
+
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ tmptup.t_data = td;
+
+ arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+ }
}
else
{
- if (!fcinfo->argnull[i])
+ if (fcinfo->argnull[i])
+ arg = NULL;
+ else
{
char *ct;
Datum dt;
@@ -938,8 +963,6 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
arg = (proc->args[i].in.d.func) (ct);
pfree(ct);
}
- else
- arg = NULL;
}
if (arg == NULL)
@@ -1096,7 +1119,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
procStruct->prorettype);
rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
- if (rvTypeStruct->typrelid == InvalidOid)
+ if (rvTypeStruct->typtype != 'c')
PLy_output_datum_func(&proc->result, rvTypeStruct);
else
ereport(ERROR,
@@ -1135,17 +1158,12 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
procStruct->proargtypes[i]);
argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
- if (argTypeStruct->typrelid == InvalidOid)
+ if (argTypeStruct->typtype != 'c')
PLy_input_datum_func(&(proc->args[i]),
procStruct->proargtypes[i],
argTypeStruct);
else
- {
- TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
- PLy_input_tuple_funcs(&(proc->args[i]),
- slot->ttc_tupleDescriptor);
- }
+ proc->args[i].is_rowtype = 2; /* still need to set I/O funcs */
ReleaseSysCache(argTypeTup);
}
@@ -1279,7 +1297,7 @@ PLy_procedure_delete(PLyProcedure * proc)
if (proc->pyname)
PLy_free(proc->pyname);
for (i = 0; i < proc->nargs; i++)
- if (proc->args[i].is_rel == 1)
+ if (proc->args[i].is_rowtype == 1)
{
if (proc->args[i].in.r.atts)
PLy_free(proc->args[i].in.r.atts);
@@ -1300,10 +1318,10 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
enter();
- if (arg->is_rel == 0)
+ if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
- arg->is_rel = 1;
+ arg->is_rowtype = 1;
arg->in.r.natts = desc->natts;
arg->in.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb));
@@ -1338,10 +1356,10 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
enter();
- if (arg->is_rel == 0)
+ if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
- arg->is_rel = 1;
+ arg->is_rowtype = 1;
arg->out.r.natts = desc->natts;
arg->out.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb));
@@ -1372,9 +1390,9 @@ PLy_output_datum_func(PLyTypeInfo * arg, Form_pg_type typeStruct)
{
enter();
- if (arg->is_rel == 1)
+ if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
- arg->is_rel = 0;
+ arg->is_rowtype = 0;
PLy_output_datum_func2(&(arg->out.d), typeStruct);
}
@@ -1393,9 +1411,9 @@ PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, Form_pg_type typeStruct)
{
enter();
- if (arg->is_rel == 1)
+ if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
- arg->is_rel = 0;
+ arg->is_rowtype = 0;
PLy_input_datum_func2(&(arg->in.d), typeOid, typeStruct);
}
@@ -1434,7 +1452,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, Form_pg_type typeStruct)
void
PLy_typeinfo_init(PLyTypeInfo * arg)
{
- arg->is_rel = -1;
+ arg->is_rowtype = -1;
arg->in.r.natts = arg->out.r.natts = 0;
arg->in.r.atts = NULL;
arg->out.r.atts = NULL;
@@ -1443,7 +1461,7 @@ PLy_typeinfo_init(PLyTypeInfo * arg)
void
PLy_typeinfo_dealloc(PLyTypeInfo * arg)
{
- if (arg->is_rel == 1)
+ if (arg->is_rowtype == 1)
{
if (arg->in.r.atts)
PLy_free(arg->in.r.atts);
@@ -1515,7 +1533,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
enter();
- if (info->is_rel != 1)
+ if (info->is_rowtype != 1)
elog(ERROR, "PLyTypeInfo structure describes a datum");
dict = PyDict_New();
@@ -2009,7 +2027,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
plan->types[i] = HeapTupleGetOid(typeTup);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
- if (typeStruct->typrelid == InvalidOid)
+ if (typeStruct->typtype != 'c')
PLy_output_datum_func(&plan->args[i], typeStruct);
else
{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 37e515ff66b..d9382aff83b 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -31,7 +31,7 @@
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.82 2004/01/24 23:06:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.83 2004/04/01 21:28:46 tgl Exp $
*
**********************************************************************/
@@ -60,6 +60,8 @@
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
+
#if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
&& TCL_MINOR_VERSION > 0
@@ -107,7 +109,7 @@ typedef struct pltcl_proc_desc
int nargs;
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
Oid arg_out_elem[FUNC_MAX_ARGS];
- int arg_is_rel[FUNC_MAX_ARGS];
+ bool arg_is_rowtype[FUNC_MAX_ARGS];
} pltcl_proc_desc;
@@ -497,21 +499,35 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
************************************************************/
for (i = 0; i < prodesc->nargs; i++)
{
- if (prodesc->arg_is_rel[i])
+ if (prodesc->arg_is_rowtype[i])
{
/**************************************************
* For tuple values, add a list for 'array set ...'
**************************************************/
- TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
- Assert(slot != NULL && !fcinfo->argnull[i]);
- Tcl_DStringInit(&list_tmp);
- pltcl_build_tuple_argument(slot->val,
- slot->ttc_tupleDescriptor,
- &list_tmp);
- Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp));
- Tcl_DStringFree(&list_tmp);
- Tcl_DStringInit(&list_tmp);
+ if (fcinfo->argnull[i])
+ Tcl_DStringAppendElement(&tcl_cmd, "");
+ else
+ {
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+
+ td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ tmptup.t_data = td;
+
+ Tcl_DStringSetLength(&list_tmp, 0);
+ pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
+ Tcl_DStringAppendElement(&tcl_cmd,
+ Tcl_DStringValue(&list_tmp));
+ }
}
else
{
@@ -1041,11 +1057,11 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
Form_pg_type typeStruct;
Tcl_DString proc_internal_def;
Tcl_DString proc_internal_body;
- char proc_internal_args[4096];
+ char proc_internal_args[33 * FUNC_MAX_ARGS];
Datum prosrcdatum;
bool isnull;
char *proc_source;
- char buf[512];
+ char buf[32];
/************************************************************
* Allocate a new procedure description block
@@ -1124,7 +1140,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
}
}
- if (typeStruct->typrelid != InvalidOid)
+ if (typeStruct->typtype == 'c')
{
free(prodesc->proname);
free(prodesc);
@@ -1172,25 +1188,22 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
format_type_be(procStruct->proargtypes[i]))));
}
- if (typeStruct->typrelid != InvalidOid)
+ if (typeStruct->typtype == 'c')
{
- prodesc->arg_is_rel[i] = 1;
- if (i > 0)
- strcat(proc_internal_args, " ");
+ prodesc->arg_is_rowtype[i] = true;
snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
- strcat(proc_internal_args, buf);
- ReleaseSysCache(typeTup);
- continue;
}
else
- prodesc->arg_is_rel[i] = 0;
-
- perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
- prodesc->arg_out_elem[i] = typeStruct->typelem;
+ {
+ prodesc->arg_is_rowtype[i] = false;
+ perm_fmgr_info(typeStruct->typoutput,
+ &(prodesc->arg_out_func[i]));
+ prodesc->arg_out_elem[i] = typeStruct->typelem;
+ snprintf(buf, sizeof(buf), "%d", i + 1);
+ }
if (i > 0)
strcat(proc_internal_args, " ");
- snprintf(buf, sizeof(buf), "%d", i + 1);
strcat(proc_internal_args, buf);
ReleaseSysCache(typeTup);
@@ -1225,11 +1238,13 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
{
for (i = 0; i < prodesc->nargs; i++)
{
- if (!prodesc->arg_is_rel[i])
- continue;
- snprintf(buf, sizeof(buf), "array set %d $__PLTcl_Tup_%d\n",
- i + 1, i + 1);
- Tcl_DStringAppend(&proc_internal_body, buf, -1);
+ if (prodesc->arg_is_rowtype[i])
+ {
+ snprintf(buf, sizeof(buf),
+ "array set %d $__PLTcl_Tup_%d\n",
+ i + 1, i + 1);
+ Tcl_DStringAppend(&proc_internal_body, buf, -1);
+ }
}
}
else
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source
index c10f73c5c65..ebf13626c7f 100644
--- a/src/test/regress/input/misc.source
+++ b/src/test/regress/input/misc.source
@@ -212,10 +212,12 @@ SELECT name(equipment(p.hobbies)), name(p.hobbies), p.name FROM person* p;
SELECT user_relns() AS user_relns
ORDER BY user_relns;
---SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
+SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
SELECT hobbies_by_name('basketball');
+SELECT name, overpaid(emp.*) FROM emp;
+
--
-- check that old-style C functions work properly with TOASTed values
--
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source
index c478dd71897..3173f718c6c 100644
--- a/src/test/regress/output/misc.source
+++ b/src/test/regress/output/misc.source
@@ -663,13 +663,29 @@ SELECT user_relns() AS user_relns
xacttest
(97 rows)
---SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
+SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
+ name
+------
+ guts
+(1 row)
+
SELECT hobbies_by_name('basketball');
hobbies_by_name
-----------------
joe
(1 row)
+SELECT name, overpaid(emp.*) FROM emp;
+ name | overpaid
+--------+----------
+ sharon | t
+ sam | t
+ bill | t
+ jeff | f
+ cim | f
+ linda | f
+(6 rows)
+
--
-- check that old-style C functions work properly with TOASTed values
--
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index b26eed5b371..31210a8e0d9 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -1,5 +1,5 @@
/*
- * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.59 2003/11/29 19:52:14 pgsql Exp $
+ * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.60 2004/04/01 21:28:47 tgl Exp $
*/
#include "postgres.h"
@@ -15,8 +15,6 @@
#define RDELIM ')'
#define DELIM ','
-typedef TupleTableSlot *TUPLE;
-
extern Datum regress_dist_ptpath(PG_FUNCTION_ARGS);
extern Datum regress_path_dist(PG_FUNCTION_ARGS);
extern PATH *poly2path(POLYGON *poly);
@@ -196,7 +194,7 @@ PG_FUNCTION_INFO_V1(overpaid);
Datum
overpaid(PG_FUNCTION_ARGS)
{
- TUPLE tuple = (TUPLE) PG_GETARG_POINTER(0);
+ HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
bool isnull;
int32 salary;
diff --git a/src/tutorial/funcs.c b/src/tutorial/funcs.c
index d2c123a0094..5e8ad6ee707 100644
--- a/src/tutorial/funcs.c
+++ b/src/tutorial/funcs.c
@@ -22,7 +22,7 @@ float8 *add_one_float8(float8 *arg);
Point *makepoint(Point *pointx, Point *pointy);
text *copytext(text *t);
text *concat_text(text *arg1, text *arg2);
-bool c_overpaid(TupleTableSlot *t, /* the current instance of EMP */
+bool c_overpaid(HeapTupleHeader t, /* the current instance of EMP */
int32 limit);
@@ -94,7 +94,7 @@ concat_text(text *arg1, text *arg2)
/* Composite types */
bool
-c_overpaid(TupleTableSlot *t, /* the current instance of EMP */
+c_overpaid(HeapTupleHeader t, /* the current instance of EMP */
int32 limit)
{
bool isnull;
diff --git a/src/tutorial/funcs_new.c b/src/tutorial/funcs_new.c
index c9413096bcf..e548b85ee41 100644
--- a/src/tutorial/funcs_new.c
+++ b/src/tutorial/funcs_new.c
@@ -115,7 +115,7 @@ PG_FUNCTION_INFO_V1(c_overpaid);
Datum
c_overpaid(PG_FUNCTION_ARGS)
{
- TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
+ HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
int32 limit = PG_GETARG_INT32(1);
bool isnull;
int32 salary;