Replace TupleTableSlot convention for whole-row variables and function

results with tuples as ordinary varlena Datums.  This commit does not
in itself do much for us, except eliminate the horrid memory leak
associated with evaluation of whole-row variables.  However, it lays the
groundwork for allowing composite types as table columns, and perhaps
some other useful features as well.  Per my proposal of a few days ago.
This commit is contained in:
Tom Lane 2004-04-01 21:28:47 +00:00
parent 8590a62b75
commit 375369acd1
60 changed files with 1779 additions and 1733 deletions

View File

@ -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);
}

View File

@ -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"

View File

@ -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++)

View File

@ -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;

View File

@ -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++;

View File

@ -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++;

View File

@ -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">
@ -809,24 +809,6 @@
</entry>
</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>

View File

@ -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>

View File

@ -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>
@ -1674,6 +1675,11 @@ c_overpaid(PG_FUNCTION_ARGS)
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function> macro.
</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:
@ -1681,8 +1687,11 @@ c_overpaid(PG_FUNCTION_ARGS)
<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
@ -1778,25 +1781,13 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
be called again for each row you return.
</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]);

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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,
attrno,
slot->ttc_tupleDescriptor,
isNull);
if (*isNull)
return (Datum) 0;
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
return retval;
/*
* 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,
isNull);
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,
attrno,
tupdesc,
isNull);
if (*isNull)
return (Datum) 0;
/*
* 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;
return retval;
result = heap_getattr(&tmptup,
attrno,
tupDesc,
isNull);
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
@ -1281,6 +1289,16 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
if (rsinfo.isDone == ExprEndResult)
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
@ -1288,25 +1306,18 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
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;
resSlot = (TupleTableSlot *) DatumGetPointer(result);
Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
result = heap_getattr(resSlot->val,
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);
}
/*
* 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);
}

View File

@ -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,16 +523,13 @@ 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;
nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
ExecSetSlotDescriptor(slot, tupType, false);
nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
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;

View File

@ -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);
}
/*

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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 \

View File

@ -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);
}

View File

@ -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 $
*
*-------------------------------------------------------------------------
*/
@ -27,59 +27,6 @@
#include "utils/builtins.h"
/*
* 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.
*

View File

@ -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 */
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
/* ----------------

View File

@ -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) ? \
@ -260,6 +322,117 @@ do { \
} while (0)
/*
* BITMAPLEN(NATTS) -
* Computes size of null bitmap given number of data columns.
*/
#define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
/*
* MaxTupleSize is the maximum allowed size of a tuple, including header and
* MAXALIGN alignment padding. Basically it's BLCKSZ minus the other stuff
* that has to be on a disk page. The "other stuff" includes access-method-
* dependent "special space", which we assume will be no more than
* MaxSpecialSpace bytes (currently, on heap pages it's actually zero).
*
* NOTE: we do not need to count an ItemId for the tuple because
* sizeof(PageHeaderData) includes the first ItemId on the page.
*/
#define MaxSpecialSpace 32
#define MaxTupleSize \
(BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + MaxSpecialSpace))
/*
* MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
* data fields of char(n) and similar types. It need not have anything
* directly to do with the *actual* upper limit of varlena values, which
* is currently 1Gb (see struct varattrib in postgres.h). I've set it
* at 10Mb which seems like a reasonable number --- tgl 8/6/00.
*/
#define MaxAttrSize (10 * 1024 * 1024)
/*
* Attribute numbers for the system-defined attributes
*/
#define SelfItemPointerAttributeNumber (-1)
#define ObjectIdAttributeNumber (-2)
#define MinTransactionIdAttributeNumber (-3)
#define MinCommandIdAttributeNumber (-4)
#define MaxTransactionIdAttributeNumber (-5)
#define MaxCommandIdAttributeNumber (-6)
#define TableOidAttributeNumber (-7)
#define FirstLowInvalidHeapAttributeNumber (-8)
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
*
* This new HeapTuple for version >= 6.5 and this is why it was changed:
*
* 1. t_len moved off on-disk tuple data - ItemIdData is used to get len;
* 2. t_ctid above is not self tuple TID now - it may point to
* updated version of tuple (required by MVCC);
* 3. someday someone let tuple to cross block boundaries -
* he have to add something below...
*
* Change for 7.0:
* Up to now t_data could be NULL, the memory location directly following
* HeapTupleData, or pointing into a buffer. Now, it could also point to
* a separate allocation that was done in the t_datamcxt memory context.
*/
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
MemoryContext t_datamcxt; /* memory context of allocation */
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
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)
/*
* Accessor macros to be used with HeapTuple pointers.
*/
#define HeapTupleIsValid(tuple) PointerIsValid(tuple)
#define HeapTupleHasNulls(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASNULL) != 0)
#define HeapTupleNoNulls(tuple) \
(!((tuple)->t_data->t_infomask & HEAP_HASNULL))
#define HeapTupleHasVarWidth(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH) != 0)
#define HeapTupleAllFixed(tuple) \
(!((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH))
#define HeapTupleHasExternal(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
#define HeapTupleHasCompressed(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASCOMPRESSED) != 0)
#define HeapTupleHasExtended(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTENDED) != 0)
#define HeapTupleGetOid(tuple) \
HeapTupleHeaderGetOid((tuple)->t_data)
#define HeapTupleSetOid(tuple, oid) \
HeapTupleHeaderSetOid((tuple)->t_data, (oid))
/*
* WAL record definitions for heapam.c's WAL operations
*
@ -349,118 +522,4 @@ typedef struct xl_heap_clean
#define SizeOfHeapClean (offsetof(xl_heap_clean, block) + sizeof(BlockNumber))
/*
* MaxTupleSize is the maximum allowed size of a tuple, including header and
* MAXALIGN alignment padding. Basically it's BLCKSZ minus the other stuff
* that has to be on a disk page. The "other stuff" includes access-method-
* dependent "special space", which we assume will be no more than
* MaxSpecialSpace bytes (currently, on heap pages it's actually zero).
*
* NOTE: we do not need to count an ItemId for the tuple because
* sizeof(PageHeaderData) includes the first ItemId on the page.
*/
#define MaxSpecialSpace 32
#define MaxTupleSize \
(BLCKSZ - MAXALIGN(sizeof(PageHeaderData) + MaxSpecialSpace))
/*
* MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
* data fields of char(n) and similar types. It need not have anything
* directly to do with the *actual* upper limit of varlena values, which
* is currently 1Gb (see struct varattrib in postgres.h). I've set it
* at 10Mb which seems like a reasonable number --- tgl 8/6/00.
*/
#define MaxAttrSize (10 * 1024 * 1024)
/*
* Attribute numbers for the system-defined attributes
*/
#define SelfItemPointerAttributeNumber (-1)
#define ObjectIdAttributeNumber (-2)
#define MinTransactionIdAttributeNumber (-3)
#define MinCommandIdAttributeNumber (-4)
#define MaxTransactionIdAttributeNumber (-5)
#define MaxCommandIdAttributeNumber (-6)
#define TableOidAttributeNumber (-7)
#define FirstLowInvalidHeapAttributeNumber (-8)
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
*
* This new HeapTuple for version >= 6.5 and this is why it was changed:
*
* 1. t_len moved off on-disk tuple data - ItemIdData is used to get len;
* 2. t_ctid above is not self tuple TID now - it may point to
* updated version of tuple (required by MVCC);
* 3. someday someone let tuple to cross block boundaries -
* he have to add something below...
*
* Change for 7.0:
* Up to now t_data could be NULL, the memory location directly following
* HeapTupleData, or pointing into a buffer. Now, it could also point to
* a separate allocation that was done in the t_datamcxt memory context.
*/
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
MemoryContext t_datamcxt; /* memory context of allocation */
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
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.
*/
#define HeapTupleIsValid(tuple) PointerIsValid(tuple)
#define HeapTupleHasNulls(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASNULL) != 0)
#define HeapTupleNoNulls(tuple) \
(!((tuple)->t_data->t_infomask & HEAP_HASNULL))
#define HeapTupleHasVarWidth(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH) != 0)
#define HeapTupleAllFixed(tuple) \
(!((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH))
#define HeapTupleHasExternal(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
#define HeapTupleHasCompressed(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASCOMPRESSED) != 0)
#define HeapTupleHasExtended(tuple) \
(((tuple)->t_data->t_infomask & HEAP_HASEXTENDED) != 0)
#define HeapTupleGetOid(tuple) \
HeapTupleHeaderGetOid((tuple)->t_data)
#define HeapTupleSetOid(tuple, oid) \
HeapTupleHeaderSetOid((tuple)->t_data, (oid))
#endif /* HTUP_H */

View File

@ -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);

View File

@ -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

View File

@ -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 }
{ 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 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));
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 }
{ 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 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));
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 }
{ 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 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));
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 }
{ 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 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));
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 */

