relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
"relation_size"));
- relation = relation_openrv(relrv, AccessShareLock);
+ relation = heap_openrv(relrv, AccessShareLock);
relnode = relation->rd_rel->relfilenode;
segcount++;
}
- relation_close(relation, AccessShareLock);
+ heap_close(relation, AccessShareLock);
PG_RETURN_INT64(totalsize);
}
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.33 2002/08/23 00:33:24 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.34 2002/08/29 00:17:01 tgl Exp $
PostgreSQL documentation
-->
)
CREATE TYPE <replaceable class="parameter">typename</replaceable> AS
- ( <replaceable class="PARAMETER">column_definition_list</replaceable> )
-
-where <replaceable class="PARAMETER">column_definition_list</replaceable> can be:
-
-( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] )
+ ( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] )
</synopsis>
<refsect2 id="R2-SQL-CREATETYPE-1">
type names also must not conflict with table names in the same schema.)
</para>
+ <refsect2>
+ <title>Base Types</title>
+
<para>
- The first form of <command>CREATE TYPE</command> requires the
+ The first form of <command>CREATE TYPE</command> creates a new base type
+ (scalar type). It requires the
registration of two functions (using CREATE FUNCTION) before defining the
type. The representation of a new base type is determined by
<replaceable class="parameter">input_function</replaceable>, which
a row fit, but they will be kept in the main table preferentially over
<literal>extended</literal> and <literal>external</literal> items.)
</para>
+ </refsect2>
+
+ <refsect2>
+ <title>Composite Types</title>
<para>
- The second form of <command>CREATE TYPE</command> requires a column
- definition list in the form ( <replaceable class="PARAMETER">column_name</replaceable>
- <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ). This
- creates a composite type, similar to that of a TABLE or VIEW relation.
- A stand-alone composite type is useful as the return type of FUNCTION.
+ The second form of <command>CREATE TYPE</command>
+ creates a composite type.
+ The composite type is specified by a list of column names and datatypes.
+ This is essentially the same as the row type
+ of a table, but using <command>CREATE TYPE</command> avoids the need to
+ create an actual table when all that is wanted is to define a type.
+ A stand-alone composite type is useful as the return type of a function.
</para>
+ </refsect2>
<refsect2>
<title>Array Types</title>
<para>
- Whenever a user-defined data type is created,
+ Whenever a user-defined base data type is created,
<productname>PostgreSQL</productname> automatically creates an
associated array type, whose name consists of the base type's
name prepended with an underscore. The parser understands this
This example creates a composite type and uses it in
a table function definition:
<programlisting>
-CREATE TYPE compfoo AS (f1 int, f2 int);
-CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, foorefid FROM foo' LANGUAGE SQL;
+CREATE TYPE compfoo AS (f1 int, f2 text);
+CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, fooname FROM foo' LANGUAGE SQL;
</programlisting>
</para>
</refsect1>
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.57 2002/08/28 14:35:37 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.58 2002/08/29 00:17:01 tgl Exp $
PostgreSQL documentation
-->
( <replaceable class="PARAMETER">select</replaceable> )
[ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ]
|
-<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] )
[ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> | <replaceable class="PARAMETER">column_definition_list</replaceable> ) ]
|
-<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] )
AS ( <replaceable class="PARAMETER">column_definition_list</replaceable> )
|
<replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable>
<term><replaceable class="PARAMETER">alias</replaceable></term>
<listitem>
<para>
- A substitute name for the preceding
- <replaceable class="PARAMETER">table_name</replaceable>.
+ A substitute name for the FROM item containing the alias.
An alias is used for brevity or to eliminate ambiguity for self-joins
- (where the same table is scanned multiple times). If an alias is
+ (where the same table is scanned multiple times). When an alias
+ is provided, it completely hides the actual name of the table or
+ table function; for example given <literal>FROM foo AS f</>, the
+ remainder of the SELECT must refer to this FROM item as <literal>f</>
+ not <literal>foo</>.
+ If an alias is
written, a column alias list can also be written to provide
substitute names for one or more columns of the table.
</para>
A table function can appear in the FROM clause. This acts as though
its output were created as a temporary table for the duration of
this single SELECT command. An alias may also be used. If an alias is
- written, a column alias list can also be written to provide substitute names
- for one or more columns of the table function. If the table function has been
- defined as returning the RECORD data type, an alias, or the keyword AS, must
- also be present, followed by a column definition list in the form
- ( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ).
- The column definition list must match the actual number and types returned by the function.
+ written, a column alias list can also be written to provide substitute
+ names for one or more columns of the table function. If the table
+ function has been defined as returning the <type>record</> data type,
+ an alias, or the keyword <literal>AS</>, must be present, followed by
+ a column definition list in the form ( <replaceable
+ class="PARAMETER">column_name</replaceable> <replaceable
+ class="PARAMETER">data_type</replaceable> [, ... ] ).
+ The column definition list must match the actual number and types
+ of columns returned by the function.
</para>
</listitem>
</varlistentry>
this was the default result, and adding sub-tables was done
by appending <command>*</command> to the table name.
This old behavior is available via the command
- <command>SET SQL_Inheritance TO OFF;</command>
+ <command>SET SQL_Inheritance TO OFF</command>.
</para>
<para>
</para>
<para>
- A FROM item can be a table function (i.e. a function that returns
- multiple rows and columns). When a table function is created, it may
- be defined to return a named scalar or composite data type (an existing
- scalar data type, or a table or view name), or it may be defined to return
- a RECORD data type. When a table function is defined to return RECORD, it
- must be followed in the FROM clause by an alias, or the keyword AS alone,
- and then by a parenthesized list of column names and types. This provides
- a query-time composite type definition. The FROM clause composite type
- must match the actual composite type returned from the function or an
- ERROR will be generated.
+ A FROM item can be a table function (typically, a function that returns
+ multiple rows and/or columns, though actually any function can be used).
+ The function is invoked with the given argument value(s), and then its
+ output is scanned as though it were a table.
+ </para>
+
+ <para>
+ In some cases it is useful to define table functions that can return
+ different column sets depending on how they are invoked. To support this,
+ the table function can be declared as returning the pseudo-type
+ <type>record</>. When such a function is used in FROM, it must be
+ followed by an alias, or the keyword <literal>AS</> alone,
+ and then by a parenthesized list of column names and types. This provides
+ a query-time composite type definition. The composite type definition
+ must match the actual composite type returned from the function, or an
+ error will be reported at run-time.
</para>
<para>
unless ORDER BY is used to constrain the order.
</para>
</refsect2>
+
+ <refsect2 id="SQL-FOR-UPDATE">
+ <refsect2info>
+ <date>2002-08-28</date>
+ </refsect2info>
+ <title id="sql-for-update-title">
+ FOR UPDATE Clause
+ </title>
+ <para>
+ <synopsis>
+ FOR UPDATE [ OF <replaceable class="PARAMETER">tablename</replaceable> [, ...] ]
+ </synopsis>
+ </para>
+
+ <para>
+ FOR UPDATE causes the rows retrieved by the query to be locked as though
+ for update. This prevents them from being modified or deleted by other
+ transactions until the current transaction ends.
+ </para>
+
+ <para>
+ If specific tables are named in FOR UPDATE, then only rows coming from
+ those tables are locked.
+ </para>
+
+ <para>
+ FOR UPDATE cannot be used in contexts where returned rows can't be clearly
+ identified with individual table rows; for example it can't be used with
+ aggregation.
+ </para>
+ </refsect2>
+
</refsect1>
<refsect1 id="R1-SQL-SELECT-2">
<productname>PostgreSQL</productname> allows one to omit
the <command>FROM</command> clause from a query. This feature
was retained from the original PostQuel query language. It has
-a straightforward use to compute the results of simple constant
-expressions:
+a straightforward use to compute the results of simple expressions:
<programlisting>
SELECT 2+2;
contains an explicit FROM clause.
</para>
+
+ <para>
+ The table-function feature is a <productname>PostgreSQL</productname>
+ extension.
+ </para>
</refsect2>
<refsect2 id="R2-SQL-SELECT-5">
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/select_into.sgml,v 1.19 2002/08/28 14:35:37 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/select_into.sgml,v 1.20 2002/08/29 00:17:01 tgl Exp $
PostgreSQL documentation
-->
[ HAVING <replaceable class="PARAMETER">condition</replaceable> [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="PARAMETER">select</replaceable> ]
[ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
- [ LIMIT [ <replaceable class="PARAMETER">start</replaceable> , ] { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
+ [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
[ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
[ FOR UPDATE [ OF <replaceable class="PARAMETER">tablename</replaceable> [, ...] ] ]
-
-where <replaceable class="PARAMETER">from_item</replaceable> can be:
-
-[ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ]
- [ [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ] ]
-|
-( <replaceable class="PARAMETER">select</replaceable> )
- [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ]
-|
-<replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable>
- [ ON <replaceable class="PARAMETER">join_condition</replaceable> | USING ( <replaceable class="PARAMETER">join_column_list</replaceable> ) ]
</synopsis>
<refsect2 id="R2-SQL-SELECTINTO-1">
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.152 2002/08/27 04:55:07 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.153 2002/08/29 00:17:01 tgl Exp $
-->
<appendix id="release">
<literallayout><![CDATA[
PREPARE statement allows caching query plans for interactive statements
Type OPAQUE is now deprecated in favor of pseudo-types cstring, trigger, etc
+Standalone composite types can now be created with CREATE TYPE
Files larger than 2 GB are now supported (if supported by the operating system)
SERIAL no longer implies UNIQUE; specify explicitly if index is wanted
pg_dump -n and -N options have been removed. The new behavior is like -n but knows about key words.
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.56 2002/08/23 16:41:37 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.57 2002/08/29 00:17:02 tgl Exp $
-->
<chapter id="xfunc">
<sect1 id="xfunc-intro">
<title>Introduction</title>
- <comment>
- Historically, functions were perhaps considered a tool for creating
- types. Today, few people build their own types but many write
- their own functions. This introduction ought to be changed to
- reflect this.
- </comment>
-
- <para>
- As it turns out, part of defining a new type is the
- definition of functions that describe its behavior.
- Consequently, while it is possible to define a new
- function without defining a new type, the reverse is
- not true. We therefore describe how to add new functions
- to <productname>PostgreSQL</productname> before describing
- how to add new types.
- </para>
-
<para>
<productname>PostgreSQL</productname> provides four kinds of
functions:
<para>
It is also possible to build a function that returns a composite type.
- (However, as we'll see below, there are some
- unfortunate restrictions on how the function may be used.)
This is an example of a function
that returns a single <type>EMP</type> row:
</para>
<para>
- In the present release of <productname>PostgreSQL</productname>
- there are some unpleasant restrictions on how functions returning
- composite types can be used. Briefly, when calling a function that
- returns a row, we cannot retrieve the entire row. We must either
+ A function that returns a row (composite type) can be used as a table
+ function, as described below. It can also be called in the context
+ of an SQL expression, but only when you
extract a single attribute out of the row or pass the entire row into
- another function. (Trying to display the entire row value will yield
+ another function that accepts the same composite type. (Trying to
+ display the entire row value will yield
a meaningless number.) For example,
<programlisting>
</para>
<para>
- Another approach is to use
- functional notation for extracting attributes. The simple way
+ Another option is to use
+ functional notation for extracting an attribute. The simple way
to explain this is that we can use the
notations <literal>attribute(table)</> and <literal>table.attribute</>
interchangeably:
</sect2>
<sect2>
- <title><acronym>SQL</acronym> Table Functions (Functions Returning Sets)</title>
+ <title><acronym>SQL</acronym> Table Functions</title>
<para>
A table function is one that may be used in the <command>FROM</command>
- clause of a query. All SQL Language functions may be used in this manner.
+ clause of a query. All SQL language functions may be used in this manner,
+ but it is particularly useful for functions returning composite types.
If the function is defined to return a base type, the table function
- produces a one column result set. If the function is defined to
- return <literal>SETOF <replaceable>sometype</></literal>, the table
- function returns multiple rows. To illustrate a SQL table function,
- consider the following, which returns <literal>SETOF</literal> a
- composite type:
+ produces a one-column table. If the function is defined to return
+ a composite type, the table function produces a column for each column
+ of the composite type.
+ </para>
+
+ <para>
+ Here is an example:
<programlisting>
-CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
+CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES(1,1,'Joe');
INSERT INTO foo VALUES(1,2,'Ed');
INSERT INTO foo VALUES(2,1,'Mary');
+
+CREATE FUNCTION getfoo(int) RETURNS foo AS '
+ SELECT * FROM foo WHERE fooid = $1;
+' LANGUAGE SQL;
+
+SELECT *, upper(fooname) FROM getfoo(1) AS t1;
+</programlisting>
+
+<screen>
+ fooid | foosubid | fooname | upper
+-------+----------+---------+-------
+ 1 | 1 | Joe | JOE
+(2 rows)
+</screen>
+
+ As the example shows, we can work with the columns of the function's
+ result just the same as if they were columns of a regular table.
+ </para>
+
+ <para>
+ Note that we only got one row out of the function. This is because
+ we did not say <literal>SETOF</>.
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title><acronym>SQL</acronym> Functions Returning Sets</title>
+
+ <para>
+ When an SQL function is declared as returning <literal>SETOF</literal>
+ <replaceable>sometype</>, the function's final
+ <command>SELECT</> query is executed to completion, and each row it
+ outputs is returned as an element of the set.
+ </para>
+
+ <para>
+ This feature is normally used by calling the function as a table
+ function. In this case each row returned by the function becomes
+ a row of the table seen by the query. For example, assume that
+ table <literal>foo</> has the same contents as above, and we say:
+
+<programlisting>
CREATE FUNCTION getfoo(int) RETURNS setof foo AS '
SELECT * FROM foo WHERE fooid = $1;
' LANGUAGE SQL;
+
SELECT * FROM getfoo(1) AS t1;
</programlisting>
</para>
<para>
- When an SQL function is declared as returning <literal>SETOF
- <replaceable>sometype</></literal>, the function's final
- <command>SELECT</> query is executed to completion, and each row it
- outputs is returned as an element of the set.
- </para>
-
- <para>
- Functions returning sets may also currently be called in the target list
+ Currently, functions returning sets may also be called in the target list
of a <command>SELECT</> query. For each row that the <command>SELECT</>
generates by itself, the function returning set is invoked, and an output
row is generated for each element of the function's result set. Note,
<function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
guarantees to return a copy of the specified parameter which is
safe for writing into. (The normal macros will sometimes return a
- pointer to the value which must not be written to. Using the
+ pointer to a value that is physically stored in a table, and so
+ must not be written to. Using the
<function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
macros guarantees a writable result.)
</para>
<title>Table Function API</title>
<para>
- The Table Function API assists in the creation of a user defined
- C Language table functions (<xref linkend="xfunc-tablefunctions">).
+ The Table Function API assists in the creation of user-defined
+ C language table functions (<xref linkend="xfunc-tablefunctions">).
Table functions are functions that produce a set of rows, made up of
either base (scalar) data types, or composite (multi-column) data types.
The API is split into two main components: support for returning
<para>
The Table Function API relies on macros and functions to suppress most
- of the complexity of building composite data types and return multiple
- results. In addition to the version-1 conventions discussed elsewhere,
- a table function always requires the following:
+ of the complexity of building composite data types and returning multiple
+ results. A table function must follow the version-1 calling convention
+ described above. In addition, the source file must include:
<programlisting>
#include "funcapi.h"
</programlisting>
</para>
+ <sect3>
+ <title>Returning Tuples (Composite Types)</title>
+
<para>
The Table Function API support for returning composite data types
(or tuples) starts with the AttInMetadata struct. This struct holds
arrays of individual attribute information needed to create a tuple from
- raw C strings. It also requires a copy of the TupleDesc. The information
+ raw C strings. It also saves a pointer to the TupleDesc. The information
carried here is derived from the TupleDesc, but it is stored here to
- avoid redundant cpu cycles on each call to a Table Function.
+ avoid redundant CPU cycles on each call to a Table Function. In the
+ case of a function returning a set, the AttInMetadata struct should be
+ computed once during the first call and saved for re-use in later calls.
<programlisting>
-typedef struct
+typedef struct AttInMetadata
{
- /* full TupleDesc */
- TupleDesc tupdesc;
-
- /* pointer to array of attribute "type"in finfo */
- FmgrInfo *attinfuncs;
+ /* full TupleDesc */
+ TupleDesc tupdesc;
- /* pointer to array of attribute type typelem */
- Oid *attelems;
+ /* array of attribute type input function finfo */
+ FmgrInfo *attinfuncs;
- /* pointer to array of attribute type typtypmod */
- int4 *atttypmods;
+ /* array of attribute type typelem */
+ Oid *attelems;
+ /* array of attribute typmod */
+ int32 *atttypmods;
} AttInMetadata;
</programlisting>
To assist you in populating this struct, several functions and a macro
are available. Use
<programlisting>
-TupleDesc RelationNameGetTupleDesc(char *relname)
+TupleDesc RelationNameGetTupleDesc(const char *relname)
</programlisting>
- to get a TupleDesc based on the function's return type relation, or
+ to get a TupleDesc based on a specified relation, or
<programlisting>
TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
</programlisting>
- to get a TupleDesc based on the function's type oid. This can be used to
- get a TupleDesc for a base (scalar), or composite (relation) type. Then
+ to get a TupleDesc based on a type OID. This can be used to
+ get a TupleDesc for a base (scalar) or composite (relation) type. Then
<programlisting>
AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
</programlisting>
will return a pointer to an AttInMetadata struct, initialized based on
- the function's TupleDesc. AttInMetadata is be used in conjunction with
+ the given TupleDesc. AttInMetadata can be used in conjunction with
C strings to produce a properly formed tuple. The metadata is stored here
- for use across calls to avoid redundant work.
+ to avoid redundant work across multiple calls.
</para>
<para>
- In order to return a tuple you must create a tuple slot based on the
+ To return a tuple you must create a tuple slot based on the
TupleDesc. You can use
<programlisting>
TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
</programlisting>
to initialize this tuple slot, or obtain one through other (user provided)
means. The tuple slot is needed to create a Datum for return by the
- function.
+ function. The same slot can (and should) be re-used on each call.
</para>
<para>
- If desired,
+ After constructing an AttInMetadata structure,
<programlisting>
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
</programlisting>
can be used to build a HeapTuple given user data in C string form.
"values" is an array of C strings, one for each attribute of the return
- tuple. The C strings should be in the form expected by the "in" function
- of the attribute data type. For more information on this requirement,
- see the individual data type "in" functions in the source code
- (e.g. textin() for data type TEXT). In order to return a NULL value for
+ tuple. Each C string should be in the form expected by the input function
+ of the attribute data type. In order to return a NULL value for
one of the attributes, the corresponding pointer in the "values" array
- should be set to NULL.
+ should be set to NULL. This function will need to be called again
+ for each tuple you return.
</para>
<para>
- In order to get an attribute "in" function and typelem value given the
- typeid, use
-<programlisting>
-void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
-</programlisting>
+ Building a tuple via TupleDescGetAttInMetadata and BuildTupleFromCStrings
+ is only convenient if your function naturally computes the values to
+ be returned as text strings. If your code naturally computes the
+ values as a set of Datums, you should instead use the underlying
+ heap_formtuple routine to convert the Datums directly into a tuple.
+ You will still need the TupleDesc and a TupleTableSlot, but not
+ AttInMetadata.
</para>
<para>
- Finally, in order to return a tuple using the SRF portion of the API
- (described below), the tuple must be converted into a Datum. Use
+ Once you have built a tuple to return from your function, the tuple must
+ be converted into a Datum. Use
<programlisting>
TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
</programlisting>
- to get a Datum given a tuple and a slot.
+ to get a Datum given a tuple and a slot. This Datum can be returned
+ directly if you intend to return just a single row, or it can be used
+ as the current return value in a set-returning function.
+ </para>
+
+ <para>
+ An example appears below.
</para>
+ </sect3>
+
+ <sect3>
+ <title>Returning Sets</title>
+
<para>
- The Table Function API support for set returning functions starts with
- the FuncCallContext struct. This struct holds function context for
- SRFs using fcinfo->flinfo->fn_extra to hold a pointer to it across calls.
+ A set-returning function (SRF) is normally called once for each item it
+ returns. The SRF must therefore save enough state to remember what it
+ was doing and return the next item on each call. The Table Function API
+ provides the FuncCallContext struct to help control this process.
+ <literal>fcinfo->flinfo->fn_extra</> is used to
+ hold a pointer to FuncCallContext across calls.
<programlisting>
typedef struct
{
} FuncCallContext;
</programlisting>
- To assist you in populating this struct, several functions and macros
- are available. Use
+ An SRF uses several functions and macros that automatically manipulate
+ the FuncCallContext struct (and expect to find it via
+ <literal>fn_extra</>). Use
<programlisting>
SRF_IS_FIRSTCALL()
</programlisting>
- to determine if your function has been called for the first or a
+ to determine if your function is being called for the first or a
subsequent time. On the first call (only) use
<programlisting>
SRF_FIRSTCALL_INIT()
<programlisting>
SRF_RETURN_NEXT(funcctx, result)
</programlisting>
- to send it and prepare for the next call. Finally, when your function
- is finished returning data, use
+ to return it to the caller. (The <literal>result</>
+ must be a Datum, either a single value or a tuple prepared as described
+ earlier.) Finally, when your function is finished returning data, use
<programlisting>
SRF_RETURN_DONE(funcctx)
</programlisting>
Datum
my_Set_Returning_Function(PG_FUNCTION_ARGS)
{
- FuncCallContext *funcctx;
- Datum result;
-
- [user defined declarations]
-
- if(SRF_IS_FIRSTCALL())
- {
- [user defined code]
- funcctx = SRF_FIRSTCALL_INIT();
- [if returning composite]
- [obtain slot]
- funcctx->slot = slot;
- [endif returning composite]
- [user defined code]
- }
- [user defined code]
- funcctx = SRF_PERCALL_SETUP();
- [user defined code]
-
- if (funcctx->call_cntr < funcctx->max_calls)
- {
- [user defined code]
- [obtain result Datum]
- SRF_RETURN_NEXT(funcctx, result);
- }
- else
- {
- SRF_RETURN_DONE(funcctx);
- }
+ FuncCallContext *funcctx;
+ Datum result;
+ [user defined declarations]
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ /* one-time setup code appears here: */
+ [user defined code]
+ funcctx = SRF_FIRSTCALL_INIT();
+ [if returning composite]
+ [build TupleDesc, and perhaps AttInMetadata]
+ [obtain slot]
+ funcctx->slot = slot;
+ [endif returning composite]
+ [user defined code]
+ }
+
+ /* each-time setup code appears here: */
+ [user defined code]
+ funcctx = SRF_PERCALL_SETUP();
+ [user defined code]
+
+ /* this is just one way we might test whether we are done: */
+ if (funcctx->call_cntr < funcctx->max_calls)
+ {
+ /* here we want to return another item: */
+ [user defined code]
+ [obtain result Datum]
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ else
+ {
+ /* here we are done returning items, and just need to clean up: */
+ [user defined code]
+ SRF_RETURN_DONE(funcctx);
+ }
}
</programlisting>
</para>
<para>
- An example of a simple composite returning SRF looks like:
+ A complete example of a simple SRF returning a composite type looks like:
<programlisting>
PG_FUNCTION_INFO_V1(testpassbyval);
Datum
testpassbyval(PG_FUNCTION_ARGS)
{
- FuncCallContext *funcctx;
- int call_cntr;
- int max_calls;
- TupleDesc tupdesc;
- TupleTableSlot *slot;
- AttInMetadata *attinmeta;
+ FuncCallContext *funcctx;
+ int call_cntr;
+ int max_calls;
+ TupleDesc tupdesc;
+ TupleTableSlot *slot;
+ AttInMetadata *attinmeta;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL())
+ {
+ /* create a function context for cross-call persistence */
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = PG_GETARG_UINT32(0);
- /* stuff done only on the first call of the function */
- if(SRF_IS_FIRSTCALL())
- {
- /* create a function context for cross-call persistence */
- funcctx = SRF_FIRSTCALL_INIT();
-
- /* total number of tuples to be returned */
- funcctx->max_calls = PG_GETARG_UINT32(0);
-
- /*
- * Build a tuple description for a __testpassbyval tuple
- */
- tupdesc = RelationNameGetTupleDesc("__testpassbyval");
+ /*
+ * Build a tuple description for a __testpassbyval tuple
+ */
+ tupdesc = RelationNameGetTupleDesc("__testpassbyval");
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
+ /* allocate a slot for a tuple with this tupdesc */
+ slot = TupleDescGetSlot(tupdesc);
- /* assign slot to function context */
- funcctx->slot = slot;
+ /* assign slot to function context */
+ funcctx->slot = slot;
- /*
- * Generate attribute metadata needed later to produce tuples from raw
- * C strings
- */
- attinmeta = TupleDescGetAttInMetadata(tupdesc);
- funcctx->attinmeta = attinmeta;
+ /*
+ * Generate attribute metadata needed later to produce tuples from raw
+ * C strings
+ */
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ funcctx->attinmeta = attinmeta;
}
- /* stuff done on every call of the function */
- funcctx = SRF_PERCALL_SETUP();
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- slot = funcctx->slot;
- attinmeta = funcctx->attinmeta;
+ 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 */
- {
- char **values;
- HeapTuple tuple;
- Datum result;
-
- /*
- * Prepare a values array for storage in our slot.
- * This should be an array of C strings which will
- * be processed later by the appropriate "in" functions.
- */
- values = (char **) palloc(3 * sizeof(char *));
- values[0] = (char *) palloc(16 * sizeof(char));
- values[1] = (char *) palloc(16 * sizeof(char));
- values[2] = (char *) palloc(16 * sizeof(char));
-
- snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
- snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
- snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
-
- /* build a tuple */
- tuple = BuildTupleFromCStrings(attinmeta, values);
-
- /* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
-
- /* Clean up */
- pfree(values[0]);
- pfree(values[1]);
- pfree(values[2]);
- pfree(values);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- else /* do when there is no more left */
- {
- SRF_RETURN_DONE(funcctx);
- }
+ if (call_cntr < max_calls) /* do when there is more left to send */
+ {
+ char **values;
+ HeapTuple tuple;
+ Datum result;
+
+ /*
+ * Prepare a values array for storage in our slot.
+ * This should be an array of C strings which will
+ * be processed later by the appropriate "in" functions.
+ */
+ values = (char **) palloc(3 * sizeof(char *));
+ values[0] = (char *) palloc(16 * sizeof(char));
+ values[1] = (char *) palloc(16 * sizeof(char));
+ values[2] = (char *) palloc(16 * sizeof(char));
+
+ snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
+ snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
+ snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
+
+ /* build a tuple */
+ tuple = BuildTupleFromCStrings(attinmeta, values);
+
+ /* make the tuple into a datum */
+ result = TupleGetDatum(slot, tuple);
+
+ /* Clean up */
+ pfree(values[0]);
+ pfree(values[1]);
+ pfree(values[2]);
+ pfree(values);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ else /* do when there is no more left */
+ {
+ SRF_RETURN_DONE(funcctx);
+ }
}
</programlisting>
with supporting SQL code of
<programlisting>
-CREATE VIEW __testpassbyval AS
- SELECT
- 0::INT4 AS f1,
- 0::INT4 AS f2,
- 0::INT4 AS f3;
+CREATE TYPE __testpassbyval AS (f1 int4, f2 int4, f3 int4);
CREATE OR REPLACE FUNCTION testpassbyval(int4, int4) RETURNS setof __testpassbyval
AS 'MODULE_PATHNAME','testpassbyval' LANGUAGE 'c' IMMUTABLE STRICT;
<para>
See contrib/tablefunc for more examples of Table Functions.
</para>
+
+ </sect3>
+
</sect2>
<sect2>
Table functions work wherever tables do in <literal>SELECT</> statements.
For example
<programlisting>
-CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
-CREATE FUNCTION getfoo(int) RETURNS foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+CREATE TABLE foo (fooid int, foosubid int, fooname text);
+
+CREATE FUNCTION getfoo(int) RETURNS setof foo AS '
+ SELECT * FROM foo WHERE fooid = $1;
+' LANGUAGE SQL;
+
SELECT * FROM getfoo(1) AS t1;
-SELECT * FROM foo where foosubid in (select foosubid from getfoo(foo.fooid) z where z.fooid = foo.fooid);
+
+SELECT * FROM foo
+WHERE foosubid in (select foosubid from getfoo(foo.fooid) z
+ where z.fooid = foo.fooid);
+
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
</programlisting>
are all valid statements.
</para>
-
- <para>
- Currently, table functions are supported as SQL language functions
- (<xref linkend="xfunc-sql">) and C language functions
- (<xref linkend="xfunc-c">). See these individual sections for more
- details.
- </para>
-
</sect1>
<sect1 id="xfunc-plhandler">
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.85 2002/08/05 02:30:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.86 2002/08/29 00:17:02 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
* Given a (possibly qualified) relation name, build a TupleDesc.
*/
TupleDesc
-RelationNameGetTupleDesc(char *relname)
+RelationNameGetTupleDesc(const char *relname)
{
RangeVar *relvar;
Relation rel;
/* Open relation and get the tuple description */
relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
relvar = makeRangeVarFromNameList(relname_list);
- rel = heap_openrv(relvar, AccessShareLock);
+ rel = relation_openrv(relvar, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
relation_close(rel, AccessShareLock);
{
/* Composite data type, i.e. a table's row type */
Oid relid = typeidTypeRelid(typeoid);
+ Relation rel;
+ int natts;
- if (OidIsValid(relid))
- {
- Relation rel;
- int natts;
+ if (!OidIsValid(relid))
+ elog(ERROR, "Invalid typrelid for complex type %u", typeoid);
- rel = relation_open(relid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- natts = tupdesc->natts;
- relation_close(rel, AccessShareLock);
+ rel = relation_open(relid, AccessShareLock);
+ tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ natts = tupdesc->natts;
+ relation_close(rel, AccessShareLock);
+ /* XXX should we hold the lock to ensure table doesn't change? */
- /* check to see if we've given column aliases */
- if(colaliases != NIL)
- {
- char *label;
- int varattno;
+ if (colaliases != NIL)
+ {
+ int varattno;
- /* does the List length match the number of attributes */
- if (length(colaliases) != natts)
- elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
+ /* does the list length match the number of attributes? */
+ if (length(colaliases) != natts)
+ elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
- /* OK, use the aliases instead */
- for (varattno = 0; varattno < natts; varattno++)
- {
- label = strVal(nth(varattno, colaliases));
+ /* OK, use the aliases instead */
+ for (varattno = 0; varattno < natts; varattno++)
+ {
+ char *label = strVal(nth(varattno, colaliases));
- if (label != NULL)
- namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
- }
+ if (label != NULL)
+ namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
}
}
- else
- elog(ERROR, "Invalid return relation specified for function");
}
else if (functyptype == 'b' || functyptype == 'd')
{
/* Must be a base data type, i.e. scalar */
char *attname;
- /* the alias List is required for base types */
+ /* the alias list is required for base types */
if (colaliases == NIL)
elog(ERROR, "TypeGetTupleDesc: no column alias was provided");
- /* the alias List length must be 1 */
+ /* the alias list length must be 1 */
if (length(colaliases) != 1)
elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
false);
}
else if (functyptype == 'p' && typeoid == RECORDOID)
- elog(ERROR, "Unable to determine tuple description for function"
- " returning \"record\"");
+ elog(ERROR, "Unable to determine tuple description for function returning \"record\"");
else
elog(ERROR, "Unknown kind of return type specified for function");
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.145 2002/08/13 20:11:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.146 2002/08/29 00:17:02 tgl Exp $
*
*
* INTERFACE ROUTINES
else if (r->rd_rel->relkind == RELKIND_SPECIAL)
elog(ERROR, "%s is a special relation",
RelationGetRelationName(r));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ elog(ERROR, "%s is a composite type",
+ RelationGetRelationName(r));
pgstat_initstats(&r->pgstat_info, r);
else if (r->rd_rel->relkind == RELKIND_SPECIAL)
elog(ERROR, "%s is a special relation",
RelationGetRelationName(r));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ elog(ERROR, "%s is a composite type",
+ RelationGetRelationName(r));
pgstat_initstats(&r->pgstat_info, r);
else if (r->rd_rel->relkind == RELKIND_SPECIAL)
elog(ERROR, "%s is a special relation",
RelationGetRelationName(r));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ elog(ERROR, "%s is a composite type",
+ RelationGetRelationName(r));
pgstat_initstats(&r->pgstat_info, r);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.221 2002/08/15 16:36:00 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.222 2002/08/29 00:17:02 tgl Exp $
*
*
* INTERFACE ROUTINES
static void AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
+ char new_rel_kind,
Oid new_type_oid);
static void RelationRemoveInheritance(Relation relation);
static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
/*
* first check for collision with system attribute names
*
- * Skip this for a view and type relation, since it doesn't have system
+ * Skip this for a view or type relation, since those don't have system
* attributes.
*/
if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
+ char new_rel_kind,
Oid new_type_oid)
{
/*
typeNamespace, /* type namespace */
new_type_oid, /* preassigned oid for type */
new_rel_oid, /* relation oid */
+ new_rel_kind, /* relation kind */
sizeof(Oid), /* internal size */
'c', /* type-type (complex) */
',', /* default array delimiter */
* NOTE: we could get a unique-index failure here, in case the same name
* has already been used for a type.
*/
- AddNewRelationType(relname, relnamespace, new_rel_oid, new_type_oid);
+ AddNewRelationType(relname,
+ relnamespace,
+ new_rel_oid,
+ relkind,
+ new_type_oid);
/*
* now add tuples to pg_attribute for the attributes in our new
* did this ... but when cascading from a drop of some other object,
* we may not have any lock.)
*/
- rel = heap_open(relid, AccessExclusiveLock);
+ rel = relation_open(relid, AccessExclusiveLock);
attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
heap_close(attr_rel, RowExclusiveLock);
- heap_close(rel, NoLock);
+ relation_close(rel, NoLock);
}
/*
myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
/* Get an exclusive lock on the relation owning the attribute */
- myrel = heap_open(myrelid, AccessExclusiveLock);
+ myrel = relation_open(myrelid, AccessExclusiveLock);
/* Now we can delete the pg_attrdef row */
simple_heap_delete(attrdef_rel, &tuple->t_self);
heap_close(attr_rel, RowExclusiveLock);
/* Keep lock on attribute's rel until end of xact */
- heap_close(myrel, NoLock);
+ relation_close(myrel, NoLock);
}
/* ----------------------------------------------------------------
/*
* Open and lock the relation.
*/
- rel = heap_open(rid, AccessExclusiveLock);
+ rel = relation_open(rid, AccessExclusiveLock);
/*
* Release all buffers that belong to this relation, after writing any
* unlink the relation's physical file and finish up.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW &&
- rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
smgrunlink(DEFAULT_SMGR, rel);
/*
* relation until transaction commit. This ensures no one else will
* try to do something with the doomed relation.
*/
- heap_close(rel, NoLock);
+ relation_close(rel, NoLock);
/*
* flush the relation from the relcache
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.31 2002/08/15 16:36:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.32 2002/08/29 00:17:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
case RELKIND_RELATION:
case RELKIND_SEQUENCE:
case RELKIND_VIEW:
- case RELKIND_COMPOSITE_TYPE:
AssertTupleDescHasOid(pgclass->rd_att);
object.classId = RelOid_pg_class;
object.objectId = HeapTupleGetOid(tuple);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.90 2002/08/23 16:41:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.91 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* attributes to ensure that they match the datatypes of the
* non-resjunk columns.
*/
- reln = heap_open(typerelid, AccessShareLock);
+ reln = relation_open(typerelid, AccessShareLock);
relnatts = reln->rd_rel->relnatts;
rellogcols = 0; /* we'll count nondeleted cols as we go */
colindex = 0;
elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
format_type_be(rettype), rellogcols);
- heap_close(reln, AccessShareLock);
+ relation_close(reln, AccessShareLock);
}
else if (fn_typtype == 'p' && rettype == RECORDOID)
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.79 2002/08/24 15:00:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.80 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
TypeCreate(const char *typeName,
Oid typeNamespace,
Oid assignedTypeOid,
- Oid relationOid, /* only for 'c'atalog typeTypes */
+ Oid relationOid, /* only for 'c'atalog typeType */
+ char relationKind, /* ditto */
int16 internalSize,
char typeType,
char typDelim,
*/
if (OidIsValid(relationOid))
{
- Relation rel = relation_open(relationOid, AccessShareLock);
- char relkind = rel->rd_rel->relkind;
- relation_close(rel, AccessShareLock);
-
referenced.classId = RelOid_pg_class;
referenced.objectId = relationOid;
referenced.objectSubId = 0;
- if (relkind != RELKIND_COMPOSITE_TYPE)
+ if (relationKind != RELKIND_COMPOSITE_TYPE)
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
else
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.57 2002/08/22 00:01:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.58 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Open the containing relation to ensure it won't go away meanwhile */
rel = makeRangeVarFromNameList(relname);
- relation = heap_openrv(rel, AccessShareLock);
+ relation = relation_openrv(rel, AccessShareLock);
/* Check object security */
/* Done, but hold lock until commit */
- heap_close(relation, NoLock);
+ relation_close(relation, NoLock);
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.167 2002/08/24 15:00:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.168 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (rel->rd_rel->relkind == RELKIND_VIEW)
elog(ERROR, "You cannot copy view %s",
RelationGetRelationName(rel));
- else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- elog(ERROR, "You cannot copy type relation %s",
- RelationGetRelationName(rel));
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
elog(ERROR, "You cannot change sequence relation %s",
RelationGetRelationName(rel));
if (rel->rd_rel->relkind == RELKIND_VIEW)
elog(ERROR, "You cannot copy view %s",
RelationGetRelationName(rel));
- else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- elog(ERROR, "You cannot copy type relation %s",
- RelationGetRelationName(rel));
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
elog(ERROR, "You cannot copy sequence %s",
RelationGetRelationName(rel));
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.84 2002/07/20 15:12:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.85 2002/08/29 00:17:03 tgl Exp $
*
*/
if (query->commandType == CMD_UTILITY)
{
/* rewriter will not cope with utility statements */
- PROJECT_LINE_OF_TEXT(tstate, "Utility statements have no plan structure");
+ do_text_output_oneline(tstate, "Utility statements have no plan structure");
}
else
{
if (rewritten == NIL)
{
/* In the case of an INSTEAD NOTHING, tell at least that */
- PROJECT_LINE_OF_TEXT(tstate, "Query rewrites to nothing");
+ do_text_output_oneline(tstate, "Query rewrites to nothing");
}
else
{
ExplainOneQuery(lfirst(l), stmt, tstate);
/* put a blank line between plans */
if (lnext(l) != NIL)
- PROJECT_LINE_OF_TEXT(tstate, "");
+ do_text_output_oneline(tstate, "");
}
}
}
if (query->commandType == CMD_UTILITY)
{
if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
- PROJECT_LINE_OF_TEXT(tstate, "NOTIFY");
+ do_text_output_oneline(tstate, "NOTIFY");
else
- PROJECT_LINE_OF_TEXT(tstate, "UTILITY");
+ do_text_output_oneline(tstate, "UTILITY");
return;
}
do_text_output_multiline(tstate, f);
pfree(f);
if (es->printCost)
- PROJECT_LINE_OF_TEXT(tstate, ""); /* separator line */
+ do_text_output_oneline(tstate, ""); /* separator line */
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.35 2002/08/28 20:18:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.36 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* see the new rel anyway until we commit), but it keeps the lock
* manager from complaining about deadlock risks.
*/
- rel = heap_open(relationId, AccessExclusiveLock);
+ rel = relation_open(relationId, AccessExclusiveLock);
/*
* Now add any newly specified column default values and CHECK
* Clean up. We keep lock on new relation (although it shouldn't be
* visible to anyone else anyway, until commit).
*/
- heap_close(rel, NoLock);
+ relation_close(rel, NoLock);
return relationId;
}
* Grab an exclusive lock on the target table, which we will NOT
* release until end of transaction.
*/
- targetrelation = heap_open(relid, AccessExclusiveLock);
+ targetrelation = relation_open(relid, AccessExclusiveLock);
/*
* permissions checking. this would normally be done in utility.c,
true, false);
}
- heap_close(targetrelation, NoLock); /* close rel but keep lock! */
+ relation_close(targetrelation, NoLock); /* close rel but keep lock! */
}
/*
case RELKIND_RELATION:
case RELKIND_INDEX:
case RELKIND_VIEW:
- case RELKIND_COMPOSITE_TYPE:
case RELKIND_SEQUENCE:
case RELKIND_TOASTVALUE:
/* ok to change owner */
break;
default:
- elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, type, or sequence",
+ elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
NameStr(tuple_class->relname));
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.11 2002/08/23 16:41:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.12 2002/08/29 00:17:03 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
typeNamespace, /* namespace */
InvalidOid, /* preassigned type oid (not done here) */
InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
internalLength, /* internal size */
'b', /* type-type (base type) */
delimiter, /* array element delimiter */
typeNamespace, /* namespace */
InvalidOid, /* preassigned type oid (not done here) */
InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
-1, /* internal size */
'b', /* type-type (base type) */
DEFAULT_TYPDELIM, /* array element delimiter */
domainNamespace, /* namespace */
InvalidOid, /* preassigned type oid (none here) */
InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
internalLength, /* internal size */
'd', /* type-type (domain type) */
delimiter, /* array element delimiter */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.175 2002/08/28 20:46:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.176 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
elog(ERROR, "You can't change view relation %s",
RelationGetRelationName(resultRelationDesc));
break;
- case RELKIND_COMPOSITE_TYPE:
- elog(ERROR, "You can't change type relation %s",
- RelationGetRelationName(resultRelationDesc));
- break;
}
MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.56 2002/07/20 05:49:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.57 2002/08/29 00:17:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "funcapi.h"
#include "access/heapam.h"
-#include "catalog/pg_type.h"
#include "executor/executor.h"
+#include "utils/lsyscache.h"
+
/* ----------------------------------------------------------------
* tuple table create/delete functions
}
/*
- * TupleDescGetSlot - Initialize a slot based on the supplied
- * tupledesc
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
*/
TupleTableSlot *
TupleDescGetSlot(TupleDesc tupdesc)
}
/*
- * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the
+ * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
* supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
* to produce a properly formed tuple.
*/
AttInMetadata *
TupleDescGetAttInMetadata(TupleDesc tupdesc)
{
- int natts;
+ int natts = tupdesc->natts;
int i;
Oid atttypeid;
Oid attinfuncid;
- Oid attelem;
FmgrInfo *attinfuncinfo;
Oid *attelems;
- int4 *atttypmods;
+ int32 *atttypmods;
AttInMetadata *attinmeta;
attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
- natts = tupdesc->natts;
/*
* Gather info needed later to call the "in" function for each attribute
*/
attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
attelems = (Oid *) palloc(natts * sizeof(Oid));
- atttypmods = (int4 *) palloc(natts * sizeof(int4));
+ atttypmods = (int32 *) palloc(natts * sizeof(int32));
for (i = 0; i < natts; i++)
{
atttypeid = tupdesc->attrs[i]->atttypid;
- get_type_metadata(atttypeid, &attinfuncid, &attelem);
-
+ getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
fmgr_info(attinfuncid, &attinfuncinfo[i]);
- attelems[i] = attelem;
atttypmods[i] = tupdesc->attrs[i]->atttypmod;
}
attinmeta->tupdesc = tupdesc;
HeapTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
- TupleDesc tupdesc;
- int natts;
- HeapTuple tuple;
+ TupleDesc tupdesc = attinmeta->tupdesc;
+ int natts = tupdesc->natts;
+ Datum *dvalues;
char *nulls;
int i;
- Datum *dvalues;
- FmgrInfo attinfuncinfo;
Oid attelem;
- int4 atttypmod;
-
- tupdesc = attinmeta->tupdesc;
- natts = tupdesc->natts;
+ int32 atttypmod;
+ HeapTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (char *) palloc(natts * sizeof(char));
- /* Call the "in" function for each attribute */
+ /* Call the "in" function for each non-null attribute */
for (i = 0; i < natts; i++)
{
if (values[i] != NULL)
{
- attinfuncinfo = attinmeta->attinfuncs[i];
attelem = attinmeta->attelems[i];
atttypmod = attinmeta->atttypmods[i];
- dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
- ObjectIdGetDatum(attelem),
- Int32GetDatum(atttypmod));
+ dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
+ CStringGetDatum(values[i]),
+ ObjectIdGetDatum(attelem),
+ Int32GetDatum(atttypmod));
nulls[i] = ' ';
}
else
{
- dvalues[i] = PointerGetDatum(NULL);
+ dvalues[i] = (Datum) 0;
nulls[i] = 'n';
}
}
*/
tuple = heap_formtuple(tupdesc, dvalues, nulls);
+ /*
+ * Release locally palloc'd space. XXX would probably be good to
+ * pfree values of pass-by-reference datums, as well.
+ */
+ pfree(dvalues);
+ pfree(nulls);
+
return tuple;
}
tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
- tstate->tupdesc = tupdesc;
+ tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
tstate->destfunc = DestToFunction(dest);
(*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
do_tup_output(TupOutputState *tstate, char **values)
{
/* build a tuple from the input strings using the tupdesc */
- AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tstate->tupdesc);
- HeapTuple tuple = BuildTupleFromCStrings(attinmeta, values);
+ HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values);
/* send the tuple to the receiver */
(*tstate->destfunc->receiveTuple) (tuple,
- tstate->tupdesc,
+ tstate->metadata->tupdesc,
tstate->destfunc);
/* clean up */
heap_freetuple(tuple);
}
-/* write a chunk of text, breaking at newline characters
+/*
+ * write a chunk of text, breaking at newline characters
+ *
* NB: scribbles on its input!
- * Should only be used for a single TEXT attribute tupdesc.
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
*/
void
do_text_output_multiline(TupOutputState *tstate, char *text)
end_tup_output(TupOutputState *tstate)
{
(*tstate->destfunc->cleanup) (tstate->destfunc);
+ /* XXX worth cleaning up the attinmetadata? */
pfree(tstate);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.55 2002/08/23 16:41:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.56 2002/08/29 00:17:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
fcache->typlen = typeStruct->typlen;
- if (typeStruct->typtype != 'c')
+ if (typeStruct->typtype != 'c' &&
+ procedureStruct->prorettype != RECORDOID)
{
- /* The return type is not a relation, so just use byval */
+ /* The return type is not a composite type, so just use byval */
fcache->typbyval = typeStruct->typbyval;
fcache->returnsTuple = false;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.5 2002/08/05 02:30:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.6 2002/08/29 00:17:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecFunctionScan(node)
*
- * Scans the Function sequentially and returns the next qualifying
+ * Scans the function sequentially and returns the next qualifying
* tuple.
* It calls the ExecScan() routine and passes it the access method
- * which retrieve tuples sequentially.
+ * which retrieves tuples sequentially.
*
*/
FunctionScanState *scanstate;
RangeTblEntry *rte;
Oid funcrettype;
- Oid funcrelid;
char functyptype;
TupleDesc tupdesc = NULL;
/*
* Now determine if the function returns a simple or composite type,
- * and check/add column aliases.
+ * and build an appropriate tupdesc.
*/
functyptype = get_typtype(funcrettype);
- /*
- * Build a suitable tupledesc representing the output rows
- */
if (functyptype == 'c')
{
- funcrelid = typeidTypeRelid(funcrettype);
- if (OidIsValid(funcrelid))
- {
- /*
- * Composite data type, i.e. a table's row type
- * Same as ordinary relation RTE
- */
- Relation rel;
+ /*
+ * Composite data type, i.e. a table's row type
+ */
+ Oid funcrelid;
+ Relation rel;
- rel = relation_open(funcrelid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- relation_close(rel, AccessShareLock);
- scanstate->returnsTuple = true;
- }
- else
- elog(ERROR, "Invalid return relation specified for function");
+ 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);
+ scanstate->returnsTuple = true;
}
else if (functyptype == 'b' || functyptype == 'd')
{
*/
if (fn_typtype == 'p' && fn_typeid == RECORDOID)
if (tupledesc_mismatch(tupdesc, slot->ttc_tupleDescriptor))
- elog(ERROR, "Query-specified return tuple and actual"
- " function return tuple do not match");
+ elog(ERROR, "Query-specified return tuple and actual function return tuple do not match");
}
else
{
slot, /* slot to store in */
InvalidBuffer, /* buffer associated with
* this tuple */
- true); /* pfree this pointer */
+ true); /* pfree this tuple */
}
}
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.169 2002/08/26 17:53:58 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.170 2002/08/29 00:17:04 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
static void
_outNotifyStmt(StringInfo str, NotifyStmt *node)
{
- appendStringInfo(str, "NOTIFY :relation ");
+ appendStringInfo(str, " NOTIFY :relation ");
_outNode(str, node->relation);
}
_outSelectStmt(StringInfo str, SelectStmt *node)
{
/* XXX this is pretty durn incomplete */
- appendStringInfo(str, "SELECT :where ");
+ appendStringInfo(str, " SELECT :where ");
_outNode(str, node->whereClause);
}
static void
_outFuncCall(StringInfo str, FuncCall *node)
{
- appendStringInfo(str, "FUNCTION ");
+ appendStringInfo(str, " FUNCCALL ");
_outNode(str, node->funcname);
appendStringInfo(str, " :args ");
_outNode(str, node->args);
case RTE_FUNCTION:
appendStringInfo(str, ":funcexpr ");
_outNode(str, node->funcexpr);
- appendStringInfo(str, ":coldeflist ");
+ appendStringInfo(str, " :coldeflist ");
_outNode(str, node->coldeflist);
break;
case RTE_JOIN:
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.363 2002/08/28 20:46:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.364 2002/08/29 00:17:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
%type <list> stmtblock, stmtmulti,
OptTableElementList, TableElementList, OptInherit, definition,
- opt_distinct, opt_definition, func_args, rowdefinition
- func_args_list, func_as, createfunc_opt_list
+ opt_distinct, opt_definition, func_args,
+ func_args_list, func_as, createfunc_opt_list,
oper_argtypes, RuleActionList, RuleActionMulti,
opt_column_list, columnList, opt_name_list,
sort_clause, opt_sort_clause, sortby_list, index_params,
insert_target_list, def_list, opt_indirection,
group_clause, TriggerFuncArgs, select_limit,
opt_select_limit, opclass_item_list, trans_options,
- TableFuncElementList, OptTableFuncElementList,
+ TableFuncElementList,
convert_args, prep_type_clause, prep_type_list,
execute_param_clause, execute_param_list
;
TableElementList:
- TableElementList ',' TableElement
+ TableElement
{
- $$ = lappend($1, $3);
+ $$ = makeList1($1);
}
- | TableElement
+ | TableElementList ',' TableElement
{
- $$ = makeList1($1);
+ $$ = lappend($1, $3);
}
;
n->definition = $4;
$$ = (Node *)n;
}
- | CREATE TYPE_P any_name AS rowdefinition
+ | CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
{
CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
RangeVar *r = makeNode(RangeVar);
+ /* can't use qualified_name, sigh */
switch (length($3))
{
case 1:
break;
default:
elog(ERROR,
- "Improper qualified name "
- "(too many dotted names): %s",
+ "Improper qualified name (too many dotted names): %s",
NameListToString($3));
break;
}
n->typevar = r;
- n->coldeflist = $5;
+ n->coldeflist = $6;
$$ = (Node *)n;
}
| CREATE CHARACTER SET opt_as any_name GET definition opt_collate
}
;
-rowdefinition: '(' TableFuncElementList ')' { $$ = $2; }
- ;
-
definition: '(' def_list ')' { $$ = $2; }
;
n->coldeflist = NIL;
$$ = (Node *) n;
}
- | func_table AS '(' OptTableFuncElementList ')'
+ | func_table alias_clause
+ {
+ RangeFunction *n = makeNode(RangeFunction);
+ n->funccallnode = $1;
+ n->alias = $2;
+ n->coldeflist = NIL;
+ $$ = (Node *) n;
+ }
+ | func_table AS '(' TableFuncElementList ')'
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
n->coldeflist = $4;
$$ = (Node *) n;
}
- | func_table AS ColId '(' OptTableFuncElementList ')'
+ | func_table AS ColId '(' TableFuncElementList ')'
{
RangeFunction *n = makeNode(RangeFunction);
Alias *a = makeNode(Alias);
n->coldeflist = $5;
$$ = (Node *) n;
}
- | func_table ColId '(' OptTableFuncElementList ')'
+ | func_table ColId '(' TableFuncElementList ')'
{
RangeFunction *n = makeNode(RangeFunction);
Alias *a = makeNode(Alias);
n->coldeflist = $4;
$$ = (Node *) n;
}
- | func_table alias_clause
- {
- RangeFunction *n = makeNode(RangeFunction);
- n->funccallnode = $1;
- n->alias = $2;
- n->coldeflist = NIL;
- $$ = (Node *) n;
- }
| select_with_parens
{
/*
where_clause:
WHERE a_expr { $$ = $2; }
- /* no qualifiers */
| /*EMPTY*/ { $$ = NULL; }
;
-OptTableFuncElementList:
- TableFuncElementList { $$ = $1; }
- | /*EMPTY*/ { $$ = NIL; }
- ;
-
TableFuncElementList:
- TableFuncElementList ',' TableFuncElement
+ TableFuncElement
{
- $$ = lappend($1, $3);
+ $$ = makeList1($1);
}
- | TableFuncElement
+ | TableFuncElementList ',' TableFuncElement
{
- $$ = makeList1($1);
+ $$ = lappend($1, $3);
}
;
n->colname = $1;
n->typename = $2;
n->constraints = NIL;
-
$$ = (Node *)n;
}
;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.77 2002/08/08 17:00:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.78 2002/08/29 00:17:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Now determine if the function returns a simple or composite type,
* and check/add column aliases.
*/
- functyptype = get_typtype(funcrettype);
-
if (coldeflist != NIL)
{
/*
* we *only* allow a coldeflist for functions returning a
* RECORD pseudo-type
*/
- if (functyptype != 'p' || (functyptype == 'p' && funcrettype != RECORDOID))
- elog(ERROR, "A column definition list is only allowed for"
- " functions returning RECORD");
+ if (funcrettype != RECORDOID)
+ elog(ERROR, "A column definition list is only allowed for functions returning RECORD");
}
else
{
* ... and a coldeflist is *required* for functions returning a
* RECORD pseudo-type
*/
- if (functyptype == 'p' && funcrettype == RECORDOID)
- elog(ERROR, "A column definition list is required for functions"
- " returning RECORD");
+ if (funcrettype == RECORDOID)
+ elog(ERROR, "A column definition list is required for functions returning RECORD");
}
+ functyptype = get_typtype(funcrettype);
+
if (functyptype == 'c')
{
/*
* Named composite data type, i.e. a table's row type
*/
Oid funcrelid = typeidTypeRelid(funcrettype);
+ Relation rel;
+ int maxattrs;
- if (OidIsValid(funcrelid))
- {
- /*
- * Get the rel's relcache entry. This access ensures that we have an
- * up-to-date relcache entry for the rel.
- */
- Relation rel;
- int maxattrs;
+ if (!OidIsValid(funcrelid))
+ elog(ERROR, "Invalid typrelid for complex type %u",
+ funcrettype);
- rel = heap_open(funcrelid, AccessShareLock);
-
- /*
- * Since the rel is open anyway, let's check that the number of column
- * aliases is reasonable.
- */
- maxattrs = RelationGetNumberOfAttributes(rel);
- if (maxattrs < numaliases)
- elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
- RelationGetRelationName(rel), maxattrs, numaliases);
+ /*
+ * Get the rel's relcache entry. This access ensures that we have an
+ * up-to-date relcache entry for the rel.
+ */
+ rel = relation_open(funcrelid, AccessShareLock);
- /* fill in alias columns using actual column names */
- for (varattno = numaliases; varattno < maxattrs; varattno++)
- {
- char *attrname;
+ /*
+ * Since the rel is open anyway, let's check that the number of column
+ * aliases is reasonable.
+ */
+ maxattrs = RelationGetNumberOfAttributes(rel);
+ if (maxattrs < numaliases)
+ elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+ RelationGetRelationName(rel), maxattrs, numaliases);
- attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
- eref->colnames = lappend(eref->colnames, makeString(attrname));
- }
+ /* fill in alias columns using actual column names */
+ for (varattno = numaliases; varattno < maxattrs; varattno++)
+ {
+ char *attrname;
- /*
- * Drop the rel refcount, but keep the access lock till end of
- * transaction so that the table can't be deleted or have its schema
- * modified underneath us.
- */
- heap_close(rel, NoLock);
+ attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+ eref->colnames = lappend(eref->colnames, makeString(attrname));
}
- else
- elog(ERROR, "Invalid return relation specified for function %s",
- funcname);
+
+ /*
+ * Drop the rel refcount, but keep the access lock till end of
+ * transaction so that the table can't be deleted or have its schema
+ * modified underneath us.
+ */
+ relation_close(rel, NoLock);
}
else if (functyptype == 'b' || functyptype == 'd')
{
{
List *col;
+ /* Use the column definition list to form the alias list */
+ eref->colnames = NIL;
foreach(col, coldeflist)
{
- char *attrname;
ColumnDef *n = lfirst(col);
+ char *attrname;
attrname = pstrdup(n->colname);
eref->colnames = lappend(eref->colnames, makeString(attrname));
char functyptype = get_typtype(funcrettype);
List *coldeflist = rte->coldeflist;
- /*
- * Build a suitable tupledesc representing the output rows
- */
if (functyptype == 'c')
{
+ /*
+ * Composite data type, i.e. a table's row type
+ * Same as ordinary relation RTE
+ */
Oid funcrelid = typeidTypeRelid(funcrettype);
- if (OidIsValid(funcrelid))
+ Relation rel;
+ int maxattrs;
+ int numaliases;
+
+ if (!OidIsValid(funcrelid))
+ elog(ERROR, "Invalid typrelid for complex type %u",
+ funcrettype);
+
+ rel = relation_open(funcrelid, AccessShareLock);
+ maxattrs = RelationGetNumberOfAttributes(rel);
+ numaliases = length(rte->eref->colnames);
+
+ for (varattno = 0; varattno < maxattrs; varattno++)
{
- /*
- * Composite data type, i.e. a table's row type
- * Same as ordinary relation RTE
- */
- Relation rel;
- int maxattrs;
- int numaliases;
-
- rel = heap_open(funcrelid, AccessShareLock);
- maxattrs = RelationGetNumberOfAttributes(rel);
- numaliases = length(rte->eref->colnames);
-
- for (varattno = 0; varattno < maxattrs; varattno++)
+ Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+
+ if (attr->attisdropped)
+ continue;
+
+ if (colnames)
{
- Form_pg_attribute attr = rel->rd_att->attrs[varattno];
-
- if (attr->attisdropped)
- continue;
-
- if (colnames)
- {
- char *label;
-
- if (varattno < numaliases)
- label = strVal(nth(varattno, rte->eref->colnames));
- else
- label = NameStr(attr->attname);
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- }
-
- if (colvars)
- {
- Var *varnode;
-
- varnode = makeVar(rtindex,
- attr->attnum,
- attr->atttypid,
- attr->atttypmod,
- sublevels_up);
-
- *colvars = lappend(*colvars, varnode);
- }
+ char *label;
+
+ if (varattno < numaliases)
+ label = strVal(nth(varattno, rte->eref->colnames));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
}
- heap_close(rel, AccessShareLock);
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex,
+ attr->attnum,
+ attr->atttypid,
+ attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
}
- else
- elog(ERROR, "Invalid return relation specified"
- " for function");
+
+ relation_close(rel, AccessShareLock);
}
else if (functyptype == 'b' || functyptype == 'd')
{
if (colvars)
{
Var *varnode;
- HeapTuple typeTuple;
Oid atttypid;
- typeTuple = typenameType(colDef->typename);
- atttypid = HeapTupleGetOid(typeTuple);
- ReleaseSysCache(typeTuple);
+ atttypid = typenameTypeId(colDef->typename);
varnode = makeVar(rtindex,
attnum,
}
}
else
- elog(ERROR, "Unknown kind of return type specified"
- " for function");
+ elog(ERROR, "Unknown kind of return type specified for function");
}
break;
case RTE_JOIN:
char functyptype = get_typtype(funcrettype);
List *coldeflist = rte->coldeflist;
- /*
- * Build a suitable tupledesc representing the output rows
- */
if (functyptype == 'c')
{
/*
* Same as ordinary relation RTE
*/
Oid funcrelid = typeidTypeRelid(funcrettype);
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
- if (OidIsValid(funcrelid))
- {
- HeapTuple tp;
- Form_pg_attribute att_tup;
-
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(funcrelid),
- Int16GetDatum(attnum),
- 0, 0);
- /* this shouldn't happen... */
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "Relation %s does not have attribute %d",
- get_rel_name(funcrelid), attnum);
- att_tup = (Form_pg_attribute) GETSTRUCT(tp);
- /*
- * If dropped column, pretend it ain't there. See notes
- * in scanRTEForColumn.
- */
- if (att_tup->attisdropped)
- elog(ERROR, "Relation \"%s\" has no column \"%s\"",
- get_rel_name(funcrelid),
- NameStr(att_tup->attname));
- *vartype = att_tup->atttypid;
- *vartypmod = att_tup->atttypmod;
- ReleaseSysCache(tp);
- }
- else
- elog(ERROR, "Invalid return relation specified"
- " for function");
+ if (!OidIsValid(funcrelid))
+ elog(ERROR, "Invalid typrelid for complex type %u",
+ funcrettype);
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(funcrelid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation \"%s\" does not have attribute %d",
+ get_rel_name(funcrelid), attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ /*
+ * If dropped column, pretend it ain't there. See notes
+ * in scanRTEForColumn.
+ */
+ if (att_tup->attisdropped)
+ elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+ get_rel_name(funcrelid),
+ NameStr(att_tup->attname));
+ *vartype = att_tup->atttypid;
+ *vartypmod = att_tup->atttypmod;
+ ReleaseSysCache(tp);
}
else if (functyptype == 'b' || functyptype == 'd')
{
else if (functyptype == 'p' && funcrettype == RECORDOID)
{
ColumnDef *colDef = nth(attnum - 1, coldeflist);
- HeapTuple typeTuple;
- Oid atttypid;
-
- typeTuple = typenameType(colDef->typename);
- atttypid = HeapTupleGetOid(typeTuple);
- ReleaseSysCache(typeTuple);
- *vartype = atttypid;
+ *vartype = typenameTypeId(colDef->typename);
*vartypmod = -1;
}
else
- elog(ERROR, "Unknown kind of return type specified"
- " for function");
+ elog(ERROR, "Unknown kind of return type specified for function");
}
break;
case RTE_JOIN:
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.173 2002/08/27 04:55:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.174 2002/08/29 00:17:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
{
- Oid relid;
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
- /*
- * DefineCompositeType returns relid for use when creating
- * an implicit composite type during function creation
- */
- relid = DefineCompositeType(stmt->typevar, stmt->coldeflist);
+ DefineCompositeType(stmt->typevar, stmt->coldeflist);
}
break;
* Copyright (c) 2002, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.2 2002/08/27 04:00:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
*/
-
#include "postgres.h"
-#include "fmgr.h"
+
#include "funcapi.h"
#include "catalog/pg_type.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
+#include "utils/builtins.h"
-Datum pg_lock_status(PG_FUNCTION_ARGS);
static int next_lock(int locks[]);
+
Datum
pg_lock_status(PG_FUNCTION_ARGS)
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.34 2002/08/28 20:46:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.35 2002/08/29 00:17:05 tgl Exp $
*
* NOTES
* input routine largely stolen from boxin().
if (rel->rd_rel->relkind == RELKIND_VIEW)
return currtid_for_view(rel, tid);
- if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- elog(ERROR, "currtid can't handle type relations");
-
ItemPointerCopy(tid, result);
heap_get_latest_tid(rel, SnapshotNow, result);
if (rel->rd_rel->relkind == RELKIND_VIEW)
return currtid_for_view(rel, tid);
- if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- elog(ERROR, "currtid can't handle type relations");
-
result = (ItemPointer) palloc(sizeof(ItemPointerData));
ItemPointerCopy(tid, result);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.80 2002/08/26 17:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.81 2002/08/29 00:17:05 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
return '\0';
}
+/*
+ * getTypeInputInfo
+ *
+ * Get info needed for converting values of a type to internal form
+ */
+void
+getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
+{
+ HeapTuple typeTuple;
+ Form_pg_type pt;
+
+ typeTuple = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(type),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "getTypeInputInfo: Cache lookup of type %u failed", type);
+ pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ if (!pt->typisdefined)
+ elog(ERROR, "Type \"%s\" is only a shell", NameStr(pt->typname));
+
+ *typInput = pt->typinput;
+ *typElem = pt->typelem;
+
+ ReleaseSysCache(typeTuple);
+}
+
/*
* getTypeOutputInfo
*
*
* Copyright (c) 2002, PostgreSQL Global Development Group
*
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/funcapi.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
+ *
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
#include "funcapi.h"
#include "catalog/pg_type.h"
#include "utils/syscache.h"
+
/*
* init_MultiFuncCall
* Create an empty FuncCallContext data structure
void
end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
{
- MemoryContext oldcontext;
-
/* unbind from fcinfo */
fcinfo->flinfo->fn_extra = NULL;
* Caller is responsible to free up memory for individual
* struct elements other than att_in_funcinfo and elements.
*/
- oldcontext = MemoryContextSwitchTo(funcctx->fmctx);
-
if (funcctx->attinmeta != NULL)
pfree(funcctx->attinmeta);
pfree(funcctx);
-
- MemoryContextSwitchTo(oldcontext);
-}
-
-void
-get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
-{
- HeapTuple typeTuple;
- Form_pg_type typtup;
-
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typeid),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "get_type_metadata: Cache lookup of type %u failed", typeid);
-
- typtup = (Form_pg_type) GETSTRUCT(typeTuple);
-
- *attinfuncid = typtup->typinput;
- *attelem = typtup->typelem;
-
- ReleaseSysCache(typeTuple);
}
* command, configuration file, and command line options.
* See src/backend/utils/misc/README for more information.
*
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.84 2002/08/26 17:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.85 2002/08/29 00:17:05 tgl Exp $
*
* Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
tstate = begin_tup_output_tupdesc(dest, tupdesc);
/* Send it */
- PROJECT_LINE_OF_TEXT(tstate, value);
+ do_text_output_oneline(tstate, value);
end_tup_output(tstate);
}
if (call_cntr < max_calls) /* do when there is more left to send */
{
- char **values;
+ char *values[2];
char *varname;
char *varval;
bool noshow;
*/
do
{
- varval = GetConfigOptionByNum(call_cntr, (const char **) &varname, &noshow);
+ varval = GetConfigOptionByNum(call_cntr,
+ (const char **) &varname,
+ &noshow);
if (noshow)
{
/* varval is a palloc'd copy, so free it */
* This should be an array of C strings which will
* be processed later by the appropriate "in" functions.
*/
- values = (char **) palloc(2 * sizeof(char *));
- values[0] = pstrdup(varname);
- values[1] = varval; /* varval is already a palloc'd copy */
+ values[0] = varname;
+ values[1] = varval;
/* build a tuple */
tuple = BuildTupleFromCStrings(attinmeta, values);
result = TupleGetDatum(slot, tuple);
/* Clean up */
- pfree(values[0]);
if (varval != NULL)
- pfree(values[1]);
- pfree(values);
+ pfree(varval);
SRF_RETURN_NEXT(funcctx, result);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.68 2002/08/15 16:36:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.69 2002/08/29 00:17:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
for (i = 0; i < numTables; i++)
{
- /* Sequences, views, and types never have parents */
+ /* Sequences and views never have parents */
if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
- tblinfo[i].relkind == RELKIND_VIEW ||
- tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+ tblinfo[i].relkind == RELKIND_VIEW)
continue;
/* Don't bother computing anything for non-target tables, either */
for (i = 0; i < numTables; i++)
{
- /* Sequences, views, and types never have parents */
+ /* Sequences and views never have parents */
if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
- tblinfo[i].relkind == RELKIND_VIEW ||
- tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+ tblinfo[i].relkind == RELKIND_VIEW)
continue;
/* Don't bother computing anything for non-target tables, either */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.294 2002/08/28 20:57:22 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.295 2002/08/29 00:17:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (tblinfo[i].relkind == RELKIND_VIEW)
continue;
- /* Skip TYPE relations */
- if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
- continue;
-
if (tblinfo[i].relkind == RELKIND_SEQUENCE) /* already dumped */
continue;
"typnamespace, "
"(select usename from pg_user where typowner = usesysid) as usename, "
"typelem, typrelid, "
- "(select relkind from pg_class where oid = typrelid) as typrelkind, "
+ "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
"FROM pg_type");
}
"0::oid as typnamespace, "
"(select usename from pg_user where typowner = usesysid) as usename, "
"typelem, typrelid, "
- "''::char as typrelkind, "
+ "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
"FROM pg_type");
}
}
else if (g_fout->remoteVersion >= 70200)
{
- /* before 7.3 there were no type relations with relkind 'c' */
appendPQExpBuffer(query,
"SELECT pg_class.oid, relname, relacl, relkind, "
"0::oid as relnamespace, "
if (tbinfo->relkind == RELKIND_SEQUENCE)
continue;
- /* Don't bother to collect info for type relations */
- if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE)
- continue;
-
/* Don't bother with uninteresting tables, either */
if (!tbinfo->interesting)
continue;
/* DROP must be fully qualified in case same name appears in pg_catalog */
appendPQExpBuffer(delq, "DROP DOMAIN %s.",
fmtId(tinfo->typnamespace->nspname));
- appendPQExpBuffer(delq, "%s RESTRICT;\n",
+ appendPQExpBuffer(delq, "%s;\n",
fmtId(tinfo->typname));
appendPQExpBuffer(q,
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int ntups;
- char *attname;
- char *atttypdefn;
- char *attbasetype;
- const char *((*deps)[]);
- int depIdx = 0;
+ int i_attname;
+ int i_atttypdefn;
int i;
- deps = malloc(sizeof(char *) * 10);
-
/* Set proper schema search path so type references list correctly */
selectSourceSchema(tinfo->typnamespace->nspname);
/* We assume here that remoteVersion must be at least 70300 */
appendPQExpBuffer(query, "SELECT a.attname, "
- "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn, "
- "a.atttypid as attbasetype "
+ "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
"FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
"WHERE t.oid = '%s'::pg_catalog.oid "
- "AND a.attrelid = t.typrelid",
+ "AND a.attrelid = t.typrelid "
+ "AND NOT a.attisdropped "
+ "ORDER BY a.attnum ",
tinfo->oid);
res = PQexec(g_conn, query->data);
exit_nicely();
}
- /* DROP must be fully qualified in case same name appears in pg_catalog */
- appendPQExpBuffer(delq, "DROP TYPE %s.",
- fmtId(tinfo->typnamespace->nspname));
- appendPQExpBuffer(delq, "%s RESTRICT;\n",
- fmtId(tinfo->typname));
+ i_attname = PQfnumber(res, "attname");
+ i_atttypdefn = PQfnumber(res, "atttypdefn");
- appendPQExpBuffer(q,
- "CREATE TYPE %s AS (",
+ appendPQExpBuffer(q, "CREATE TYPE %s AS (",
fmtId(tinfo->typname));
for (i = 0; i < ntups; i++)
{
- attname = PQgetvalue(res, i, PQfnumber(res, "attname"));
- atttypdefn = PQgetvalue(res, i, PQfnumber(res, "atttypdefn"));
- attbasetype = PQgetvalue(res, i, PQfnumber(res, "attbasetype"));
+ char *attname;
+ char *atttypdefn;
- if (i > 0)
- appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
- else
- appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
+ attname = PQgetvalue(res, i, i_attname);
+ atttypdefn = PQgetvalue(res, i, i_atttypdefn);
- /* Depends on the base type */
- (*deps)[depIdx++] = strdup(attbasetype);
+ if (i > 0)
+ appendPQExpBuffer(q, ",\n\t");
+ appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
}
appendPQExpBuffer(q, ");\n");
- (*deps)[depIdx++] = NULL; /* End of List */
+ /* DROP must be fully qualified in case same name appears in pg_catalog */
+ appendPQExpBuffer(delq, "DROP TYPE %s.",
+ fmtId(tinfo->typnamespace->nspname));
+ appendPQExpBuffer(delq, "%s;\n",
+ fmtId(tinfo->typname));
ArchiveEntry(fout, tinfo->oid, tinfo->typname,
tinfo->typnamespace->nspname,
- tinfo->usename, "TYPE", deps,
+ tinfo->usename, "TYPE", NULL,
q->data, delq->data, NULL, NULL, NULL);
/*** Dump Type Comments ***/
if (!tinfo[i].typnamespace->dump)
continue;
- /* skip relation types for non-stand-alone type relations*/
+ /* skip complex types, except for standalone composite types */
if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
continue;
if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
continue;
- if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
- continue;
if (tbinfo->dump)
{
*
* Copyright 2000-2002 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.66 2002/08/27 20:16:48 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.67 2002/08/29 00:17:05 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
if (verbose)
appendPQExpBuffer(&buf,
" t.typname AS \"%s\",\n"
- " CASE WHEN t.typlen < 0\n"
- " THEN CAST('var' AS pg_catalog.text)\n"
+ " CASE WHEN t.typrelid != 0\n"
+ " THEN CAST('tuple' AS pg_catalog.text)\n"
+ " WHEN t.typlen < 0\n"
+ " THEN CAST('var' AS pg_catalog.text)\n"
" ELSE CAST(t.typlen AS pg_catalog.text)\n"
" END AS \"%s\",\n",
_("Internal name"), _("Size"));
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
/*
- * do not include array types (start with underscore), do not include
- * user relations (typrelid!=0) unless they are type relations
+ * do not include array types (start with underscore); do not include
+ * complex types (typrelid!=0) unless they are standalone composite types
*/
appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
- appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
- "where c.oid = t.typrelid)) ");
+ appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
+ "WHERE c.oid = t.typrelid)) ");
appendPQExpBuffer(&buf, "AND t.typname !~ '^_'\n");
/* Match name pattern against either internal or external name */
printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
schemaname, relationname);
break;
+ case 'c':
+ printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
+ schemaname, relationname);
+ break;
default:
printfPQExpBuffer(&title, _("?%c? \"%s.%s\""),
tableinfo.relkind, schemaname, relationname);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_type.h,v 1.130 2002/08/26 17:54:01 tgl Exp $
+ * $Id: pg_type.h,v 1.131 2002/08/29 00:17:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
Oid typeNamespace,
Oid assignedTypeOid,
Oid relationOid,
+ char relationKind,
int16 internalSize,
char typeType,
char typDelim,
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: executor.h,v 1.73 2002/08/02 18:15:09 tgl Exp $
+ * $Id: executor.h,v 1.74 2002/08/29 00:17:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct TupOutputState
{
- TupleDesc tupdesc;
+ /* use "struct" here to allow forward reference */
+ struct AttInMetadata *metadata;
DestReceiver *destfunc;
} TupOutputState;
extern void do_text_output_multiline(TupOutputState *tstate, char *text);
extern void end_tup_output(TupOutputState *tstate);
-#define PROJECT_LINE_OF_TEXT(tstate, text_to_project) \
+/*
+ * Write a single line of text given as a C string.
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
+ */
+#define do_text_output_oneline(tstate, text_to_emit) \
do { \
char *values_[1]; \
- values_[0] = (text_to_project); \
+ values_[0] = (text_to_emit); \
do_tup_output(tstate, values_); \
} while (0)
*
* Copyright (c) 2002, PostgreSQL Global Development Group
*
+ * $Id: funcapi.h,v 1.5 2002/08/29 00:17:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef FUNCAPI_H
#define FUNCAPI_H
-#include "postgres.h"
-
#include "fmgr.h"
-#include "access/htup.h"
#include "access/tupdesc.h"
#include "executor/executor.h"
#include "executor/tuptable.h"
-/*
- * All functions that can be called directly by fmgr must have this signature.
- * (Other functions can be called by using a handler that does have this
- * signature.)
- */
-
/*-------------------------------------------------------------------------
* Support to ease writing Functions returning composite types
* is derived from the TupleDesc, but it is stored here to
* avoid redundant cpu cycles on each call to an SRF.
*/
-typedef struct
+typedef struct AttInMetadata
{
/* full TupleDesc */
TupleDesc tupdesc;
- /* pointer to array of attribute "type"in finfo */
+ /* array of attribute type input function finfo */
FmgrInfo *attinfuncs;
- /* pointer to array of attribute type typelem */
+ /* array of attribute type typelem */
Oid *attelems;
- /* pointer to array of attribute type typtypmod */
- int4 *atttypmods;
-
+ /* array of attribute typmod */
+ int32 *atttypmods;
} AttInMetadata;
/*-------------------------------------------------------------------------
* This struct holds function context for Set Returning Functions.
* Use fn_extra to hold a pointer to it across calls
*/
-typedef struct
+typedef struct FuncCallContext
{
/*
* Number of times we've been called before.
} FuncCallContext;
-/*-------------------------------------------------------------------------
+/*----------
* Support to ease writing Functions returning composite types
*
* External declarations:
- * TupleDesc RelationNameGetTupleDesc(char *relname) - Use to get a TupleDesc
- * based on the function's return type relation.
+ * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
+ * TupleDesc based on a specified relation.
* TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use to get a
- * TupleDesc based on the function's type oid. This can be used to get
- * a TupleDesc for a base (scalar), or composite (relation) type.
+ * TupleDesc based on a type OID. This can be used to get
+ * a TupleDesc for a base (scalar) or composite (relation) type.
* TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
* given a TupleDesc.
- * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Get a pointer
- * to AttInMetadata based on the function's TupleDesc. AttInMetadata can
+ * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Build an
+ * AttInMetadata struct based on the given TupleDesc. AttInMetadata can
* be used in conjunction with C strings to produce a properly formed
* tuple. Store the metadata here for use across calls to avoid redundant
* work.
* HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -
* build a HeapTuple given user data in C string form. values is an array
* of C strings, one for each attribute of the return tuple.
- * void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem) - Get
- * an attribute "in" function and typelem value given the typeid.
*
* Macro declarations:
* TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
* given a tuple and a slot.
+ *----------
*/
/* from tupdesc.c */
-extern TupleDesc RelationNameGetTupleDesc(char *relname);
+extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
-/* from funcapi.c */
-extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem);
-
#define TupleGetDatum(_slot, _tuple) \
PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, true))
-/*-------------------------------------------------------------------------
+
+/*----------
* Support for Set Returning Functions (SRFs)
*
* The basic API for SRFs looks something like:
* }
* }
*
+ *----------
*/
/* from funcapi.c */
extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
#define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
+
#define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
+
#define SRF_PERCALL_SETUP() per_MultiFuncCall(fcinfo)
+
#define SRF_RETURN_NEXT(_funcctx, _result) \
do { \
ReturnSetInfo *rsi; \
- _funcctx->call_cntr++; \
+ (_funcctx)->call_cntr++; \
rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
rsi->isDone = ExprMultipleResult; \
PG_RETURN_DATUM(_result); \
end_MultiFuncCall(fcinfo, _funcctx); \
rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
rsi->isDone = ExprEndResult; \
- _funcctx->slot = NULL; \
PG_RETURN_NULL(); \
} while (0)
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execnodes.h,v 1.71 2002/08/04 19:48:10 momjian Exp $
+ * $Id: execnodes.h,v 1.72 2002/08/29 00:17:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Function nodes are used to scan the results of a
* function appearing in FROM (typically a function returning set).
*
- * functionmode function operating mode:
- * - repeated call
- * - materialize
- * - return query
+ * functionmode function operating mode
* tupdesc function's return tuple description
* tuplestorestate private state of tuplestore.c
* funcexpr function expression being evaluated
* returnsTuple does function return tuples?
* fn_typeid OID of function return type
- * fn_typtype return Datum type, i.e. 'b'ase,
- * 'c'atalog, or 'p'seudo
+ * fn_typtype return type's typtype
* ----------------
*/
typedef enum FunctionMode
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: builtins.h,v 1.195 2002/08/22 03:24:01 momjian Exp $
+ * $Id: builtins.h,v 1.196 2002/08/29 00:17:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum set_config_by_name(PG_FUNCTION_ARGS);
extern Datum show_all_settings(PG_FUNCTION_ARGS);
+/* lockfuncs.c */
+extern Datum pg_lock_status(PG_FUNCTION_ARGS);
+
/* catalog/pg_conversion.c */
extern Datum pg_convert3(PG_FUNCTION_ARGS);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.59 2002/08/26 17:54:02 tgl Exp $
+ * $Id: lsyscache.h,v 1.60 2002/08/29 00:17:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern char get_typstorage(Oid typid);
extern Node *get_typdefault(Oid typid);
extern char get_typtype(Oid typid);
+extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
bool *typIsVarlena);
extern Oid getBaseType(Oid typid);
1 | 2 | Ed
(2 rows)
+-- sql, proretset = f, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+ fooid | foosubid | fooname
+-------+----------+---------
+ 1 | 1 | Joe
+(1 row)
+
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+ fooid | foosubid | fooname
+-------+----------+---------
+ 1 | 1 | Joe
+(1 row)
+
+-- sql, proretset = t, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS setof record AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+ fooid | foosubid | fooname
+-------+----------+---------
+ 1 | 1 | Joe
+ 1 | 2 | Ed
+(2 rows)
+
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+ fooid | foosubid | fooname
+-------+----------+---------
+ 1 | 1 | Joe
+ 1 | 2 | Ed
+(2 rows)
+
-- plpgsql, proretset = f, prorettype = b
DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
+-- sql, proretset = f, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+
+-- sql, proretset = t, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS setof record AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+
-- plpgsql, proretset = f, prorettype = b
DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);