Second phase of committing Rod Taylor's pg_depend/pg_constraint patch.

pg_relcheck is gone; CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY
constraints all have real live entries in pg_constraint.  pg_depend
exists, and RESTRICT/CASCADE options work on most kinds of DROP;
however, pg_depend is not yet very well populated with dependencies.
(Most of the ones that are present at this point just replace formerly
hardwired associations, such as the implicit drop of a relation's pg_type
entry when the relation is dropped.)  Need to add more logic to create
dependency entries, improve pg_dump to dump constraints in place of
indexes and triggers, and add some regression tests.
This commit is contained in:
Tom Lane 2002-07-12 18:43:19 +00:00
parent 791a40f943
commit 7c6df91dda
77 changed files with 4074 additions and 1987 deletions

View File

@ -1,6 +1,6 @@
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.44 2002/06/20 15:44:06 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.45 2002/07/12 18:43:12 tgl Exp $
-->
<chapter id="catalogs">
@ -71,11 +71,21 @@
<entry>tables, indexes, sequences (<quote>relations</quote>)</entry>
</row>
<row>
<entry>pg_constraint</entry>
<entry>check constraints, unique / primary key constraints, foreign key constraints</entry>
</row>
<row>
<entry>pg_database</entry>
<entry>databases within this database cluster</entry>
</row>
<row>
<entry>pg_depend</entry>
<entry>dependencies between database objects</entry>
</row>
<row>
<entry>pg_description</entry>
<entry>descriptions or comments on database objects</entry>
@ -131,11 +141,6 @@
<entry>functions and procedures</entry>
</row>
<row>
<entry>pg_relcheck</entry>
<entry>check constraints</entry>
</row>
<row>
<entry>pg_rewrite</entry>
<entry>query rewriter rules</entry>
@ -680,7 +685,7 @@
<entry></entry>
<entry>
Number of check constraints on the table; see
<structname>pg_relcheck</structname> catalog
<structname>pg_constraint</structname> catalog
</entry>
</row>
@ -764,6 +769,167 @@
</table>
</sect1>
<sect1 id="catalog-pg-constraint">
<title>pg_constraint</title>
<para>
This system catalog stores CHECK, PRIMARY KEY, UNIQUE, and FOREIGN KEY
constraints on tables. (Column
constraints are not treated specially. Every column constraint is
equivalent to some table constraint.) See under <command>CREATE
TABLE</command> for more information.
</para>
<note>
<para>
NOT NULL constraints are represented in the <structname>pg_attribute</>
catalog.
</para>
</note>
<para>
CHECK constraints on domains are stored here, too. Global ASSERTIONS
(a currently-unsupported SQL feature) may someday appear here as well.
</para>
<table>
<title>pg_constraint Columns</title>
<tgroup cols=4>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>conname</entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Constraint name (not necessarily unique!)</entry>
</row>
<row>
<entry>connamespace</entry>
<entry><type>oid</type></entry>
<entry>pg_namespace.oid</entry>
<entry>
The OID of the namespace that contains this constraint
</entry>
</row>
<row>
<entry>contype</entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>
'c' = check constraint,
'f' = foreign key constraint,
'p' = primary key constraint,
'u' = unique constraint
</entry>
</row>
<row>
<entry>condeferrable</entry>
<entry><type>boolean</type></entry>
<entry></entry>
<entry>Is the constraint deferrable?</entry>
</row>
<row>
<entry>condeferred</entry>
<entry><type>boolean</type></entry>
<entry></entry>
<entry>Is the constraint deferred by default?</entry>
</row>
<row>
<entry>conrelid</entry>
<entry><type>oid</type></entry>
<entry>pg_class.oid</entry>
<entry>The table this constraint is on; 0 if not a table constraint</entry>
</row>
<row>
<entry>contypid</entry>
<entry><type>oid</type></entry>
<entry>pg_type.oid</entry>
<entry>The domain this constraint is on; 0 if not a domain constraint</entry>
</row>
<row>
<entry>confrelid</entry>
<entry><type>oid</type></entry>
<entry>pg_class.oid</entry>
<entry>If a foreign key, the referenced table; else 0</entry>
</row>
<row>
<entry>confupdtype</entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>Foreign key update action code</entry>
</row>
<row>
<entry>confdeltype</entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>Foreign key deletion action code</entry>
</row>
<row>
<entry>confmatchtype</entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>Foreign key match type</entry>
</row>
<row>
<entry>conkey</entry>
<entry><type>smallint[]</type></entry>
<entry>pg_attribute.attnum</entry>
<entry>If a table constraint, list of columns which the constraint constrains</entry>
</row>
<row>
<entry>confkey</entry>
<entry><type>smallint[]</type></entry>
<entry>pg_attribute.attnum</entry>
<entry>If a foreign key, list of the referenced columns</entry>
</row>
<row>
<entry>conbin</entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>If a check constraint, an internal representation of the expression</entry>
</row>
<row>
<entry>consrc</entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>If a check constraint, a human-readable representation of the expression</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
<structname>pg_class</structname>.<structfield>relchecks</structfield>
needs to agree with the number of check-constraint entries found in this
table for the given relation.
</para>
</note>
</sect1>
<sect1 id="catalog-pg-database">
<title>pg_database</title>
@ -903,6 +1069,151 @@
</sect1>
<sect1 id="catalog-pg-depend">
<title>pg_depend</title>
<para>
The <structname>pg_depend</structname> table records the dependency
relationships between database objects. This information allows
<command>DROP</> commands to find which other objects must be dropped
by <command>DROP CASCADE</>, or prevent dropping in the <command>DROP
RESTRICT</> case.
</para>
<table>
<title>pg_depend Columns</title>
<tgroup cols=4>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>classid</entry>
<entry><type>oid</type></entry>
<entry>pg_class.oid</entry>
<entry>The oid of the system catalog the dependent object is in</entry>
</row>
<row>
<entry>objid</entry>
<entry><type>oid</type></entry>
<entry>any oid attribute</entry>
<entry>The oid of the specific dependent object</entry>
</row>
<row>
<entry>objsubid</entry>
<entry><type>integer</type></entry>
<entry></entry>
<entry>For a table attribute, this is the attribute's
column number (the objid and classid refer to the table itself).
For all other object types, this field is presently zero.
</entry>
</row>
<row>
<entry>refclassid</entry>
<entry><type>oid</type></entry>
<entry>pg_class.oid</entry>
<entry>The oid of the system catalog the referenced object is in</entry>
</row>
<row>
<entry>refobjid</entry>
<entry><type>oid</type></entry>
<entry>any oid attribute</entry>
<entry>The oid of the specific referenced object</entry>
</row>
<row>
<entry>refobjsubid</entry>
<entry><type>integer</type></entry>
<entry></entry>
<entry>For a table attribute, this is the attribute's
column number (the objid and classid refer to the table itself).
For all other object types, this field is presently zero.
</entry>
</row>
<row>
<entry>deptype</entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>
A code defining the specific semantics of this dependency relationship.
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
In all cases, a <structname>pg_depend</structname> entry indicates that the
referenced object may not be dropped without also dropping the dependent
object. However, there are several subflavors identified by
<structfield>deptype</>:
<itemizedlist>
<listitem>
<para>
DEPENDENCY_NORMAL ('n'): normal relationship between separately-created
objects. The dependent object may be dropped without affecting the
referenced object. The referenced object may only be dropped by
specifying CASCADE, in which case the dependent object is dropped too.
Example: a table column has a normal dependency on its datatype.
</para>
</listitem>
<listitem>
<para>
DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately
from the referenced object, and should be automatically dropped
(regardless of RESTRICT or CASCADE mode) if the referenced object
is dropped.
Example: a named constraint on a table is made auto-dependent on
the table, so that it will go away if the table is dropped.
</para>
</listitem>
<listitem>
<para>
DEPENDENCY_INTERNAL ('i'): the dependent object was created as part
of creation of the referenced object, and is really just a part of
its internal implementation. A DROP of the dependent object will be
disallowed outright (we'll tell the user to issue a DROP against the
referenced object, instead). A DROP of the referenced object will be
propagated through to drop the dependent object whether CASCADE is
specified or not.
Example: a trigger that's created to enforce a foreign-key constraint
is made internally dependent on the constraint's pg_constraint entry.
</para>
</listitem>
<listitem>
<para>
DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
is a signal that the system itself depends on the referenced object,
and so that object must never be deleted. Entries of this type are
created only during initdb. The fields for the dependent object
contain zeroes.
</para>
</listitem>
</itemizedlist>
Other dependency flavors may be needed in future.
</para>
</sect1>
<sect1 id="catalog-pg-description">
<title>pg_description</title>
@ -1866,72 +2177,6 @@
</sect1>
<sect1 id="catalog-pg-relcheck">
<title>pg_relcheck</title>
<para>
This system catalog stores CHECK constraints on tables. (Column
constraints are not treated specially. Every column constraint is
equivalent to some table constraint.) See under <command>CREATE
TABLE</command> for more information.
</para>
<table>
<title>pg_relcheck Columns</title>
<tgroup cols=4>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>rcrelid</entry>
<entry><type>oid</type></entry>
<entry>pg_class.oid</entry>
<entry>The table this check constraint is on</entry>
</row>
<row>
<entry>rcname</entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Constraint name</entry>
</row>
<row>
<entry>rcbin</entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>An internal representation of the constraint expression</entry>
</row>
<row>
<entry>rcsrc</entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>A human-readable representation of the constraint expression</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
<structname>pg_class</structname>.<structfield>relchecks</structfield>
needs to match up with the entries in this table.
</para>
</note>
</sect1>
<sect1 id="catalog-pg-rewrite">
<title>pg_rewrite</title>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.45 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.46 2002/07/12 18:43:12 tgl Exp $
PostgreSQL documentation
-->
@ -39,7 +39,7 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
ADD <replaceable class="PARAMETER">table_constraint_definition</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable>
DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> { RESTRICT | CASCADE }
DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
</synopsis>
@ -315,26 +315,6 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
form after you've entered non-null values for the column in all rows.
</para>
<para>
In DROP CONSTRAINT, the RESTRICT keyword is required, although
dependencies are not yet checked. The CASCADE option is unsupported.
Currently DROP CONSTRAINT only handles CHECK constraints.
To remove a PRIMARY or UNIQUE constraint, drop the
relevant index using the <xref linkend="SQL-DROPINDEX" endterm="sql-dropindex-title"> command.
To remove FOREIGN KEY constraints you need to recreate
and reload the table, using other parameters to the
<xref linkend="SQL-CREATETABLE" endterm="sql-createtable-title"> command.
</para>
<para>
For example, to drop all constraints on a table <literal>distributors</literal>:
<programlisting>
CREATE TABLE temp AS SELECT * FROM distributors;
DROP TABLE distributors;
CREATE TABLE distributors AS SELECT * FROM temp;
DROP TABLE temp;
</programlisting>
</para>
<para>
Changing any part of the schema of a system
catalog is not permitted.
@ -395,7 +375,7 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
<para>
To remove a check constraint from a table and all its children:
<programlisting>
ALTER TABLE distributors DROP CONSTRAINT zipchk RESTRICT;
ALTER TABLE distributors DROP CONSTRAINT zipchk;
</programlisting>
</para>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.19 2002/05/13 17:45:30 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.20 2002/07/12 18:43:12 tgl Exp $
PostgreSQL documentation
-->
@ -26,6 +26,7 @@ COMMENT ON
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable>) |
CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1_type</replaceable>, <replaceable class="PARAMETER">arg2_type</replaceable>, ...) |
@ -52,7 +53,7 @@ COMMENT ON
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">object_name,
table_name.column_name, agg_name, func_name, op, rule_name, trigger_name</replaceable></term>
table_name.column_name, agg_name, constraint_name, func_name, op, rule_name, trigger_name</replaceable></term>
<listitem>
<para>
The name of the object to be be commented. Names of tables,

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.18 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.19 2002/07/12 18:43:12 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> )
DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> ) [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPAGGREGATE-1">
@ -54,6 +54,23 @@ DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the aggregate.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the aggregate if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_domain.sgml,v 1.6 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_domain.sgml,v 1.7 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -48,8 +48,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C
<term><literal>CASCADE</></term>
<listitem>
<para>
Automatically drop objects that depend on the domain. This
behavior is not currently supported.
Automatically drop objects that depend on the domain
(such as table columns).
</para>
</listitem>
</varlistentry>
@ -58,7 +58,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C
<term><literal>RESTRICT</></term>
<listitem>
<para>
Do not drop dependent objects. This is the default.
Refuse to drop the domain if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
@ -143,19 +144,14 @@ DROP DOMAIN box;
<refsect1 id="SQL-DROPDOMAIN-compatibility">
<title>Compatibility</title>
<refsect2 id="R2-SQL-DROPDOMAIN-sql92">
<title>
SQL92
</title>
<para>
A <command>DROP DOMAIN</command> statement exists in SQL99. As with
most other <quote>drop</quote> commands, <command>DROP
DOMAIN</command> in SQL99 requires a <quote>drop behavior</quote>
clause to select between dropping all dependent objects or refusing
to drop if dependent objects exist:
<synopsis>
DROP DOMAIN <replaceable>name</replaceable> { CASCADE | RESTRICT }
</synopsis>
<productname>PostgreSQL</productname> accepts only the RESTRICT
option, and currently does not check for existence of dependent objects.
</para>
<para></para>
</refsect2>
</refsect1>
<refsect1 id="SQL-DROPDOMAIN-see-also">

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.20 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.21 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] )
DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPFUNCTION-1">
@ -49,6 +49,24 @@ DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the function
(such as operators or triggers).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the function if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -136,15 +154,8 @@ DROP FUNCTION sqrt(integer);
<title>Compatibility</title>
<para>
A <command>DROP FUNCTION</command> statement is defined in SQL99. One of its syntax forms is:
<synopsis>
DROP FUNCTION <replaceable class="parameter">name</replaceable> (<replaceable>arg</>, ...) { RESTRICT | CASCADE }
</synopsis>
where <literal>CASCADE</> specifies dropping all objects that
depend on the function and <literal>RESTRICT</literal> refuses to
drop the function if dependent objects exist.
A <command>DROP FUNCTION</command> statement is defined in SQL99. One of
its syntax forms is similar to PostgreSQL's.
</para>
</refsect1>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_index.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_index.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...]
DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPINDEX-1">
@ -41,6 +41,23 @@ DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...]
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the index.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the index if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_language.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_language.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable>
DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable> [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPLANGUAGE-1">
@ -43,7 +43,26 @@ DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the language
(such as functions in the language).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the language if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -112,14 +131,6 @@ ERROR: Language "<replaceable class="parameter">name</replaceable>" doesn't exis
<xref linkend="sql-createlanguage" endterm="sql-createlanguage-title">
for information on how to create procedural languages.
</para>
<para>
No checks are made if functions or trigger procedures registered
in this language still exist. To re-enable them without having
to drop and recreate all the functions, the pg_proc's prolang
attribute of the functions must be adjusted to the new object
ID of the recreated pg_language entry for the PL.
</para>
</refsect2>
</refsect1>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_operator.sgml,v 1.16 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_operator.sgml,v 1.17 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -22,7 +22,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable class="PARAMETER">lefttype</replaceable> | NONE , <replaceable class="PARAMETER">righttype</replaceable> | NONE )
DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable class="PARAMETER">lefttype</replaceable> | NONE , <replaceable class="PARAMETER">righttype</replaceable> | NONE ) [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPOPERATOR-1">
@ -60,6 +60,23 @@ DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable cla
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the operator if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_rule.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_rule.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1998-09-22</date>
</refsynopsisdivinfo>
<synopsis>
DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">relation</replaceable>
DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">relation</replaceable> [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPRULE-1">
@ -50,7 +50,25 @@ DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable clas
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the rule.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the rule if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,8 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...]
DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPSEQUENCE-1">
@ -75,7 +76,25 @@ ERROR: sequence "<replaceable class="parameter">name</replaceable>" does not exi
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the sequence.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the sequence if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_table.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_table.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,8 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...]
DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPTABLE-1">
@ -41,6 +42,24 @@ DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...]
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the table
(such as views).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the table if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -136,44 +155,11 @@ DROP TABLE films, distributors;
</title>
<refsect2 id="R2-SQL-DROPTABLE-4">
<refsect2info>
<date>1998-09-22</date>
</refsect2info>
<title>
SQL92
</title>
<para>
SQL92 specifies some additional capabilities for DROP TABLE:
</para>
<synopsis>
DROP TABLE <replaceable class="parameter">table</replaceable> { RESTRICT | CASCADE }
</synopsis>
<variablelist>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Ensures that only a table with no dependent views or
integrity constraints can be destroyed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Any referencing views or integrity constraints
will also be dropped.
</para>
</listitem>
</varlistentry>
</variablelist>
<tip>
<para>
At present, to remove a referencing view you must drop
it explicitly.
</para>
</tip>
</refsect2>
</refsect1>
</refentry>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_trigger.sgml,v 1.12 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_trigger.sgml,v 1.13 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1998-09-22</date>
</refsynopsisdivinfo>
<synopsis>
DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table</replaceable>
DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table</replaceable> [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPTRIGGER-1">
@ -50,6 +50,23 @@ DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable c
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the trigger.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the trigger if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_type.sgml,v 1.17 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_type.sgml,v 1.18 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,8 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...]
DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPTYPE-1">
@ -41,6 +42,24 @@ DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...]
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the type
(such as table columns, functions, operators, etc).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the type if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -75,6 +94,7 @@ ERROR: RemoveType: type '<replaceable class="parameter">typename</replaceable>'
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>
@ -132,19 +152,6 @@ DROP TYPE box;
<refsect1 id="SQL-DROPTYPE-compatibility">
<title>Compatibility</title>
<para>
A <command>DROP TYPE</command> statement exists in SQL99. As with
most other <quote>drop</quote> commands, <command>DROP
TYPE</command> in SQL99 requires a <quote>drop behavior</quote>
clause to select between dropping all dependent objects or refusing
to drop if dependent objects exist:
<synopsis>
DROP TYPE <replaceable>name</replaceable> { CASCADE | RESTRICT }
</synopsis>
<productname>PostgreSQL</productname> currently ignores
dependencies altogether.
</para>
<para>
Note that the <command>CREATE TYPE</command> command and the data
type extension mechanisms in <productname>PostgreSQL</productname>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_view.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_view.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $
PostgreSQL documentation
-->
@ -21,7 +21,7 @@ PostgreSQL documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...]
DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
<refsect2 id="R2-SQL-DROPVIEW-1">
@ -42,7 +42,26 @@ DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...]
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the view
(such as other views).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the view if there are any dependent objects.
This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
@ -134,58 +153,7 @@ DROP VIEW kinds;
SQL92
</title>
<para>
<acronym>SQL92</acronym> specifies some additional capabilities for
<command>DROP VIEW</command>:
<synopsis>
DROP VIEW <replaceable class="parameter">view</replaceable> { RESTRICT | CASCADE }
</synopsis>
</para>
<refsect3 id="R3-SQL-DROPVIEW-1">
<refsect3info>
<date>1998-09-22</date>
</refsect3info>
<title>
Inputs
</title>
<para>
<variablelist>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Ensures that only a view with no dependent views or
integrity constraints can be destroyed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Any referencing views and integrity constraints
will be dropped as well.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect3>
<refsect3 id="R3-SQL-DROPVIEW-2">
<refsect3info>
<date>1998-09-22</date>
</refsect3info>
<title>
Notes
</title>
<para>
At present, to remove a referencing view from a
<productname>PostgreSQL</productname> database,
you must drop it explicitly.
</para>
</refsect3>
</refsect2>
</refsect1>
</refentry>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.139 2002/06/11 15:32:33 thomas Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.140 2002/07/12 18:43:12 tgl Exp $
-->
<appendix id="release">
@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters.
-->
<literallayout><![CDATA[
Most forms of DROP now support RESTRICT and CASCADE options
Recursive SQL functions can be defined
User-defined procedural languages can register a validator function to check new functions as they are created
Functions can be executed with the privileges of the owner

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.48 2002/06/20 20:29:25 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.49 2002/07/12 18:43:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -240,7 +240,8 @@ Boot_DeclareIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($5)),
LexIDStr($3),
LexIDStr($7),
$9, false, false, NULL, NIL);
$9,
false, false, false, NULL, NIL);
do_end();
}
;
@ -253,7 +254,8 @@ Boot_DeclareUniqueIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
LexIDStr($4),
LexIDStr($8),
$10, true, false, NULL, NIL);
$10,
true, false, false, NULL, NIL);
do_end();
}
;

View File

@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.40 2002/07/11 07:39:27 ishii Exp $
# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.41 2002/07/12 18:43:13 tgl Exp $
#
#-------------------------------------------------------------------------
@ -10,9 +10,9 @@ subdir = src/backend/catalog
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = catalog.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_type.o pg_conversion.o
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o
BKIFILES = postgres.bki postgres.description
@ -27,12 +27,12 @@ SUBSYS.o: $(OBJS)
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
pg_attrdef.h pg_relcheck.h pg_inherits.h pg_index.h \
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \
pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
indexing.h \
pg_depend.h indexing.h \
)
pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)

View File