View File

@ -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("");

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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)
/*-------------------------------------------------------------------------

View File

@ -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

View File

@ -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 $
*
*-------------------------------------------------------------------------
*/
@ -563,6 +563,17 @@ typedef struct SubPlanState
FmgrInfo *hashfunctions; /* lookup data for hash functions */
} SubPlanState;
/* ----------------
* FieldSelectState node
* ----------------
*/
typedef struct FieldSelectState
{
ExprState xprstate;
ExprState *arg; /* input expression */
TupleDesc argdesc; /* tupdesc for most recent input */
} FieldSelectState;
/* ----------------
* CaseExprState node
* ----------------

View File

@ -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,

View File

@ -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.
* ----------------
*/

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
if (fcinfo->argnull[i])
XPUSHs(&PL_sv_undef);
else
{
HeapTupleHeader td;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tmptup;
SV *hashref;
Assert(slot != NULL && !fcinfo->argnull[i]);
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(slot->val,
slot->ttc_tupleDescriptor);
XPUSHs(hashref);
/*
* 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);
}
}

View File

@ -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
{

View File

@ -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];
if (fcinfo->argnull[i])
arg = NULL;
else
{
HeapTupleHeader td;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tmptup;
arg = PLyDict_FromTuple(&(proc->args[i]), slot->val,
slot->ttc_tupleDescriptor);
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
{

View File

@ -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];
if (fcinfo->argnull[i])
Tcl_DStringAppendElement(&tcl_cmd, "");
else
{
HeapTupleHeader td;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tmptup;
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);
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

View File

@ -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
--

View File

@ -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
--

View File

@ -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;

View File

@ -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;

View File

@ -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;