Code review for standalone composite types, query-specified composite
types, SRFs. Not happy with memory management yet, but I'll commit these other changes.
This commit is contained in:
parent
7483749d82
commit
64505ed58b
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
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 FROM clause composite type
|
||||
must match the actual composite type returned from the function or an
|
||||
ERROR will be generated.
|
||||
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">
|
||||
|
@ -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">
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
/* 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;
|
||||
</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>
|
||||
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.
|
||||
An example appears below.
|
||||
</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Returning Sets</title>
|
||||
|
||||
<para>
|
||||
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>
|
||||
@ -1679,31 +1722,38 @@ my_Set_Returning_Function(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
Datum result;
|
||||
|
||||
[user defined declarations]
|
||||
|
||||
if(SRF_IS_FIRSTCALL())
|
||||
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;
|
||||
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]
|
||||
|
||||
if (funcctx->call_cntr < funcctx->max_calls)
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
@ -1711,7 +1761,7 @@ my_Set_Returning_Function(PG_FUNCTION_ARGS)
|
||||
</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
|
||||
@ -1725,13 +1775,13 @@ testpassbyval(PG_FUNCTION_ARGS)
|
||||
AttInMetadata *attinmeta;
|
||||
|
||||
/* stuff done only on the first call of the function */
|
||||
if(SRF_IS_FIRSTCALL())
|
||||
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);
|
||||
funcctx->max_calls = PG_GETARG_UINT32(0);
|
||||
|
||||
/*
|
||||
* Build a tuple description for a __testpassbyval tuple
|
||||
@ -1742,25 +1792,25 @@ testpassbyval(PG_FUNCTION_ARGS)
|
||||
slot = TupleDescGetSlot(tupdesc);
|
||||
|
||||
/* assign slot to function context */
|
||||
funcctx->slot = slot;
|
||||
funcctx->slot = slot;
|
||||
|
||||
/*
|
||||
* Generate attribute metadata needed later to produce tuples from raw
|
||||
* C strings
|
||||
*/
|
||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||
funcctx->attinmeta = attinmeta;
|
||||
funcctx->attinmeta = attinmeta;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
if (call_cntr < max_calls) /* do when there is more left to send */
|
||||
{
|
||||
char **values;
|
||||
HeapTuple tuple;
|
||||
@ -1802,11 +1852,7 @@ testpassbyval(PG_FUNCTION_ARGS)
|
||||
</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">
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
/* XXX should we hold the lock to ensure table doesn't change? */
|
||||
|
||||
/* check to see if we've given column aliases */
|
||||
if(colaliases != NIL)
|
||||
if (colaliases != NIL)
|
||||
{
|
||||
char *label;
|
||||
int varattno;
|
||||
|
||||
/* does the List length match the 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));
|
||||
char *label = strVal(nth(varattno, colaliases));
|
||||
|
||||
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");
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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));
|
||||
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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]),
|
||||
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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,32 +200,27 @@ 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
|
||||
*/
|
||||
Oid funcrelid;
|
||||
Relation rel;
|
||||
|
||||
funcrelid = typeidTypeRelid(funcrettype);
|
||||
if (!OidIsValid(funcrelid))
|
||||
elog(ERROR, "Invalid typrelid for complex type %u",
|
||||
funcrettype);
|
||||
rel = relation_open(funcrelid, AccessShareLock);
|
||||
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
||||
relation_close(rel, AccessShareLock);
|
||||
scanstate->returnsTuple = true;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Invalid return relation specified for function");
|
||||
}
|
||||
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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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,14 +1424,14 @@ OptTableElementList:
|
||||
;
|
||||
|
||||
TableElementList:
|
||||
TableElementList ',' TableElement
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
| TableElement
|
||||
TableElement
|
||||
{
|
||||
$$ = makeList1($1);
|
||||
}
|
||||
| TableElementList ',' TableElement
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
;
|
||||
|
||||
TableElement:
|
||||
@ -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,25 +4812,19 @@ func_table: func_name '(' ')'
|
||||
|
||||
where_clause:
|
||||
WHERE a_expr { $$ = $2; }
|
||||
/* no qualifiers */
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
;
|
||||
|
||||
|
||||
OptTableFuncElementList:
|
||||
TableFuncElementList { $$ = $1; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
TableFuncElementList:
|
||||
TableFuncElementList ',' TableFuncElement
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
| TableFuncElement
|
||||
TableFuncElement
|
||||
{
|
||||
$$ = makeList1($1);
|
||||
}
|
||||
| TableFuncElementList ',' TableFuncElement
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
;
|
||||
|
||||
TableFuncElement: ColId Typename
|
||||
@ -4842,7 +4833,6 @@ TableFuncElement: ColId Typename
|
||||
n->colname = $1;
|
||||
n->typename = $2;
|
||||
n->constraints = NIL;
|
||||
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
@ -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,28 +915,30 @@ 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))
|
||||
elog(ERROR, "Invalid typrelid for complex type %u",
|
||||
funcrettype);
|
||||
|
||||
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;
|
||||
|
||||
rel = heap_open(funcrelid, AccessShareLock);
|
||||
rel = relation_open(funcrelid, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Since the rel is open anyway, let's check that the number of column
|
||||
@ -964,11 +963,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
|
||||
* transaction so that the table can't be deleted or have its schema
|
||||
* modified underneath us.
|
||||
*/
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Invalid return relation specified for function %s",
|
||||
funcname);
|
||||
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,23 +1274,22 @@ 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')
|
||||
{
|
||||
Oid funcrelid = typeidTypeRelid(funcrettype);
|
||||
if (OidIsValid(funcrelid))
|
||||
{
|
||||
/*
|
||||
* Composite data type, i.e. a table's row type
|
||||
* Same as ordinary relation RTE
|
||||
*/
|
||||
Oid funcrelid = typeidTypeRelid(funcrettype);
|
||||
Relation rel;
|
||||
int maxattrs;
|
||||
int numaliases;
|
||||
|
||||
rel = heap_open(funcrelid, AccessShareLock);
|
||||
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);
|
||||
|
||||
@ -1329,11 +1325,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
}
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
}
|
||||
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,19 +1590,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
||||
* Same as ordinary relation RTE
|
||||
*/
|
||||
Oid funcrelid = typeidTypeRelid(funcrettype);
|
||||
|
||||
if (OidIsValid(funcrelid))
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_attribute att_tup;
|
||||
|
||||
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",
|
||||
elog(ERROR, "Relation \"%s\" does not have attribute %d",
|
||||
get_rel_name(funcrelid), attnum);
|
||||
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
|
||||
/*
|
||||
@ -1632,10 +1618,6 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
||||
*vartypmod = att_tup->atttypmod;
|
||||
ReleaseSysCache(tp);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Invalid return relation specified"
|
||||
" for function");
|
||||
}
|
||||
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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
29
src/backend/utils/cache/lsyscache.c
vendored
29
src/backend/utils/cache/lsyscache.c
vendored
@ -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.
|
||||
@ -1190,6 +1190,33 @@ get_typtype(Oid typid)
|
||||
return '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* getTypeInputInfo
|
||||
*
|
||||
* Get info needed for converting values of a type to internal form
|
||||
*/
|
||||
void
|
||||
getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
Form_pg_type pt;
|
||||
|
||||
typeTuple = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(typeTuple))
|
||||
elog(ERROR, "getTypeInputInfo: Cache lookup of type %u failed", type);
|
||||
pt = (Form_pg_type) GETSTRUCT(typeTuple);
|
||||
|
||||
if (!pt->typisdefined)
|
||||
elog(ERROR, "Type \"%s\" is only a shell", NameStr(pt->typname));
|
||||
|
||||
*typInput = pt->typinput;
|
||||
*typElem = pt->typelem;
|
||||
|
||||
ReleaseSysCache(typeTuple);
|
||||
}
|
||||
|
||||
/*
|
||||
* getTypeOutputInfo
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 <peter_e@gmx.net>.
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
attname = PQgetvalue(res, i, i_attname);
|
||||
atttypdefn = PQgetvalue(res, i, i_atttypdefn);
|
||||
|
||||
if (i > 0)
|
||||
appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
|
||||
else
|
||||
appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
|
||||
|
||||
/* Depends on the base type */
|
||||
(*deps)[depIdx++] = strdup(attbasetype);
|
||||
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)
|
||||
{
|
||||
|
@ -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,7 +196,9 @@ describeTypes(const char *pattern, bool verbose)
|
||||
if (verbose)
|
||||
appendPQExpBuffer(&buf,
|
||||
" t.typname AS \"%s\",\n"
|
||||
" CASE WHEN t.typlen < 0\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",
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user