@ -0,0 +1,731 @@
/*-------------------------------------------------------------------------
*
* dependency.c
* Routines to support inter-object dependencies.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.1 2002/07/12 18:43:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_language.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/trigger.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/* This enum covers all system catalogs whose OIDs can appear in classid. */
typedef enum ObjectClasses
{
OCLASS_CLASS, /* pg_class */
OCLASS_PROC, /* pg_proc */
OCLASS_TYPE, /* pg_type */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_LANGUAGE, /* pg_language */
OCLASS_OPERATOR, /* pg_operator */
OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER /* pg_trigger */
} ObjectClasses;
static bool recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
int recursionLevel,
Relation depRel);
static void doDeletion(const ObjectAddress *object);
static ObjectClasses getObjectClass(const ObjectAddress *object);
static char *getObjectDescription(const ObjectAddress *object);
/*
* performDeletion: attempt to drop the specified object. If CASCADE
* behavior is specified, also drop any dependent objects (recursively).
* If RESTRICT behavior is specified, error out if there are any dependent
* objects, except for those that should be implicitly dropped anyway
* according to the dependency type.
*
* This is the outer control routine for all forms of DROP that drop objects
* that can participate in dependencies.
*/
void
performDeletion(const ObjectAddress *object,
DropBehavior behavior)
{
char *objDescription;
Relation depRel;
/*
* Get object description for possible use in failure message.
* Must do this before deleting it ...
*/
objDescription = getObjectDescription(object);
/*
* We save some cycles by opening pg_depend just once and passing the
* Relation pointer down to all the recursive deletion steps.
*/
depRel = heap_openr(DependRelationName, RowExclusiveLock);
if (!recursiveDeletion(object, behavior, 0, depRel))
elog(ERROR, "Cannot drop %s because other objects depend on it"
"\n\tUse DROP ... CASCADE to drop the dependent objects too",
objDescription);
heap_close(depRel, RowExclusiveLock);
pfree(objDescription);
}
/*
* recursiveDeletion: delete a single object for performDeletion.
*
* Returns TRUE if successful, FALSE if not. recursionLevel is 0 at the
* outer level, >0 when deleting a dependent object.
*
* In RESTRICT mode, we perform all the deletions anyway, but elog a NOTICE
* and return FALSE if we find a restriction violation. performDeletion
* will then abort the transaction to nullify the deletions. We have to
* do it this way to (a) report all the direct and indirect dependencies
* while (b) not going into infinite recursion if there's a cycle.
*/
static bool
recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
int recursionLevel,
Relation depRel)
{
bool ok = true;
char *objDescription;
ScanKeyData key[3];
int nkeys;
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
/*
* Get object description for possible use in messages. Must do this
* before deleting it ...
*/
objDescription = getObjectDescription(object);
/*
* Step 1: find and remove pg_depend records that link from this
* object to others. We have to do this anyway, and doing it first
* ensures that we avoid infinite recursion in the case of cycles.
* Also, some dependency types require an error here.
*
* When dropping a whole object (subId = 0), remove all pg_depend
* records for its sub-objects too.
*/
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_classid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_objid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyEntryInitialize(&key[2], 0x0,
Anum_pg_depend_objsubid, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(depRel, DependDependerIndex, true,
SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
otherObject.classId = foundDep->refclassid;
otherObject.objectId = foundDep->refobjid;
otherObject.objectSubId = foundDep->refobjsubid;
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
case DEPENDENCY_AUTO:
/* no problem */
break;
case DEPENDENCY_INTERNAL:
/*
* Disallow direct DROP of an object that is part of the
* implementation of another object. (We just elog here,
* rather than issuing a notice and continuing, since
* no other dependencies are likely to be interesting.)
*/
if (recursionLevel == 0)
elog(ERROR, "Cannot drop %s because %s requires it"
"\n\tYou may DROP the other object instead",
objDescription,
getObjectDescription(&otherObject));
break;
case DEPENDENCY_PIN:
/*
* Should not happen; PIN dependencies should have zeroes
* in the depender fields...
*/
elog(ERROR, "recursiveDeletion: incorrect use of PIN dependency with %s",
objDescription);
break;
default:
elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
foundDep->deptype, objDescription);
break;
}
simple_heap_delete(depRel, &tup->t_self);
}
systable_endscan(scan);
/*
* CommandCounterIncrement here to ensure that preceding changes
* are all visible; in particular, that the above deletions of pg_depend
* entries are visible. That prevents infinite recursion in case of
* a dependency loop (which is perfectly legal).
*/
CommandCounterIncrement();
/*
* Step 2: scan pg_depend records that link to this object, showing
* the things that depend on it. Recursively delete those things.
* (We don't delete the pg_depend records here, as the recursive call
* will do that.) Note it's important to delete the dependent objects
* before the referenced one, since the deletion routines might do
* things like try to update the pg_class record when deleting a
* check constraint.
*
* Again, when dropping a whole object (subId = 0), find pg_depend
* records for its sub-objects too.
*
* NOTE: because we are using SnapshotNow, if a recursive call deletes
* any pg_depend tuples that our scan hasn't yet visited, we will not see
* them as good when we do visit them. This is essential for correct
* behavior if there are multiple dependency paths between two objects
* --- else we might try to delete an already-deleted object.
*/
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_refclassid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_refobjid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyEntryInitialize(&key[2], 0x0,
Anum_pg_depend_refobjsubid, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(depRel, DependReferenceIndex, true,
SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
otherObject.classId = foundDep->classid;
otherObject.objectId = foundDep->objid;
otherObject.objectSubId = foundDep->objsubid;
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
if (behavior == DROP_RESTRICT)
{
elog(NOTICE, "%s depends on %s",
getObjectDescription(&otherObject),
objDescription);
ok = false;
}
else
elog(NOTICE, "Drop cascades to %s",
getObjectDescription(&otherObject));
if (!recursiveDeletion(&otherObject, behavior,
recursionLevel + 1, depRel))
ok = false;
break;
case DEPENDENCY_AUTO:
case DEPENDENCY_INTERNAL:
/*
* We propagate the DROP without complaint even in the
* RESTRICT case. (However, normal dependencies on the
* component object could still cause failure.)
*/
elog(DEBUG1, "Drop internally cascades to %s",
getObjectDescription(&otherObject));
if (!recursiveDeletion(&otherObject, behavior,
recursionLevel + 1, depRel))
ok = false;
break;
case DEPENDENCY_PIN:
/*
* For a PIN dependency we just elog immediately; there
* won't be any others to report.
*/
elog(ERROR, "Cannot drop %s because it is required by the database system",
objDescription);
break;
default:
elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
foundDep->deptype, objDescription);
break;
}
}
systable_endscan(scan);
/*
* We do not need CommandCounterIncrement here, since if step 2 did
* anything then each recursive call will have ended with one.
*/
/*
* Step 3: delete the object itself.
*/
doDeletion(object);
/*
* Delete any comments associated with this object. (This is a convenient
* place to do it instead of having every object type know to do it.)
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
/*
* CommandCounterIncrement here to ensure that preceding changes
* are all visible.
*/
CommandCounterIncrement();
/*
* And we're done!
*/
pfree(objDescription);
return ok;
}
/*
* doDeletion: actually delete a single object
*/
static void
doDeletion(const ObjectAddress *object)
{
switch (getObjectClass(object))
{
case OCLASS_CLASS:
{
HeapTuple relTup;
char relKind;
/*
* Need the relkind to figure out how to drop.
*/
relTup = SearchSysCache(RELOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "doDeletion: Relation %u does not exist",
object->objectId);
relKind = ((Form_pg_class) GETSTRUCT(relTup))->relkind;
ReleaseSysCache(relTup);
if (relKind == RELKIND_INDEX)
{
Assert(object->objectSubId == 0);
index_drop(object->objectId);
}
else
{
if (object->objectSubId != 0)
elog(ERROR, "DROP COLUMN not implemented yet");
else
heap_drop_with_catalog(object->objectId);
}
break;
}
case OCLASS_PROC:
RemoveFunctionById(object->objectId);
break;
case OCLASS_TYPE:
RemoveTypeById(object->objectId);
break;
case OCLASS_CONSTRAINT:
RemoveConstraintById(object->objectId);
break;
case OCLASS_LANGUAGE:
DropProceduralLanguageById(object->objectId);
break;
case OCLASS_OPERATOR:
RemoveOperatorById(object->objectId);
break;
case OCLASS_REWRITE:
RemoveRewriteRuleById(object->objectId);
break;
case OCLASS_TRIGGER:
RemoveTriggerById(object->objectId);
break;
default:
elog(ERROR, "doDeletion: Unsupported object class %u",
object->classId);
}
}
/*
* Determine the class of a given object identified by objectAddress.
*
* This function is needed just because some of the system catalogs do
* not have hardwired-at-compile-time OIDs.
*/
static ObjectClasses
getObjectClass(const ObjectAddress *object)
{
static bool reloids_initialized = false;
static Oid reloid_pg_constraint;
static Oid reloid_pg_language;
static Oid reloid_pg_operator;
static Oid reloid_pg_rewrite;
static Oid reloid_pg_trigger;
/* Easy for the bootstrapped catalogs... */
switch (object->classId)
{
case RelOid_pg_class:
/* caller must check objectSubId */
return OCLASS_CLASS;
case RelOid_pg_proc:
Assert(object->objectSubId == 0);
return OCLASS_PROC;
case RelOid_pg_type:
Assert(object->objectSubId == 0);
return OCLASS_TYPE;
}
/*
* Handle cases where catalog's OID is not hardwired.
*
* Although these OIDs aren't compile-time constants, they surely
* shouldn't change during a backend's run. So, look them up the
* first time through and then cache them.
*/
if (!reloids_initialized)
{
reloid_pg_constraint = get_system_catalog_relid(ConstraintRelationName);
reloid_pg_language = get_system_catalog_relid(LanguageRelationName);
reloid_pg_operator = get_system_catalog_relid(OperatorRelationName);
reloid_pg_rewrite = get_system_catalog_relid(RewriteRelationName);
reloid_pg_trigger = get_system_catalog_relid(TriggerRelationName);
reloids_initialized = true;
}
if (object->classId == reloid_pg_constraint)
{
Assert(object->objectSubId == 0);
return OCLASS_CONSTRAINT;
}
if (object->classId == reloid_pg_language)
{
Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE;
}
if (object->classId == reloid_pg_operator)
{
Assert(object->objectSubId == 0);
return OCLASS_OPERATOR;
}
if (object->classId == reloid_pg_rewrite)
{
Assert(object->objectSubId == 0);
return OCLASS_REWRITE;
}
if (object->classId == reloid_pg_trigger)
{
Assert(object->objectSubId == 0);
return OCLASS_TRIGGER;
}
elog(ERROR, "getObjectClass: Unknown object class %u",
object->classId);
return OCLASS_CLASS; /* keep compiler quiet */
}
/*
* getObjectDescription: build an object description for messages
*
* The result is a palloc'd string.
*/
static char *
getObjectDescription(const ObjectAddress *object)
{
StringInfoData buffer;
initStringInfo(&buffer);
switch (getObjectClass(object))
{
case OCLASS_CLASS:
{
HeapTuple relTup;
Form_pg_class relForm;
relTup = SearchSysCache(RELOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "getObjectDescription: Relation %u does not exist",
object->objectId);
relForm = (Form_pg_class) GETSTRUCT(relTup);
switch (relForm->relkind)
{
case RELKIND_RELATION:
appendStringInfo(&buffer, "table %s",
NameStr(relForm->relname));
break;
case RELKIND_INDEX:
appendStringInfo(&buffer, "index %s",
NameStr(relForm->relname));
break;
case RELKIND_SPECIAL:
appendStringInfo(&buffer, "special system relation %s",
NameStr(relForm->relname));
break;
case RELKIND_SEQUENCE:
appendStringInfo(&buffer, "sequence %s",
NameStr(relForm->relname));
break;
case RELKIND_UNCATALOGED:
appendStringInfo(&buffer, "uncataloged table %s",
NameStr(relForm->relname));
break;
case RELKIND_TOASTVALUE:
appendStringInfo(&buffer, "toast table %s",
NameStr(relForm->relname));
break;
case RELKIND_VIEW:
appendStringInfo(&buffer, "view %s",
NameStr(relForm->relname));
break;
default:
/* shouldn't get here */
appendStringInfo(&buffer, "relation %s",
NameStr(relForm->relname));
break;
}
if (object->objectSubId != 0)
appendStringInfo(&buffer, " column %s",
get_attname(object->objectId,
object->objectSubId));
ReleaseSysCache(relTup);
break;
}
case OCLASS_PROC:
/* XXX could improve on this */
appendStringInfo(&buffer, "function %s",
get_func_name(object->objectId));
break;
case OCLASS_TYPE:
{
HeapTuple typeTup;
typeTup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "getObjectDescription: Type %u does not exist",
object->objectId);
appendStringInfo(&buffer, "type %s",
NameStr(((Form_pg_type) GETSTRUCT(typeTup))->typname));
ReleaseSysCache(typeTup);
break;
}
case OCLASS_CONSTRAINT:
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(rcscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Constraint %u does not exist",
object->objectId);
con = (Form_pg_constraint) GETSTRUCT(tup);
appendStringInfo(&buffer, "constraint %s",
NameStr(con->conname));
if (OidIsValid(con->conrelid))
appendStringInfo(&buffer, " on table %s",
get_rel_name(con->conrelid));
systable_endscan(rcscan);
heap_close(conDesc, AccessShareLock);
break;
}
case OCLASS_LANGUAGE:
{
HeapTuple langTup;
langTup = SearchSysCache(LANGOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(langTup))
elog(ERROR, "getObjectDescription: Language %u does not exist",
object->objectId);
appendStringInfo(&buffer, "language %s",
NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
ReleaseSysCache(langTup);
break;
}
case OCLASS_OPERATOR:
/* XXX could improve on this */
appendStringInfo(&buffer, "operator %s",
get_opname(object->objectId));
break;
case OCLASS_REWRITE:
{
Relation ruleDesc;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_rewrite rule;
ruleDesc = heap_openr(RewriteRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(ruleDesc, RewriteOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(rcscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Rule %u does not exist",
object->objectId);
rule = (Form_pg_rewrite) GETSTRUCT(tup);
appendStringInfo(&buffer, "rule %s",
NameStr(rule->rulename));
if (OidIsValid(rule->ev_class))
appendStringInfo(&buffer, " on table %s",
get_rel_name(rule->ev_class));
systable_endscan(rcscan);
heap_close(ruleDesc, AccessShareLock);
break;
}
case OCLASS_TRIGGER:
{
Relation trigDesc;
ScanKeyData skey[1];
SysScanDesc tgscan;
HeapTuple tup;
Form_pg_trigger trig;
trigDesc = heap_openr(TriggerRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
tgscan = systable_beginscan(trigDesc, TriggerOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(tgscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Trigger %u does not exist",
object->objectId);
trig = (Form_pg_trigger) GETSTRUCT(tup);
appendStringInfo(&buffer, "trigger %s",
NameStr(trig->tgname));
if (OidIsValid(trig->tgrelid))
appendStringInfo(&buffer, " on table %s",
get_rel_name(trig->tgrelid));
systable_endscan(tgscan);
heap_close(trigDesc, AccessShareLock);
break;
}
default:
appendStringInfo(&buffer, "unknown object %u %u %d",
object->classId,
object->objectId,
object->objectSubId);
break;
}
return buffer.data;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.204 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.205 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -33,21 +33,20 @@
#include "access/genam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/trigger.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
@ -69,8 +68,6 @@ static void AddNewRelationTuple(Relation pg_class_desc,
char relkind, bool relhasoids);
static void DeleteAttributeTuples(Relation rel);
static void DeleteRelationTuple(Relation rel);
static void DeleteTypeTuple(Relation rel);
static void RelationRemoveIndexes(Relation relation);
static void RelationRemoveInheritance(Relation relation);
static void AddNewRelationType(const char *typeName,
Oid typeNamespace,
@ -80,7 +77,7 @@ static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
static void StoreConstraints(Relation rel, TupleDesc tupdesc);
static void SetRelationNumChecks(Relation rel, int numchecks);
static void RemoveConstraints(Relation rel);
static void RemoveDefaults(Relation rel);
static void RemoveStatistics(Relation rel);
@ -760,106 +757,42 @@ heap_create_with_catalog(const char *relname,
}
/* --------------------------------
/*
* RelationRemoveInheritance
*
* Note: for now, we cause an exception if relation is a
* superclass. Someday, we may want to allow this and merge
* the type info into subclass procedures.... this seems like
* lots of work.
* --------------------------------
* Formerly, this routine checked for child relations and aborted the
* deletion if any were found. Now we rely on the dependency mechanism
* to check for or delete child relations. By the time we get here,
* there are no children and we need only remove the pg_inherits rows.
*/
static void
RelationRemoveInheritance(Relation relation)
{
Relation catalogRelation;
HeapTuple tuple;
HeapScanDesc scan;
SysScanDesc scan;
ScanKeyData entry;
bool found = false;
/*
* open pg_inherits
*/
catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
/*
* form a scan key for the subclasses of this class and begin scanning
*/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent,
F_OIDEQ,
ScanKeyEntryInitialize(&entry, 0x0,
Anum_pg_inherits_inhrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
scan = heap_beginscan(catalogRelation,
SnapshotNow,
1,
&entry);
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true,
SnapshotNow, 1, &entry);
/*
* if any subclasses exist, then we disallow the deletion.
*/
if ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid subclass = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
char *subclassname;
subclassname = get_rel_name(subclass);
/* Just in case get_rel_name fails... */
if (subclassname)
elog(ERROR, "Relation \"%s\" inherits from \"%s\"",
subclassname, RelationGetRelationName(relation));
else
elog(ERROR, "Relation %u inherits from \"%s\"",
subclass, RelationGetRelationName(relation));
}
heap_endscan(scan);
/*
* If we get here, it means the relation has no subclasses so we can
* trash it. First we remove dead INHERITS tuples.
*/
entry.sk_attno = Anum_pg_inherits_inhrelid;
scan = heap_beginscan(catalogRelation,
SnapshotNow,
1,
&entry);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
simple_heap_delete(catalogRelation, &tuple->t_self);
found = true;
}
heap_endscan(scan);
systable_endscan(scan);
heap_close(catalogRelation, RowExclusiveLock);
}
/*
* RelationRemoveIndexes
*/
static void
RelationRemoveIndexes(Relation relation)
{
List *indexoidlist,
*indexoidscan;
indexoidlist = RelationGetIndexList(relation);
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirsti(indexoidscan);
index_drop(indexoid);
}
freeList(indexoidlist);
}
/* --------------------------------
* DeleteRelationTuple
*
* --------------------------------
*/
static void
DeleteRelationTuple(Relation rel)
@ -1049,163 +982,34 @@ DeleteAttributeTuples(Relation rel)
heap_close(pg_attribute_desc, RowExclusiveLock);
}
/* --------------------------------
* DeleteTypeTuple
*
* If the user attempts to destroy a relation and there
* exists attributes in other relations of type
* "relation we are deleting", then we have to do something
* special. presently we disallow the destroy.
* --------------------------------
*/
static void
DeleteTypeTuple(Relation rel)
{
Relation pg_type_desc;
HeapScanDesc pg_type_scan;
Relation pg_attribute_desc;
HeapScanDesc pg_attribute_scan;
ScanKeyData key;
ScanKeyData attkey;
HeapTuple tup;
HeapTuple atttup;
Oid typoid;
/*
* open pg_type
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
/*
* create a scan key to locate the type tuple corresponding to this
* relation.
*/
ScanKeyEntryInitialize(&key, 0,
Anum_pg_type_typrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
pg_type_scan = heap_beginscan(pg_type_desc,
SnapshotNow,
1,
&key);
/*
* use heap_getnext() to fetch the pg_type tuple. If this tuple is
* not valid then something's wrong.
*/
tup = heap_getnext(pg_type_scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
{
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "DeleteTypeTuple: type \"%s\" does not exist",
RelationGetRelationName(rel));
}
/*
* now scan pg_attribute. if any other relations have attributes of
* the type of the relation we are deleteing then we have to disallow
* the deletion. should talk to stonebraker about this. -cim 6/19/90
*/
typoid = tup->t_data->t_oid;
pg_attribute_desc = heap_openr(AttributeRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&attkey,
0,
Anum_pg_attribute_atttypid,
F_OIDEQ,
ObjectIdGetDatum(typoid));
pg_attribute_scan = heap_beginscan(pg_attribute_desc,
SnapshotNow,
1,
&attkey);
/*
* try and get a pg_attribute tuple. if we succeed it means we can't
* delete the relation because something depends on the schema.
*/
atttup = heap_getnext(pg_attribute_scan, ForwardScanDirection);
if (HeapTupleIsValid(atttup))
{
Oid relid = ((Form_pg_attribute) GETSTRUCT(atttup))->attrelid;
heap_endscan(pg_attribute_scan);
heap_close(pg_attribute_desc, RowExclusiveLock);
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "DeleteTypeTuple: column of type %s exists in relation %u",
RelationGetRelationName(rel), relid);
}
heap_endscan(pg_attribute_scan);
heap_close(pg_attribute_desc, RowExclusiveLock);
/*
* Ok, it's safe so we delete the relation tuple from pg_type and
* finish up.
*/
simple_heap_delete(pg_type_desc, &tup->t_self);
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
}
/* ----------------------------------------------------------------
* heap_drop_with_catalog - removes all record of named relation from catalogs
* heap_drop_with_catalog - removes specified relation from catalogs
*
* 1) open relation, check for existence, etc.
* 2) remove inheritance information
* 3) remove indexes
* 4) remove pg_class tuple
* 5) remove pg_attribute tuples and related descriptions
* 6) remove pg_description tuples
* 7) remove pg_type tuples
* 8) RemoveConstraints ()
* 9) unlink relation
* 1) open relation, acquire exclusive lock.
* 2) flush relation buffers from bufmgr
* 3) remove inheritance information
* 4) remove pg_statistic tuples
* 5) remove pg_attribute tuples and related items
* 6) remove pg_class tuple
* 7) unlink relation file
*
* old comments
* Except for vital relations, removes relation from
* relation catalog, and related attributes from
* attribute catalog (needed?). (Anything else?)
*
* get proper relation from relation catalog (if not arg)
* scan attribute catalog deleting attributes of reldesc
* (necessary?)
* delete relation from relation catalog
* (How are the tuples of the relation discarded?)
*
* XXX Must fix to work with indexes.
* There may be a better order for doing things.
* Problems with destroying a deleted database--cannot create
* a struct reldesc without having an open file descriptor.
* Note that this routine is not responsible for dropping objects that are
* linked to the pg_class entry via dependencies (for example, indexes and
* constraints). Those are deleted by the dependency-tracing logic in
* dependency.c before control gets here. In general, therefore, this routine
* should never be called directly; go through performDeletion() instead.
* ----------------------------------------------------------------
*/
void
heap_drop_with_catalog(Oid rid,
bool allow_system_table_mods)
heap_drop_with_catalog(Oid rid)
{
Relation rel;
Oid toasttableOid;
int i;
/*
* Open and lock the relation.
*/
rel = heap_open(rid, AccessExclusiveLock);
toasttableOid = rel->rd_rel->reltoastrelid;
/*
* prevent deletion of system relations
*/
if (!allow_system_table_mods &&
IsSystemRelation(rel))
elog(ERROR, "System relation \"%s\" may not be dropped",
RelationGetRelationName(rel));
/*
* Release all buffers that belong to this relation, after writing any
@ -1216,43 +1020,22 @@ heap_drop_with_catalog(Oid rid,
elog(ERROR, "heap_drop_with_catalog: FlushRelationBuffers returned %d",
i);
/*
* remove rules if necessary
*/
if (rel->rd_rules != NULL)
RelationRemoveRules(rid);
/* triggers */
RelationRemoveTriggers(rel);
/*
* remove inheritance information
*/
RelationRemoveInheritance(rel);
/*
* remove indexes if necessary
* delete statistics
*/
RelationRemoveIndexes(rel);
RemoveStatistics(rel);
/*
* delete attribute tuples
* delete attribute tuples and associated defaults
*/
DeleteAttributeTuples(rel);
/*
* delete comments, statistics, and constraints
*/
DeleteComments(rid, RelOid_pg_class);
RemoveStatistics(rel);
RemoveConstraints(rel);
/*
* delete type tuple
*/
DeleteTypeTuple(rel);
RemoveDefaults(rel);
/*
* delete relation tuple
@ -1276,10 +1059,6 @@ heap_drop_with_catalog(Oid rid,
* flush the relation from the relcache
*/
RelationForgetRelation(rid);
/* If it has a toast table, recurse to get rid of that too */
if (OidIsValid(toasttableOid))
heap_drop_with_catalog(toasttableOid, true);
}
@ -1374,11 +1153,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
{
Node *expr;
char *ccsrc;
Relation rcrel;
Relation idescs[Num_pg_relcheck_indices];
HeapTuple tuple;
Datum values[4];
static char nulls[4] = {' ', ' ', ' ', ' '};
List *varList;
int keycount;
int16 *attNos;
/*
* Convert condition to a normal boolean expression tree.
@ -1394,26 +1171,55 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
RelationGetRelid(rel)),
false);
values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(ccname));
values[Anum_pg_relcheck_rcbin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(ccbin));
values[Anum_pg_relcheck_rcsrc - 1] = DirectFunctionCall1(textin,
CStringGetDatum(ccsrc));
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
tuple = heap_formtuple(rcrel->rd_att, values, nulls);
simple_heap_insert(rcrel, tuple);
CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices,
idescs);
CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple);
CatalogCloseIndices(Num_pg_relcheck_indices, idescs);
heap_close(rcrel, RowExclusiveLock);
/*
* Find columns of rel that are used in ccbin
*/
varList = pull_var_clause(expr, false);
keycount = length(varList);
if (keycount > 0)
{
List *vl;
int i = 0;
attNos = (int16 *) palloc(keycount * sizeof(int16));
foreach(vl, varList)
{
Var *var = (Var *) lfirst(vl);
int j;
for (j = 0; j < i; j++)
if (attNos[j] == var->varattno)
break;
if (j == i)
attNos[i++] = var->varattno;
}
keycount = i;
}
else
attNos = NULL;
/*
* Create the Check Constraint
*/
CreateConstraintEntry(ccname, /* Constraint Name */
RelationGetNamespace(rel), /* namespace */
CONSTRAINT_CHECK, /* Constraint Type */
false, /* Is Deferrable */
false, /* Is Deferred */
RelationGetRelid(rel), /* relation */
attNos, /* List of attributes in the constraint */
keycount, /* # attributes in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
0,
' ',
' ',
' ',
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcname - 1]));
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1]));
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1]));
heap_freetuple(tuple);
pfree(ccsrc);
}
@ -1488,6 +1294,7 @@ AddRelationRawConstraints(Relation rel,
ParseState *pstate;
RangeTblEntry *rte;
int numchecks;
int constr_name_ctr = 0;
List *listptr;
Node *expr;
@ -1549,18 +1356,17 @@ AddRelationRawConstraints(Relation rel,
/* Check name uniqueness, or generate a new name */
if (cdef->name != NULL)
{
int i;
List *listptr2;
ccname = cdef->name;
/* Check against old constraints */
for (i = 0; i < numoldchecks; i++)
{
if (strcmp(oldchecks[i].ccname, ccname) == 0)
elog(ERROR, "Duplicate CHECK constraint name: '%s'",
ccname);
}
/* Check against pre-existing constraints */
if (ConstraintNameIsUsed(RelationGetRelid(rel),
RelationGetNamespace(rel),
ccname))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
ccname, RelationGetRelationName(rel));
/* Check against other new constraints */
/* Needed because we don't do CommandCounterIncrement in loop */
foreach(listptr2, rawConstraints)
{
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
@ -1577,55 +1383,40 @@ AddRelationRawConstraints(Relation rel,
}
else
{
int i;
int j;
bool success;
List *listptr2;
ccname = (char *) palloc(NAMEDATALEN);
/* Loop until we find a non-conflicting constraint name */
/* What happens if this loops forever? */
j = numchecks + 1;
do
{
success = true;
snprintf(ccname, NAMEDATALEN, "$%d", j);
List *listptr2;
/* Check against old constraints */
for (i = 0; i < numoldchecks; i++)
/*
* Generate a name that does not conflict with pre-existing
* constraints, nor with any auto-generated names so far.
*/
ccname = GenerateConstraintName(RelationGetRelid(rel),
RelationGetNamespace(rel),
&constr_name_ctr);
/*
* Check against other new constraints, in case the user
* has specified a name that looks like an auto-generated
* name.
*/
success = true;
foreach(listptr2, rawConstraints)
{
if (strcmp(oldchecks[i].ccname, ccname) == 0)
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
if (cdef2 == cdef ||
cdef2->contype != CONSTR_CHECK ||
cdef2->raw_expr == NULL ||
cdef2->name == NULL)
continue;
if (strcmp(cdef2->name, ccname) == 0)
{
success = false;
break;
}
}
/*
* Check against other new constraints, if the check
* hasn't already failed
*/
if (success)
{
foreach(listptr2, rawConstraints)
{
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
if (cdef2 == cdef ||
cdef2->contype != CONSTR_CHECK ||
cdef2->raw_expr == NULL ||
cdef2->name == NULL)
continue;
if (strcmp(cdef2->name, ccname) == 0)
{
success = false;
break;
}
}
}
++j;
} while (!success);
}
@ -1852,157 +1643,74 @@ RemoveAttrDefaults(Relation rel)
heap_close(adrel, RowExclusiveLock);
}
static void
RemoveRelChecks(Relation rel)
{
Relation rcrel;
HeapScanDesc rcscan;
ScanKeyData key;
HeapTuple tup;
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
rcscan = heap_beginscan(rcrel, SnapshotNow, 1, &key);
while ((tup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
simple_heap_delete(rcrel, &tup->t_self);
heap_endscan(rcscan);
heap_close(rcrel, RowExclusiveLock);
}
/*
* Removes all CHECK constraints on a relation that match the given name.
* It is the responsibility of the calling function to acquire a lock on
* the relation.
* Returns: The number of CHECK constraints removed.
* Removes all constraints on a relation that match the given name.
*
* It is the responsibility of the calling function to acquire a suitable
* lock on the relation.
*
* Returns: The number of constraints removed.
*/
int
RemoveCheckConstraint(Relation rel, const char *constrName, bool inh)
RemoveRelConstraints(Relation rel, const char *constrName,
DropBehavior behavior)
{
Oid relid;
Relation rcrel;
TupleDesc tupleDesc;
TupleConstr *oldconstr;
int numoldchecks;
int numchecks;
HeapScanDesc rcscan;
ScanKeyData key[2];
HeapTuple rctup;
int rel_deleted = 0;
int all_deleted = 0;
int ndeleted = 0;
Relation conrel;
SysScanDesc conscan;
ScanKeyData key[1];
HeapTuple contup;
/* Find id of the relation */
relid = RelationGetRelid(rel);
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
/* Use the index to scan only constraints of the target relation */
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_constraint_conrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
SnapshotNow, 1, key);
/*
* Process child tables and remove constraints of the same name.
* Scan over the result set, removing any matching entries.
*/
if (inh)
while ((contup = systable_getnext(conscan)) != NULL)
{
List *child,
*children;
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
/* This routine is actually in the planner */
children = find_all_inheritors(relid);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all of
* the relids in the list that it returns.
*/
foreach(child, children)
if (strcmp(NameStr(con->conname), constrName) == 0)
{
Oid childrelid = lfirsti(child);
Relation inhrel;
ObjectAddress conobj;
if (childrelid == relid)
continue;
inhrel = heap_open(childrelid, AccessExclusiveLock);
all_deleted += RemoveCheckConstraint(inhrel, constrName, false);
heap_close(inhrel, NoLock);
conobj.classId = RelationGetRelid(conrel);
conobj.objectId = contup->t_data->t_oid;
conobj.objectSubId = 0;
performDeletion(&conobj, behavior);
ndeleted++;
}
}
/*
* Get number of existing constraints.
*/
tupleDesc = RelationGetDescr(rel);
oldconstr = tupleDesc->constr;
if (oldconstr)
numoldchecks = oldconstr->num_check;
else
numoldchecks = 0;
/* Grab an appropriate lock on the pg_relcheck relation */
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
/*
* Create two scan keys. We need to match on the oid of the table the
* CHECK is in and also we need to match the name of the CHECK
* constraint.
*/
ScanKeyEntryInitialize(&key[0], 0, Anum_pg_relcheck_rcrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
ScanKeyEntryInitialize(&key[1], 0, Anum_pg_relcheck_rcname,
F_NAMEEQ,
PointerGetDatum(constrName));
/* Begin scanning the heap */
rcscan = heap_beginscan(rcrel, SnapshotNow, 2, key);
/*
* Scan over the result set, removing any matching entries. Note that
* this has the side-effect of removing ALL CHECK constraints that
* share the specified constraint name.
*/
while ((rctup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
{
simple_heap_delete(rcrel, &rctup->t_self);
++rel_deleted;
++all_deleted;
}
/* Clean up after the scan */
heap_endscan(rcscan);
heap_close(rcrel, RowExclusiveLock);
systable_endscan(conscan);
heap_close(conrel, RowExclusiveLock);
if (rel_deleted)
{
/*
* Update the count of constraints in the relation's pg_class tuple.
*/
numchecks = numoldchecks - rel_deleted;
if (numchecks < 0)
elog(ERROR, "check count became negative");
SetRelationNumChecks(rel, numchecks);
}
/* Return the number of tuples deleted, including all children */
return all_deleted;
return ndeleted;
}
static void
RemoveConstraints(Relation rel)
RemoveDefaults(Relation rel)
{
TupleConstr *constr = rel->rd_att->constr;
if (!constr)
return;
if (constr->num_defval > 0)
/*
* We can skip looking at pg_attrdef if there are no defaults recorded
* in the Relation.
*/
if (constr && constr->num_defval > 0)
RemoveAttrDefaults(rel);
if (constr->num_check > 0)
RemoveRelChecks(rel);
}
static void

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.181 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.182 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -29,14 +29,15 @@
#include "bootstrap/bootstrap.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@ -535,6 +536,7 @@ index_create(Oid heapRelationId,
Oid accessMethodObjectId,
Oid *classObjectId,
bool primary,
bool isconstraint,
bool allow_system_table_mods)
{
Relation heapRelation;
@ -543,6 +545,7 @@ index_create(Oid heapRelationId,
bool shared_relation;
Oid namespaceId;
Oid indexoid;
int i;
SetReindexProcessing(false);
@ -660,7 +663,89 @@ index_create(Oid heapRelationId,
classObjectId, primary);
/*
* fill in the index strategy structure with information from the
* Register constraint and dependencies for the index.
*
* If the index is from a CONSTRAINT clause, construct a pg_constraint
* entry. The index is then linked to the constraint, which in turn is
* linked to the table. If it's not a CONSTRAINT, make the dependency
* directly on the table.
*
* During bootstrap we can't register any dependencies, and we don't
* try to make a constraint either.
*/
if (!IsBootstrapProcessingMode())
{
ObjectAddress myself,
referenced;
myself.classId = RelOid_pg_class;
myself.objectId = indexoid;
myself.objectSubId = 0;
if (isconstraint)
{
char constraintType;
Oid conOid;
if (primary)
constraintType = CONSTRAINT_PRIMARY;
else if (indexInfo->ii_Unique)
constraintType = CONSTRAINT_UNIQUE;
else
{
elog(ERROR, "index_create: constraint must be PRIMARY or UNIQUE");
constraintType = 0; /* keep compiler quiet */
}
conOid = CreateConstraintEntry(indexRelationName,
namespaceId,
constraintType,
false, /* isDeferrable */
false, /* isDeferred */
heapRelationId,
indexInfo->ii_KeyAttrNumbers,
indexInfo->ii_NumIndexAttrs,
InvalidOid, /* no domain */
InvalidOid, /* no foreign key */
NULL,
0,
' ',
' ',
' ',
NULL, /* Constraint Bin & Src */
NULL);
referenced.classId = get_system_catalog_relid(ConstraintRelationName);
referenced.objectId = conOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
else
{
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
referenced.classId = RelOid_pg_class;
referenced.objectId = heapRelationId;
referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
/* Store the dependency on the function (if appropriate) */
if (OidIsValid(indexInfo->ii_FuncOid))
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = indexInfo->ii_FuncOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
/*
* Fill in the index strategy structure with information from the
* catalogs. First we must advance the command counter so that we
* will see the newly-entered index catalog tuples.
*/
@ -691,11 +776,11 @@ index_create(Oid heapRelationId,
return indexoid;
}
/* ----------------------------------------------------------------
*
/*
* index_drop
*
* ----------------------------------------------------------------
* NOTE: this routine should now only be called through performDeletion(),
* else associated dependencies won't be cleaned up.
*/
void
index_drop(Oid indexId)
@ -730,17 +815,6 @@ index_drop(Oid indexId)
userIndexRelation = index_open(indexId);
LockRelation(userIndexRelation, AccessExclusiveLock);
/*
* Note: unlike heap_drop_with_catalog, we do not need to prevent
* deletion of system indexes here; that's checked for upstream. If we
* did check it here, deletion of TOAST tables would fail...
*/
/*
* fix DESCRIPTION relation
*/
DeleteComments(indexId, RelOid_pg_class);
/*
* fix RELATION relation
*/

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.95 2002/07/11 07:39:27 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.96 2002/07/12 18:43:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -45,10 +45,14 @@ char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] =
{AttrDefaultIndex};
char *Name_pg_class_indices[Num_pg_class_indices] =
{ClassNameNspIndex, ClassOidIndex};
char *Name_pg_constraint_indices[Num_pg_constraint_indices] =
{ConstraintNameNspIndex, ConstraintOidIndex, ConstraintRelidIndex};
char *Name_pg_conversion_indices[Num_pg_conversion_indices] =
{ConversionNameNspIndex, ConversionDefaultIndex};
char *Name_pg_database_indices[Num_pg_database_indices] =
{DatabaseNameIndex, DatabaseOidIndex};
char *Name_pg_depend_indices[Num_pg_depend_indices] =
{DependDependerIndex, DependReferenceIndex};
char *Name_pg_group_indices[Num_pg_group_indices] =
{GroupNameIndex, GroupSysidIndex};
char *Name_pg_index_indices[Num_pg_index_indices] =
@ -67,8 +71,6 @@ char *Name_pg_operator_indices[Num_pg_operator_indices] =
{OperatorOidIndex, OperatorNameNspIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] =
{ProcedureOidIndex, ProcedureNameNspIndex};
char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] =
{RelCheckIndex};
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
{RewriteOidIndex, RewriteRelRulenameIndex};
char *Name_pg_shadow_indices[Num_pg_shadow_indices] =

View File

@ -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.23 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.24 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,7 @@
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits.h"
@ -128,25 +129,10 @@ static Oid mySpecialNamespace = InvalidOid;
char *namespace_search_path = NULL;
/*
* Deletion ordering constraint item.
*/
typedef struct DelConstraint
{
Oid referencer; /* table to delete first */
Oid referencee; /* table to delete second */
int pred; /* workspace for TopoSortRels */
struct DelConstraint *link; /* workspace for TopoSortRels */
} DelConstraint;
/* Local functions */
static void recomputeNamespacePath(void);
static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static List *FindTempRelations(Oid tempNamespaceId);
static List *FindDeletionConstraints(List *relOids);
static List *TopoSortRels(List *relOids, List *constraintList);
static void RemoveTempRelationsCallback(void);
static void NamespaceCallback(Datum arg, Oid relid);
@ -1531,56 +1517,22 @@ AtEOXact_Namespace(bool isCommit)
static void
RemoveTempRelations(Oid tempNamespaceId)
{
List *tempRelList;
List *constraintList;
List *lptr;
/* Get a list of relations to delete */
tempRelList = FindTempRelations(tempNamespaceId);
if (tempRelList == NIL)
return; /* nothing to do */
/* If more than one, sort them to respect any deletion-order constraints */
if (length(tempRelList) > 1)
{
constraintList = FindDeletionConstraints(tempRelList);
if (constraintList != NIL)
tempRelList = TopoSortRels(tempRelList, constraintList);
}
/* Scan the list and delete all entries */
foreach(lptr, tempRelList)
{
Oid reloid = (Oid) lfirsti(lptr);
heap_drop_with_catalog(reloid, true);
/*
* Advance cmd counter to make catalog changes visible, in case
* a later entry depends on this one.
*/
CommandCounterIncrement();
}
}
/*
* Find all relations in the specified temp namespace.
*
* Returns a list of relation OIDs.
*/
static List *
FindTempRelations(Oid tempNamespaceId)
{
List *tempRelList = NIL;
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
ObjectAddress object;
/*
* Scan pg_class to find all the relations in the target namespace.
* Ignore indexes, though, on the assumption that they'll go away
* when their tables are deleted.
*
* NOTE: if there are deletion constraints between temp relations,
* then our CASCADE delete call may cause as-yet-unvisited objects
* to go away. This is okay because we are using SnapshotNow; when
* the scan does reach those pg_class tuples, they'll be ignored as
* already deleted.
*/
ScanKeyEntryInitialize(&key, 0x0,
Anum_pg_class_relnamespace,
@ -1597,7 +1549,10 @@ FindTempRelations(Oid tempNamespaceId)
case RELKIND_RELATION:
case RELKIND_SEQUENCE:
case RELKIND_VIEW:
tempRelList = lconsi(tuple->t_data->t_oid, tempRelList);
object.classId = RelOid_pg_class;
object.objectId = tuple->t_data->t_oid;
object.objectSubId = 0;
performDeletion(&object, DROP_CASCADE);
break;
default:
break;
@ -1606,164 +1561,6 @@ FindTempRelations(Oid tempNamespaceId)
heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
return tempRelList;
}
/*
* Find deletion-order constraints involving the given relation OIDs.
*
* Returns a list of DelConstraint objects.
*/
static List *
FindDeletionConstraints(List *relOids)
{
List *constraintList = NIL;
Relation inheritsrel;
HeapScanDesc scan;
HeapTuple tuple;
/*
* Scan pg_inherits to find parents and children that are in the list.
*/
inheritsrel = heap_openr(InheritsRelationName, AccessShareLock);
scan = heap_beginscan(inheritsrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
Oid inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
if (intMember(inhrelid, relOids) && intMember(inhparent, relOids))
{
DelConstraint *item;
item = (DelConstraint *) palloc(sizeof(DelConstraint));
item->referencer = inhrelid;
item->referencee = inhparent;
constraintList = lcons(item, constraintList);
}
}
heap_endscan(scan);
heap_close(inheritsrel, AccessShareLock);
return constraintList;
}
/*
* TopoSortRels -- topological sort of a list of rels to delete
*
* This is a lot simpler and slower than, for example, the topological sort
* algorithm shown in Knuth's Volume 1. However, we are not likely to be
* working with more than a few constraints, so the apparent slowness of the
* algorithm won't really matter.
*/
static List *
TopoSortRels(List *relOids, List *constraintList)
{
int queue_size = length(relOids);
Oid *rels;
int *beforeConstraints;
DelConstraint **afterConstraints;
List *resultList = NIL;
List *lptr;
int i,
j,
k,
last;
/* Allocate workspace */
rels = (Oid *) palloc(queue_size * sizeof(Oid));
beforeConstraints = (int *) palloc(queue_size * sizeof(int));
afterConstraints = (DelConstraint **)
palloc(queue_size * sizeof(DelConstraint*));
/* Build an array of the target relation OIDs */
i = 0;
foreach(lptr, relOids)
{
rels[i++] = (Oid) lfirsti(lptr);
}
/*
* Scan the constraints, and for each rel in the array, generate a
* count of the number of constraints that say it must be before
* something else, plus a list of the constraints that say it must be
* after something else. The count for the j'th rel is stored in
* beforeConstraints[j], and the head of its list in
* afterConstraints[j]. Each constraint stores its list link in
* its link field (note any constraint will be in just one list).
* The array index for the before-rel of each constraint is
* remembered in the constraint's pred field.
*/
MemSet(beforeConstraints, 0, queue_size * sizeof(int));
MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*));
foreach(lptr, constraintList)
{
DelConstraint *constraint = (DelConstraint *) lfirst(lptr);
Oid rel;
/* Find the referencer rel in the array */
rel = constraint->referencer;
for (j = queue_size; --j >= 0;)
{
if (rels[j] == rel)
break;
}
Assert(j >= 0); /* should have found a match */
/* Find the referencee rel in the array */
rel = constraint->referencee;
for (k = queue_size; --k >= 0;)
{
if (rels[k] == rel)
break;
}
Assert(k >= 0); /* should have found a match */
beforeConstraints[j]++; /* referencer must come before */
/* add this constraint to list of after-constraints for referencee */
constraint->pred = j;
constraint->link = afterConstraints[k];
afterConstraints[k] = constraint;
}
/*--------------------
* Now scan the rels array backwards. At each step, output the
* last rel that has no remaining before-constraints, and decrease
* the beforeConstraints count of each of the rels it was constrained
* against. (This is the right order since we are building the result
* list back-to-front.)
* i = counter for number of rels left to output
* j = search index for rels[]
* dc = temp for scanning constraint list for rel j
* last = last valid index in rels (avoid redundant searches)
*--------------------
*/
last = queue_size - 1;
for (i = queue_size; --i >= 0;)
{
DelConstraint *dc;
/* Find next candidate to output */
while (rels[last] == InvalidOid)
last--;
for (j = last; j >= 0; j--)
{
if (rels[j] != InvalidOid && beforeConstraints[j] == 0)
break;
}
/* If no available candidate, topological sort fails */
if (j < 0)
elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering");
/* Output candidate, and mark it done by zeroing rels[] entry */
resultList = lconsi(rels[j], resultList);
rels[j] = InvalidOid;
/* Update beforeConstraints counts of its predecessors */
for (dc = afterConstraints[j]; dc; dc = dc->link)
beforeConstraints[dc->pred]--;
}
/* Done */
return resultList;
}
/*

View File

@ -0,0 +1,453 @@
/*-------------------------------------------------------------------------
*
* pg_constraint.c
* routines to support manipulation of the pg_constraint relation
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/genam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
/*
* CreateConstraintEntry
* Create a constraint table entry.
*
* Subsidiary records (such as triggers or indexes to implement the
* constraint) are *not* created here. But we do make dependency links
* from the constraint to the things it depends on.
*/
Oid
CreateConstraintEntry(const char *constraintName,
Oid constraintNamespace,
char constraintType,
bool isDeferrable,
bool isDeferred,
Oid relId,
const int16 *constraintKey,
int constraintNKeys,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
const char *conBin,
const char *conSrc)
{
Relation conDesc;
Oid conOid;
HeapTuple tup;
char nulls[Natts_pg_constraint];
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
NameData cname;
int i;
ObjectAddress conobject;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
Assert(constraintName);
namestrcpy(&cname, constraintName);
/*
* Convert C arrays into Postgres arrays.
*/
if (constraintNKeys > 0)
{
Datum *conkey;
conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
for (i = 0; i < constraintNKeys; i++)
conkey[i] = Int16GetDatum(constraintKey[i]);
conkeyArray = construct_array(conkey, constraintNKeys,
true, 2, 's');
}
else
conkeyArray = NULL;
if (foreignNKeys > 0)
{
Datum *confkey;
confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
for (i = 0; i < foreignNKeys; i++)
confkey[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(confkey, foreignNKeys,
true, 2, 's');
}
else
confkeyArray = NULL;
/* initialize nulls and values */
for (i = 0; i < Natts_pg_constraint; i++)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
else
nulls[Anum_pg_constraint_conkey - 1] = 'n';
if (confkeyArray)
values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
else
nulls[Anum_pg_constraint_confkey - 1] = 'n';
/*
* initialize the binary form of the check constraint.
*/
if (conBin)
values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(conBin));
else
nulls[Anum_pg_constraint_conbin - 1] = 'n';
/*
* initialize the text form of the check constraint
*/
if (conSrc)
values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin,
CStringGetDatum(conSrc));
else
nulls[Anum_pg_constraint_consrc - 1] = 'n';
tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
conOid = simple_heap_insert(conDesc, tup);
/* Handle Indices */
if (RelationGetForm(conDesc)->relhasindex)
{
Relation idescs[Num_pg_constraint_indices];
CatalogOpenIndices(Num_pg_constraint_indices, Name_pg_constraint_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_constraint_indices, conDesc, tup);
CatalogCloseIndices(Num_pg_constraint_indices, idescs);
}
conobject.classId = RelationGetRelid(conDesc);
conobject.objectId = conOid;
conobject.objectSubId = 0;
heap_close(conDesc, RowExclusiveLock);
if (OidIsValid(relId))
{
/*
* Register auto dependency from constraint to owning relation,
* or to specific column(s) if any are mentioned.
*/
ObjectAddress relobject;
relobject.classId = RelOid_pg_class;
relobject.objectId = relId;
if (constraintNKeys > 0)
{
for (i = 0; i < constraintNKeys; i++)
{
relobject.objectSubId = constraintKey[i];
recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
}
}
else
{
relobject.objectSubId = 0;
recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
}
}
if (OidIsValid(foreignRelId))
{
/*
* Register dependency from constraint to foreign relation,
* or to specific column(s) if any are mentioned.
*
* In normal case of two separate relations, make this a NORMAL
* dependency (so dropping the FK table would require CASCADE).
* However, for a self-reference just make it AUTO.
*/
DependencyType deptype;
ObjectAddress relobject;
deptype = (foreignRelId == relId) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL;
relobject.classId = RelOid_pg_class;
relobject.objectId = foreignRelId;
if (foreignNKeys > 0)
{
for (i = 0; i < foreignNKeys; i++)
{
relobject.objectSubId = foreignKey[i];
recordDependencyOn(&conobject, &relobject, deptype);
}
}
else
{
relobject.objectSubId = 0;
recordDependencyOn(&conobject, &relobject, deptype);
}
}
return conOid;
}
/*
* Test whether given name is currently used as a constraint name
* for the given relation.
*
* NB: Caller should hold exclusive lock on the given relation, else
* this test is not very meaningful.
*/
bool
ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
{
bool found;
Relation conDesc;
SysScanDesc conscan;
ScanKeyData skey[2];
HeapTuple tup;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
found = false;
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conname, F_NAMEEQ,
CStringGetDatum(cname));
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
{
found = true;
break;
}
}
systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock);
return found;
}
/*
* Generate a currently-unused constraint name for the given relation.
*
* The passed counter should be initialized to 0 the first time through.
* If multiple constraint names are to be generated in a single command,
* pass the new counter value to each successive call, else the same
* name will be generated each time.
*
* NB: Caller should hold exclusive lock on the given relation, else
* someone else might choose the same name concurrently!
*/
char *
GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
{
bool found;
Relation conDesc;
char *cname;
cname = (char *) palloc(NAMEDATALEN * sizeof(char));
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
/* Loop until we find a non-conflicting constraint name */
/* We assume there will be one eventually ... */
do
{
SysScanDesc conscan;
ScanKeyData skey[2];
HeapTuple tup;
++(*counter);
snprintf(cname, NAMEDATALEN, "$%d", *counter);
/*
* This duplicates ConstraintNameIsUsed() so that we can avoid
* re-opening pg_constraint for each iteration.
*/
found = false;
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conname, F_NAMEEQ,
CStringGetDatum(cname));
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
{
found = true;
break;
}
}
systable_endscan(conscan);
} while (found);
heap_close(conDesc, RowExclusiveLock);
return cname;
}
/*
* Does the given name look like a generated constraint name?
*
* This is a test on the form of the name, *not* on whether it has
* actually been assigned.
*/
bool
ConstraintNameIsGenerated(const char *cname)
{
if (cname[0] != '$')
return false;
if (strspn(cname+1, "0123456789") != strlen(cname+1))
return false;
return true;
}
/*
* Delete a single constraint record.
*/
void
RemoveConstraintById(Oid conId)
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc conscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(conId));
conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(conscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RemoveConstraintById: constraint %u not found",
conId);
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
* If the constraint is for a relation, open and exclusive-lock
* the relation it's for.
*
* XXX not clear what we should lock, if anything, for other constraints.
*/
if (OidIsValid(con->conrelid))
{
Relation rel;
rel = heap_open(con->conrelid, AccessExclusiveLock);
/*
* We need to update the relcheck count if it is a check constraint
* being dropped. This update will force backends to rebuild
* relcache entries when we commit.
*/
if (con->contype == CONSTRAINT_CHECK)
{
Relation pgrel;
HeapTuple relTup;
Form_pg_class classForm;
Relation ridescs[Num_pg_class_indices];
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
relTup = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(con->conrelid),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "cache lookup of relation %u failed",
con->conrelid);
classForm = (Form_pg_class) GETSTRUCT(relTup);
if (classForm->relchecks == 0)
elog(ERROR, "RemoveConstraintById: relation %s has relchecks = 0",
RelationGetRelationName(rel));
classForm->relchecks--;
simple_heap_update(pgrel, &relTup->t_self, relTup);
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, relTup);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
heap_freetuple(relTup);
heap_close(pgrel, RowExclusiveLock);
}
/* Keep lock on constraint's rel until end of xact */
heap_close(rel, NoLock);
}
/* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self);
/* Clean up */
systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock);
}

View File

@ -0,0 +1,147 @@
/*-------------------------------------------------------------------------
*
* pg_depend.c
* routines to support manipulation of the pg_depend relation
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/dependency.h"
#include "catalog/pg_depend.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
static bool isObjectPinned(const ObjectAddress *object, Relation rel);
/*
* Record a dependency between 2 objects via their respective objectAddress.
* The first argument is the dependent object, the second the one it
* references.
*
* This simply creates an entry in pg_depend, without any other processing.
*/
void
recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
Relation dependDesc;
HeapTuple tup;
int i;
char nulls[Natts_pg_depend];
Datum values[Natts_pg_depend];
Relation idescs[Num_pg_depend_indices];
/*
* During bootstrap, do nothing since pg_depend may not exist yet.
* initdb will fill in appropriate pg_depend entries after bootstrap.
*/
if (IsBootstrapProcessingMode())
return;
dependDesc = heap_openr(DependRelationName, RowExclusiveLock);
/*
* If the referenced object is pinned by the system, there's no real
* need to record dependencies on it. This saves lots of space in
* pg_depend, so it's worth the time taken to check.
*/
if (!isObjectPinned(referenced, dependDesc))
{
/*
* Record the Dependency. Note we don't bother to check for
* duplicate dependencies; there's no harm in them.
*/
for (i = 0; i < Natts_pg_depend; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) 0;
}
values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
values[Anum_pg_depend_deptype -1] = CharGetDatum((char) behavior);
tup = heap_formtuple(dependDesc->rd_att, values, nulls);
simple_heap_insert(dependDesc, tup);
/*
* Keep indices current
*/
CatalogOpenIndices(Num_pg_depend_indices, Name_pg_depend_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_depend_indices, dependDesc, tup);
CatalogCloseIndices(Num_pg_depend_indices, idescs);
}
heap_close(dependDesc, RowExclusiveLock);
}
/*
* isObjectPinned()
*
* Test if an object is required for basic database functionality.
* Caller must already have opened pg_depend.
*
* The passed subId, if any, is ignored; we assume that only whole objects
* are pinned (and that this implies pinning their components).
*/
static bool
isObjectPinned(const ObjectAddress *object, Relation rel)
{
bool ret = false;
SysScanDesc scan;
HeapTuple tup;
ScanKeyData key[2];
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_refclassid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_refobjid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
scan = systable_beginscan(rel, DependReferenceIndex, true,
SnapshotNow, 2, key);
/*
* Since we won't generate additional pg_depend entries for pinned
* objects, there can be at most one entry referencing a pinned
* object. Hence, it's sufficient to look at the first returned
* tuple; we don't need to loop.
*/
tup = systable_getnext(scan);
if (HeapTupleIsValid(tup))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
if (foundDep->deptype == DEPENDENCY_PIN)
ret = true;
}
systable_endscan(scan);
return ret;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.72 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.73 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@ -166,6 +167,8 @@ TypeCreate(const char *typeName,
NameData name;
TupleDesc tupDesc;
int i;
ObjectAddress myself,
referenced;
/*
* validate size specifications: either positive (fixed-length) or -1
@ -298,6 +301,77 @@ TypeCreate(const char *typeName,
CatalogCloseIndices(Num_pg_type_indices, idescs);
}
/*
* Create dependencies
*/
myself.classId = RelOid_pg_type;
myself.objectId = typeObjectId;
myself.objectSubId = 0;
/* Normal dependencies on the I/O functions */
referenced.classId = RelOid_pg_proc;
referenced.objectId = inputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
referenced.classId = RelOid_pg_proc;
referenced.objectId = outputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (receiveProcedure != inputProcedure)
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = receiveProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
if (sendProcedure != outputProcedure)
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = sendProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
* If the type is a rowtype for a relation, mark it as internally
* dependent on the relation. This allows it to be auto-dropped
* when the relation is, and not otherwise.
*/
if (OidIsValid(relationOid))
{
referenced.classId = RelOid_pg_class;
referenced.objectId = relationOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
/*
* If the type is an array type, mark it auto-dependent on the
* base type. (This is a compromise between the typical case where the
* array type is automatically generated and the case where it is manually
* created: we'd prefer INTERNAL for the former case and NORMAL for the
* latter.)
*/
if (OidIsValid(elementType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = elementType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
/* Normal dependency from a domain to its base type. */
if (OidIsValid(baseType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = baseType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
* finish up
*/

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.3 2002/07/12 18:43:15 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -24,10 +24,10 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters)
}
/*
* RemoveAggregate
* Deletes an aggregate.
*/
void
RemoveAggregate(List *aggName, TypeName *aggType)
RemoveAggregate(RemoveAggrStmt *stmt)
{
Relation relation;
HeapTuple tup;
List *aggName = stmt->aggname;
TypeName *aggType = stmt->aggtype;
Oid basetypeID;
Oid procOid;
HeapTuple tup;
ObjectAddress object;
/*
* if a basetype is passed in, then attempt to find an aggregate for
@ -164,8 +170,9 @@ RemoveAggregate(List *aggName, TypeName *aggType)
procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
/*
* Find the function tuple, do permissions and validity checks
*/
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
/* Delete any comments associated with this function */
DeleteComments(procOid, RelationGetRelid(relation));
/* Remove the pg_proc tuple */
simple_heap_delete(relation, &tup->t_self);
/* find_aggregate_func already checked it is an aggregate */
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
/*
* Do the deletion
*/
object.classId = RelOid_pg_proc;
object.objectId = procOid;
object.objectSubId = 0;
/* Remove the pg_aggregate tuple */
relation = heap_openr(AggregateRelationName, RowExclusiveLock);
tup = SearchSysCache(AGGFNOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
NameListToString(aggName));
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
performDeletion(&object, stmt->behavior);
}

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.82 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,6 +24,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/pg_index.h"
@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname)
OldIndex;
char NewHeapName[NAMEDATALEN];
char NewIndexName[NAMEDATALEN];
ObjectAddress object;
/*
* We grab exclusive access to the target rel and index for the
@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname)
CommandCounterIncrement();
/* Destroy old heap (along with its index) and rename new. */
heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods);
object.classId = RelOid_pg_class;
object.objectId = OIDOldHeap;
object.objectSubId = 0;
CommandCounterIncrement();
/* XXX better to use DROP_CASCADE here? */
performDeletion(&object, DROP_RESTRICT);
/* performDeletion does CommandCounterIncrement at end */
renamerel(OIDNewHeap, oldrelation->relname);
@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
OldIndex->rd_rel->relam,
OldIndex->rd_index->indclass,
OldIndex->rd_index->indisprimary,
false, /* XXX losing constraint status */
allowSystemTableMods);
setRelhasindex(OIDNewHeap, true,

View File

@ -7,7 +7,7 @@
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.49 2002/06/20 20:51:45 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.50 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -225,38 +225,45 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
}
/*
* DeleteComments --
* DeleteComments -- remove comments for an object
*
* This routine is used to purge all comments associated with an object,
* regardless of their objsubid. It is called, for example, when a relation
* is destroyed.
* If subid is nonzero then only comments matching it will be removed.
* If subid is zero, all comments matching the oid/classoid will be removed
* (this corresponds to deleting a whole object).
*/
void
DeleteComments(Oid oid, Oid classoid)
DeleteComments(Oid oid, Oid classoid, int32 subid)
{
Relation description;
ScanKeyData skey[2];
ScanKeyData skey[3];
int nkeys;
SysScanDesc sd;
HeapTuple oldtuple;
/* Use the index to search for all matching old tuples */
ScanKeyEntryInitialize(&skey[0],
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) F_OIDEQ,
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
ScanKeyEntryInitialize(&skey[1],
(bits16) 0x0,
(AttrNumber) 2,
(RegProcedure) F_OIDEQ,
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_description_classoid, F_OIDEQ,
ObjectIdGetDatum(classoid));
if (subid != 0)
{
ScanKeyEntryInitialize(&skey[2], 0x0,
Anum_pg_description_objsubid, F_INT4EQ,
Int32GetDatum(subid));
nkeys = 3;
}
else
nkeys = 2;
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
sd = systable_beginscan(description, DescriptionObjIndex, true,
SnapshotNow, 2, skey);
SnapshotNow, nkeys, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
{
@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid)
/* Done */
systable_endscan(sd);
heap_close(description, NoLock);
heap_close(description, RowExclusiveLock);
}
/*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.95 2002/06/20 20:29:27 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.96 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -456,8 +456,13 @@ dropdb(const char *dbname)
heap_endscan(pgdbscan);
/* Delete any comments associated with the database */
DeleteComments(db_id, RelationGetRelid(pgdbrel));
/*
* Delete any comments associated with the database
*
* NOTE: this is probably dead code since any such comments should have
* been in that database, not mine.
*/
DeleteComments(db_id, RelationGetRelid(pgdbrel), 0);
/*
* Close pg_database, but keep exclusive lock till commit to ensure

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.7 2002/06/20 20:29:27 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@ -33,11 +33,11 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt)
/*
* RemoveFunction
* Deletes a function.
*
* Exceptions:
* BadArg if name is invalid.
* "ERROR" if function nonexistent.
* ...
*/
void
RemoveFunction(List *functionName, /* function name to be removed */
List *argTypes) /* list of TypeName nodes */
RemoveFunction(RemoveFuncStmt *stmt)
{
List *functionName = stmt->funcname;
List *argTypes = stmt->args; /* list of TypeName nodes */
Oid funcOid;
Relation relation;
HeapTuple tup;
ObjectAddress object;
/*
* Find the function, do permissions and validity checks
*/
funcOid = LookupFuncNameTypeNames(functionName, argTypes,
true, "RemoveFunction");
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */
NameListToString(functionName));
}
/* Delete any comments associated with this function */
DeleteComments(funcOid, RelationGetRelid(relation));
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = RelOid_pg_proc;
object.objectId = funcOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Guts of function deletion.
*
* Note: this is also used for aggregate deletion, since the OIDs of
* both functions and aggregates point to pg_proc.
*/
void
RemoveFunctionById(Oid funcOid)
{
Relation relation;
HeapTuple tup;
bool isagg;
/*
* Delete the pg_proc tuple.
*/
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveFunctionById: couldn't find tuple for function %u",
funcOid);
isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
/*
* If there's a pg_aggregate tuple, delete that too.
*/
if (isagg)
{
relation = heap_openr(AggregateRelationName, RowExclusiveLock);
tup = SearchSysCache(AGGFNOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveFunctionById: couldn't find pg_aggregate tuple for %u",
funcOid);
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.76 2002/07/01 15:27:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.77 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_opclass.h"
@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation,
List *attributeList,
bool unique,
bool primary,
bool isconstraint,
Expr *predicate,
List *rangetable)
{
@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation,
index_create(relationId, indexRelationName,
indexInfo, accessMethodId, classObjectId,
primary, allowSystemTableMods);
primary, isconstraint, allowSystemTableMods);
/*
* We update the relation's pg_class tuple even if it already has
@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
{
Oid indOid;
HeapTuple tuple;
ObjectAddress object;
indOid = RangeVarGetRelid(relation, false);
tuple = SearchSysCache(RELOID,
@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
ReleaseSysCache(tuple);
index_drop(indOid);
object.classId = RelOid_pg_class;
object.objectId = indOid;
object.objectSubId = 0;
performDeletion(&object, behavior);
}
/*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.4 2002/07/01 15:27:46 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -36,9 +36,9 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt)
TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
Oid operOid;
Relation relation;
HeapTuple tup;
ObjectAddress object;
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
"RemoveOperator");
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
tup = SearchSysCacheCopy(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
NameListToString(operatorName));
@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
/* Delete any comments associated with this operator */
DeleteComments(operOid, RelationGetRelid(relation));
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = get_system_catalog_relid(OperatorRelationName);
object.objectId = operOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Guts of operator deletion.
*/
void
RemoveOperatorById(Oid operOid)
{
Relation relation;
HeapTuple tup;
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(operOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u",
operOid);
simple_heap_delete(relation, &tup->t_self);
heap_freetuple(tup);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.34 2002/06/20 20:29:27 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.35 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
{
char languageName[NAMEDATALEN];
HeapTuple langTup;
Relation rel;
ObjectAddress object;
/*
* Check permission
@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt)
*/
case_translate_language_name(stmt->plname, languageName);
rel = heap_openr(LanguageRelationName, RowExclusiveLock);
langTup = SearchSysCacheCopy(LANGNAME,
PointerGetDatum(languageName),
0, 0, 0);
langTup = SearchSysCache(LANGNAME,
CStringGetDatum(languageName),
0, 0, 0);
if (!HeapTupleIsValid(langTup))
elog(ERROR, "Language %s doesn't exist", languageName);
@ -167,8 +166,39 @@ DropProceduralLanguage(DropPLangStmt *stmt)
elog(ERROR, "Language %s isn't a created procedural language",
languageName);
object.classId = get_system_catalog_relid(LanguageRelationName);
object.objectId = langTup->t_data->t_oid;
object.objectSubId = 0;
ReleaseSysCache(langTup);
/*
* Do the deletion
*/
performDeletion(&object, stmt->behavior);
}
/*
* Guts of language dropping.
*/
void
DropProceduralLanguageById(Oid langOid)
{
Relation rel;
HeapTuple langTup;
rel = heap_openr(LanguageRelationName, RowExclusiveLock);
langTup = SearchSysCache(LANGOID,
ObjectIdGetDatum(langOid),
0, 0, 0);
if (!HeapTupleIsValid(langTup))
elog(ERROR, "DropProceduralLanguageById: language %u not found",
langOid);
simple_heap_delete(rel, &langTup->t_self);
heap_freetuple(langTup);
ReleaseSysCache(langTup);
heap_close(rel, RowExclusiveLock);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.19 2002/07/06 20:16:35 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.20 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,11 +18,13 @@
#include "access/tuptoaster.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
@ -36,6 +38,7 @@
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "parser/gramparse.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
@ -57,6 +60,13 @@ static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
static void drop_default(Oid relid, int16 attnum);
static void CheckTupleType(Form_pg_class tuple_class);
static bool needs_toast_table(Relation rel);
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel, Relation pkrel);
static Oid createForeignKeyConstraint(Relation rel, Relation pkrel,
FkConstraint *fkconstraint);
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
Oid constrOid);
static char *fkMatchTypeToString(char match_type);
/* Used by attribute and relation renaming routines: */
@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
sizeof(ConstrCheck));
int ncheck = 0;
int constr_name_ctr = 0;
foreach(listptr, old_constraints)
{
@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
}
else
{
/*
* Generate a constraint name. NB: this should match the
* form of names that GenerateConstraintName() may produce
* for names added later. We are assured that there is
* no name conflict, because MergeAttributes() did not pass
* back any names of this form.
*/
check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d",
++constr_name_ctr);
}
Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind)
/*
* RemoveRelation
* Deletes a relation.
*
* Exceptions:
* BadArg if name is invalid.
*
* Note:
* If the relation has indices defined on it, then the index relations
* themselves will be destroyed, too.
*/
void
RemoveRelation(const RangeVar *relation, DropBehavior behavior)
{
Oid relOid;
ObjectAddress object;
relOid = RangeVarGetRelid(relation, false);
heap_drop_with_catalog(relOid, allowSystemTableMods);
object.classId = RelOid_pg_class;
object.objectId = relOid;
object.objectSubId = 0;
performDeletion(&object, behavior);
}
/*
@ -580,7 +598,13 @@ MergeAttributes(List *schema, List *supers, bool istemp,
Node *expr;
cdef->contype = CONSTR_CHECK;
if (check[i].ccname[0] == '$')
/*
* Do not inherit generated constraint names, since they
* might conflict across multiple inheritance parents.
* (But conflicts between user-assigned names will cause
* an error.)
*/
if (ConstraintNameIsGenerated(check[i].ccname))
cdef->name = NULL;
else
cdef->name = pstrdup(check[i].ccname);
@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
/*
* complementary static functions for MergeAttributes().
*
* Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
* Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit
* constraints from parent classes, since the inherited attributes could
* be given different column numbers in multiple-inheritance cases.
*
@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
return;
/*
* Catalog INHERITS information using direct ancestors only.
* Store INHERITS information in pg_inherits using direct ancestors only.
* Also enter dependencies on the direct ancestors.
*/
relation = heap_openr(InheritsRelationName, RowExclusiveLock);
desc = RelationGetDescr(relation);
@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
Oid entryOid = lfirsti(entry);
Datum datum[Natts_pg_inherits];
char nullarr[Natts_pg_inherits];
ObjectAddress childobject,
parentobject;
datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */
@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers)
heap_freetuple(tuple);
/*
* Store a dependency too
*/
parentobject.classId = RelOid_pg_class;
parentobject.objectId = entryOid;
parentobject.objectSubId = 0;
childobject.classId = RelOid_pg_class;
childobject.objectId = relationId;
childobject.objectSubId = 0;
recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
seqNumber += 1;
}
@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid,
{
Relation rel;
List *listptr;
int counter = 0;
/*
* Grab an exclusive lock on the target table, which we will NOT
@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid,
foreach(listptr, newConstraints)
{
Node *newConstraint = lfirst(listptr);
/*
* copy is because we may destructively alter the node below
* by inserting a generated name; this name is not necessarily
* correct for children or parents.
*/
Node *newConstraint = copyObject(lfirst(listptr));
switch (nodeTag(newConstraint))
{
@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid,
RangeTblEntry *rte;
List *qual;
Node *expr;
char *name;
/*
* Assign or validate constraint name
*/
if (constr->name)
name = constr->name;
{
if (ConstraintNameIsUsed(RelationGetRelid(rel),
RelationGetNamespace(rel),
constr->name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
constr->name,
RelationGetRelationName(rel));
}
else
name = "<unnamed>";
constr->name = GenerateConstraintName(RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
/*
* We need to make a parse state and range
@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid,
pfree(slot);
if (!successful)
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s",
constr->name);
/*
* Call AddRelationRawConstraints to do
@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid,
{
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
Relation pkrel;
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
List *list;
int count;
Oid constrOid;
/*
* Assign or validate constraint name
*/
if (fkconstraint->constr_name)
{
if (ConstraintNameIsUsed(RelationGetRelid(rel),
RelationGetNamespace(rel),
fkconstraint->constr_name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
fkconstraint->constr_name,
RelationGetRelationName(rel));
}
else
fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
/*
* Grab an exclusive lock on the pk table, so that
* someone doesn't delete rows out from under us.
*
* XXX wouldn't a lesser lock be sufficient?
* (Although a lesser lock would do for that purpose,
* we'll need exclusive lock anyway to add triggers
* to the pk table; trying to start with a lesser lock
* will just create a risk of deadlock.)
*/
pkrel = heap_openrv(fkconstraint->pktable,
AccessExclusiveLock);
@ -2500,100 +2572,46 @@ AlterTableAddConstraint(Oid myrelid,
* Validity checks
*/
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "referenced table \"%s\" not a relation",
fkconstraint->pktable->relname);
elog(ERROR, "referenced relation \"%s\" is not a table",
RelationGetRelationName(pkrel));
if (!allowSystemTableMods
&& IsSystemRelation(pkrel))
elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
RelationGetRelationName(pkrel));
/* XXX shouldn't there be a permission check too? */
if (isTempNamespace(RelationGetNamespace(pkrel)) &&
!isTempNamespace(RelationGetNamespace(rel)))
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint");
/*
* First we check for limited correctness of the
* constraint.
* Check that the constraint is satisfied by existing
* rows (we can skip this during table creation).
*
* NOTE: we assume parser has already checked for
* existence of an appropriate unique index on the
* referenced relation, and that the column datatypes
* are comparable.
*
* Scan through each tuple, calling RI_FKey_check_ins
* (insert trigger) as if that tuple had just been
* inserted. If any of those fail, it should
* elog(ERROR) and that's that.
*/
MemSet(&trig, 0, sizeof(trig));
trig.tgoid = InvalidOid;
if (fkconstraint->constr_name)
trig.tgname = fkconstraint->constr_name;
else
trig.tgname = "<unknown>";
trig.tgenabled = TRUE;
trig.tgisconstraint = TRUE;
trig.tgconstrrelid = RelationGetRelid(pkrel);
trig.tgdeferrable = FALSE;
trig.tginitdeferred = FALSE;
if (!fkconstraint->skip_validation)
validateForeignKeyConstraint(fkconstraint, rel, pkrel);
trig.tgargs = (char **) palloc(
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
+ length(fkconstraint->pk_attrs)));
/*
* Record the FK constraint in pg_constraint.
*/
constrOid = createForeignKeyConstraint(rel, pkrel,
fkconstraint);
trig.tgargs[0] = trig.tgname;
trig.tgargs[1] = RelationGetRelationName(rel);
trig.tgargs[2] = RelationGetRelationName(pkrel);
trig.tgargs[3] = fkconstraint->match_type;
count = 4;
foreach(list, fkconstraint->fk_attrs)
{
Ident *fk_at = lfirst(list);
trig.tgargs[count] = fk_at->name;
count += 2;
}
count = 5;
foreach(list, fkconstraint->pk_attrs)
{
Ident *pk_at = lfirst(list);
trig.tgargs[count] = pk_at->name;
count += 2;
}
trig.tgnargs = count - 1;
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
/* Make a call to the check function */
/*
* No parameters are passed, but we do set a
* context
*/
FunctionCallInfoData fcinfo;
TriggerData trigdata;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* We assume RI_FKey_check_ins won't look at
* flinfo...
*/
trigdata.type = T_TriggerData;
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
trigdata.tg_relation = rel;
trigdata.tg_trigtuple = tuple;
trigdata.tg_newtuple = NULL;
trigdata.tg_trigger = &trig;
fcinfo.context = (Node *) &trigdata;
RI_FKey_check_ins(&fcinfo);
}
heap_endscan(scan);
pfree(trig.tgargs);
/*
* Create the triggers that will enforce the constraint.
*/
createForeignKeyTriggers(rel, fkconstraint, constrOid);
/*
* Close pk table, but keep lock until we've committed.
*/
heap_close(pkrel, NoLock);
break;
@ -2607,12 +2625,418 @@ AlterTableAddConstraint(Oid myrelid,
heap_close(rel, NoLock);
}
/*
* Scan the existing rows in a table to verify they meet a proposed FK
* constraint.
*
* Caller must have opened and locked both relations.
*/
static void
validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel,
Relation pkrel)
{
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
List *list;
int count;
/*
* Scan through each tuple, calling RI_FKey_check_ins
* (insert trigger) as if that tuple had just been
* inserted. If any of those fail, it should
* elog(ERROR) and that's that.
*/
MemSet(&trig, 0, sizeof(trig));
trig.tgoid = InvalidOid;
trig.tgname = fkconstraint->constr_name;
trig.tgenabled = TRUE;
trig.tgisconstraint = TRUE;
trig.tgconstrrelid = RelationGetRelid(pkrel);
trig.tgdeferrable = FALSE;
trig.tginitdeferred = FALSE;
trig.tgargs = (char **) palloc(sizeof(char *) *
(4 + length(fkconstraint->fk_attrs)
+ length(fkconstraint->pk_attrs)));
trig.tgargs[0] = trig.tgname;
trig.tgargs[1] = RelationGetRelationName(rel);
trig.tgargs[2] = RelationGetRelationName(pkrel);
trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype);
count = 4;
foreach(list, fkconstraint->fk_attrs)
{
Ident *fk_at = lfirst(list);
trig.tgargs[count] = fk_at->name;
count += 2;
}
count = 5;
foreach(list, fkconstraint->pk_attrs)
{
Ident *pk_at = lfirst(list);
trig.tgargs[count] = pk_at->name;
count += 2;
}
trig.tgnargs = count - 1;
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
/*
* Make a call to the trigger function
*
* No parameters are passed, but we do set a context
*/
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* We assume RI_FKey_check_ins won't look at flinfo...
*/
trigdata.type = T_TriggerData;
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
trigdata.tg_relation = rel;
trigdata.tg_trigtuple = tuple;
trigdata.tg_newtuple = NULL;
trigdata.tg_trigger = &trig;
fcinfo.context = (Node *) &trigdata;
RI_FKey_check_ins(&fcinfo);
}
heap_endscan(scan);
pfree(trig.tgargs);
}
/*
* Record an FK constraint in pg_constraint.
*/
static Oid
createForeignKeyConstraint(Relation rel, Relation pkrel,
FkConstraint *fkconstraint)
{
int16 *fkattr;
int16 *pkattr;
int fkcount;
int pkcount;
List *l;
int i;
/* Convert foreign-key attr names to attr number array */
fkcount = length(fkconstraint->fk_attrs);
fkattr = (int16 *) palloc(fkcount * sizeof(int16));
i = 0;
foreach(l, fkconstraint->fk_attrs)
{
Ident *id = (Ident *) lfirst(l);
fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name);
}
/* The same for the referenced primary key attrs */
pkcount = length(fkconstraint->pk_attrs);
pkattr = (int16 *) palloc(pkcount * sizeof(int16));
i = 0;
foreach(l, fkconstraint->pk_attrs)
{
Ident *id = (Ident *) lfirst(l);
pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name);
}
/* Now we can make the pg_constraint entry */
return CreateConstraintEntry(fkconstraint->constr_name,
RelationGetNamespace(rel),
CONSTRAINT_FOREIGN,
fkconstraint->deferrable,
fkconstraint->initdeferred,
RelationGetRelid(rel),
fkattr,
fkcount,
InvalidOid, /* not a domain constraint */
RelationGetRelid(pkrel),
pkattr,
pkcount,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
fkconstraint->fk_matchtype,
NULL,
NULL);
}
/*
* Create the triggers that implement an FK constraint.
*/
static void
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
Oid constrOid)
{
RangeVar *myRel;
CreateTrigStmt *fk_trigger;
List *fk_attr;
List *pk_attr;
Ident *id;
ObjectAddress trigobj,
constrobj;
/*
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
*/
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
RelationGetRelationName(rel));
/*
* Preset objectAddress fields
*/
constrobj.classId = get_system_catalog_relid(ConstraintRelationName);
constrobj.objectId = constrOid;
constrobj.objectSubId = 0;
trigobj.classId = get_system_catalog_relid(TriggerRelationName);
trigobj.objectSubId = 0;
/* Make changes-so-far visible */
CommandCounterIncrement();
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
* CHECK action.
*/
fk_trigger = makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = myRel;
fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'i';
fk_trigger->actions[1] = 'u';
fk_trigger->actions[2] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = fkconstraint->pktable;
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
if (length(fk_attr) != length(pk_attr))
elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
"\n\tIllegal FOREIGN KEY definition references \"%s\"",
fkconstraint->pktable->relname);
while (fk_attr != NIL)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
}
trigobj.objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
/* Make changes-so-far visible */
CommandCounterIncrement();
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
* ON DELETE action on the referenced table.
*/
fk_trigger = makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'd';
fk_trigger->actions[1] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = myRel;
switch (fkconstraint->fk_del_action)
{
case FKCONSTR_ACTION_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
break;
case FKCONSTR_ACTION_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
break;
case FKCONSTR_ACTION_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
break;
case FKCONSTR_ACTION_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
break;
case FKCONSTR_ACTION_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
break;
default:
elog(ERROR, "Unrecognized ON DELETE action for FOREIGN KEY constraint");
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
while (fk_attr != NIL)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
}
trigobj.objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
/* Make changes-so-far visible */
CommandCounterIncrement();
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
* ON UPDATE action on the referenced table.
*/
fk_trigger = makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'u';
fk_trigger->actions[1] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = myRel;
switch (fkconstraint->fk_upd_action)
{
case FKCONSTR_ACTION_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
break;
case FKCONSTR_ACTION_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
break;
case FKCONSTR_ACTION_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
break;
case FKCONSTR_ACTION_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
break;
case FKCONSTR_ACTION_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
break;
default:
elog(ERROR, "Unrecognized ON UPDATE action for FOREIGN KEY constraint");
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
while (fk_attr != NIL)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
}
trigobj.objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
}
/*
* fkMatchTypeToString -
* convert FKCONSTR_MATCH_xxx code to string to use in trigger args
*/
static char *
fkMatchTypeToString(char match_type)
{
switch (match_type)
{
case FKCONSTR_MATCH_FULL:
return pstrdup("FULL");
case FKCONSTR_MATCH_PARTIAL:
return pstrdup("PARTIAL");
case FKCONSTR_MATCH_UNSPECIFIED:
return pstrdup("UNSPECIFIED");
default:
elog(ERROR, "fkMatchTypeToString: Unknown MATCH TYPE '%c'",
match_type);
}
return NULL; /* can't get here */
}
/*
* ALTER TABLE DROP CONSTRAINT
* Note: It is legal to remove a constraint with name "" as it is possible
* to add a constraint with name "".
* Christopher Kings-Lynne
*/
void
AlterTableDropConstraint(Oid myrelid,
@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid,
DropBehavior behavior)
{
Relation rel;
int deleted;
/*
* We don't support CASCADE yet - in fact, RESTRICT doesn't work to
* the spec either!
*/
if (behavior == DROP_CASCADE)
elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
int deleted = 0;
/*
* Acquire an exclusive lock on the target relation for the duration
@ -2649,26 +3066,39 @@ AlterTableDropConstraint(Oid myrelid,
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
/*
* Since all we have is the name of the constraint, we have to look
* through all catalogs that could possibly contain a constraint for
* this relation. We also keep a count of the number of constraints
* removed.
* Process child tables if requested.
*/
if (inh)
{
List *child,
*children;
deleted = 0;
/* This routine is actually in the planner */
children = find_all_inheritors(myrelid);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all of
* the relids in the list that it returns.
*/
foreach(child, children)
{
Oid childrelid = lfirsti(child);
Relation inhrel;
if (childrelid == myrelid)
continue;
inhrel = heap_open(childrelid, AccessExclusiveLock);
/* do NOT count child constraints in deleted. */
RemoveRelConstraints(inhrel, constrName, behavior);
heap_close(inhrel, NoLock);
}
}
/*
* First, we remove all CHECK constraints with the given name
*/
deleted += RemoveCheckConstraint(rel, constrName, inh);
/*
* Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
* constraints.
*
* Unimplemented.
* Now do the thing on this relation.
*/
deleted += RemoveRelConstraints(rel, constrName, behavior);
/* Close the target relation */
heap_close(rel, NoLock);
@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
char toast_idxname[NAMEDATALEN];
IndexInfo *indexInfo;
Oid classObjectId[2];
ObjectAddress baseobject,
toastobject;
/*
* Grab an exclusive lock on the target table, which we will NOT
@ -2957,7 +3389,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
BTREE_AM_OID, classObjectId,
true, true);
true, false, true);
/*
* Update toast rel's pg_class entry to show that it has an index. The
@ -2981,6 +3413,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
heap_freetuple(reltup);
/*
* Register dependency from the toast table to the master, so that
* the toast table will be deleted if the master is.
*/
baseobject.classId = RelOid_pg_class;
baseobject.objectId = relOid;
baseobject.objectSubId = 0;
toastobject.classId = RelOid_pg_class;
toastobject.objectId = toast_relid;
toastobject.objectSubId = 0;
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
/*
* Close relations and make changes visible
*/

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.120 2002/06/20 20:29:27 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.121 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,12 +17,12 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "commands/comment.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
MemoryContext per_tuple_context);
void
CreateTrigger(CreateTrigStmt *stmt)
Oid
CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
{
int16 tgtype;
int16 tgattr[FUNC_MAX_ARGS];
@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt)
Oid fargtypes[FUNC_MAX_ARGS];
Oid funcoid;
Oid funclang;
Oid trigoid;
int found = 0;
int i;
char constrtrigname[NAMEDATALEN];
char *constrname = "";
Oid constrrelid = InvalidOid;
char *trigname;
char *constrname;
Oid constrrelid;
ObjectAddress myself,
referenced;
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt)
aclcheck_error(aclresult, RelationGetRelationName(rel));
/*
* If trigger is an RI constraint, use trigger name as constraint name
* and build a unique trigger name instead.
* If trigger is an RI constraint, use specified trigger name as
* constraint name and build a unique trigger name instead.
* This is mainly for backwards compatibility with CREATE CONSTRAINT
* TRIGGER commands.
*/
if (stmt->isconstraint)
{
constrname = stmt->trigname;
snprintf(constrtrigname, sizeof(constrtrigname),
"RI_ConstraintTrigger_%u", newoid());
stmt->trigname = constrtrigname;
if (stmt->constrrel != NULL)
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
else
constrrelid = InvalidOid;
trigname = constrtrigname;
constrname = stmt->trigname;
}
else
{
trigname = stmt->trigname;
constrname = "";
}
if (stmt->constrrel != NULL)
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
else
constrrelid = InvalidOid;
TRIGGER_CLEAR_TYPE(tgtype);
if (stmt->before)
@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt)
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
stmt->trigname, stmt->relation->relname);
trigname, stmt->relation->relname);
found++;
}
systable_endscan(tgscan);
@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt)
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(stmt->trigname));
CStringGetDatum(trigname));
values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname);
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(constrname));
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt)
/*
* Insert tuple into pg_trigger.
*/
simple_heap_insert(tgrel, tuple);
trigoid = simple_heap_insert(tgrel, tuple);
CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
CatalogCloseIndices(Num_pg_trigger_indices, idescs);
myself.classId = RelationGetRelid(tgrel);
myself.objectId = trigoid;
myself.objectSubId = 0;
heap_freetuple(tuple);
heap_close(tgrel, RowExclusiveLock);
@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt)
stmt->relation->relname);
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
simple_heap_update(pgrel, &tuple->t_self, tuple);
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
heap_freetuple(tuple);
heap_close(pgrel, RowExclusiveLock);
@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt)
* upcoming CommandCounterIncrement...
*/
/*
* Record dependencies for trigger. Always place a normal dependency
* on the function. If we are doing this in response to an explicit
* CREATE TRIGGER command, also make trigger be auto-dropped if its
* relation is dropped or if the FK relation is dropped. (Auto drop
* is compatible with our pre-7.3 behavior.) If the trigger is being
* made for a constraint, we can skip the relation links; the dependency
* on the constraint will indirectly depend on the relations.
*/
referenced.classId = RelOid_pg_proc;
referenced.objectId = funcoid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (!forConstraint)
{
referenced.classId = RelOid_pg_class;
referenced.objectId = RelationGetRelid(rel);
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
if (constrrelid != InvalidOid)
{
referenced.classId = RelOid_pg_class;
referenced.objectId = constrrelid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
return trigoid;
}
/*
* DropTrigger - drop an individual trigger by name
*/
void
DropTrigger(Oid relid, const char *trigname)
DropTrigger(Oid relid, const char *trigname, DropBehavior behavior)
{
Relation tgrel;
ScanKeyData skey[2];
SysScanDesc tgscan;
HeapTuple tup;
ObjectAddress object;
/*
* Find the trigger, verify permissions, set up object address
*/
tgrel = heap_openr(TriggerRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_trigger_tgrelid, F_OIDEQ,
ObjectIdGetDatum(relid));
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_trigger_tgname, F_NAMEEQ,
CStringGetDatum(trigname));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
SnapshotNow, 2, skey);
tup = systable_getnext(tgscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
trigname, get_rel_name(relid));
if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid));
object.classId = RelationGetRelid(tgrel);
object.objectId = tup->t_data->t_oid;
object.objectSubId = 0;
systable_endscan(tgscan);
heap_close(tgrel, AccessShareLock);
/*
* Do the deletion
*/
performDeletion(&object, behavior);
}
/*
* Guts of trigger deletion.
*/
void
RemoveTriggerById(Oid trigOid)
{
Relation rel;
Relation tgrel;
SysScanDesc tgscan;
ScanKeyData key;
ScanKeyData skey[1];
HeapTuple tup;
Oid relid;
Relation rel;
Relation pgrel;
HeapTuple tuple;
Form_pg_class classForm;
Relation ridescs[Num_pg_class_indices];
int remaining = 0;
int found = 0;
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
/*
* Find the trigger to delete.
*/
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(trigOid));
tgscan = systable_beginscan(tgrel, TriggerOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(tgscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RemoveTriggerById: Trigger %u does not exist",
trigOid);
/*
* Open and exclusive-lock the relation the trigger belongs to.
*/
relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
rel = heap_open(relid, AccessExclusiveLock);
@ -337,55 +462,22 @@ DropTrigger(Oid relid, const char *trigname)
elog(ERROR, "DropTrigger: can't drop trigger for system relation %s",
RelationGetRelationName(rel));
if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
/*
* Search pg_trigger, delete target trigger, count remaining triggers
* for relation. (Although we could fetch and delete the target
* trigger directly, we'd still have to scan the remaining triggers,
* so we may as well do both in one indexscan.)
*
* Note this is OK only because we have AccessExclusiveLock on the rel,
* so no one else is creating/deleting triggers on this rel at the same
* time.
* Delete the pg_trigger tuple.
*/
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&key, 0,
Anum_pg_trigger_tgrelid,
F_OIDEQ,
ObjectIdGetDatum(relid));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
SnapshotNow, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
simple_heap_delete(tgrel, &tup->t_self);
if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
{
/* Delete any comments associated with this trigger */
DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel));
simple_heap_delete(tgrel, &tuple->t_self);
found++;
}
else
remaining++;
}
systable_endscan(tgscan);
heap_close(tgrel, RowExclusiveLock);
if (found == 0)
elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
trigname, RelationGetRelationName(rel));
if (found > 1) /* shouldn't happen */
elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
found, trigname, RelationGetRelationName(rel));
/*
* Update relation's pg_class entry. Crucial side-effect: other
* backends (and this one too!) are sent SI message to make them
* rebuild relcache entries.
*
* Note this is OK only because we have AccessExclusiveLock on the rel,
* so no one else is creating/deleting triggers on this rel at the same
* time.
*/
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(RELOID,
@ -394,115 +486,27 @@ DropTrigger(Oid relid, const char *trigname)
if (!HeapTupleIsValid(tuple))
elog(ERROR, "DropTrigger: relation %s not found in pg_class",
RelationGetRelationName(rel));
classForm = (Form_pg_class) GETSTRUCT(tuple);
if (classForm->reltriggers == 0)
elog(ERROR, "DropTrigger: relation %s has reltriggers = 0",
RelationGetRelationName(rel));
classForm->reltriggers--;
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining;
simple_heap_update(pgrel, &tuple->t_self, tuple);
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
heap_freetuple(tuple);
heap_close(pgrel, RowExclusiveLock);
/* Keep lock on target rel until end of xact */
/* Keep lock on trigger's rel until end of xact */
heap_close(rel, NoLock);
}
/*
* Remove all triggers for a relation that's being deleted.
*/
void
RelationRemoveTriggers(Relation rel)
{
Relation tgrel;
SysScanDesc tgscan;
ScanKeyData key;
HeapTuple tup;
bool found = false;
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&key, 0,
Anum_pg_trigger_tgrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
SnapshotNow, 1, &key);
while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
{
/* Delete any comments associated with this trigger */
DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
simple_heap_delete(tgrel, &tup->t_self);
found = true;
}
systable_endscan(tgscan);
/*
* If we deleted any triggers, must update pg_class entry and advance
* command counter to make the updated entry visible. This is fairly
* annoying, since we'e just going to drop the durn thing later, but
* it's necessary to have a consistent state in case we do
* CommandCounterIncrement() below --- if RelationBuildTriggers()
* runs, it will complain otherwise. Perhaps RelationBuildTriggers()
* shouldn't be so picky...
*/
if (found)
{
Relation pgrel;
Relation ridescs[Num_pg_class_indices];
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
tup = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(RelationGetRelid(rel)),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class",
RelationGetRelid(rel));
((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0;
simple_heap_update(pgrel, &tup->t_self, tup);
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
heap_freetuple(tup);
heap_close(pgrel, RowExclusiveLock);
CommandCounterIncrement();
}
/*
* Also drop all constraint triggers referencing this relation
*/
ScanKeyEntryInitialize(&key, 0,
Anum_pg_trigger_tgconstrrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true,
SnapshotNow, 1, &key);
while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"",
get_rel_name(pg_trigger->tgrelid));
DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname));
/*
* Need to do a command counter increment here to show up new
* pg_class.reltriggers in the next loop iteration (in case there
* are multiple referential integrity action triggers for the same
* FK table defined on the PK table).
*/
CommandCounterIncrement();
}
systable_endscan(tgscan);
heap_close(tgrel, RowExclusiveLock);
}
/*
* renametrig - changes the name of a trigger on a relation
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.4 2002/07/01 15:27:48 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -33,10 +33,10 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@ -262,17 +262,14 @@ DefineType(List *names, List *parameters)
/*
* RemoveType
* Removes a datatype.
*
* NOTE: since this tries to remove the associated array type too, it'll
* only work on scalar types.
*/
void
RemoveType(List *names, DropBehavior behavior)
{
TypeName *typename;
Relation relation;
Oid typeoid;
HeapTuple tup;
ObjectAddress object;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior)
typename->typmod = -1;
typename->arrayBounds = NIL;
relation = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */
typeoid = LookupTypeName(typename);
if (!OidIsValid(typeoid))
@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
/* Delete any comments associated with this type */
DeleteComments(typeoid, RelationGetRelid(relation));
/* Remove the type tuple from pg_type */
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
/* Now, delete the "array of" that type */
typename->arrayBounds = makeList1(makeInteger(1));
/*
* Do the deletion
*/
object.classId = RelOid_pg_type;
object.objectId = typeoid;
object.objectSubId = 0;
typeoid = LookupTypeName(typename);
if (!OidIsValid(typeoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
performDeletion(&object, behavior);
}
/*
* Guts of type deletion.
*/
void
RemoveTypeById(Oid typeOid)
{
Relation relation;
HeapTuple tup;
relation = heap_openr(TypeRelationName, RowExclusiveLock);
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typeoid),
ObjectIdGetDatum(typeOid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
DeleteComments(typeoid, RelationGetRelid(relation));
elog(ERROR, "RemoveTypeById: type %u not found",
typeOid);
simple_heap_delete(relation, &tup->t_self);
@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt)
HeapTuple typeTup;
List *schema = stmt->constraints;
List *listptr;
Oid basetypeoid;
Form_pg_type baseType;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt)
*/
typeTup = typenameType(stmt->typename);
baseType = (Form_pg_type) GETSTRUCT(typeTup);
basetypeoid = typeTup->t_data->t_oid;
/*
* What we really don't want is domains of domains. This could cause all sorts
* of neat issues if we allow that.
*
* With testing, we may determine complex types should be allowed
*/
typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
typtype = baseType->typtype;
if (typtype != 'b')
elog(ERROR, "DefineDomain: %s is not a basetype",
TypeNameToString(stmt->typename));
/* passed by value */
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
byValue = baseType->typbyval;
/* Required Alignment */
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
alignment = baseType->typalign;
/* TOAST Strategy */
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
storage = baseType->typstorage;
/* Storage Length */
internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
internalLength = baseType->typlen;
/* External Length (unused) */
externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
externalLength = baseType->typprtlen;
/* Array element Delimiter */
delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
delimiter = baseType->typdelim;
/* I/O Functions */
inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
inputProcedure = baseType->typinput;
outputProcedure = baseType->typoutput;
receiveProcedure = baseType->typreceive;
sendProcedure = baseType->typsend;
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt)
*
* This is what enables us to make a domain of an array
*/
basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
basetypelem = baseType->typelem;
/*
* Run through constraints manually to avoid the additional
@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt)
* Note: Name is strictly for error message
*/
expr = cookDefault(pstate, colDef->raw_expr,
typeTup->t_data->t_oid,
basetypeoid,
stmt->typename->typmod,
domainName);
/*
@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt)
*/
TypeCreate(domainName, /* type name */
domainNamespace, /* namespace */
InvalidOid, /* preassigned type oid (not done here) */
InvalidOid, /* preassigned type oid (none here) */
InvalidOid, /* relation oid (n/a here) */
internalLength, /* internal size */
externalLength, /* external size */
@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt)
receiveProcedure, /* receive procedure */
sendProcedure, /* send procedure */
basetypelem, /* element type ID */
typeTup->t_data->t_oid, /* base type ID */
basetypeoid, /* base type ID */
defaultValue, /* default type value (text) */
defaultValueBin, /* default type value (binary) */
byValue, /* passed by value */
@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt)
/*
* RemoveDomain
* Removes a domain.
*
* This is identical to RemoveType except we insist it be a domain.
*/
void
RemoveDomain(List *names, DropBehavior behavior)
{
TypeName *typename;
Relation relation;
Oid typeoid;
HeapTuple tup;
char typtype;
/* CASCADE unsupported */
if (behavior == DROP_CASCADE)
elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
ObjectAddress object;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior)
typename->typmod = -1;
typename->arrayBounds = NIL;
relation = heap_openr(TypeRelationName, RowExclusiveLock);
typeoid = typenameTypeId(typename);
/* Use LookupTypeName here so that shell types can be removed. */
typeoid = LookupTypeName(typename);
if (!OidIsValid(typeoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typeoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RemoveDomain: type '%s' does not exist",
elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Permission check: must own type or its namespace */
@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior)
elog(ERROR, "%s is not a domain",
TypeNameToString(typename));
/* Delete any comments associated with this type */
DeleteComments(typeoid, RelationGetRelid(relation));
/* Remove the type tuple from pg_type */
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
/* At present, domains don't have associated array types */
/*
* Do the deletion
*/
object.classId = RelOid_pg_type;
object.objectId = typeoid;
object.objectSubId = 0;
heap_close(relation, RowExclusiveLock);
performDeletion(&object, behavior);
}

View File

@ -6,13 +6,14 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: view.c,v 1.65 2002/07/01 15:27:49 tgl Exp $
* $Id: view.c,v 1.66 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "commands/tablecmds.h"
@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse)
* RemoveView
*
* Remove a view given its name
*
* We just have to drop the relation; the associated rules will be
* cleaned up automatically.
*/
void
RemoveView(const RangeVar *view, DropBehavior behavior)
{
Oid viewOid;
ObjectAddress object;
viewOid = RangeVarGetRelid(view, false);
/*
* We just have to drop the relation; the associated rules will be
* cleaned up automatically.
*/
heap_drop_with_catalog(viewOid, allowSystemTableMods);
object.classId = RelOid_pg_class;
object.objectId = viewOid;
object.objectSubId = 0;
performDeletion(&object, behavior);
}

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.192 2002/07/01 15:27:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.193 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1505,11 +1505,12 @@ _copyFkConstraint(FkConstraint *from)
Node_Copy(from, newnode, pktable);
Node_Copy(from, newnode, fk_attrs);
Node_Copy(from, newnode, pk_attrs);
if (from->match_type)
newnode->match_type = pstrdup(from->match_type);
newnode->actions = from->actions;
newnode->fk_matchtype = from->fk_matchtype;
newnode->fk_upd_action = from->fk_upd_action;
newnode->fk_del_action = from->fk_del_action;
newnode->deferrable = from->deferrable;
newnode->initdeferred = from->initdeferred;
newnode->skip_validation = from->skip_validation;
return newnode;
}
@ -2089,6 +2090,7 @@ _copyIndexStmt(IndexStmt *from)
Node_Copy(from, newnode, rangetable);
newnode->unique = from->unique;
newnode->primary = from->primary;
newnode->isconstraint = from->isconstraint;
return newnode;
}

View File

@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.139 2002/07/01 15:27:52 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.140 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -912,6 +912,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
return false;
if (a->primary != b->primary)
return false;
if (a->isconstraint != b->isconstraint)
return false;
return true;
}
@ -1734,14 +1736,18 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b)
return false;
if (!equal(a->pk_attrs, b->pk_attrs))
return false;
if (!equalstr(a->match_type, b->match_type))
if (a->fk_matchtype != b->fk_matchtype)
return false;
if (a->actions != b->actions)
if (a->fk_upd_action != b->fk_upd_action)
return false;
if (a->fk_del_action != b->fk_del_action)
return false;
if (a->deferrable != b->deferrable)
return false;
if (a->initdeferred != b->initdeferred)
return false;
if (a->skip_validation != b->skip_validation)
return false;
return true;
}

View File

@ -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.161 2002/07/04 15:23:53 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.162 2002/07/12 18:43:16 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -136,9 +136,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
_outNode(str, node->whereClause);
appendStringInfo(str, " :rangetable ");
_outNode(str, node->rangetable);
appendStringInfo(str, " :unique %s :primary %s ",
appendStringInfo(str, " :unique %s :primary %s :isconstraint %s ",
booltostr(node->unique),
booltostr(node->primary));
booltostr(node->primary),
booltostr(node->isconstraint));
}
static void
@ -1447,12 +1448,13 @@ _outFkConstraint(StringInfo str, FkConstraint *node)
_outNode(str, node->fk_attrs);
appendStringInfo(str, " :pk_attrs ");
_outNode(str, node->pk_attrs);
appendStringInfo(str, " :match_type ");
_outToken(str, node->match_type);
appendStringInfo(str, " :actions %d :deferrable %s :initdeferred %s",
node->actions,
appendStringInfo(str, " :fk_matchtype %c :fk_upd_action %c :fk_del_action %c :deferrable %s :initdeferred %s :skip_validation %s",
node->fk_matchtype,
node->fk_upd_action,
node->fk_del_action,
booltostr(node->deferrable),
booltostr(node->initdeferred));
booltostr(node->initdeferred),
booltostr(node->skip_validation));
}
static void

View File

@ -6,7 +6,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/parser/analyze.c,v 1.237 2002/06/20 20:29:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.238 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -47,7 +47,7 @@
/* State shared by transformCreateSchemaStmt and its subroutines */
typedef struct
{
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
char *schemaname; /* name of schema */
char *authid; /* owner of schema */
List *tables; /* CREATE TABLE items */
@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
cxt->stmtType, (cxt->relation)->relname);
cxt->pkey = index;
}
index->isconstraint = true;
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
static void
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
{
CreateTrigStmt *fk_trigger;
List *fkactions = NIL;
List *fkclist;
List *fk_attr;
List *pk_attr;
Ident *id;
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
int i;
if (cxt->fkconstraints == NIL)
return;
@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
foreach(fkclist, cxt->fkconstraints)
{
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
int i;
int attnum;
List *fkattrs;
/*
* If the constraint has no name, set it to <unnamed>
*/
if (fkconstraint->constr_name == NULL)
fkconstraint->constr_name = "<unnamed>";
for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
}
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
* action.
* For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
* gin up an ALTER TABLE ADD CONSTRAINT command to execute after
* the basic CREATE TABLE is complete.
*/
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = cxt->relation;
fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'i';
fk_trigger->actions[1] = 'u';
fk_trigger->actions[2] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = fkconstraint->pktable;
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
if (length(fk_attr) != length(pk_attr))
elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
"\n\tIllegal FOREIGN KEY definition references \"%s\"",
fkconstraint->pktable->relname);
while (fk_attr != NIL)
if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
alterstmt->subtype = 'c'; /* preprocessed add constraint */
alterstmt->relation = cxt->relation;
alterstmt->name = NULL;
alterstmt->def = (Node *) makeList1(fkconstraint);
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
/* Don't need to scan the table contents in this case */
fkconstraint->skip_validation = true;
fkactions = lappend(fkactions, (Node *) alterstmt);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the ON DELETE
* action fired on the PK table !!!
*/
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'd';
fk_trigger->actions[1] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = cxt->relation;
switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
>> FKCONSTR_ON_DELETE_SHIFT)
{
case FKCONSTR_ON_KEY_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
break;
case FKCONSTR_ON_KEY_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
break;
case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
break;
case FKCONSTR_ON_KEY_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
break;
case FKCONSTR_ON_KEY_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
break;
default:
elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
while (fk_attr != NIL)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE
* action fired on the PK table !!!
*/
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->actions[0] = 'u';
fk_trigger->actions[1] = '\0';
fk_trigger->lang = NULL;
fk_trigger->text = NULL;
fk_trigger->attr = NIL;
fk_trigger->when = NULL;
fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = cxt->relation;
switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
>> FKCONSTR_ON_UPDATE_SHIFT)
{
case FKCONSTR_ON_KEY_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
break;
case FKCONSTR_ON_KEY_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
break;
case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
break;
case FKCONSTR_ON_KEY_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
break;
case FKCONSTR_ON_KEY_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
break;
default:
elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
fk_attr = fkconstraint->fk_attrs;
pk_attr = fkconstraint->pk_attrs;
while (fk_attr != NIL)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
}
/*
@ -2642,6 +2454,14 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
*extras_after = nconc(cxt.alist, *extras_after);
break;
case 'c':
/*
* Already-transformed ADD CONSTRAINT, so just make it look
* like the standard case.
*/
stmt->subtype = 'C';
break;
default:
break;
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.338 2002/07/11 07:39:25 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.339 2002/07/12 18:43:17 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -309,8 +309,7 @@ static void doNegateFloat(Value *v);
%type <node> TableConstraint, TableLikeClause
%type <list> ColQualList
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
%type <ival> key_actions, key_delete, key_update, key_reference
%type <str> key_match
%type <ival> key_actions, key_delete, key_match, key_update, key_action
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
ConstraintTimeSpec
@ -1594,8 +1593,9 @@ ColConstraintElem:
n->pktable = $2;
n->fk_attrs = NIL;
n->pk_attrs = $3;
n->match_type = $4;
n->actions = $5;
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
n->deferrable = FALSE;
n->initdeferred = FALSE;
$$ = (Node *)n;
@ -1714,16 +1714,16 @@ ConstraintElem:
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
opt_column_list
key_match key_actions ConstraintAttributeSpec
opt_column_list key_match key_actions ConstraintAttributeSpec
{
FkConstraint *n = makeNode(FkConstraint);
n->constr_name = NULL;
n->pktable = $7;
n->fk_attrs = $4;
n->pk_attrs = $8;
n->match_type = $9;
n->actions = $10;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
n->fk_del_action = (char) ($10 & 0xFF);
n->deferrable = ($11 & 1) != 0;
n->initdeferred = ($11 & 2) != 0;
$$ = (Node *)n;
@ -1750,45 +1750,54 @@ columnElem: ColId
key_match: MATCH FULL
{
$$ = "FULL";
$$ = FKCONSTR_MATCH_FULL;
}
| MATCH PARTIAL
{
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
$$ = "PARTIAL";
$$ = FKCONSTR_MATCH_PARTIAL;
}
| MATCH SIMPLE
{
$$ = "UNSPECIFIED";
$$ = FKCONSTR_MATCH_UNSPECIFIED;
}
| /*EMPTY*/
{
$$ = "UNSPECIFIED";
$$ = FKCONSTR_MATCH_UNSPECIFIED;
}
;
/*
* We combine the update and delete actions into one value temporarily
* for simplicity of parsing, and then break them down again in the
* calling production. update is in the left 8 bits, delete in the right.
* Note that NOACTION is the default.
*/
key_actions:
key_delete { $$ = $1; }
| key_update { $$ = $1; }
| key_delete key_update { $$ = $1 | $2; }
| key_update key_delete { $$ = $1 | $2; }
| /*EMPTY*/ { $$ = 0; }
key_update
{ $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
| key_delete
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
| key_update key_delete
{ $$ = ($1 << 8) | ($2 & 0xFF); }
| key_delete key_update
{ $$ = ($2 << 8) | ($1 & 0xFF); }
| /*EMPTY*/
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
;
key_delete: ON DELETE_P key_reference
{ $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; }
key_update: ON UPDATE key_action { $$ = $3; }
;
key_update: ON UPDATE key_reference
{ $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; }
key_delete: ON DELETE_P key_action { $$ = $3; }
;
key_reference:
NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; }
| RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; }
| CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; }
| SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; }
| SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; }
key_action:
NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
| RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
| CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
| SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
| SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
;
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; }
| INDEX { $$ = DROP_INDEX; }
| TYPE_P { $$ = DROP_TYPE; }
| DOMAIN_P { $$ = DROP_DOMAIN; }
| CONVERSION_P { $$ = DROP_CONVERSION; }
| CONVERSION_P { $$ = DROP_CONVERSION; }
;
any_name_list:
@ -6673,6 +6682,7 @@ unreserved_keyword:
| COMMIT
| COMMITTED
| CONSTRAINTS
| CONVERSION_P
| COPY
| CREATEDB
| CREATEUSER
@ -6857,6 +6867,7 @@ col_name_keyword:
| SUBSTRING
| TIME
| TIMESTAMP
| TREAT
| TRIM
| VARCHAR
;
@ -6963,7 +6974,6 @@ reserved_keyword:
| THEN
| TO
| TRAILING
| TREAT
| TRUE_P
| UNION
| UNIQUE

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.73 2002/06/20 20:29:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.74 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/view.h"
@ -57,6 +58,8 @@ InsertRule(char *rulname,
TupleDesc tupDesc;
HeapTuple tup;
Oid rewriteObjectId;
ObjectAddress myself,
referenced;
if (IsDefinedRewriteRule(eventrel_oid, rulname))
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
@ -103,6 +106,23 @@ InsertRule(char *rulname,
heap_freetuple(tup);
/*
* Install dependency on rule's relation to ensure it will go away
* on relation deletion. If the rule is ON SELECT, make the dependency
* implicit --- this prevents deleting a view's SELECT rule. Other
* kinds of rules can be AUTO.
*/
myself.classId = RelationGetRelid(pg_rewrite_desc);
myself.objectId = rewriteObjectId;
myself.objectSubId = 0;
referenced.classId = RelOid_pg_class;
referenced.objectId = eventrel_oid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced,
(evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
heap_close(pg_rewrite_desc, RowExclusiveLock);
return rewriteObjectId;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.50 2002/06/20 20:29:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.51 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,14 +17,15 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/comment.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@ -34,15 +35,62 @@
* Delete a rule given its name.
*/
void
RemoveRewriteRule(Oid owningRel, const char *ruleName)
RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior)
{
HeapTuple tuple;
Oid eventRelationOid;
AclResult aclresult;
ObjectAddress object;
/*
* Find the tuple for the target rule.
*/
tuple = SearchSysCache(RULERELNAME,
ObjectIdGetDatum(owningRel),
PointerGetDatum(ruleName),
0, 0);
/*
* complain if no rule with such name exists
*/
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Rule \"%s\" not found", ruleName);
/*
* Verify user has appropriate permissions.
*/
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
Assert(eventRelationOid == owningRel);
aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_rel_name(eventRelationOid));
/*
* Do the deletion
*/
object.classId = get_system_catalog_relid(RewriteRelationName);
object.objectId = tuple->t_data->t_oid;
object.objectSubId = 0;
ReleaseSysCache(tuple);
performDeletion(&object, behavior);
}
/*
* Guts of rule deletion.
*/
void
RemoveRewriteRuleById(Oid ruleOid)
{
Relation RewriteRelation;
ScanKeyData skey[1];
SysScanDesc rcscan;
Relation event_relation;
HeapTuple tuple;
Oid ruleId;
Oid eventRelationOid;
bool hasMoreRules;
AclResult aclresult;
/*
* Open the pg_rewrite relation.
@ -52,24 +100,18 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
/*
* Find the tuple for the target rule.
*/
tuple = SearchSysCacheCopy(RULERELNAME,
ObjectIdGetDatum(owningRel),
PointerGetDatum(ruleName),
0, 0);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(ruleOid));
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true,
SnapshotNow, 1, skey);
tuple = systable_getnext(rcscan);
/*
* complain if no rule with such name existed
*/
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Rule \"%s\" not found", ruleName);
/*
* Save the OID of the rule (i.e. the tuple's OID) and the event
* relation's OID
*/
ruleId = tuple->t_data->t_oid;
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
Assert(eventRelationOid == owningRel);
elog(ERROR, "RemoveRewriteRuleById: Rule %u does not exist",
ruleOid);
/*
* We had better grab AccessExclusiveLock so that we know no other
@ -77,34 +119,18 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
* cannot set relhasrules correctly. Besides, we don't want to be
* changing the ruleset while queries are executing on the rel.
*/
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
/*
* Verify user has appropriate permissions.
*/
aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, RelationGetRelationName(event_relation));
/* do not allow the removal of a view's SELECT rule */
if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1')
elog(ERROR, "Cannot remove a view's SELECT rule");
hasMoreRules = event_relation->rd_rules != NULL &&
event_relation->rd_rules->numLocks > 1;
/*
* Delete any comments associated with this rule
*/
DeleteComments(ruleId, RelationGetRelid(RewriteRelation));
/*
* Now delete the pg_rewrite tuple for the rule
*/
simple_heap_delete(RewriteRelation, &tuple->t_self);
heap_freetuple(tuple);
systable_endscan(rcscan);
heap_close(RewriteRelation, RowExclusiveLock);
@ -120,49 +146,3 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
}
/*
* RelationRemoveRules -
* removes all rules associated with the relation when the relation is
* being removed.
*/
void
RelationRemoveRules(Oid relid)
{
Relation RewriteRelation;
SysScanDesc scanDesc;
ScanKeyData scanKeyData;
HeapTuple tuple;
/*
* Open the pg_rewrite relation.
*/
RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
/*
* Scan pg_rewrite for all the tuples that have the same ev_class
* as relid (the relation to be removed).
*/
ScanKeyEntryInitialize(&scanKeyData,
0,
Anum_pg_rewrite_ev_class,
F_OIDEQ,
ObjectIdGetDatum(relid));
scanDesc = systable_beginscan(RewriteRelation,
RewriteRelRulenameIndex,
true, SnapshotNow,
1, &scanKeyData);
while (HeapTupleIsValid(tuple = systable_getnext(scanDesc)))
{
/* Delete any comments associated with this rule */
DeleteComments(tuple->t_data->t_oid, RelationGetRelid(RewriteRelation));
simple_heap_delete(RewriteRelation, &tuple->t_self);
}
systable_endscan(scanDesc);
heap_close(RewriteRelation, RowExclusiveLock);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.52 2002/06/20 20:29:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.53 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,7 @@
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "rewrite/rewriteSupport.h"
#include "utils/inval.h"
#include "utils/syscache.h"
@ -44,9 +45,8 @@ IsDefinedRewriteRule(Oid owningRel, const char *ruleName)
* NOTE: an important side-effect of this operation is that an SI invalidation
* message is sent out to all backends --- including me --- causing relcache
* entries to be flushed or updated with the new set of rules for the table.
* Therefore, we execute the update even if relhasrules has the right value
* already. Possible future improvement: skip the disk update and just send
* an SI message in that case.
* This must happen even if we find that no change is needed in the pg_class
* row.
*/
void
SetRelationRuleStatus(Oid relationId, bool relHasRules,
@ -54,6 +54,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
{
Relation relationRelation;
HeapTuple tuple;
Form_pg_class classForm;
Relation idescs[Num_pg_class_indices];
/*
@ -66,18 +67,28 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId);
classForm = (Form_pg_class) GETSTRUCT(tuple);
/* Do the update */
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
if (relIsBecomingView)
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
if (classForm->relhasrules != relHasRules ||
(relIsBecomingView && classForm->relkind != RELKIND_VIEW))
{
/* Do the update */
classForm->relhasrules = relHasRules;
if (relIsBecomingView)
classForm->relkind = RELKIND_VIEW;
simple_heap_update(relationRelation, &tuple->t_self, tuple);
simple_heap_update(relationRelation, &tuple->t_self, tuple);
/* Keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
/* Keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
}
else
{
/* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relationId);
}
heap_freetuple(tuple);
heap_close(relationRelation, RowExclusiveLock);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.161 2002/07/11 07:39:26 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.162 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -582,6 +582,7 @@ ProcessUtility(Node *parsetree,
stmt->indexParams, /* parameters */
stmt->unique,
stmt->primary,
stmt->isconstraint,
(Expr *) stmt->whereClause,
stmt->rangetable);
}
@ -596,19 +597,11 @@ ProcessUtility(Node *parsetree,
break;
case T_RemoveAggrStmt:
{
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
RemoveAggregate(stmt->aggname, stmt->aggtype);
}
RemoveAggregate((RemoveAggrStmt *) parsetree);
break;
case T_RemoveFuncStmt:
{
RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
RemoveFunction(stmt->funcname, stmt->args);
}
RemoveFunction((RemoveFuncStmt *) parsetree);
break;
case T_RemoveOperStmt:
@ -719,7 +712,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
CreateTrigger((CreateTrigStmt *) parsetree);
CreateTrigger((CreateTrigStmt *) parsetree, false);
break;
case T_DropPropertyStmt:
@ -733,11 +726,13 @@ ProcessUtility(Node *parsetree,
{
case DROP_RULE:
/* RemoveRewriteRule checks permissions */
RemoveRewriteRule(relId, stmt->property);
RemoveRewriteRule(relId, stmt->property,
stmt->behavior);
break;
case DROP_TRIGGER:
/* DropTrigger checks permissions */
DropTrigger(relId, stmt->property);
DropTrigger(relId, stmt->property,
stmt->behavior);
break;
}
}

View File

@ -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.75 2002/07/06 20:16:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -671,6 +671,25 @@ get_relname_relid(const char *relname, Oid relnamespace)
0, 0);
}
/*
* get_system_catalog_relid
* Get the OID of a system catalog identified by name.
*/
Oid
get_system_catalog_relid(const char *catname)
{
Oid relid;
relid = GetSysCacheOid(RELNAMENSP,
PointerGetDatum(catname),
ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
0, 0);
if (!OidIsValid(relid))
elog(ERROR, "get_system_catalog_relid: cannot find %s", catname);
return relid;
}
#ifdef NOT_USED
/*
* get_relnatts
@ -1060,7 +1079,7 @@ getBaseType(Oid typid)
/*
* getBaseTypeTypeMod
* If the given type is a domain, return its base type;
* otherwise return the type's own OID.
* otherwise return the type's own OID. Also return base typmod.
*/
Oid
getBaseTypeTypeMod(Oid typid, int32 *typmod)
@ -1077,7 +1096,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
ObjectIdGetDatum(typid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getBaseType: failed to lookup type %u", typid);
elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != 'd')
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.165 2002/06/20 20:29:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.166 2002/07/12 18:43:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,11 +43,11 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
@ -296,7 +296,7 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation);
static void AttrDefaultFetch(Relation relation);
static void RelCheckFetch(Relation relation);
static void CheckConstraintFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
static void IndexSupportInitialize(Form_pg_index iform,
IndexStrategy indexStrategy,
@ -451,7 +451,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
* RelationBuildTupleDesc
*
* Form the relation's tuple descriptor from information in
* the pg_attribute, pg_attrdef & pg_relcheck system catalogs.
* the pg_attribute, pg_attrdef & pg_constraint system catalogs.
*/
static void
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
@ -603,7 +603,7 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
MemoryContextAlloc(CacheMemoryContext,
constr->num_check * sizeof(ConstrCheck));
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
RelCheckFetch(relation);
CheckConstraintFetch(relation);
}
else
constr->num_check = 0;
@ -2483,62 +2483,60 @@ AttrDefaultFetch(Relation relation)
}
static void
RelCheckFetch(Relation relation)
CheckConstraintFetch(Relation relation)
{
ConstrCheck *check = relation->rd_att->constr->check;
int ncheck = relation->rd_att->constr->num_check;
Relation rcrel;
SysScanDesc rcscan;
ScanKeyData skey;
Relation conrel;
SysScanDesc conscan;
ScanKeyData skey[1];
HeapTuple htup;
Name rcname;
Datum val;
bool isnull;
int found;
int found = 0;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) Anum_pg_relcheck_rcrelid,
(RegProcedure) F_OIDEQ,
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
rcscan = systable_beginscan(rcrel, RelCheckIndex, true,
SnapshotNow,
1, &skey);
found = 0;
conrel = heap_openr(ConstraintRelationName, AccessShareLock);
conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
SnapshotNow, 1, skey);
while (HeapTupleIsValid(htup = systable_getnext(rcscan)))
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
/* We want check constraints only */
if (conform->contype != CONSTRAINT_CHECK)
continue;
if (found == ncheck)
elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
elog(ERROR, "CheckConstraintFetch: unexpected record found for rel %s",
RelationGetRelationName(relation));
rcname = (Name) fastgetattr(htup,
Anum_pg_relcheck_rcname,
rcrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
NameStr(*rcname));
NameStr(conform->conname));
/* Grab and test conbin is actually set */
val = fastgetattr(htup,
Anum_pg_relcheck_rcbin,
rcrel->rd_att, &isnull);
Anum_pg_constraint_conbin,
conrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
elog(ERROR, "CheckConstraintFetch: conbin IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val)));
found++;
}
systable_endscan(rcscan);
heap_close(rcrel, AccessShareLock);
systable_endscan(conscan);
heap_close(conrel, AccessShareLock);
if (found != ncheck)
elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s",
elog(ERROR, "CheckConstraintFetch: %d record(s) not found for rel %s",
ncheck - found, RelationGetRelationName(relation));
}

