Commit 375369ac authored by Tom Lane's avatar Tom Lane

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.
parent 8590a62b
......@@ -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);
}
......
......@@ -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"
......
/*
* $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++)
......
......@@ -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;
......
......@@ -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++;
......
......@@ -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++;
......
<!--
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>
......
<!--
$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>
......
This diff is collapsed.
......@@ -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;
......
This diff is collapsed.
......@@ -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
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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
......
This diff is collapsed.
......@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.75 2004/01/07 18:56:26 neilc Exp $
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -109,8 +109,11 @@
#include "funcapi.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
......@@ -144,16 +147,11 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in
/*
* Now allocate our new table along with space for the pointers to the
* tuples.
* tuples. Zero out the slots.
*/
newtable = (TupleTable) palloc(sizeof(TupleTableData));
array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
/*
* clean out the slots we just allocated
*/
MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
/*
* initialize the new table and return it to the caller.
......@@ -514,6 +512,10 @@ TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
struct tupleDesc nullTupleDesc;
HeapTuple nullTuple;
Datum values[1];
char nulls[1];
/*
* Since heap_getattr() will treat attributes beyond a tuple's t_natts
......@@ -521,15 +523,12 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
* of zero length. However, the slot descriptor must match the real
* tupType.
*/
HeapTuple nullTuple;
Datum values[1];
char nulls[1];
static struct tupleDesc NullTupleDesc; /* we assume this inits to
* zeroes */
nullTupleDesc = *tupType;
nullTupleDesc.natts = 0;
ExecSetSlotDescriptor(slot, tupType, false);
nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
ExecSetSlotDescriptor(slot, tupType, false);
return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
}
......@@ -590,21 +589,45 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
resdom->resname,
resdom->restype,
resdom->restypmod,
0,
false);
0);
}
return typeInfo;
}
/*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
*
* Rowtype Datums returned by a function must contain valid type information.
* This happens "for free" if the tupdesc came from a relcache entry, but
* not if we have manufactured a tupdesc for a transient RECORD datatype.
* In that case we have to notify typcache.c of the existence of the type.
*/
TupleDesc
BlessTupleDesc(TupleDesc tupdesc)
{
if (tupdesc->tdtypeid == RECORDOID &&
tupdesc->tdtypmod < 0)
assign_record_type_typmod(tupdesc);
return tupdesc; /* just for notational convenience */
}
/*
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
*
* Note: this is obsolete; it is sufficient to call BlessTupleDesc on
* the tupdesc. We keep it around just for backwards compatibility with
* existing user-written SRFs.
*/
TupleTableSlot *
TupleDescGetSlot(TupleDesc tupdesc)
{
TupleTableSlot *slot;
/* The useful work is here */
BlessTupleDesc(tupdesc);
/* Make a standalone slot */
slot = MakeTupleTableSlot();
......@@ -634,6 +657,9 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
/* "Bless" the tupledesc so that we can make rowtype datums with it */
attinmeta->tupdesc = BlessTupleDesc(tupdesc);
/*
* Gather info needed later to call the "in" function for each
* attribute
......@@ -653,7 +679,6 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
atttypmods[i] = tupdesc->attrs[i]->atttypmod;
}
}
attinmeta->tupdesc = tupdesc;
attinmeta->attinfuncs = attinfuncinfo;
attinmeta->attelems = attelems;
attinmeta->atttypmods = atttypmods;
......
......@@ -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->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);
}
/*
......
......@@ -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);
......
......@@ -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
......
......@@ -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,
......
......@@ -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,15 +523,6 @@ 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);
}
......
#
# 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 \
......
......@@ -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);
}
......
......@@ -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.
*
......
/*-------------------------------------------------------------------------
*
* 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 */
}
/*-------------------------------------------------------------------------
*
* 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);
}
......@@ -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;
......
......@@ -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;
}
......@@ -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,
MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
sizeof(FuncCallContext));
MemSet(retval, 0, 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);
......
......@@ -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);
}
......
......@@ -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);
/* ----------------
......
This diff is collapsed.
......@@ -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);
......
......@@ -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
This diff is collapsed.
......@@ -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("");
......
......@@ -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");
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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)
/*-------------------------------------------------------------------------
......
This diff is collapsed.
......@@ -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
* ----------------
......
......@@ -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,
......
......@@ -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.
* ----------------
*/
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment