diff options
author | Tom Lane | 2002-08-29 00:17:06 +0000 |
---|---|---|
committer | Tom Lane | 2002-08-29 00:17:06 +0000 |
commit | 64505ed58ba71df3221e2467dc458af2e1912895 (patch) | |
tree | 3c110a6d9e3badd87d741976871028760b8f55b5 | |
parent | 7483749d8207c0cbcce5ce69161400ace31a6856 (diff) |
Code review for standalone composite types, query-specified composite
types, SRFs. Not happy with memory management yet, but I'll commit these
other changes.
41 files changed, 847 insertions, 755 deletions
diff --git a/contrib/dbsize/dbsize.c b/contrib/dbsize/dbsize.c index 0e5e63d1eee..8bc216bf799 100644 --- a/contrib/dbsize/dbsize.c +++ b/contrib/dbsize/dbsize.c @@ -112,7 +112,7 @@ relation_size(PG_FUNCTION_ARGS) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, "relation_size")); - relation = relation_openrv(relrv, AccessShareLock); + relation = heap_openrv(relrv, AccessShareLock); relnode = relation->rd_rel->relfilenode; @@ -140,7 +140,7 @@ relation_size(PG_FUNCTION_ARGS) segcount++; } - relation_close(relation, AccessShareLock); + heap_close(relation, AccessShareLock); PG_RETURN_INT64(totalsize); } diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index f100f4d87b5..b2d454f129b 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -32,11 +32,7 @@ CREATE TYPE <replaceable class="parameter">typename</replaceable> ( INPUT = <rep ) 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"> @@ -216,8 +212,12 @@ CREATE TYPE 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 @@ -338,20 +338,27 @@ CREATE TYPE 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 @@ -436,8 +443,8 @@ CREATE TABLE big_objs (id int4, obj bigobj); 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> diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index 5fdaf90f0ac..f6dd0397570 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -40,10 +40,10 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: ( <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> @@ -142,10 +142,14 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: <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> @@ -172,12 +176,15 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: 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> @@ -395,7 +402,7 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: 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> @@ -406,16 +413,22 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be: </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> @@ -827,6 +840,38 @@ SELECT name FROM distributors ORDER BY code; 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"> @@ -1019,8 +1064,7 @@ SELECT * FROM distributors_2(111) AS (f1 int, f2 text); <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; @@ -1062,6 +1106,11 @@ and later will warn if the implicit-FROM feature is used in a query that also 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"> diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index 13e139ffbaa..8eed28791a1 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -1,5 +1,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 --> @@ -29,20 +29,9 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac [ 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"> diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index e457504ebef..60c78d0588f 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ <!-- -$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"> @@ -26,6 +26,7 @@ worries about funny characters. <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. diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index a38305ce0bc..b3f653a28a1 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,5 +1,5 @@ <!-- -$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"> @@ -10,23 +10,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.56 2002/08/23 16:41:37 tgl E <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: @@ -285,8 +268,6 @@ SELECT name, double_salary(EMP) AS dream <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: @@ -330,12 +311,12 @@ ERROR: function declared to return emp returns varchar instead of text at colum </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> @@ -357,8 +338,8 @@ ERROR: parser: parse error at or near "." </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: @@ -412,26 +393,73 @@ SELECT getname(new_emp()); </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> @@ -445,14 +473,7 @@ SELECT * FROM getfoo(1) AS t1; </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, @@ -1346,7 +1367,8 @@ concat_text(PG_FUNCTION_ARGS) <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> @@ -1471,8 +1493,8 @@ LANGUAGE C; <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 @@ -1482,105 +1504,124 @@ LANGUAGE C; <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 { @@ -1639,12 +1680,13 @@ 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() @@ -1663,8 +1705,9 @@ SRF_PERCALL_SETUP() <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> @@ -1677,136 +1720,139 @@ SRF_RETURN_DONE(funcctx) 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; @@ -1816,6 +1862,9 @@ CREATE OR REPLACE FUNCTION testpassbyval(int4, int4) RETURNS setof __testpassbyv <para> See contrib/tablefunc for more examples of Table Functions. </para> + + </sect3> + </sect2> <sect2> @@ -2031,23 +2080,23 @@ CREATE FUNCTION test(int, int) RETURNS int 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"> diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 0e1765fc09e..8637c72b492 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * 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 @@ -570,7 +570,7 @@ BuildDescForRelation(List *schema) * Given a (possibly qualified) relation name, build a TupleDesc. */ TupleDesc -RelationNameGetTupleDesc(char *relname) +RelationNameGetTupleDesc(const char *relname) { RangeVar *relvar; Relation rel; @@ -580,7 +580,7 @@ RelationNameGetTupleDesc(char *relname) /* 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); @@ -611,50 +611,46 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) { /* 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"); @@ -671,8 +667,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) 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"); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 325dff88dbc..89e0fec0ed5 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * 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 @@ -607,6 +607,9 @@ heap_open(Oid relationId, LOCKMODE lockmode) 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); @@ -633,6 +636,9 @@ heap_openrv(const RangeVar *relation, LOCKMODE lockmode) 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); @@ -659,6 +665,9 @@ heap_openr(const char *sysRelationName, LOCKMODE lockmode) 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); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 0c21400ca1d..c768ca2d554 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * 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 @@ -69,6 +69,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, 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); @@ -357,7 +358,7 @@ CheckAttributeNames(TupleDesc tupdesc, bool relhasoids, char relkind) /* * 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) @@ -618,6 +619,7 @@ static void AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, + char new_rel_kind, Oid new_type_oid) { /* @@ -633,6 +635,7 @@ AddNewRelationType(const char *typeName, 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 */ @@ -728,7 +731,11 @@ heap_create_with_catalog(const char *relname, * 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 @@ -904,7 +911,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) * 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); @@ -943,7 +950,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) heap_close(attr_rel, RowExclusiveLock); - heap_close(rel, NoLock); + relation_close(rel, NoLock); } /* @@ -1036,7 +1043,7 @@ RemoveAttrDefaultById(Oid attrdefId) 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); @@ -1069,7 +1076,7 @@ RemoveAttrDefaultById(Oid attrdefId) heap_close(attr_rel, RowExclusiveLock); /* Keep lock on attribute's rel until end of xact */ - heap_close(myrel, NoLock); + relation_close(myrel, NoLock); } /* ---------------------------------------------------------------- @@ -1099,7 +1106,7 @@ heap_drop_with_catalog(Oid rid) /* * 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 @@ -1134,7 +1141,7 @@ heap_drop_with_catalog(Oid rid) * 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); /* @@ -1142,7 +1149,7 @@ heap_drop_with_catalog(Oid rid) * 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 diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 8f6caa5d4d9..33fc901e867 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -1585,7 +1585,6 @@ RemoveTempRelations(Oid tempNamespaceId) 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); diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 1bdb3796e66..e8608925bf5 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -400,7 +400,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList) * 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; @@ -447,7 +447,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList) 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) { diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 6c6a135a0b9..ec0704b78e6 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -129,7 +129,8 @@ Oid 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, @@ -332,15 +333,11 @@ TypeCreate(const char *typeName, */ 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); diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index ce489280182..ea04fd687dc 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -362,7 +362,7 @@ CommentAttribute(List *qualname, char *comment) /* 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 */ @@ -383,7 +383,7 @@ CommentAttribute(List *qualname, char *comment) /* Done, but hold lock until commit */ - heap_close(relation, NoLock); + relation_close(relation, NoLock); } /* diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index c0b40c6e143..b9eecf4b8be 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -399,9 +399,6 @@ DoCopy(const CopyStmt *stmt) 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)); @@ -447,9 +444,6 @@ DoCopy(const CopyStmt *stmt) 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)); diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 13cf272525c..efae790dd8c 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -5,7 +5,7 @@ * 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 $ * */ @@ -79,7 +79,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) 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 { @@ -89,7 +89,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) 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 { @@ -99,7 +99,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) 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, ""); } } } @@ -122,9 +122,9 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *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; } @@ -189,7 +189,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) 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 */ } } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 81f1c4cfb72..04b0266dbc5 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -228,7 +228,7 @@ DefineRelation(CreateStmt *stmt, char relkind) * 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 @@ -293,7 +293,7 @@ DefineRelation(CreateStmt *stmt, char relkind) * 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; } @@ -1064,7 +1064,7 @@ renameatt(Oid relid, * 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, @@ -1210,7 +1210,7 @@ renameatt(Oid relid, true, false); } - heap_close(targetrelation, NoLock); /* close rel but keep lock! */ + relation_close(targetrelation, NoLock); /* close rel but keep lock! */ } /* @@ -3247,13 +3247,12 @@ CheckTupleType(Form_pg_class tuple_class) 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)); } } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 02e5a686e77..bb1250b3572 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * 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 @@ -233,6 +233,7 @@ DefineType(List *names, List *parameters) 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 */ @@ -262,6 +263,7 @@ DefineType(List *names, List *parameters) 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 */ @@ -562,6 +564,7 @@ DefineDomain(CreateDomainStmt *stmt) 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 */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 7a6e1c69956..6318d79d4eb 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -786,10 +786,6 @@ initResultRelInfo(ResultRelInfo *resultRelInfo, 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)); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 06a784ce26d..afadcd3137b 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -109,8 +109,9 @@ #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 @@ -676,8 +677,7 @@ ExecTypeFromTL(List *targetList, hasoid_t withoid) } /* - * TupleDescGetSlot - Initialize a slot based on the supplied - * tupledesc + * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc */ TupleTableSlot * TupleDescGetSlot(TupleDesc tupdesc) @@ -695,40 +695,36 @@ 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; @@ -746,39 +742,35 @@ TupleDescGetAttInMetadata(TupleDesc 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'; } } @@ -788,6 +780,13 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) */ 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; } @@ -804,7 +803,7 @@ begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc) tstate = (TupOutputState *) palloc(sizeof(TupOutputState)); - tstate->tupdesc = tupdesc; + tstate->metadata = TupleDescGetAttInMetadata(tupdesc); tstate->destfunc = DestToFunction(dest); (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT, @@ -823,20 +822,22 @@ void 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) @@ -859,5 +860,6 @@ void end_tup_output(TupOutputState *tstate) { (*tstate->destfunc->cleanup) (tstate->destfunc); + /* XXX worth cleaning up the attinmetadata? */ pfree(tstate); } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index eba919b8b66..fe473404b91 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -193,9 +193,10 @@ init_sql_fcache(FmgrInfo *finfo) */ 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; } diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index b8aecc2b825..381b6047bf0 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -130,10 +130,10 @@ FunctionNext(FunctionScan *node) /* ---------------------------------------------------------------- * 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. * */ @@ -156,7 +156,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) FunctionScanState *scanstate; RangeTblEntry *rte; Oid funcrettype; - Oid funcrelid; char functyptype; TupleDesc tupdesc = NULL; @@ -201,31 +200,26 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) /* * 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') { @@ -461,8 +455,7 @@ function_getonetuple(FunctionScanState *scanstate, */ 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 { @@ -480,7 +473,7 @@ function_getonetuple(FunctionScanState *scanstate, slot, /* slot to store in */ InvalidBuffer, /* buffer associated with * this tuple */ - true); /* pfree this pointer */ + true); /* pfree this tuple */ } } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3a407753392..ca88b520f96 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * 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 @@ -145,7 +145,7 @@ _outIndexStmt(StringInfo str, IndexStmt *node) static void _outNotifyStmt(StringInfo str, NotifyStmt *node) { - appendStringInfo(str, "NOTIFY :relation "); + appendStringInfo(str, " NOTIFY :relation "); _outNode(str, node->relation); } @@ -153,14 +153,14 @@ static void _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); @@ -1006,7 +1006,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) case RTE_FUNCTION: appendStringInfo(str, ":funcexpr "); _outNode(str, node->funcexpr); - appendStringInfo(str, ":coldeflist "); + appendStringInfo(str, " :coldeflist "); _outNode(str, node->coldeflist); break; case RTE_JOIN: diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 561145bb97d..463a8d5a4e5 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * 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 @@ -204,8 +204,8 @@ static void doNegateFloat(Value *v); %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, @@ -216,7 +216,7 @@ static void doNegateFloat(Value *v); 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 @@ -1424,13 +1424,13 @@ OptTableElementList: ; TableElementList: - TableElementList ',' TableElement + TableElement { - $$ = lappend($1, $3); + $$ = makeList1($1); } - | TableElement + | TableElementList ',' TableElement { - $$ = makeList1($1); + $$ = lappend($1, $3); } ; @@ -2234,11 +2234,12 @@ DefineStmt: 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: @@ -2258,13 +2259,12 @@ DefineStmt: 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 @@ -2277,9 +2277,6 @@ DefineStmt: } ; -rowdefinition: '(' TableFuncElementList ')' { $$ = $2; } - ; - definition: '(' def_list ')' { $$ = $2; } ; @@ -4539,14 +4536,22 @@ table_ref: relation_expr 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); @@ -4556,7 +4561,7 @@ table_ref: relation_expr n->coldeflist = $5; $$ = (Node *) n; } - | func_table ColId '(' OptTableFuncElementList ')' + | func_table ColId '(' TableFuncElementList ')' { RangeFunction *n = makeNode(RangeFunction); Alias *a = makeNode(Alias); @@ -4566,14 +4571,6 @@ table_ref: relation_expr 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 { /* @@ -4815,24 +4812,18 @@ func_table: func_name '(' ')' 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); } ; @@ -4842,7 +4833,6 @@ TableFuncElement: ColId Typename n->colname = $1; n->typename = $2; n->constraints = NIL; - $$ = (Node *)n; } ; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 6713c665098..174c05790d9 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -900,17 +900,14 @@ addRangeTableEntryForFunction(ParseState *pstate, * 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 { @@ -918,57 +915,55 @@ addRangeTableEntryForFunction(ParseState *pstate, * ... 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') { @@ -986,10 +981,12 @@ addRangeTableEntryForFunction(ParseState *pstate, { 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)); @@ -1277,63 +1274,58 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, 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') { @@ -1376,12 +1368,9 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, 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, @@ -1394,8 +1383,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, } } 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: @@ -1595,9 +1583,6 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, char functyptype = get_typtype(funcrettype); List *coldeflist = rte->coldeflist; - /* - * Build a suitable tupledesc representing the output rows - */ if (functyptype == 'c') { /* @@ -1605,36 +1590,33 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, * 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') { @@ -1647,19 +1629,12 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, 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: diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index b16adef54db..65745be3c00 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -583,14 +583,9 @@ ProcessUtility(Node *parsetree, 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; diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 38e540e3c85..83d0d1051df 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -5,22 +5,22 @@ * 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) { diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index bd642a26e97..d90c20adf98 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -8,7 +8,7 @@ * * * 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(). @@ -226,9 +226,6 @@ currtid_byreloid(PG_FUNCTION_ARGS) 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); @@ -252,9 +249,6 @@ currtid_byrelname(PG_FUNCTION_ARGS) 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); diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 079ba2152a7..66dc58d6c4b 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * 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. @@ -1191,6 +1191,33 @@ get_typtype(Oid typid) } /* + * 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 * * Get info needed for printing values of a type diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index a8d4e4fb5f5..28311c26b7b 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -6,13 +6,18 @@ * * 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 @@ -99,8 +104,6 @@ per_MultiFuncCall(PG_FUNCTION_ARGS) void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx) { - MemoryContext oldcontext; - /* unbind from fcinfo */ fcinfo->flinfo->fn_extra = NULL; @@ -108,32 +111,8 @@ end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx) * 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); } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index b73118289fc..660cd124ba9 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5,7 +5,7 @@ * 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 <[email protected]>. @@ -2284,7 +2284,7 @@ ShowGUCConfigOption(const char *name) 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); } @@ -2462,7 +2462,7 @@ show_all_settings(PG_FUNCTION_ARGS) if (call_cntr < max_calls) /* do when there is more left to send */ { - char **values; + char *values[2]; char *varname; char *varval; bool noshow; @@ -2474,7 +2474,9 @@ show_all_settings(PG_FUNCTION_ARGS) */ 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 */ @@ -2495,9 +2497,8 @@ show_all_settings(PG_FUNCTION_ARGS) * 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); @@ -2506,10 +2507,8 @@ show_all_settings(PG_FUNCTION_ARGS) result = TupleGetDatum(slot, tuple); /* Clean up */ - pfree(values[0]); if (varval != NULL) - pfree(values[1]); - pfree(values); + pfree(varval); SRF_RETURN_NEXT(funcctx, result); } diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 4f33ff4e4fd..ddcb6179745 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -11,7 +11,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -215,10 +215,9 @@ flagInhTables(TableInfo *tblinfo, int numTables, 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 */ @@ -270,10 +269,9 @@ flagInhAttrs(TableInfo *tblinfo, int numTables, 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 */ diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a56f176841a..d552f63a3a8 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -1148,10 +1148,6 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, 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; @@ -1581,7 +1577,8 @@ getTypes(int *numTypes) "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"); } @@ -1591,7 +1588,8 @@ getTypes(int *numTypes) "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"); } @@ -2120,7 +2118,6 @@ getTables(int *numTables) } 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, " @@ -2392,10 +2389,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables) 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; @@ -3210,7 +3203,7 @@ dumpOneDomain(Archive *fout, TypeInfo *tinfo) /* 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, @@ -3263,15 +3256,10 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo) 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); @@ -3279,11 +3267,12 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo) /* 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); @@ -3302,37 +3291,35 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo) 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 ***/ @@ -3365,7 +3352,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, 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; @@ -5046,8 +5033,6 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables, if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */ continue; - if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */ - continue; if (tbinfo->dump) { diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 64cef431701..4bf3b6cc491 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * 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" @@ -196,8 +196,10 @@ describeTypes(const char *pattern, bool verbose) 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")); @@ -209,12 +211,12 @@ describeTypes(const char *pattern, bool verbose) " 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 */ @@ -801,6 +803,10 @@ describeOneTableDetails(const char *schemaname, 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); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 8efb6c07a3d..3708a71bab9 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * 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 @@ -542,6 +542,7 @@ extern Oid TypeCreate(const char *typeName, Oid typeNamespace, Oid assignedTypeOid, Oid relationOid, + char relationKind, int16 internalSize, char typeType, char typDelim, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 2fdb5bc210c..88104565976 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -123,7 +123,8 @@ extern void SetChangedParamList(Plan *node, List *newchg); typedef struct TupOutputState { - TupleDesc tupdesc; + /* use "struct" here to allow forward reference */ + struct AttInMetadata *metadata; DestReceiver *destfunc; } TupOutputState; @@ -132,10 +133,15 @@ extern void do_tup_output(TupOutputState *tstate, char **values); 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) diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 29414753820..27dbdf20e62 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -9,26 +9,18 @@ * * 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 @@ -40,20 +32,19 @@ * 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; /*------------------------------------------------------------------------- @@ -63,7 +54,7 @@ typedef struct * 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. @@ -120,35 +111,34 @@ typedef struct } 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 */ @@ -156,13 +146,11 @@ extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc); 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: @@ -200,6 +188,7 @@ extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem); * } * } * + *---------- */ /* from funcapi.c */ @@ -208,12 +197,15 @@ extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS); 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); \ @@ -225,7 +217,6 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx); end_MultiFuncCall(fcinfo, _funcctx); \ rsi = (ReturnSetInfo *) fcinfo->resultinfo; \ rsi->isDone = ExprEndResult; \ - _funcctx->slot = NULL; \ PG_RETURN_NULL(); \ } while (0) diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index ee25cc62c95..6e146e2ca6d 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -509,17 +509,13 @@ typedef struct SubqueryScanState * 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 diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index bdee336b002..6f33ee4e42c 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -690,6 +690,9 @@ extern Datum show_config_by_name(PG_FUNCTION_ARGS); 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); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 78d09908659..2681d67139f 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -54,6 +54,7 @@ extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, 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); diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 582859d15a5..bf7d2df24dc 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -134,6 +134,44 @@ SELECT * FROM vw_getfoo; 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); diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index 0ace80e5d4d..03a3a558461 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -63,6 +63,24 @@ SELECT * FROM getfoo(1) AS t1; 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); |