View File

@ -27,7 +27,7 @@
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.156 2002/06/20 20:29:41 momjian Exp $
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.157 2002/07/12 18:43:18 tgl Exp $
#
#-------------------------------------------------------------------------
@ -682,11 +682,11 @@ $ECHO_N "enabling unlimited row size for system tables... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
ALTER TABLE pg_constraint CREATE TOAST TABLE;
ALTER TABLE pg_database CREATE TOAST TABLE;
ALTER TABLE pg_description CREATE TOAST TABLE;
ALTER TABLE pg_group CREATE TOAST TABLE;
ALTER TABLE pg_proc CREATE TOAST TABLE;
ALTER TABLE pg_relcheck CREATE TOAST TABLE;
ALTER TABLE pg_rewrite CREATE TOAST TABLE;
ALTER TABLE pg_shadow CREATE TOAST TABLE;
ALTER TABLE pg_statistic CREATE TOAST TABLE;

View File

@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.270 2002/07/04 15:35:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.271 2002/07/12 18:43:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -4656,8 +4656,8 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
if (tbinfo->ncheck > 0)
{
PGresult *res2;
int i_rcname,
i_rcsrc;
int i_conname,
i_consrc;
int ntups2;
if (g_verbose)
@ -4666,24 +4666,25 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 70300)
appendPQExpBuffer(query, "SELECT rcname, rcsrc"
" from pg_catalog.pg_relcheck c1"
" where rcrelid = '%s'::pg_catalog.oid "
appendPQExpBuffer(query, "SELECT conname, consrc"
" from pg_catalog.pg_constraint c1"
" where conrelid = '%s'::pg_catalog.oid "
" and contype = 'c' "
" and not exists "
" (select 1 from "
" pg_catalog.pg_relcheck c2, "
" pg_catalog.pg_constraint c2, "
" pg_catalog.pg_inherits i "
" where i.inhrelid = c1.rcrelid "
" and (c2.rcname = c1.rcname "
" or (c2.rcname[0] = '$' "
" and c1.rcname[0] = '$')"
" where i.inhrelid = c1.conrelid "
" and (c2.conname = c1.conname "
" or (c2.conname[0] = '$' "
" and c1.conname[0] = '$')"
" )"
" and c2.rcsrc = c1.rcsrc "
" and c2.rcrelid = i.inhparent) "
" order by rcname ",
" and c2.consrc = c1.consrc "
" and c2.conrelid = i.inhparent) "
" order by conname ",
tbinfo->oid);
else
appendPQExpBuffer(query, "SELECT rcname, rcsrc"
appendPQExpBuffer(query, "SELECT rcname as conname, rcsrc as consrc"
" from pg_relcheck c1"
" where rcrelid = '%s'::oid "
" and not exists "
@ -4714,13 +4715,13 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
exit_nicely();
}
i_rcname = PQfnumber(res2, "rcname");
i_rcsrc = PQfnumber(res2, "rcsrc");
i_conname = PQfnumber(res2, "conname");
i_consrc = PQfnumber(res2, "consrc");
for (j = 0; j < ntups2; j++)
{
const char *name = PQgetvalue(res2, j, i_rcname);
const char *expr = PQgetvalue(res2, j, i_rcsrc);
const char *name = PQgetvalue(res2, j, i_conname);
const char *expr = PQgetvalue(res2, j, i_consrc);
if (actual_atts + j > 0)
appendPQExpBuffer(q, ",\n\t");

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.54 2002/05/13 17:45:30 tgl Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.55 2002/07/12 18:43:19 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
@ -747,7 +747,7 @@ describeTableDetails(const char *name, bool desc)
*result3 = NULL,
*result4 = NULL;
int index_count = 0,
constr_count = 0,
check_count = 0,
rule_count = 0,
trigger_count = 0;
int count_footers = 0;
@ -770,19 +770,20 @@ describeTableDetails(const char *name, bool desc)
index_count = PQntuples(result1);
}
/* count table (and column) constraints */
/* count table (and column) check constraints */
if (tableinfo.checks)
{
printfPQExpBuffer(&buf,
"SELECT rcsrc, rcname\n"
"FROM pg_relcheck r, pg_class c\n"
"WHERE c.relname='%s' AND c.oid = r.rcrelid",
"SELECT consrc, conname\n"
"FROM pg_constraint r, pg_class c\n"
"WHERE c.relname='%s' AND c.oid = r.conrelid\n"
"AND r.contype = 'c'",
name);
result2 = PSQLexec(buf.data);
if (!result2)
goto error_return;
else
constr_count = PQntuples(result2);
check_count = PQntuples(result2);
}
/* count rules */
@ -815,7 +816,7 @@ describeTableDetails(const char *name, bool desc)
trigger_count = PQntuples(result4);
}
footers = xmalloc((index_count + constr_count + rule_count + trigger_count + 1)
footers = xmalloc((index_count + check_count + rule_count + trigger_count + 1)
* sizeof(*footers));
/* print indexes */
@ -846,8 +847,8 @@ describeTableDetails(const char *name, bool desc)
}
/* print constraints */
for (i = 0; i < constr_count; i++)
/* print check constraints */
for (i = 0; i < check_count; i++)
{
char *s = _("Check constraints");

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tupdesc.h,v 1.35 2002/06/20 20:29:43 momjian Exp $
* $Id: tupdesc.h,v 1.36 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,7 +43,7 @@ typedef struct tupleConstr
/*
* This structure contains all information (i.e. from Classes
* pg_attribute, pg_attrdef, pg_relcheck) for a tuple.
* pg_attribute, pg_attrdef, pg_constraint) for a tuple.
*/
typedef struct tupleDesc
{

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catname.h,v 1.27 2002/07/11 07:39:27 ishii Exp $
* $Id: catname.h,v 1.28 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,8 +20,10 @@
#define AccessMethodOperatorRelationName "pg_amop"
#define AccessMethodProcedureRelationName "pg_amproc"
#define AttributeRelationName "pg_attribute"
#define ConstraintRelationName "pg_constraint"
#define ConversionRelationName "pg_conversion"
#define DatabaseRelationName "pg_database"
#define DependRelationName "pg_depend"
#define DescriptionRelationName "pg_description"
#define GroupRelationName "pg_group"
#define IndexRelationName "pg_index"
@ -40,7 +42,6 @@
#define TypeRelationName "pg_type"
#define VersionRelationName "pg_version"
#define AttrDefaultRelationName "pg_attrdef"
#define RelCheckRelationName "pg_relcheck"
#define TriggerRelationName "pg_trigger"
#endif /* CATNAME_H */

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.138 2002/07/11 07:39:27 ishii Exp $
* $Id: catversion.h,v 1.139 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200207111
#define CATALOG_VERSION_NO 200207112
#endif

View File

@ -0,0 +1,92 @@
/*-------------------------------------------------------------------------
*
* dependency.h
* Routines to support inter-object dependencies.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: dependency.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef DEPENDENCY_H
#define DEPENDENCY_H
#include "nodes/parsenodes.h" /* for DropBehavior */
/*
* Precise semantics of a dependency relationship are specified by the
* DependencyType code (which is stored in a "char" field in pg_depend,
* so we assign ASCII-code values to the enumeration members).
*
* In all cases, a dependency relationship indicates that the referenced
* object may not be dropped without also dropping the dependent object.
* However, there are several subflavors:
*
* DEPENDENCY_NORMAL ('n'): normal relationship between separately-created
* objects. The dependent object may be dropped without affecting the
* referenced object. The referenced object may only be dropped by
* specifying CASCADE, in which case the dependent object is dropped too.
* Example: a table column has a normal dependency on its datatype.
*
* DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately
* from the referenced object, and should be automatically dropped
* (regardless of RESTRICT or CASCADE mode) if the referenced object
* is dropped.
* Example: a named constraint on a table is made auto-dependent on
* the table, so that it will go away if the table is dropped.
*
* DEPENDENCY_INTERNAL ('i'): the dependent object was created as part
* of creation of the referenced object, and is really just a part of
* its internal implementation. A DROP of the dependent object will be
* disallowed outright (we'll tell the user to issue a DROP against the
* referenced object, instead). A DROP of the referenced object will be
* propagated through to drop the dependent object whether CASCADE is
* specified or not.
* Example: a trigger that's created to enforce a foreign-key constraint
* is made internally dependent on the constraint's pg_constraint entry.
*
* DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
* is a signal that the system itself depends on the referenced object,
* and so that object must never be deleted. Entries of this type are
* created only during initdb. The fields for the dependent object
* contain zeroes.
*
* Other dependency flavors may be needed in future.
*/
typedef enum DependencyType
{
DEPENDENCY_NORMAL = 'n',
DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i',
DEPENDENCY_PIN = 'p'
} DependencyType;
/*
* The two objects related by a dependency are identified by ObjectAddresses.
*/
typedef struct ObjectAddress
{
Oid classId; /* Class Id from pg_class */
Oid objectId; /* OID of the object */
int32 objectSubId; /* Subitem within the object (column of table) */
} ObjectAddress;
/* in dependency.c */
extern void performDeletion(const ObjectAddress *object,
DropBehavior behavior);
/* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior);
#endif /* DEPENDENCY_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: heap.h,v 1.51 2002/06/20 20:29:43 momjian Exp $
* $Id: heap.h,v 1.52 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -44,7 +44,7 @@ extern Oid heap_create_with_catalog(const char *relname,
bool relhasoids,
bool allow_system_table_mods);
extern void heap_drop_with_catalog(Oid rid, bool allow_system_table_mods);
extern void heap_drop_with_catalog(Oid rid);
extern void heap_truncate(Oid rid);
@ -58,7 +58,8 @@ extern Node *cookDefault(ParseState *pstate,
int32 atttypmod,
char *attname);
extern int RemoveCheckConstraint(Relation rel, const char *constrName, bool inh);
extern int RemoveRelConstraints(Relation rel, const char *constrName,
DropBehavior behavior);
extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
bool relhasoids);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: index.h,v 1.48 2002/06/20 20:29:43 momjian Exp $
* $Id: index.h,v 1.49 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,6 +35,7 @@ extern Oid index_create(Oid heapRelationId,
Oid accessMethodObjectId,
Oid *classObjectId,
bool primary,
bool isconstraint,
bool allow_system_table_mods);
extern void index_drop(Oid indexId);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: indexing.h,v 1.68 2002/07/11 07:39:27 ishii Exp $
* $Id: indexing.h,v 1.69 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -27,8 +27,10 @@
#define Num_pg_attr_indices 2
#define Num_pg_attrdef_indices 1
#define Num_pg_class_indices 2
#define Num_pg_constraint_indices 3
#define Num_pg_conversion_indices 2
#define Num_pg_database_indices 2
#define Num_pg_depend_indices 2
#define Num_pg_description_indices 1
#define Num_pg_group_indices 2
#define Num_pg_index_indices 2
@ -39,7 +41,6 @@
#define Num_pg_opclass_indices 2
#define Num_pg_operator_indices 2
#define Num_pg_proc_indices 2
#define Num_pg_relcheck_indices 1
#define Num_pg_rewrite_indices 2
#define Num_pg_shadow_indices 2
#define Num_pg_statistic_indices 1
@ -60,10 +61,15 @@
#define AttributeRelidNumIndex "pg_attribute_relid_attnum_index"
#define ClassNameNspIndex "pg_class_relname_nsp_index"
#define ClassOidIndex "pg_class_oid_index"
#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index"
#define ConstraintOidIndex "pg_constraint_oid_index"
#define ConstraintRelidIndex "pg_constraint_conrelid_index"
#define ConversionDefaultIndex "pg_conversion_default_index"
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
#define DatabaseNameIndex "pg_database_datname_index"
#define DatabaseOidIndex "pg_database_oid_index"
#define DependDependerIndex "pg_depend_depender_index"
#define DependReferenceIndex "pg_depend_reference_index"
#define DescriptionObjIndex "pg_description_o_c_o_index"
#define GroupNameIndex "pg_group_name_index"
#define GroupSysidIndex "pg_group_sysid_index"
@ -81,7 +87,6 @@
#define OperatorOidIndex "pg_operator_oid_index"
#define ProcedureNameNspIndex "pg_proc_proname_args_nsp_index"
#define ProcedureOidIndex "pg_proc_oid_index"
#define RelCheckIndex "pg_relcheck_rcrelid_index"
#define RewriteOidIndex "pg_rewrite_oid_index"
#define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index"
#define ShadowNameIndex "pg_shadow_usename_index"
@ -102,8 +107,10 @@ extern char *Name_pg_amproc_indices[];
extern char *Name_pg_attr_indices[];
extern char *Name_pg_attrdef_indices[];
extern char *Name_pg_class_indices[];
extern char *Name_pg_constraint_indices[];
extern char *Name_pg_conversion_indices[];
extern char *Name_pg_database_indices[];
extern char *Name_pg_depend_indices[];
extern char *Name_pg_description_indices[];
extern char *Name_pg_group_indices[];
extern char *Name_pg_index_indices[];
@ -114,7 +121,6 @@ extern char *Name_pg_namespace_indices[];
extern char *Name_pg_opclass_indices[];
extern char *Name_pg_operator_indices[];
extern char *Name_pg_proc_indices[];
extern char *Name_pg_relcheck_indices[];
extern char *Name_pg_rewrite_indices[];
extern char *Name_pg_shadow_indices[];
extern char *Name_pg_statistic_indices[];
@ -160,10 +166,19 @@ DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnum_index on pg_attribute using btree
DECLARE_UNIQUE_INDEX(pg_class_oid_index on pg_class using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname name_ops, relnamespace oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops));
DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));
DECLARE_UNIQUE_INDEX(pg_database_datname_index on pg_database using btree(datname name_ops));
DECLARE_UNIQUE_INDEX(pg_database_oid_index on pg_database using btree(oid oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_depend_depender_index on pg_depend using btree(classid oid_ops, objid oid_ops, objsubid int4_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_depend_reference_index on pg_depend using btree(refclassid oid_ops, refobjid oid_ops, refobjsubid int4_ops));
DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index on pg_description using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops));
DECLARE_UNIQUE_INDEX(pg_group_name_index on pg_group using btree(groname name_ops));
DECLARE_UNIQUE_INDEX(pg_group_sysid_index on pg_group using btree(grosysid int4_ops));
@ -183,7 +198,6 @@ DECLARE_UNIQUE_INDEX(pg_operator_oprname_l_r_n_index on pg_operator using btree(
DECLARE_UNIQUE_INDEX(pg_proc_oid_index on pg_proc using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index on pg_proc using btree(proname name_ops, pronargs int2_ops, proargtypes oidvector_ops, pronamespace oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_relcheck_rcrelid_index on pg_relcheck using btree(rcrelid oid_ops));
DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index on pg_rewrite using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops));

View File

@ -0,0 +1,172 @@
/*-------------------------------------------------------------------------
*
* pg_constraint.h
* definition of the system "constraint" relation (pg_constraint)
* along with the relation's initial contents.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_constraint.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
* information from the DATA() statements.
*
*-------------------------------------------------------------------------
*/
#ifndef PG_CONSTRAINT_H
#define PG_CONSTRAINT_H
/* ----------------
* postgres.h contains the system type definintions and the
* CATALOG(), BOOTSTRAP and DATA() sugar words so this file
* can be read by both genbki.sh and the C compiler.
* ----------------
*/
/* ----------------
* pg_constraint definition. cpp turns this into
* typedef struct FormData_pg_constraint
* ----------------
*/
CATALOG(pg_constraint)
{
/*
* conname + connamespace is deliberately not unique; we allow, for
* example, the same name to be used for constraints of different
* relations. This is partly for backwards compatibility with past
* Postgres practice, and partly because we don't want to have to obtain
* a global lock to generate a globally unique name for a nameless
* constraint. We associate a namespace with constraint names only
* for SQL92 compatibility.
*/
NameData conname; /* name of this constraint */
Oid connamespace; /* OID of namespace containing constraint */
char contype; /* constraint type; see codes below */
bool condeferrable; /* deferrable constraint? */
bool condeferred; /* deferred by default? */
/*
* conrelid and conkey are only meaningful if the constraint applies
* to a specific relation (this excludes domain constraints and
* assertions). Otherwise conrelid is 0 and conkey is NULL.
*/
Oid conrelid; /* relation this constraint constrains */
/*
* contypid links to the pg_type row for a domain if this is a domain
* constraint. Otherwise it's 0.
*
* For SQL-style global ASSERTIONs, both conrelid and contypid would
* be zero. This is not presently supported, however.
*/
Oid contypid; /* domain this constraint constrains */
/*
* These fields, plus confkey, are only meaningful for a foreign-key
* constraint. Otherwise confrelid is 0 and the char fields are spaces.
*/
Oid confrelid; /* relation referenced by foreign key */
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
/*
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
*/
/*
* Columns of conrelid that the constraint applies to
*/
int2 conkey[1];
/*
* If a foreign key, the referenced columns of confrelid
*/
int2 confkey[1];
/*
* If a check constraint, nodeToString representation of expression
*/
text conbin;
/*
* If a check constraint, source-text representation of expression
*/
text consrc;
} FormData_pg_constraint;
/* ----------------
* Form_pg_constraint corresponds to a pointer to a tuple with
* the format of pg_constraint relation.
* ----------------
*/
typedef FormData_pg_constraint *Form_pg_constraint;
/* ----------------
* compiler constants for pg_constraint
* ----------------
*/
#define Natts_pg_constraint 15
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
#define Anum_pg_constraint_condeferrable 4
#define Anum_pg_constraint_condeferred 5
#define Anum_pg_constraint_conrelid 6
#define Anum_pg_constraint_contypid 7
#define Anum_pg_constraint_confrelid 8
#define Anum_pg_constraint_confupdtype 9
#define Anum_pg_constraint_confdeltype 10
#define Anum_pg_constraint_confmatchtype 11
#define Anum_pg_constraint_conkey 12
#define Anum_pg_constraint_confkey 13
#define Anum_pg_constraint_conbin 14
#define Anum_pg_constraint_consrc 15
/* Valid values for contype */
#define CONSTRAINT_CHECK 'c'
#define CONSTRAINT_FOREIGN 'f'
#define CONSTRAINT_PRIMARY 'p'
#define CONSTRAINT_UNIQUE 'u'
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
* the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
*/
/*
* prototypes for functions in pg_constraint.c
*/
extern Oid CreateConstraintEntry(const char *constraintName,
Oid constraintNamespace,
char constraintType,
bool isDeferrable,
bool isDeferred,
Oid relId,
const int16 *constraintKey,
int constraintNKeys,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
const char *conBin,
const char *conSrc);
extern void RemoveConstraintById(Oid conId);
extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace,
const char *cname);
extern char *GenerateConstraintName(Oid relId, Oid relNamespace,
int *counter);
extern bool ConstraintNameIsGenerated(const char *cname);
#endif /* PG_CONSTRAINT_H */

View File

@ -0,0 +1,93 @@
/*-------------------------------------------------------------------------
*
* pg_depend.h
* definition of the system "dependency" relation (pg_depend)
* along with the relation's initial contents.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_depend.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
* information from the DATA() statements.
*
*-------------------------------------------------------------------------
*/
#ifndef PG_DEPEND_H
#define PG_DEPEND_H
/* ----------------
* postgres.h contains the system type definitions and the
* CATALOG(), BOOTSTRAP and DATA() sugar words so this file
* can be read by both genbki.sh and the C compiler.
* ----------------
*/
/* ----------------
* pg_depend definition. cpp turns this into
* typedef struct FormData_pg_depend
* ----------------
*/
CATALOG(pg_depend) BKI_WITHOUT_OIDS
{
/*
* Identification of the dependent (referencing) object.
*
* These fields are all zeroes for a DEPENDENCY_PIN entry.
*/
Oid classid; /* OID of table containing object */
Oid objid; /* OID of object itself */
int4 objsubid; /* column number, or 0 if not used */
/*
* Identification of the independent (referenced) object.
*/
Oid refclassid; /* OID of table containing object */
Oid refobjid; /* OID of object itself */
int4 refobjsubid; /* column number, or 0 if not used */
/*
* Precise semantics of the relationship are specified by the deptype
* field. See DependencyType in catalog/dependency.h.
*/
char deptype; /* see codes in dependency.h */
} FormData_pg_depend;
/* ----------------
* Form_pg_depend corresponds to a pointer to a row with
* the format of pg_depend relation.
* ----------------
*/
typedef FormData_pg_depend *Form_pg_depend;
/* ----------------
* compiler constants for pg_depend
* ----------------
*/
#define Natts_pg_depend 7
#define Anum_pg_depend_classid 1
#define Anum_pg_depend_objid 2
#define Anum_pg_depend_objsubid 3
#define Anum_pg_depend_refclassid 4
#define Anum_pg_depend_refobjid 5
#define Anum_pg_depend_refobjsubid 6
#define Anum_pg_depend_deptype 7
/*
* pg_depend has no preloaded contents; system-defined dependencies are
* loaded into it during a late stage of the initdb process.
*
* NOTE: we do not represent all possible dependency pairs in pg_depend;
* for example, there's not much value in creating an explicit dependency
* from an attribute to its relation. Usually we make a dependency for
* cases where the relationship is conditional rather than essential
* (for example, not all triggers are dependent on constraints, but all
* attributes are dependent on relations) or where the dependency is not
* convenient to find from the contents of other catalogs.
*/
#endif /* PG_DEPEND_H */

View File

@ -1,55 +0,0 @@
/*-------------------------------------------------------------------------
*
* pg_relcheck.h
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* NOTES
* the genbki.sh script reads this file and generates .bki
* information from the DATA() statements.
*
*-------------------------------------------------------------------------
*/
#ifndef PG_RELCHECK_H
#define PG_RELCHECK_H
/* ----------------
* postgres.h contains the system type definintions and the
* CATALOG(), BOOTSTRAP and DATA() sugar words so this file
* can be read by both genbki.sh and the C compiler.
* ----------------
*/
/* ----------------
* pg_relcheck definition. cpp turns this into
* typedef struct FormData_pg_relcheck
* ----------------
*/
CATALOG(pg_relcheck) BKI_WITHOUT_OIDS
{
Oid rcrelid;
NameData rcname;
text rcbin;
text rcsrc;
} FormData_pg_relcheck;
/* ----------------
* Form_pg_relcheck corresponds to a pointer to a tuple with
* the format of pg_relcheck relation.
* ----------------
*/
typedef FormData_pg_relcheck *Form_pg_relcheck;
/* ----------------
* compiler constants for pg_relcheck
* ----------------
*/
#define Natts_pg_relcheck 4
#define Anum_pg_relcheck_rcrelid 1
#define Anum_pg_relcheck_rcname 2
#define Anum_pg_relcheck_rcbin 3
#define Anum_pg_relcheck_rcsrc 4
#endif /* PG_RELCHECK_H */

View File

@ -27,7 +27,7 @@
extern void CommentObject(CommentStmt *stmt);
extern void DeleteComments(Oid oid, Oid classoid);
extern void DeleteComments(Oid oid, Oid classoid, int32 subid);
extern void CreateComments(Oid oid, Oid classoid, int32 subid, char *comment);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: defrem.h,v 1.40 2002/07/01 15:27:56 tgl Exp $
* $Id: defrem.h,v 1.41 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -27,6 +27,7 @@ extern void DefineIndex(RangeVar *heapRelation,
List *attributeList,
bool unique,
bool primary,
bool isconstraint,
Expr *predicate,
List *rangetable);
extern void RemoveIndex(RangeVar *relation, DropBehavior behavior);
@ -39,16 +40,19 @@ extern void ReindexDatabase(const char *databaseName, bool force, bool all);
*/
extern void CreateFunction(CreateFunctionStmt *stmt);
extern void RemoveFunction(List *functionName, List *argTypes);
extern void RemoveFunction(RemoveFuncStmt *stmt);
extern void RemoveFunctionById(Oid funcOid);
extern void DefineOperator(List *names, List *parameters);
extern void RemoveOperator(RemoveOperStmt *stmt);
extern void RemoveOperatorById(Oid operOid);
extern void DefineAggregate(List *names, List *parameters);
extern void RemoveAggregate(List *aggName, TypeName *aggType);
extern void RemoveAggregate(RemoveAggrStmt *stmt);
extern void DefineType(List *names, List *parameters);
extern void RemoveType(List *names, DropBehavior behavior);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void RemoveDomain(List *names, DropBehavior behavior);

View File

@ -13,5 +13,6 @@
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
extern void DropProceduralLanguage(DropPLangStmt *stmt);
extern void DropProceduralLanguageById(Oid langOid);
#endif /* PROCLANG_H */

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: trigger.h,v 1.36 2002/06/20 20:29:49 momjian Exp $
* $Id: trigger.h,v 1.37 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -101,9 +101,11 @@ typedef struct TriggerData
#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
extern void CreateTrigger(CreateTrigStmt *stmt);
extern void DropTrigger(Oid relid, const char *trigname);
extern void RelationRemoveTriggers(Relation rel);
extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint);
extern void DropTrigger(Oid relid, const char *trigname,
DropBehavior behavior);
extern void RemoveTriggerById(Oid trigOid);
extern void renametrig(Oid relid, const char *oldname, const char *newname);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.184 2002/07/11 07:39:27 ishii Exp $
* $Id: parsenodes.h,v 1.185 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -762,6 +762,8 @@ typedef struct AlterTableStmt
* M = alter column storage
* D = drop column
* C = add constraint
* c = pre-processed add constraint
* (local in parser/analyze.c)
* X = drop constraint
* E = create toast table
* U = change owner
@ -929,31 +931,41 @@ typedef struct Constraint
/* ----------
* Definitions for FOREIGN KEY constraints in CreateStmt
*
* Note: FKCONSTR_ACTION_xxx values are stored into pg_constraint.confupdtype
* and pg_constraint.confdeltype columns; FKCONSTR_MATCH_xxx values are
* stored into pg_constraint.confmatchtype. Changing the code values may
* require an initdb!
*
* If skip_validation is true then we skip checking that the existing rows
* in the table satisfy the constraint, and just install the catalog entries
* for the constraint. This is currently used only during CREATE TABLE
* (when we know the table must be empty).
* ----------
*/
#define FKCONSTR_ON_KEY_NOACTION 0x0000
#define FKCONSTR_ON_KEY_RESTRICT 0x0001
#define FKCONSTR_ON_KEY_CASCADE 0x0002
#define FKCONSTR_ON_KEY_SETNULL 0x0004
#define FKCONSTR_ON_KEY_SETDEFAULT 0x0008
#define FKCONSTR_ACTION_NOACTION 'a'
#define FKCONSTR_ACTION_RESTRICT 'r'
#define FKCONSTR_ACTION_CASCADE 'c'
#define FKCONSTR_ACTION_SETNULL 'n'
#define FKCONSTR_ACTION_SETDEFAULT 'd'
#define FKCONSTR_ON_DELETE_MASK 0x000F
#define FKCONSTR_ON_DELETE_SHIFT 0
#define FKCONSTR_ON_UPDATE_MASK 0x00F0
#define FKCONSTR_ON_UPDATE_SHIFT 4
#define FKCONSTR_MATCH_FULL 'f'
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_UNSPECIFIED 'u'
typedef struct FkConstraint
{
NodeTag type;
char *constr_name; /* Constraint name */
char *constr_name; /* Constraint name, or NULL if unnamed */
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
char *match_type; /* FULL or PARTIAL */
int32 actions; /* ON DELETE/UPDATE actions */
char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
bool deferrable; /* DEFERRABLE */
bool initdeferred; /* INITIALLY DEFERRED */
bool skip_validation; /* skip validation of existing rows? */
} FkConstraint;
/* ----------------------
@ -1201,6 +1213,7 @@ typedef struct IndexStmt
* transformStmt() */
bool unique; /* is index unique? */
bool primary; /* is index on primary key? */
bool isconstraint; /* is it from a CONSTRAINT clause? */
} IndexStmt;
/* ----------------------

View File

@ -7,14 +7,18 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: rewriteRemove.h,v 1.14 2002/06/20 20:29:52 momjian Exp $
* $Id: rewriteRemove.h,v 1.15 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef REWRITEREMOVE_H
#define REWRITEREMOVE_H
extern void RemoveRewriteRule(Oid owningRel, const char *ruleName);
extern void RelationRemoveRules(Oid relid);
#include "nodes/parsenodes.h"
extern void RemoveRewriteRule(Oid owningRel, const char *ruleName,
DropBehavior behavior);
extern void RemoveRewriteRuleById(Oid ruleOid);
#endif /* REWRITEREMOVE_H */

View File

@ -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.54 2002/07/06 20:16:36 momjian Exp $
* $Id: lsyscache.h,v 1.55 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,6 +42,7 @@ extern Oid get_func_rettype(Oid funcid);
extern bool get_func_retset(Oid funcid);
extern char func_volatile(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
extern Oid get_system_catalog_relid(const char *catname);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);

View File

@ -340,8 +340,6 @@ ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not fo
DROP TABLE tmp5;
DROP TABLE tmp4;
DROP TABLE tmp3;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2"
DROP TABLE tmp2;
-- Foreign key adding test with mixed types
-- Note: these tables are TEMP to avoid name conflicts when this test
@ -369,9 +367,9 @@ NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE pktable;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE pktable cascade;
NOTICE: Drop cascades to constraint $2 on table fktable
NOTICE: Drop cascades to constraint $1 on table fktable
DROP TABLE fktable;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));
@ -382,16 +380,16 @@ ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
-- Again, so should this...
DROP TABLE FKTABLE;
-- Again, so should this...
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
-- This fails because we mixed up the column ordering
DROP TABLE FKTABLE;
-- This fails because we mixed up the column ordering
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
@ -445,7 +443,7 @@ create table atacc1 (test int check (test>3), test2 int);
alter table atacc1 add check (test2>test);
-- should fail for $2
insert into atacc1 (test2, test) values (3, 4);
ERROR: ExecInsert: rejected due to CHECK constraint $2
ERROR: ExecInsert: rejected due to CHECK constraint $1
drop table atacc1;
-- inheritance related tests
create table atacc1 (test int);
@ -628,7 +626,7 @@ alter table atacc1 add constraint "atacc1_pkey" primary key (test);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1'
alter table atacc1 alter column test drop not null;
ERROR: ALTER TABLE: Attribute "test" is in a primary key
drop index atacc1_pkey;
alter table atacc1 drop constraint "atacc1_pkey";
alter table atacc1 alter column test drop not null;
insert into atacc1 values (null);
alter table atacc1 alter test set not null;

View File

@ -1,11 +1,13 @@
-- Test Comment / Drop
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
-- currently this will be disallowed
create domain basetypetest domaindroptest;
ERROR: DefineDomain: domaindroptest is not a basetype
drop domain domaindroptest cascade;
ERROR: DROP DOMAIN does not support the CASCADE keyword
drop domain domaindroptest;
-- this should fail because already gone
drop domain domaindroptest cascade;
ERROR: Type "domaindroptest" does not exist
-- TEST Domains.
create domain domainvarchar varchar(5);
create domain domainnumeric numeric(8,2);

View File

@ -22,7 +22,7 @@ INSERT INTO FKTABLE VALUES (3, 4);
INSERT INTO FKTABLE VALUES (NULL, 1);
-- Insert a failed row into FK TABLE
INSERT INTO FKTABLE VALUES (100, 2);
ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- Check FKTABLE
SELECT * FROM FKTABLE;
ftest1 | ftest2
@ -55,9 +55,8 @@ SELECT * FROM FKTABLE;
1 | 3
(3 rows)
DROP TABLE PKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
--
-- check set NULL and table constraint on multiple columns
--
@ -138,8 +137,8 @@ SELECT * FROM FKTABLE;
| | 8
(5 rows)
DROP TABLE PKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE PKTABLE CASCADE;
NOTICE: Drop cascades to constraint constrname on table fktable
DROP TABLE FKTABLE;
--
-- check set default and table constraint on multiple columns
@ -223,8 +222,13 @@ SELECT * FROM FKTABLE;
-1 | -2 | 8
(5 rows)
-- this should fail for lack of CASCADE
DROP TABLE PKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
NOTICE: constraint constrname2 on table fktable depends on table pktable
ERROR: Cannot drop table pktable because other objects depend on it
Use DROP ... CASCADE to drop the dependent objects too
DROP TABLE PKTABLE CASCADE;
NOTICE: Drop cascades to constraint constrname2 on table fktable
DROP TABLE FKTABLE;
--
-- First test, check with no on delete or on update
@ -246,7 +250,7 @@ INSERT INTO FKTABLE VALUES (3, 4);
INSERT INTO FKTABLE VALUES (NULL, 1);
-- Insert a failed row into FK TABLE
INSERT INTO FKTABLE VALUES (100, 2);
ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- Check FKTABLE
SELECT * FROM FKTABLE;
ftest1 | ftest2
@ -270,7 +274,7 @@ SELECT * FROM PKTABLE;
-- Delete a row from PK TABLE (should fail)
DELETE FROM PKTABLE WHERE ptest1=1;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- Delete a row from PK TABLE (should succeed)
DELETE FROM PKTABLE WHERE ptest1=5;
-- Check PKTABLE for deletes
@ -285,7 +289,7 @@ SELECT * FROM PKTABLE;
-- Update a row from PK TABLE (should fail)
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- Update a row from PK TABLE (should succeed)
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
-- Check PKTABLE for updates
@ -298,9 +302,8 @@ SELECT * FROM PKTABLE;
0 | Test4
(4 rows)
DROP TABLE PKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- MATCH unspecified
-- Base test restricting update/delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@ -363,8 +366,6 @@ SELECT * from FKTABLE;
(5 rows)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- cascade update/delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@ -462,8 +463,6 @@ SELECT * from FKTABLE;
(4 rows)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- set null update / set default delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@ -568,8 +567,6 @@ SELECT * from FKTABLE;
(6 rows)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- set default update / set null delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@ -687,8 +684,6 @@ SELECT * from FKTABLE;
(7 rows)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
@ -734,14 +729,10 @@ ERROR: Unable to identify an operator '=' for types 'inet' and 'integer'
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-- As should this
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- Two columns, two tables
CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2));
@ -775,14 +766,10 @@ ERROR: Unable to identify an operator '=' for types 'integer' and 'inet'
CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-- As does this
CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- Two columns, same table
-- Make sure this still works...
@ -832,25 +819,23 @@ insert into pktable(base1) values (1);
insert into pktable(base1) values (2);
-- let's insert a non-existant fktable value
insert into fktable(ftest1) values (3);
ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- let's make a valid row for that
insert into pktable(base1) values (3);
insert into fktable(ftest1) values (3);
-- let's try removing a row that should fail from pktable
delete from pktable where base1>2;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try updating all of the base1 values to *4
-- which should fail.
update pktable set base1=base1*4;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try an update that should work.
update pktable set base1=base1*4 where base1<3;
-- and a delete that should work
delete from pktable where base1>3;
-- cleanup
drop table fktable;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
delete from pktable;
-- Now 2 columns 2 tables, matching types
create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1));
@ -860,25 +845,23 @@ insert into pktable(base1, ptest1) values (1, 1);
insert into pktable(base1, ptest1) values (2, 2);
-- let's insert a non-existant fktable value
insert into fktable(ftest1, ftest2) values (3, 1);
ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- let's make a valid row for that
insert into pktable(base1,ptest1) values (3, 1);
insert into fktable(ftest1, ftest2) values (3, 1);
-- let's try removing a row that should fail from pktable
delete from pktable where base1>2;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try updating all of the base1 values to *4
-- which should fail.
update pktable set base1=base1*4;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try an update that should work.
update pktable set base1=base1*4 where base1<3;
-- and a delete that should work
delete from pktable where base1>3;
-- cleanup
drop table fktable;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
drop table pktable;
drop table pktable_base;
-- Now we'll do one all in 1 table with 2 columns of matching types
@ -893,13 +876,13 @@ insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1);
insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2);
-- fails (3,2) isn't in base1, ptest1
insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2);
ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable
-- fails (2,2) is being referenced
delete from pktable where base1=2;
ERROR: <unnamed> referential integrity violation - key in pktable still referenced from pktable
ERROR: $1 referential integrity violation - key in pktable still referenced from pktable
-- fails (1,1) is being referenced (twice)
update pktable set base1=3 where base1=1;
ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable
ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable
-- this sequence of two deletes will work, since after the first there will be no (2,*) references
delete from pktable where base2=2;
delete from pktable where base1=2;

View File

@ -38,8 +38,10 @@ SELECT relname, relhasindex
pg_attrdef | t
pg_attribute | t
pg_class | t
pg_constraint | t
pg_conversion | t
pg_database | t
pg_depend | t
pg_description | t
pg_group | t
pg_index | t
@ -50,7 +52,6 @@ SELECT relname, relhasindex
pg_opclass | t
pg_operator | t
pg_proc | t
pg_relcheck | t
pg_rewrite | t
pg_shadow | t
pg_statistic | t
@ -61,5 +62,5 @@ SELECT relname, relhasindex
shighway | t
tenk1 | t
tenk2 | t
(51 rows)
(52 rows)

View File

@ -123,7 +123,7 @@ INSERT INTO INSERT_TBL(y) VALUES ('Y');
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
INSERT INTO INSERT_TBL(y) VALUES ('Y');
INSERT INTO INSERT_TBL(x,z) VALUES (1, -2);
ERROR: ExecInsert: rejected due to CHECK constraint $2
ERROR: ExecInsert: rejected due to CHECK constraint $1
INSERT INTO INSERT_TBL(z,x) VALUES (-7, 7);
INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5);
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
@ -139,7 +139,7 @@ SELECT '' AS four, * FROM INSERT_TBL;
(4 rows)
INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4);
ERROR: ExecInsert: rejected due to CHECK constraint $2
ERROR: ExecInsert: rejected due to CHECK constraint $1
INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed');
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed');

View File

@ -250,7 +250,7 @@ CREATE TEMP TABLE FKTABLE (ftest1 varchar);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
DROP TABLE pktable;
DROP TABLE pktable cascade;
DROP TABLE fktable;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
@ -258,13 +258,13 @@ CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
-- This should fail, because we just chose really odd types
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
-- Again, so should this...
DROP TABLE FKTABLE;
-- Again, so should this...
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
-- This fails because we mixed up the column ordering
DROP TABLE FKTABLE;
-- This fails because we mixed up the column ordering
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
@ -486,7 +486,7 @@ alter table foo alter column bar drop not null;
create table atacc1 (test int not null);
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
alter table atacc1 alter column test drop not null;
drop index atacc1_pkey;
alter table atacc1 drop constraint "atacc1_pkey";
alter table atacc1 alter column test drop not null;
insert into atacc1 values (null);
alter table atacc1 alter test set not null;

View File

@ -4,11 +4,14 @@
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
-- currently this will be disallowed
create domain basetypetest domaindroptest;
drop domain domaindroptest cascade;
drop domain domaindroptest;
-- this should fail because already gone
drop domain domaindroptest cascade;
-- TEST Domains.

View File

@ -40,8 +40,8 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
DROP TABLE PKTABLE;
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
--
-- check set NULL and table constraint on multiple columns
@ -92,7 +92,7 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
DROP TABLE PKTABLE;
DROP TABLE PKTABLE CASCADE;
DROP TABLE FKTABLE;
--
@ -147,7 +147,9 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
-- this should fail for lack of CASCADE
DROP TABLE PKTABLE;
DROP TABLE PKTABLE CASCADE;
DROP TABLE FKTABLE;
@ -197,8 +199,8 @@ UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
-- Check PKTABLE for updates
SELECT * FROM PKTABLE;
DROP TABLE PKTABLE;
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- MATCH unspecified