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:
parent
791a40f943
commit
7c6df91dda
@ -1,6 +1,6 @@
|
|||||||
<!--
|
<!--
|
||||||
Documentation of the system catalogs, directed toward PostgreSQL developers
|
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">
|
<chapter id="catalogs">
|
||||||
@ -71,11 +71,21 @@
|
|||||||
<entry>tables, indexes, sequences (<quote>relations</quote>)</entry>
|
<entry>tables, indexes, sequences (<quote>relations</quote>)</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry>pg_constraint</entry>
|
||||||
|
<entry>check constraints, unique / primary key constraints, foreign key constraints</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry>pg_database</entry>
|
<entry>pg_database</entry>
|
||||||
<entry>databases within this database cluster</entry>
|
<entry>databases within this database cluster</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry>pg_depend</entry>
|
||||||
|
<entry>dependencies between database objects</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry>pg_description</entry>
|
<entry>pg_description</entry>
|
||||||
<entry>descriptions or comments on database objects</entry>
|
<entry>descriptions or comments on database objects</entry>
|
||||||
@ -131,11 +141,6 @@
|
|||||||
<entry>functions and procedures</entry>
|
<entry>functions and procedures</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>pg_relcheck</entry>
|
|
||||||
<entry>check constraints</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry>pg_rewrite</entry>
|
<entry>pg_rewrite</entry>
|
||||||
<entry>query rewriter rules</entry>
|
<entry>query rewriter rules</entry>
|
||||||
@ -680,7 +685,7 @@
|
|||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>
|
<entry>
|
||||||
Number of check constraints on the table; see
|
Number of check constraints on the table; see
|
||||||
<structname>pg_relcheck</structname> catalog
|
<structname>pg_constraint</structname> catalog
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
@ -764,6 +769,167 @@
|
|||||||
</table>
|
</table>
|
||||||
</sect1>
|
</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">
|
<sect1 id="catalog-pg-database">
|
||||||
<title>pg_database</title>
|
<title>pg_database</title>
|
||||||
@ -903,6 +1069,151 @@
|
|||||||
</sect1>
|
</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">
|
<sect1 id="catalog-pg-description">
|
||||||
<title>pg_description</title>
|
<title>pg_description</title>
|
||||||
|
|
||||||
@ -1866,72 +2177,6 @@
|
|||||||
|
|
||||||
</sect1>
|
</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">
|
<sect1 id="catalog-pg-rewrite">
|
||||||
<title>pg_rewrite</title>
|
<title>pg_rewrite</title>
|
||||||
|
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
|
|||||||
ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
|
ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
|
||||||
ADD <replaceable class="PARAMETER">table_constraint_definition</replaceable>
|
ADD <replaceable class="PARAMETER">table_constraint_definition</replaceable>
|
||||||
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</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>
|
ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
|
||||||
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
|
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
|
||||||
</synopsis>
|
</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.
|
form after you've entered non-null values for the column in all rows.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
Changing any part of the schema of a system
|
Changing any part of the schema of a system
|
||||||
catalog is not permitted.
|
catalog is not permitted.
|
||||||
@ -395,7 +375,7 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
|
|||||||
<para>
|
<para>
|
||||||
To remove a check constraint from a table and all its children:
|
To remove a check constraint from a table and all its children:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
ALTER TABLE distributors DROP CONSTRAINT zipchk RESTRICT;
|
ALTER TABLE distributors DROP CONSTRAINT zipchk;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ COMMENT ON
|
|||||||
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
|
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||||
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_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>) |
|
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> |
|
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||||
DOMAIN <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>, ...) |
|
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>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><replaceable class="PARAMETER">object_name,
|
<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>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The name of the object to be be commented. Names of tables,
|
The name of the object to be be commented. Names of tables,
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPAGGREGATE-1">
|
<refsect2 id="R2-SQL-DROPAGGREGATE-1">
|
||||||
@ -54,6 +54,23 @@ DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C
|
|||||||
<term><literal>CASCADE</></term>
|
<term><literal>CASCADE</></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Automatically drop objects that depend on the domain. This
|
Automatically drop objects that depend on the domain
|
||||||
behavior is not currently supported.
|
(such as table columns).
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -58,7 +58,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C
|
|||||||
<term><literal>RESTRICT</></term>
|
<term><literal>RESTRICT</></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -143,19 +144,14 @@ DROP DOMAIN box;
|
|||||||
|
|
||||||
<refsect1 id="SQL-DROPDOMAIN-compatibility">
|
<refsect1 id="SQL-DROPDOMAIN-compatibility">
|
||||||
<title>Compatibility</title>
|
<title>Compatibility</title>
|
||||||
|
|
||||||
|
<refsect2 id="R2-SQL-DROPDOMAIN-sql92">
|
||||||
|
<title>
|
||||||
|
SQL92
|
||||||
|
</title>
|
||||||
|
|
||||||
<para>
|
<para></para>
|
||||||
A <command>DROP DOMAIN</command> statement exists in SQL99. As with
|
</refsect2>
|
||||||
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>
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1 id="SQL-DROPDOMAIN-see-also">
|
<refsect1 id="SQL-DROPDOMAIN-see-also">
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPFUNCTION-1">
|
<refsect2 id="R2-SQL-DROPFUNCTION-1">
|
||||||
@ -49,6 +49,24 @@ DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
@ -136,15 +154,8 @@ DROP FUNCTION sqrt(integer);
|
|||||||
<title>Compatibility</title>
|
<title>Compatibility</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
A <command>DROP FUNCTION</command> statement is defined in SQL99. One of its syntax forms is:
|
A <command>DROP FUNCTION</command> statement is defined in SQL99. One of
|
||||||
|
its syntax forms is similar to PostgreSQL's.
|
||||||
<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.
|
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...]
|
DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...] [ CASCADE | RESTRICT ]
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPINDEX-1">
|
<refsect2 id="R2-SQL-DROPINDEX-1">
|
||||||
@ -41,6 +41,23 @@ DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...]
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable>
|
DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable> [ CASCADE | RESTRICT ]
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPLANGUAGE-1">
|
<refsect2 id="R2-SQL-DROPLANGUAGE-1">
|
||||||
@ -43,7 +43,26 @@ DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
@ -112,14 +131,6 @@ ERROR: Language "<replaceable class="parameter">name</replaceable>" doesn't exis
|
|||||||
<xref linkend="sql-createlanguage" endterm="sql-createlanguage-title">
|
<xref linkend="sql-createlanguage" endterm="sql-createlanguage-title">
|
||||||
for information on how to create procedural languages.
|
for information on how to create procedural languages.
|
||||||
</para>
|
</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>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPOPERATOR-1">
|
<refsect2 id="R2-SQL-DROPOPERATOR-1">
|
||||||
@ -60,6 +60,23 @@ DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable cla
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1998-09-22</date>
|
<date>1998-09-22</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPRULE-1">
|
<refsect2 id="R2-SQL-DROPRULE-1">
|
||||||
@ -50,7 +50,25 @@ DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable clas
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...]
|
DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPSEQUENCE-1">
|
<refsect2 id="R2-SQL-DROPSEQUENCE-1">
|
||||||
@ -75,7 +76,25 @@ ERROR: sequence "<replaceable class="parameter">name</replaceable>" does not exi
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...]
|
DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPTABLE-1">
|
<refsect2 id="R2-SQL-DROPTABLE-1">
|
||||||
@ -41,6 +42,24 @@ DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...]
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
@ -136,44 +155,11 @@ DROP TABLE films, distributors;
|
|||||||
</title>
|
</title>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPTABLE-4">
|
<refsect2 id="R2-SQL-DROPTABLE-4">
|
||||||
<refsect2info>
|
|
||||||
<date>1998-09-22</date>
|
|
||||||
</refsect2info>
|
|
||||||
<title>
|
<title>
|
||||||
SQL92
|
SQL92
|
||||||
</title>
|
</title>
|
||||||
<para>
|
<para>
|
||||||
SQL92 specifies some additional capabilities for DROP TABLE:
|
|
||||||
</para>
|
</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>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1998-09-22</date>
|
<date>1998-09-22</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPTRIGGER-1">
|
<refsect2 id="R2-SQL-DROPTRIGGER-1">
|
||||||
@ -50,6 +50,23 @@ DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable c
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...]
|
DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...] [ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPTYPE-1">
|
<refsect2 id="R2-SQL-DROPTYPE-1">
|
||||||
@ -41,6 +42,24 @@ DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...]
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
@ -75,6 +94,7 @@ ERROR: RemoveType: type '<replaceable class="parameter">typename</replaceable>'
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@ -132,19 +152,6 @@ DROP TYPE box;
|
|||||||
<refsect1 id="SQL-DROPTYPE-compatibility">
|
<refsect1 id="SQL-DROPTYPE-compatibility">
|
||||||
<title>Compatibility</title>
|
<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>
|
<para>
|
||||||
Note that the <command>CREATE TYPE</command> command and the data
|
Note that the <command>CREATE TYPE</command> command and the data
|
||||||
type extension mechanisms in <productname>PostgreSQL</productname>
|
type extension mechanisms in <productname>PostgreSQL</productname>
|
||||||
|
@ -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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...]
|
DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<refsect2 id="R2-SQL-DROPVIEW-1">
|
<refsect2 id="R2-SQL-DROPVIEW-1">
|
||||||
@ -42,7 +42,26 @@ DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...]
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
@ -134,58 +153,7 @@ DROP VIEW kinds;
|
|||||||
SQL92
|
SQL92
|
||||||
</title>
|
</title>
|
||||||
<para>
|
<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>
|
</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>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
@ -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">
|
<appendix id="release">
|
||||||
@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
|
|||||||
worries about funny characters.
|
worries about funny characters.
|
||||||
-->
|
-->
|
||||||
<literallayout><![CDATA[
|
<literallayout><![CDATA[
|
||||||
|
Most forms of DROP now support RESTRICT and CASCADE options
|
||||||
Recursive SQL functions can be defined
|
Recursive SQL functions can be defined
|
||||||
User-defined procedural languages can register a validator function to check new functions as they are created
|
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
|
Functions can be executed with the privileges of the owner
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)),
|
DefineIndex(makeRangeVar(NULL, LexIDStr($5)),
|
||||||
LexIDStr($3),
|
LexIDStr($3),
|
||||||
LexIDStr($7),
|
LexIDStr($7),
|
||||||
$9, false, false, NULL, NIL);
|
$9,
|
||||||
|
false, false, false, NULL, NIL);
|
||||||
do_end();
|
do_end();
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -253,7 +254,8 @@ Boot_DeclareUniqueIndexStmt:
|
|||||||
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
|
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
|
||||||
LexIDStr($4),
|
LexIDStr($4),
|
||||||
LexIDStr($8),
|
LexIDStr($8),
|
||||||
$10, true, false, NULL, NIL);
|
$10,
|
||||||
|
true, false, false, NULL, NIL);
|
||||||
do_end();
|
do_end();
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Makefile for backend/catalog
|
# 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 = ../../..
|
top_builddir = ../../..
|
||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
OBJS = catalog.o heap.o index.o indexing.o namespace.o aclchk.o \
|
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
|
||||||
pg_aggregate.o pg_largeobject.o pg_namespace.o \
|
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
|
||||||
pg_operator.o pg_proc.o pg_type.o pg_conversion.o
|
pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o
|
||||||
|
|
||||||
BKIFILES = postgres.bki postgres.description
|
BKIFILES = postgres.bki postgres.description
|
||||||
|
|
||||||
@ -27,12 +27,12 @@ SUBSYS.o: $(OBJS)
|
|||||||
|
|
||||||
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
|
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
|
||||||
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
|
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_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_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
|
||||||
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.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 \
|
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)
|
pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
|
||||||
|
731
src/backend/catalog/dependency.c
Normal file
731
src/backend/catalog/dependency.c
Normal 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;
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -33,21 +33,20 @@
|
|||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_attrdef.h"
|
#include "catalog/pg_attrdef.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_inherits.h"
|
#include "catalog/pg_inherits.h"
|
||||||
#include "catalog/pg_relcheck.h"
|
|
||||||
#include "catalog/pg_statistic.h"
|
#include "catalog/pg_statistic.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
#include "optimizer/prep.h"
|
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
@ -69,8 +68,6 @@ static void AddNewRelationTuple(Relation pg_class_desc,
|
|||||||
char relkind, bool relhasoids);
|
char relkind, bool relhasoids);
|
||||||
static void DeleteAttributeTuples(Relation rel);
|
static void DeleteAttributeTuples(Relation rel);
|
||||||
static void DeleteRelationTuple(Relation rel);
|
static void DeleteRelationTuple(Relation rel);
|
||||||
static void DeleteTypeTuple(Relation rel);
|
|
||||||
static void RelationRemoveIndexes(Relation relation);
|
|
||||||
static void RelationRemoveInheritance(Relation relation);
|
static void RelationRemoveInheritance(Relation relation);
|
||||||
static void AddNewRelationType(const char *typeName,
|
static void AddNewRelationType(const char *typeName,
|
||||||
Oid typeNamespace,
|
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 StoreRelCheck(Relation rel, char *ccname, char *ccbin);
|
||||||
static void StoreConstraints(Relation rel, TupleDesc tupdesc);
|
static void StoreConstraints(Relation rel, TupleDesc tupdesc);
|
||||||
static void SetRelationNumChecks(Relation rel, int numchecks);
|
static void SetRelationNumChecks(Relation rel, int numchecks);
|
||||||
static void RemoveConstraints(Relation rel);
|
static void RemoveDefaults(Relation rel);
|
||||||
static void RemoveStatistics(Relation rel);
|
static void RemoveStatistics(Relation rel);
|
||||||
|
|
||||||
|
|
||||||
@ -760,106 +757,42 @@ heap_create_with_catalog(const char *relname,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/*
|
||||||
* RelationRemoveInheritance
|
* RelationRemoveInheritance
|
||||||
*
|
*
|
||||||
* Note: for now, we cause an exception if relation is a
|
* Formerly, this routine checked for child relations and aborted the
|
||||||
* superclass. Someday, we may want to allow this and merge
|
* deletion if any were found. Now we rely on the dependency mechanism
|
||||||
* the type info into subclass procedures.... this seems like
|
* to check for or delete child relations. By the time we get here,
|
||||||
* lots of work.
|
* there are no children and we need only remove the pg_inherits rows.
|
||||||
* --------------------------------
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
RelationRemoveInheritance(Relation relation)
|
RelationRemoveInheritance(Relation relation)
|
||||||
{
|
{
|
||||||
Relation catalogRelation;
|
Relation catalogRelation;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
HeapScanDesc scan;
|
SysScanDesc scan;
|
||||||
ScanKeyData entry;
|
ScanKeyData entry;
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* open pg_inherits
|
|
||||||
*/
|
|
||||||
catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
|
catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
|
||||||
|
|
||||||
/*
|
ScanKeyEntryInitialize(&entry, 0x0,
|
||||||
* form a scan key for the subclasses of this class and begin scanning
|
Anum_pg_inherits_inhrelid, F_OIDEQ,
|
||||||
*/
|
|
||||||
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent,
|
|
||||||
F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||||
|
|
||||||
scan = heap_beginscan(catalogRelation,
|
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true,
|
||||||
SnapshotNow,
|
SnapshotNow, 1, &entry);
|
||||||
1,
|
|
||||||
&entry);
|
|
||||||
|
|
||||||
/*
|
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
simple_heap_delete(catalogRelation, &tuple->t_self);
|
simple_heap_delete(catalogRelation, &tuple->t_self);
|
||||||
found = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_endscan(scan);
|
systable_endscan(scan);
|
||||||
heap_close(catalogRelation, RowExclusiveLock);
|
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
|
* DeleteRelationTuple
|
||||||
*
|
|
||||||
* --------------------------------
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
DeleteRelationTuple(Relation rel)
|
DeleteRelationTuple(Relation rel)
|
||||||
@ -1049,163 +982,34 @@ DeleteAttributeTuples(Relation rel)
|
|||||||
heap_close(pg_attribute_desc, RowExclusiveLock);
|
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.
|
* 1) open relation, acquire exclusive lock.
|
||||||
* 2) remove inheritance information
|
* 2) flush relation buffers from bufmgr
|
||||||
* 3) remove indexes
|
* 3) remove inheritance information
|
||||||
* 4) remove pg_class tuple
|
* 4) remove pg_statistic tuples
|
||||||
* 5) remove pg_attribute tuples and related descriptions
|
* 5) remove pg_attribute tuples and related items
|
||||||
* 6) remove pg_description tuples
|
* 6) remove pg_class tuple
|
||||||
* 7) remove pg_type tuples
|
* 7) unlink relation file
|
||||||
* 8) RemoveConstraints ()
|
|
||||||
* 9) unlink relation
|
|
||||||
*
|
*
|
||||||
* old comments
|
* Note that this routine is not responsible for dropping objects that are
|
||||||
* Except for vital relations, removes relation from
|
* linked to the pg_class entry via dependencies (for example, indexes and
|
||||||
* relation catalog, and related attributes from
|
* constraints). Those are deleted by the dependency-tracing logic in
|
||||||
* attribute catalog (needed?). (Anything else?)
|
* dependency.c before control gets here. In general, therefore, this routine
|
||||||
*
|
* should never be called directly; go through performDeletion() instead.
|
||||||
* 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.
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
heap_drop_with_catalog(Oid rid,
|
heap_drop_with_catalog(Oid rid)
|
||||||
bool allow_system_table_mods)
|
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
Oid toasttableOid;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open and lock the relation.
|
* Open and lock the relation.
|
||||||
*/
|
*/
|
||||||
rel = heap_open(rid, AccessExclusiveLock);
|
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
|
* 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",
|
elog(ERROR, "heap_drop_with_catalog: FlushRelationBuffers returned %d",
|
||||||
i);
|
i);
|
||||||
|
|
||||||
/*
|
|
||||||
* remove rules if necessary
|
|
||||||
*/
|
|
||||||
if (rel->rd_rules != NULL)
|
|
||||||
RelationRemoveRules(rid);
|
|
||||||
|
|
||||||
/* triggers */
|
|
||||||
RelationRemoveTriggers(rel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* remove inheritance information
|
* remove inheritance information
|
||||||
*/
|
*/
|
||||||
RelationRemoveInheritance(rel);
|
RelationRemoveInheritance(rel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* remove indexes if necessary
|
* delete statistics
|
||||||
*/
|
*/
|
||||||
RelationRemoveIndexes(rel);
|
RemoveStatistics(rel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* delete attribute tuples
|
* delete attribute tuples and associated defaults
|
||||||
*/
|
*/
|
||||||
DeleteAttributeTuples(rel);
|
DeleteAttributeTuples(rel);
|
||||||
|
|
||||||
/*
|
RemoveDefaults(rel);
|
||||||
* delete comments, statistics, and constraints
|
|
||||||
*/
|
|
||||||
DeleteComments(rid, RelOid_pg_class);
|
|
||||||
|
|
||||||
RemoveStatistics(rel);
|
|
||||||
|
|
||||||
RemoveConstraints(rel);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* delete type tuple
|
|
||||||
*/
|
|
||||||
DeleteTypeTuple(rel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* delete relation tuple
|
* delete relation tuple
|
||||||
@ -1276,10 +1059,6 @@ heap_drop_with_catalog(Oid rid,
|
|||||||
* flush the relation from the relcache
|
* flush the relation from the relcache
|
||||||
*/
|
*/
|
||||||
RelationForgetRelation(rid);
|
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;
|
Node *expr;
|
||||||
char *ccsrc;
|
char *ccsrc;
|
||||||
Relation rcrel;
|
List *varList;
|
||||||
Relation idescs[Num_pg_relcheck_indices];
|
int keycount;
|
||||||
HeapTuple tuple;
|
int16 *attNos;
|
||||||
Datum values[4];
|
|
||||||
static char nulls[4] = {' ', ' ', ' ', ' '};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert condition to a normal boolean expression tree.
|
* Convert condition to a normal boolean expression tree.
|
||||||
@ -1394,26 +1171,55 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
|||||||
RelationGetRelid(rel)),
|
RelationGetRelid(rel)),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
|
/*
|
||||||
values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
|
* Find columns of rel that are used in ccbin
|
||||||
CStringGetDatum(ccname));
|
*/
|
||||||
values[Anum_pg_relcheck_rcbin - 1] = DirectFunctionCall1(textin,
|
varList = pull_var_clause(expr, false);
|
||||||
CStringGetDatum(ccbin));
|
keycount = length(varList);
|
||||||
values[Anum_pg_relcheck_rcsrc - 1] = DirectFunctionCall1(textin,
|
|
||||||
CStringGetDatum(ccsrc));
|
if (keycount > 0)
|
||||||
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
|
{
|
||||||
tuple = heap_formtuple(rcrel->rd_att, values, nulls);
|
List *vl;
|
||||||
simple_heap_insert(rcrel, tuple);
|
int i = 0;
|
||||||
CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices,
|
|
||||||
idescs);
|
attNos = (int16 *) palloc(keycount * sizeof(int16));
|
||||||
CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple);
|
foreach(vl, varList)
|
||||||
CatalogCloseIndices(Num_pg_relcheck_indices, idescs);
|
{
|
||||||
heap_close(rcrel, RowExclusiveLock);
|
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);
|
pfree(ccsrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1488,6 +1294,7 @@ AddRelationRawConstraints(Relation rel,
|
|||||||
ParseState *pstate;
|
ParseState *pstate;
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
int numchecks;
|
int numchecks;
|
||||||
|
int constr_name_ctr = 0;
|
||||||
List *listptr;
|
List *listptr;
|
||||||
Node *expr;
|
Node *expr;
|
||||||
|
|
||||||
@ -1549,18 +1356,17 @@ AddRelationRawConstraints(Relation rel,
|
|||||||
/* Check name uniqueness, or generate a new name */
|
/* Check name uniqueness, or generate a new name */
|
||||||
if (cdef->name != NULL)
|
if (cdef->name != NULL)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
List *listptr2;
|
List *listptr2;
|
||||||
|
|
||||||
ccname = cdef->name;
|
ccname = cdef->name;
|
||||||
/* Check against old constraints */
|
/* Check against pre-existing constraints */
|
||||||
for (i = 0; i < numoldchecks; i++)
|
if (ConstraintNameIsUsed(RelationGetRelid(rel),
|
||||||
{
|
RelationGetNamespace(rel),
|
||||||
if (strcmp(oldchecks[i].ccname, ccname) == 0)
|
ccname))
|
||||||
elog(ERROR, "Duplicate CHECK constraint name: '%s'",
|
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
|
||||||
ccname);
|
ccname, RelationGetRelationName(rel));
|
||||||
}
|
|
||||||
/* Check against other new constraints */
|
/* Check against other new constraints */
|
||||||
|
/* Needed because we don't do CommandCounterIncrement in loop */
|
||||||
foreach(listptr2, rawConstraints)
|
foreach(listptr2, rawConstraints)
|
||||||
{
|
{
|
||||||
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
|
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
|
||||||
@ -1577,55 +1383,40 @@ AddRelationRawConstraints(Relation rel,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
bool success;
|
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
|
do
|
||||||
{
|
{
|
||||||
success = true;
|
List *listptr2;
|
||||||
snprintf(ccname, NAMEDATALEN, "$%d", j);
|
|
||||||
|
|
||||||
/* 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;
|
success = false;
|
||||||
break;
|
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);
|
} while (!success);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1852,157 +1643,74 @@ RemoveAttrDefaults(Relation rel)
|
|||||||
heap_close(adrel, RowExclusiveLock);
|
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.
|
* Removes all constraints on a relation that match the given name.
|
||||||
* It is the responsibility of the calling function to acquire a lock on
|
*
|
||||||
* the relation.
|
* It is the responsibility of the calling function to acquire a suitable
|
||||||
* Returns: The number of CHECK constraints removed.
|
* lock on the relation.
|
||||||
|
*
|
||||||
|
* Returns: The number of constraints removed.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
RemoveCheckConstraint(Relation rel, const char *constrName, bool inh)
|
RemoveRelConstraints(Relation rel, const char *constrName,
|
||||||
|
DropBehavior behavior)
|
||||||
{
|
{
|
||||||
Oid relid;
|
int ndeleted = 0;
|
||||||
Relation rcrel;
|
Relation conrel;
|
||||||
TupleDesc tupleDesc;
|
SysScanDesc conscan;
|
||||||
TupleConstr *oldconstr;
|
ScanKeyData key[1];
|
||||||
int numoldchecks;
|
HeapTuple contup;
|
||||||
int numchecks;
|
|
||||||
HeapScanDesc rcscan;
|
|
||||||
ScanKeyData key[2];
|
|
||||||
HeapTuple rctup;
|
|
||||||
int rel_deleted = 0;
|
|
||||||
int all_deleted = 0;
|
|
||||||
|
|
||||||
/* Find id of the relation */
|
/* Grab an appropriate lock on the pg_constraint relation */
|
||||||
relid = RelationGetRelid(rel);
|
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,
|
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
|
||||||
*children;
|
|
||||||
|
|
||||||
/* This routine is actually in the planner */
|
if (strcmp(NameStr(con->conname), constrName) == 0)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Oid childrelid = lfirsti(child);
|
ObjectAddress conobj;
|
||||||
Relation inhrel;
|
|
||||||
|
|
||||||
if (childrelid == relid)
|
conobj.classId = RelationGetRelid(conrel);
|
||||||
continue;
|
conobj.objectId = contup->t_data->t_oid;
|
||||||
inhrel = heap_open(childrelid, AccessExclusiveLock);
|
conobj.objectSubId = 0;
|
||||||
all_deleted += RemoveCheckConstraint(inhrel, constrName, false);
|
|
||||||
heap_close(inhrel, NoLock);
|
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 */
|
/* Clean up after the scan */
|
||||||
heap_endscan(rcscan);
|
systable_endscan(conscan);
|
||||||
heap_close(rcrel, RowExclusiveLock);
|
heap_close(conrel, RowExclusiveLock);
|
||||||
|
|
||||||
if (rel_deleted)
|
return ndeleted;
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
RemoveConstraints(Relation rel)
|
RemoveDefaults(Relation rel)
|
||||||
{
|
{
|
||||||
TupleConstr *constr = rel->rd_att->constr;
|
TupleConstr *constr = rel->rd_att->constr;
|
||||||
|
|
||||||
if (!constr)
|
/*
|
||||||
return;
|
* We can skip looking at pg_attrdef if there are no defaults recorded
|
||||||
|
* in the Relation.
|
||||||
if (constr->num_defval > 0)
|
*/
|
||||||
|
if (constr && constr->num_defval > 0)
|
||||||
RemoveAttrDefaults(rel);
|
RemoveAttrDefaults(rel);
|
||||||
|
|
||||||
if (constr->num_check > 0)
|
|
||||||
RemoveRelChecks(rel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -29,14 +29,15 @@
|
|||||||
#include "bootstrap/bootstrap.h"
|
#include "bootstrap/bootstrap.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_index.h"
|
#include "catalog/pg_index.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
@ -535,6 +536,7 @@ index_create(Oid heapRelationId,
|
|||||||
Oid accessMethodObjectId,
|
Oid accessMethodObjectId,
|
||||||
Oid *classObjectId,
|
Oid *classObjectId,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool isconstraint,
|
||||||
bool allow_system_table_mods)
|
bool allow_system_table_mods)
|
||||||
{
|
{
|
||||||
Relation heapRelation;
|
Relation heapRelation;
|
||||||
@ -543,6 +545,7 @@ index_create(Oid heapRelationId,
|
|||||||
bool shared_relation;
|
bool shared_relation;
|
||||||
Oid namespaceId;
|
Oid namespaceId;
|
||||||
Oid indexoid;
|
Oid indexoid;
|
||||||
|
int i;
|
||||||
|
|
||||||
SetReindexProcessing(false);
|
SetReindexProcessing(false);
|
||||||
|
|
||||||
@ -660,7 +663,89 @@ index_create(Oid heapRelationId,
|
|||||||
classObjectId, primary);
|
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
|
* catalogs. First we must advance the command counter so that we
|
||||||
* will see the newly-entered index catalog tuples.
|
* will see the newly-entered index catalog tuples.
|
||||||
*/
|
*/
|
||||||
@ -691,11 +776,11 @@ index_create(Oid heapRelationId,
|
|||||||
return indexoid;
|
return indexoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
*
|
|
||||||
* index_drop
|
* index_drop
|
||||||
*
|
*
|
||||||
* ----------------------------------------------------------------
|
* NOTE: this routine should now only be called through performDeletion(),
|
||||||
|
* else associated dependencies won't be cleaned up.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
index_drop(Oid indexId)
|
index_drop(Oid indexId)
|
||||||
@ -730,17 +815,6 @@ index_drop(Oid indexId)
|
|||||||
userIndexRelation = index_open(indexId);
|
userIndexRelation = index_open(indexId);
|
||||||
LockRelation(userIndexRelation, AccessExclusiveLock);
|
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
|
* fix RELATION relation
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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};
|
{AttrDefaultIndex};
|
||||||
char *Name_pg_class_indices[Num_pg_class_indices] =
|
char *Name_pg_class_indices[Num_pg_class_indices] =
|
||||||
{ClassNameNspIndex, ClassOidIndex};
|
{ClassNameNspIndex, ClassOidIndex};
|
||||||
|
char *Name_pg_constraint_indices[Num_pg_constraint_indices] =
|
||||||
|
{ConstraintNameNspIndex, ConstraintOidIndex, ConstraintRelidIndex};
|
||||||
char *Name_pg_conversion_indices[Num_pg_conversion_indices] =
|
char *Name_pg_conversion_indices[Num_pg_conversion_indices] =
|
||||||
{ConversionNameNspIndex, ConversionDefaultIndex};
|
{ConversionNameNspIndex, ConversionDefaultIndex};
|
||||||
char *Name_pg_database_indices[Num_pg_database_indices] =
|
char *Name_pg_database_indices[Num_pg_database_indices] =
|
||||||
{DatabaseNameIndex, DatabaseOidIndex};
|
{DatabaseNameIndex, DatabaseOidIndex};
|
||||||
|
char *Name_pg_depend_indices[Num_pg_depend_indices] =
|
||||||
|
{DependDependerIndex, DependReferenceIndex};
|
||||||
char *Name_pg_group_indices[Num_pg_group_indices] =
|
char *Name_pg_group_indices[Num_pg_group_indices] =
|
||||||
{GroupNameIndex, GroupSysidIndex};
|
{GroupNameIndex, GroupSysidIndex};
|
||||||
char *Name_pg_index_indices[Num_pg_index_indices] =
|
char *Name_pg_index_indices[Num_pg_index_indices] =
|
||||||
@ -67,8 +71,6 @@ char *Name_pg_operator_indices[Num_pg_operator_indices] =
|
|||||||
{OperatorOidIndex, OperatorNameNspIndex};
|
{OperatorOidIndex, OperatorNameNspIndex};
|
||||||
char *Name_pg_proc_indices[Num_pg_proc_indices] =
|
char *Name_pg_proc_indices[Num_pg_proc_indices] =
|
||||||
{ProcedureOidIndex, ProcedureNameNspIndex};
|
{ProcedureOidIndex, ProcedureNameNspIndex};
|
||||||
char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] =
|
|
||||||
{RelCheckIndex};
|
|
||||||
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
|
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
|
||||||
{RewriteOidIndex, RewriteRelRulenameIndex};
|
{RewriteOidIndex, RewriteRelRulenameIndex};
|
||||||
char *Name_pg_shadow_indices[Num_pg_shadow_indices] =
|
char *Name_pg_shadow_indices[Num_pg_shadow_indices] =
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/xact.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_inherits.h"
|
#include "catalog/pg_inherits.h"
|
||||||
@ -128,25 +129,10 @@ static Oid mySpecialNamespace = InvalidOid;
|
|||||||
char *namespace_search_path = NULL;
|
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 */
|
/* Local functions */
|
||||||
static void recomputeNamespacePath(void);
|
static void recomputeNamespacePath(void);
|
||||||
static void InitTempTableNamespace(void);
|
static void InitTempTableNamespace(void);
|
||||||
static void RemoveTempRelations(Oid tempNamespaceId);
|
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 RemoveTempRelationsCallback(void);
|
||||||
static void NamespaceCallback(Datum arg, Oid relid);
|
static void NamespaceCallback(Datum arg, Oid relid);
|
||||||
|
|
||||||
@ -1531,56 +1517,22 @@ AtEOXact_Namespace(bool isCommit)
|
|||||||
static void
|
static void
|
||||||
RemoveTempRelations(Oid tempNamespaceId)
|
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;
|
Relation pgclass;
|
||||||
HeapScanDesc scan;
|
HeapScanDesc scan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
ScanKeyData key;
|
ScanKeyData key;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan pg_class to find all the relations in the target namespace.
|
* Scan pg_class to find all the relations in the target namespace.
|
||||||
* Ignore indexes, though, on the assumption that they'll go away
|
* Ignore indexes, though, on the assumption that they'll go away
|
||||||
* when their tables are deleted.
|
* 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,
|
ScanKeyEntryInitialize(&key, 0x0,
|
||||||
Anum_pg_class_relnamespace,
|
Anum_pg_class_relnamespace,
|
||||||
@ -1597,7 +1549,10 @@ FindTempRelations(Oid tempNamespaceId)
|
|||||||
case RELKIND_RELATION:
|
case RELKIND_RELATION:
|
||||||
case RELKIND_SEQUENCE:
|
case RELKIND_SEQUENCE:
|
||||||
case RELKIND_VIEW:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1606,164 +1561,6 @@ FindTempRelations(Oid tempNamespaceId)
|
|||||||
|
|
||||||
heap_endscan(scan);
|
heap_endscan(scan);
|
||||||
heap_close(pgclass, AccessShareLock);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
453
src/backend/catalog/pg_constraint.c
Normal file
453
src/backend/catalog/pg_constraint.c
Normal 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);
|
||||||
|
}
|
147
src/backend/catalog/pg_depend.c
Normal file
147
src/backend/catalog/pg_depend.c
Normal 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;
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -166,6 +167,8 @@ TypeCreate(const char *typeName,
|
|||||||
NameData name;
|
NameData name;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
int i;
|
int i;
|
||||||
|
ObjectAddress myself,
|
||||||
|
referenced;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* validate size specifications: either positive (fixed-length) or -1
|
* validate size specifications: either positive (fixed-length) or -1
|
||||||
@ -298,6 +301,77 @@ TypeCreate(const char *typeName,
|
|||||||
CatalogCloseIndices(Num_pg_type_indices, idescs);
|
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
|
* finish up
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -24,10 +24,10 @@
|
|||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_aggregate.h"
|
#include "catalog/pg_aggregate.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_func.h"
|
#include "parser/parse_func.h"
|
||||||
@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RemoveAggregate
|
||||||
|
* Deletes an aggregate.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
RemoveAggregate(List *aggName, TypeName *aggType)
|
RemoveAggregate(RemoveAggrStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation relation;
|
List *aggName = stmt->aggname;
|
||||||
HeapTuple tup;
|
TypeName *aggType = stmt->aggtype;
|
||||||
Oid basetypeID;
|
Oid basetypeID;
|
||||||
Oid procOid;
|
Oid procOid;
|
||||||
|
HeapTuple tup;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if a basetype is passed in, then attempt to find an aggregate for
|
* 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);
|
procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
|
||||||
|
|
||||||
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
/*
|
||||||
|
* Find the function tuple, do permissions and validity checks
|
||||||
|
*/
|
||||||
tup = SearchSysCache(PROCOID,
|
tup = SearchSysCache(PROCOID,
|
||||||
ObjectIdGetDatum(procOid),
|
ObjectIdGetDatum(procOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
|
|||||||
GetUserId()))
|
GetUserId()))
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
|
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
|
||||||
|
|
||||||
/* Delete any comments associated with this function */
|
/* find_aggregate_func already checked it is an aggregate */
|
||||||
DeleteComments(procOid, RelationGetRelid(relation));
|
|
||||||
|
|
||||||
/* Remove the pg_proc tuple */
|
|
||||||
simple_heap_delete(relation, &tup->t_self);
|
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
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 */
|
performDeletion(&object, stmt->behavior);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/genam.h"
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/pg_index.h"
|
#include "catalog/pg_index.h"
|
||||||
@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
|||||||
OldIndex;
|
OldIndex;
|
||||||
char NewHeapName[NAMEDATALEN];
|
char NewHeapName[NAMEDATALEN];
|
||||||
char NewIndexName[NAMEDATALEN];
|
char NewIndexName[NAMEDATALEN];
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We grab exclusive access to the target rel and index for the
|
* We grab exclusive access to the target rel and index for the
|
||||||
@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
|||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Destroy old heap (along with its index) and rename new. */
|
/* 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);
|
renamerel(OIDNewHeap, oldrelation->relname);
|
||||||
|
|
||||||
@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
|
|||||||
OldIndex->rd_rel->relam,
|
OldIndex->rd_rel->relam,
|
||||||
OldIndex->rd_index->indclass,
|
OldIndex->rd_index->indclass,
|
||||||
OldIndex->rd_index->indisprimary,
|
OldIndex->rd_index->indisprimary,
|
||||||
|
false, /* XXX losing constraint status */
|
||||||
allowSystemTableMods);
|
allowSystemTableMods);
|
||||||
|
|
||||||
setRelhasindex(OIDNewHeap, true,
|
setRelhasindex(OIDNewHeap, true,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
* If subid is nonzero then only comments matching it will be removed.
|
||||||
* regardless of their objsubid. It is called, for example, when a relation
|
* If subid is zero, all comments matching the oid/classoid will be removed
|
||||||
* is destroyed.
|
* (this corresponds to deleting a whole object).
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DeleteComments(Oid oid, Oid classoid)
|
DeleteComments(Oid oid, Oid classoid, int32 subid)
|
||||||
{
|
{
|
||||||
Relation description;
|
Relation description;
|
||||||
ScanKeyData skey[2];
|
ScanKeyData skey[3];
|
||||||
|
int nkeys;
|
||||||
SysScanDesc sd;
|
SysScanDesc sd;
|
||||||
HeapTuple oldtuple;
|
HeapTuple oldtuple;
|
||||||
|
|
||||||
/* Use the index to search for all matching old tuples */
|
/* Use the index to search for all matching old tuples */
|
||||||
|
|
||||||
ScanKeyEntryInitialize(&skey[0],
|
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||||
(bits16) 0x0,
|
Anum_pg_description_objoid, F_OIDEQ,
|
||||||
(AttrNumber) 1,
|
|
||||||
(RegProcedure) F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(oid));
|
ObjectIdGetDatum(oid));
|
||||||
|
|
||||||
ScanKeyEntryInitialize(&skey[1],
|
ScanKeyEntryInitialize(&skey[1], 0x0,
|
||||||
(bits16) 0x0,
|
Anum_pg_description_classoid, F_OIDEQ,
|
||||||
(AttrNumber) 2,
|
|
||||||
(RegProcedure) F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(classoid));
|
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);
|
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
||||||
|
|
||||||
sd = systable_beginscan(description, DescriptionObjIndex, true,
|
sd = systable_beginscan(description, DescriptionObjIndex, true,
|
||||||
SnapshotNow, 2, skey);
|
SnapshotNow, nkeys, skey);
|
||||||
|
|
||||||
while ((oldtuple = systable_getnext(sd)) != NULL)
|
while ((oldtuple = systable_getnext(sd)) != NULL)
|
||||||
{
|
{
|
||||||
@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid)
|
|||||||
/* Done */
|
/* Done */
|
||||||
|
|
||||||
systable_endscan(sd);
|
systable_endscan(sd);
|
||||||
heap_close(description, NoLock);
|
heap_close(description, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
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
|
* Close pg_database, but keep exclusive lock till commit to ensure
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DESCRIPTION
|
||||||
* These routines take the parse tree and pick out the
|
* These routines take the parse tree and pick out the
|
||||||
@ -33,11 +33,11 @@
|
|||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_language.h"
|
#include "catalog/pg_language.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt)
|
|||||||
/*
|
/*
|
||||||
* RemoveFunction
|
* RemoveFunction
|
||||||
* Deletes a function.
|
* Deletes a function.
|
||||||
*
|
|
||||||
* Exceptions:
|
|
||||||
* BadArg if name is invalid.
|
|
||||||
* "ERROR" if function nonexistent.
|
|
||||||
* ...
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RemoveFunction(List *functionName, /* function name to be removed */
|
RemoveFunction(RemoveFuncStmt *stmt)
|
||||||
List *argTypes) /* list of TypeName nodes */
|
|
||||||
{
|
{
|
||||||
|
List *functionName = stmt->funcname;
|
||||||
|
List *argTypes = stmt->args; /* list of TypeName nodes */
|
||||||
Oid funcOid;
|
Oid funcOid;
|
||||||
Relation relation;
|
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the function, do permissions and validity checks
|
||||||
|
*/
|
||||||
funcOid = LookupFuncNameTypeNames(functionName, argTypes,
|
funcOid = LookupFuncNameTypeNames(functionName, argTypes,
|
||||||
true, "RemoveFunction");
|
true, "RemoveFunction");
|
||||||
|
|
||||||
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
|
||||||
|
|
||||||
tup = SearchSysCache(PROCOID,
|
tup = SearchSysCache(PROCOID,
|
||||||
ObjectIdGetDatum(funcOid),
|
ObjectIdGetDatum(funcOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */
|
|||||||
NameListToString(functionName));
|
NameListToString(functionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete any comments associated with this function */
|
ReleaseSysCache(tup);
|
||||||
DeleteComments(funcOid, RelationGetRelid(relation));
|
|
||||||
|
/*
|
||||||
|
* 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);
|
simple_heap_delete(relation, &tup->t_self);
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
heap_close(relation, RowExclusiveLock);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation,
|
|||||||
List *attributeList,
|
List *attributeList,
|
||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool isconstraint,
|
||||||
Expr *predicate,
|
Expr *predicate,
|
||||||
List *rangetable)
|
List *rangetable)
|
||||||
{
|
{
|
||||||
@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation,
|
|||||||
|
|
||||||
index_create(relationId, indexRelationName,
|
index_create(relationId, indexRelationName,
|
||||||
indexInfo, accessMethodId, classObjectId,
|
indexInfo, accessMethodId, classObjectId,
|
||||||
primary, allowSystemTableMods);
|
primary, isconstraint, allowSystemTableMods);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We update the relation's pg_class tuple even if it already has
|
* We update the relation's pg_class tuple even if it already has
|
||||||
@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
|
|||||||
{
|
{
|
||||||
Oid indOid;
|
Oid indOid;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
indOid = RangeVarGetRelid(relation, false);
|
indOid = RangeVarGetRelid(relation, false);
|
||||||
tuple = SearchSysCache(RELOID,
|
tuple = SearchSysCache(RELOID,
|
||||||
@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
|
|||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
index_drop(indOid);
|
object.classId = RelOid_pg_class;
|
||||||
|
object.objectId = indOid;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
|
||||||
|
performDeletion(&object, behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -36,9 +36,9 @@
|
|||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_oper.h"
|
#include "parser/parse_oper.h"
|
||||||
@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt)
|
|||||||
TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
|
TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
|
||||||
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
|
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
|
||||||
Oid operOid;
|
Oid operOid;
|
||||||
Relation relation;
|
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
|
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
|
||||||
"RemoveOperator");
|
"RemoveOperator");
|
||||||
|
|
||||||
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
|
tup = SearchSysCache(OPEROID,
|
||||||
|
ObjectIdGetDatum(operOid),
|
||||||
tup = SearchSysCacheCopy(OPEROID,
|
0, 0, 0);
|
||||||
ObjectIdGetDatum(operOid),
|
|
||||||
0, 0, 0);
|
|
||||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
|
elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
|
||||||
NameListToString(operatorName));
|
NameListToString(operatorName));
|
||||||
@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt)
|
|||||||
GetUserId()))
|
GetUserId()))
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
|
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
|
||||||
|
|
||||||
/* Delete any comments associated with this operator */
|
ReleaseSysCache(tup);
|
||||||
DeleteComments(operOid, RelationGetRelid(relation));
|
|
||||||
|
/*
|
||||||
|
* 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);
|
simple_heap_delete(relation, &tup->t_self);
|
||||||
|
|
||||||
heap_freetuple(tup);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
heap_close(relation, RowExclusiveLock);
|
heap_close(relation, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_language.h"
|
#include "catalog/pg_language.h"
|
||||||
@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
|||||||
{
|
{
|
||||||
char languageName[NAMEDATALEN];
|
char languageName[NAMEDATALEN];
|
||||||
HeapTuple langTup;
|
HeapTuple langTup;
|
||||||
Relation rel;
|
ObjectAddress object;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check permission
|
* Check permission
|
||||||
@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
case_translate_language_name(stmt->plname, languageName);
|
case_translate_language_name(stmt->plname, languageName);
|
||||||
|
|
||||||
rel = heap_openr(LanguageRelationName, RowExclusiveLock);
|
langTup = SearchSysCache(LANGNAME,
|
||||||
|
CStringGetDatum(languageName),
|
||||||
langTup = SearchSysCacheCopy(LANGNAME,
|
0, 0, 0);
|
||||||
PointerGetDatum(languageName),
|
|
||||||
0, 0, 0);
|
|
||||||
if (!HeapTupleIsValid(langTup))
|
if (!HeapTupleIsValid(langTup))
|
||||||
elog(ERROR, "Language %s doesn't exist", languageName);
|
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",
|
elog(ERROR, "Language %s isn't a created procedural language",
|
||||||
languageName);
|
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);
|
simple_heap_delete(rel, &langTup->t_self);
|
||||||
|
|
||||||
heap_freetuple(langTup);
|
ReleaseSysCache(langTup);
|
||||||
|
|
||||||
heap_close(rel, RowExclusiveLock);
|
heap_close(rel, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/tuptoaster.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_attrdef.h"
|
#include "catalog/pg_attrdef.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_inherits.h"
|
#include "catalog/pg_inherits.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
@ -36,6 +38,7 @@
|
|||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
|
#include "parser/gramparse.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "parser/parse_relation.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 drop_default(Oid relid, int16 attnum);
|
||||||
static void CheckTupleType(Form_pg_class tuple_class);
|
static void CheckTupleType(Form_pg_class tuple_class);
|
||||||
static bool needs_toast_table(Relation rel);
|
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: */
|
/* Used by attribute and relation renaming routines: */
|
||||||
|
|
||||||
@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|||||||
ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
|
ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
|
||||||
sizeof(ConstrCheck));
|
sizeof(ConstrCheck));
|
||||||
int ncheck = 0;
|
int ncheck = 0;
|
||||||
|
int constr_name_ctr = 0;
|
||||||
|
|
||||||
foreach(listptr, old_constraints)
|
foreach(listptr, old_constraints)
|
||||||
{
|
{
|
||||||
@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|||||||
}
|
}
|
||||||
else
|
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);
|
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);
|
Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
|
||||||
check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
|
check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
|
||||||
@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|||||||
/*
|
/*
|
||||||
* RemoveRelation
|
* RemoveRelation
|
||||||
* Deletes a relation.
|
* 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
|
void
|
||||||
RemoveRelation(const RangeVar *relation, DropBehavior behavior)
|
RemoveRelation(const RangeVar *relation, DropBehavior behavior)
|
||||||
{
|
{
|
||||||
Oid relOid;
|
Oid relOid;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
relOid = RangeVarGetRelid(relation, false);
|
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;
|
Node *expr;
|
||||||
|
|
||||||
cdef->contype = CONSTR_CHECK;
|
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;
|
cdef->name = NULL;
|
||||||
else
|
else
|
||||||
cdef->name = pstrdup(check[i].ccname);
|
cdef->name = pstrdup(check[i].ccname);
|
||||||
@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
|||||||
/*
|
/*
|
||||||
* complementary static functions for MergeAttributes().
|
* 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
|
* constraints from parent classes, since the inherited attributes could
|
||||||
* be given different column numbers in multiple-inheritance cases.
|
* be given different column numbers in multiple-inheritance cases.
|
||||||
*
|
*
|
||||||
@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
|||||||
return;
|
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);
|
relation = heap_openr(InheritsRelationName, RowExclusiveLock);
|
||||||
desc = RelationGetDescr(relation);
|
desc = RelationGetDescr(relation);
|
||||||
@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
|||||||
Oid entryOid = lfirsti(entry);
|
Oid entryOid = lfirsti(entry);
|
||||||
Datum datum[Natts_pg_inherits];
|
Datum datum[Natts_pg_inherits];
|
||||||
char nullarr[Natts_pg_inherits];
|
char nullarr[Natts_pg_inherits];
|
||||||
|
ObjectAddress childobject,
|
||||||
|
parentobject;
|
||||||
|
|
||||||
datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
|
datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
|
||||||
datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */
|
datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */
|
||||||
@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
|||||||
|
|
||||||
heap_freetuple(tuple);
|
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;
|
seqNumber += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
List *listptr;
|
List *listptr;
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grab an exclusive lock on the target table, which we will NOT
|
* Grab an exclusive lock on the target table, which we will NOT
|
||||||
@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
|
|
||||||
foreach(listptr, newConstraints)
|
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))
|
switch (nodeTag(newConstraint))
|
||||||
{
|
{
|
||||||
@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
List *qual;
|
List *qual;
|
||||||
Node *expr;
|
Node *expr;
|
||||||
char *name;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign or validate constraint name
|
||||||
|
*/
|
||||||
if (constr->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
|
else
|
||||||
name = "<unnamed>";
|
constr->name = GenerateConstraintName(RelationGetRelid(rel),
|
||||||
|
RelationGetNamespace(rel),
|
||||||
|
&counter);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to make a parse state and range
|
* We need to make a parse state and range
|
||||||
@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
pfree(slot);
|
pfree(slot);
|
||||||
|
|
||||||
if (!successful)
|
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
|
* Call AddRelationRawConstraints to do
|
||||||
@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
{
|
{
|
||||||
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
|
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
|
||||||
Relation pkrel;
|
Relation pkrel;
|
||||||
HeapScanDesc scan;
|
Oid constrOid;
|
||||||
HeapTuple tuple;
|
|
||||||
Trigger trig;
|
/*
|
||||||
List *list;
|
* Assign or validate constraint name
|
||||||
int count;
|
*/
|
||||||
|
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
|
* Grab an exclusive lock on the pk table, so that
|
||||||
* someone doesn't delete rows out from under us.
|
* someone doesn't delete rows out from under us.
|
||||||
*
|
* (Although a lesser lock would do for that purpose,
|
||||||
* XXX wouldn't a lesser lock be sufficient?
|
* 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,
|
pkrel = heap_openrv(fkconstraint->pktable,
|
||||||
AccessExclusiveLock);
|
AccessExclusiveLock);
|
||||||
@ -2500,100 +2572,46 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
* Validity checks
|
* Validity checks
|
||||||
*/
|
*/
|
||||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
elog(ERROR, "referenced table \"%s\" not a relation",
|
elog(ERROR, "referenced relation \"%s\" is not a table",
|
||||||
fkconstraint->pktable->relname);
|
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)) &&
|
if (isTempNamespace(RelationGetNamespace(pkrel)) &&
|
||||||
!isTempNamespace(RelationGetNamespace(rel)))
|
!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
|
* Check that the constraint is satisfied by existing
|
||||||
* constraint.
|
* rows (we can skip this during table creation).
|
||||||
*
|
*
|
||||||
* NOTE: we assume parser has already checked for
|
* NOTE: we assume parser has already checked for
|
||||||
* existence of an appropriate unique index on the
|
* existence of an appropriate unique index on the
|
||||||
* referenced relation, and that the column datatypes
|
* referenced relation, and that the column datatypes
|
||||||
* are comparable.
|
* 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));
|
if (!fkconstraint->skip_validation)
|
||||||
trig.tgoid = InvalidOid;
|
validateForeignKeyConstraint(fkconstraint, rel, pkrel);
|
||||||
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;
|
|
||||||
|
|
||||||
trig.tgargs = (char **) palloc(
|
/*
|
||||||
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
|
* Record the FK constraint in pg_constraint.
|
||||||
+ length(fkconstraint->pk_attrs)));
|
*/
|
||||||
|
constrOid = createForeignKeyConstraint(rel, pkrel,
|
||||||
|
fkconstraint);
|
||||||
|
|
||||||
trig.tgargs[0] = trig.tgname;
|
/*
|
||||||
trig.tgargs[1] = RelationGetRelationName(rel);
|
* Create the triggers that will enforce the constraint.
|
||||||
trig.tgargs[2] = RelationGetRelationName(pkrel);
|
*/
|
||||||
trig.tgargs[3] = fkconstraint->match_type;
|
createForeignKeyTriggers(rel, fkconstraint, constrOid);
|
||||||
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);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close pk table, but keep lock until we've committed.
|
||||||
|
*/
|
||||||
heap_close(pkrel, NoLock);
|
heap_close(pkrel, NoLock);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -2607,12 +2625,418 @@ AlterTableAddConstraint(Oid myrelid,
|
|||||||
heap_close(rel, NoLock);
|
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
|
* 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
|
void
|
||||||
AlterTableDropConstraint(Oid myrelid,
|
AlterTableDropConstraint(Oid myrelid,
|
||||||
@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid,
|
|||||||
DropBehavior behavior)
|
DropBehavior behavior)
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
int deleted;
|
int deleted = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Acquire an exclusive lock on the target relation for the duration
|
* 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));
|
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since all we have is the name of the constraint, we have to look
|
* Process child tables if requested.
|
||||||
* through all catalogs that could possibly contain a constraint for
|
|
||||||
* this relation. We also keep a count of the number of constraints
|
|
||||||
* removed.
|
|
||||||
*/
|
*/
|
||||||
|
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
|
* Now do the thing on this relation.
|
||||||
*/
|
|
||||||
|
|
||||||
deleted += RemoveCheckConstraint(rel, constrName, inh);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
|
|
||||||
* constraints.
|
|
||||||
*
|
|
||||||
* Unimplemented.
|
|
||||||
*/
|
*/
|
||||||
|
deleted += RemoveRelConstraints(rel, constrName, behavior);
|
||||||
|
|
||||||
/* Close the target relation */
|
/* Close the target relation */
|
||||||
heap_close(rel, NoLock);
|
heap_close(rel, NoLock);
|
||||||
@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
|
|||||||
char toast_idxname[NAMEDATALEN];
|
char toast_idxname[NAMEDATALEN];
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
Oid classObjectId[2];
|
Oid classObjectId[2];
|
||||||
|
ObjectAddress baseobject,
|
||||||
|
toastobject;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grab an exclusive lock on the target table, which we will NOT
|
* 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,
|
toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
|
||||||
BTREE_AM_OID, classObjectId,
|
BTREE_AM_OID, classObjectId,
|
||||||
true, true);
|
true, false, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update toast rel's pg_class entry to show that it has an index. The
|
* 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);
|
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
|
* Close relations and make changes visible
|
||||||
*/
|
*/
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_language.h"
|
#include "catalog/pg_language.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
|
|||||||
MemoryContext per_tuple_context);
|
MemoryContext per_tuple_context);
|
||||||
|
|
||||||
|
|
||||||
void
|
Oid
|
||||||
CreateTrigger(CreateTrigStmt *stmt)
|
CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||||
{
|
{
|
||||||
int16 tgtype;
|
int16 tgtype;
|
||||||
int16 tgattr[FUNC_MAX_ARGS];
|
int16 tgattr[FUNC_MAX_ARGS];
|
||||||
@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
Oid fargtypes[FUNC_MAX_ARGS];
|
Oid fargtypes[FUNC_MAX_ARGS];
|
||||||
Oid funcoid;
|
Oid funcoid;
|
||||||
Oid funclang;
|
Oid funclang;
|
||||||
|
Oid trigoid;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
int i;
|
int i;
|
||||||
char constrtrigname[NAMEDATALEN];
|
char constrtrigname[NAMEDATALEN];
|
||||||
char *constrname = "";
|
char *trigname;
|
||||||
Oid constrrelid = InvalidOid;
|
char *constrname;
|
||||||
|
Oid constrrelid;
|
||||||
|
ObjectAddress myself,
|
||||||
|
referenced;
|
||||||
|
|
||||||
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
||||||
|
|
||||||
@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
aclcheck_error(aclresult, RelationGetRelationName(rel));
|
aclcheck_error(aclresult, RelationGetRelationName(rel));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If trigger is an RI constraint, use trigger name as constraint name
|
* If trigger is an RI constraint, use specified trigger name as
|
||||||
* and build a unique trigger name instead.
|
* constraint name and build a unique trigger name instead.
|
||||||
|
* This is mainly for backwards compatibility with CREATE CONSTRAINT
|
||||||
|
* TRIGGER commands.
|
||||||
*/
|
*/
|
||||||
if (stmt->isconstraint)
|
if (stmt->isconstraint)
|
||||||
{
|
{
|
||||||
constrname = stmt->trigname;
|
|
||||||
snprintf(constrtrigname, sizeof(constrtrigname),
|
snprintf(constrtrigname, sizeof(constrtrigname),
|
||||||
"RI_ConstraintTrigger_%u", newoid());
|
"RI_ConstraintTrigger_%u", newoid());
|
||||||
stmt->trigname = constrtrigname;
|
trigname = constrtrigname;
|
||||||
|
constrname = stmt->trigname;
|
||||||
if (stmt->constrrel != NULL)
|
|
||||||
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
|
|
||||||
else
|
|
||||||
constrrelid = InvalidOid;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trigname = stmt->trigname;
|
||||||
|
constrname = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt->constrrel != NULL)
|
||||||
|
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
|
||||||
|
else
|
||||||
|
constrrelid = InvalidOid;
|
||||||
|
|
||||||
TRIGGER_CLEAR_TYPE(tgtype);
|
TRIGGER_CLEAR_TYPE(tgtype);
|
||||||
if (stmt->before)
|
if (stmt->before)
|
||||||
@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
{
|
{
|
||||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
|
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",
|
elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
|
||||||
stmt->trigname, stmt->relation->relname);
|
trigname, stmt->relation->relname);
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
systable_endscan(tgscan);
|
systable_endscan(tgscan);
|
||||||
@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
|
|
||||||
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
|
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
|
||||||
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
|
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_tgfoid - 1] = ObjectIdGetDatum(funcoid);
|
||||||
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
|
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
|
||||||
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
|
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
|
||||||
values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
|
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_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
|
||||||
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
|
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
|
||||||
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
||||||
@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
/*
|
/*
|
||||||
* Insert tuple into pg_trigger.
|
* 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);
|
CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
|
||||||
CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
|
CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
|
||||||
CatalogCloseIndices(Num_pg_trigger_indices, idescs);
|
CatalogCloseIndices(Num_pg_trigger_indices, idescs);
|
||||||
|
|
||||||
|
myself.classId = RelationGetRelid(tgrel);
|
||||||
|
myself.objectId = trigoid;
|
||||||
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
heap_close(tgrel, RowExclusiveLock);
|
heap_close(tgrel, RowExclusiveLock);
|
||||||
|
|
||||||
@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
stmt->relation->relname);
|
stmt->relation->relname);
|
||||||
|
|
||||||
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
|
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
|
||||||
|
|
||||||
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
||||||
|
|
||||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
||||||
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
||||||
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
heap_close(pgrel, RowExclusiveLock);
|
heap_close(pgrel, RowExclusiveLock);
|
||||||
|
|
||||||
@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt)
|
|||||||
* upcoming CommandCounterIncrement...
|
* 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 */
|
/* Keep lock on target rel until end of xact */
|
||||||
heap_close(rel, NoLock);
|
heap_close(rel, NoLock);
|
||||||
|
|
||||||
|
return trigoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DropTrigger - drop an individual trigger by name
|
* DropTrigger - drop an individual trigger by name
|
||||||
*/
|
*/
|
||||||
void
|
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;
|
Relation tgrel;
|
||||||
SysScanDesc tgscan;
|
SysScanDesc tgscan;
|
||||||
ScanKeyData key;
|
ScanKeyData skey[1];
|
||||||
|
HeapTuple tup;
|
||||||
|
Oid relid;
|
||||||
|
Relation rel;
|
||||||
Relation pgrel;
|
Relation pgrel;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
Form_pg_class classForm;
|
||||||
Relation ridescs[Num_pg_class_indices];
|
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);
|
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",
|
elog(ERROR, "DropTrigger: can't drop trigger for system relation %s",
|
||||||
RelationGetRelationName(rel));
|
RelationGetRelationName(rel));
|
||||||
|
|
||||||
if (!pg_class_ownercheck(relid, GetUserId()))
|
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Search pg_trigger, delete target trigger, count remaining triggers
|
* Delete the pg_trigger tuple.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
|
simple_heap_delete(tgrel, &tup->t_self);
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
systable_endscan(tgscan);
|
||||||
heap_close(tgrel, RowExclusiveLock);
|
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
|
* Update relation's pg_class entry. Crucial side-effect: other
|
||||||
* backends (and this one too!) are sent SI message to make them
|
* backends (and this one too!) are sent SI message to make them
|
||||||
* rebuild relcache entries.
|
* 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);
|
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||||
tuple = SearchSysCacheCopy(RELOID,
|
tuple = SearchSysCacheCopy(RELOID,
|
||||||
@ -394,115 +486,27 @@ DropTrigger(Oid relid, const char *trigname)
|
|||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "DropTrigger: relation %s not found in pg_class",
|
elog(ERROR, "DropTrigger: relation %s not found in pg_class",
|
||||||
RelationGetRelationName(rel));
|
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);
|
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
||||||
|
|
||||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
||||||
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
||||||
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
|
|
||||||
heap_close(pgrel, RowExclusiveLock);
|
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);
|
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
|
* renametrig - changes the name of a trigger on a relation
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_func.h"
|
#include "parser/parse_func.h"
|
||||||
@ -262,17 +262,14 @@ DefineType(List *names, List *parameters)
|
|||||||
/*
|
/*
|
||||||
* RemoveType
|
* RemoveType
|
||||||
* Removes a datatype.
|
* Removes a datatype.
|
||||||
*
|
|
||||||
* NOTE: since this tries to remove the associated array type too, it'll
|
|
||||||
* only work on scalar types.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RemoveType(List *names, DropBehavior behavior)
|
RemoveType(List *names, DropBehavior behavior)
|
||||||
{
|
{
|
||||||
TypeName *typename;
|
TypeName *typename;
|
||||||
Relation relation;
|
|
||||||
Oid typeoid;
|
Oid typeoid;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
/* Make a TypeName so we can use standard type lookup machinery */
|
/* Make a TypeName so we can use standard type lookup machinery */
|
||||||
typename = makeNode(TypeName);
|
typename = makeNode(TypeName);
|
||||||
@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior)
|
|||||||
typename->typmod = -1;
|
typename->typmod = -1;
|
||||||
typename->arrayBounds = NIL;
|
typename->arrayBounds = NIL;
|
||||||
|
|
||||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
|
||||||
|
|
||||||
/* Use LookupTypeName here so that shell types can be removed. */
|
/* Use LookupTypeName here so that shell types can be removed. */
|
||||||
typeoid = LookupTypeName(typename);
|
typeoid = LookupTypeName(typename);
|
||||||
if (!OidIsValid(typeoid))
|
if (!OidIsValid(typeoid))
|
||||||
@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior)
|
|||||||
GetUserId()))
|
GetUserId()))
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
|
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);
|
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);
|
performDeletion(&object, behavior);
|
||||||
if (!OidIsValid(typeoid))
|
}
|
||||||
elog(ERROR, "Type \"%s\" does not exist",
|
|
||||||
TypeNameToString(typename));
|
|
||||||
|
/*
|
||||||
|
* Guts of type deletion.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RemoveTypeById(Oid typeOid)
|
||||||
|
{
|
||||||
|
Relation relation;
|
||||||
|
HeapTuple tup;
|
||||||
|
|
||||||
|
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||||
|
|
||||||
tup = SearchSysCache(TYPEOID,
|
tup = SearchSysCache(TYPEOID,
|
||||||
ObjectIdGetDatum(typeoid),
|
ObjectIdGetDatum(typeOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup))
|
if (!HeapTupleIsValid(tup))
|
||||||
elog(ERROR, "Type \"%s\" does not exist",
|
elog(ERROR, "RemoveTypeById: type %u not found",
|
||||||
TypeNameToString(typename));
|
typeOid);
|
||||||
|
|
||||||
DeleteComments(typeoid, RelationGetRelid(relation));
|
|
||||||
|
|
||||||
simple_heap_delete(relation, &tup->t_self);
|
simple_heap_delete(relation, &tup->t_self);
|
||||||
|
|
||||||
@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
HeapTuple typeTup;
|
HeapTuple typeTup;
|
||||||
List *schema = stmt->constraints;
|
List *schema = stmt->constraints;
|
||||||
List *listptr;
|
List *listptr;
|
||||||
|
Oid basetypeoid;
|
||||||
|
Form_pg_type baseType;
|
||||||
|
|
||||||
/* Convert list of names to a name and namespace */
|
/* Convert list of names to a name and namespace */
|
||||||
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
|
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
|
||||||
@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
typeTup = typenameType(stmt->typename);
|
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
|
* What we really don't want is domains of domains. This could cause all sorts
|
||||||
* of neat issues if we allow that.
|
* of neat issues if we allow that.
|
||||||
*
|
*
|
||||||
* With testing, we may determine complex types should be allowed
|
* With testing, we may determine complex types should be allowed
|
||||||
*/
|
*/
|
||||||
typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
|
typtype = baseType->typtype;
|
||||||
if (typtype != 'b')
|
if (typtype != 'b')
|
||||||
elog(ERROR, "DefineDomain: %s is not a basetype",
|
elog(ERROR, "DefineDomain: %s is not a basetype",
|
||||||
TypeNameToString(stmt->typename));
|
TypeNameToString(stmt->typename));
|
||||||
|
|
||||||
/* passed by value */
|
/* passed by value */
|
||||||
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
|
byValue = baseType->typbyval;
|
||||||
|
|
||||||
/* Required Alignment */
|
/* Required Alignment */
|
||||||
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
|
alignment = baseType->typalign;
|
||||||
|
|
||||||
/* TOAST Strategy */
|
/* TOAST Strategy */
|
||||||
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
storage = baseType->typstorage;
|
||||||
|
|
||||||
/* Storage Length */
|
/* Storage Length */
|
||||||
internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
|
internalLength = baseType->typlen;
|
||||||
|
|
||||||
/* External Length (unused) */
|
/* External Length (unused) */
|
||||||
externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
|
externalLength = baseType->typprtlen;
|
||||||
|
|
||||||
/* Array element Delimiter */
|
/* Array element Delimiter */
|
||||||
delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
|
delimiter = baseType->typdelim;
|
||||||
|
|
||||||
/* I/O Functions */
|
/* I/O Functions */
|
||||||
inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
|
inputProcedure = baseType->typinput;
|
||||||
outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
|
outputProcedure = baseType->typoutput;
|
||||||
receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
|
receiveProcedure = baseType->typreceive;
|
||||||
sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
|
sendProcedure = baseType->typsend;
|
||||||
|
|
||||||
/* Inherited default value */
|
/* Inherited default value */
|
||||||
datum = SysCacheGetAttr(TYPEOID, typeTup,
|
datum = SysCacheGetAttr(TYPEOID, typeTup,
|
||||||
@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
*
|
*
|
||||||
* This is what enables us to make a domain of an array
|
* 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
|
* Run through constraints manually to avoid the additional
|
||||||
@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
* Note: Name is strictly for error message
|
* Note: Name is strictly for error message
|
||||||
*/
|
*/
|
||||||
expr = cookDefault(pstate, colDef->raw_expr,
|
expr = cookDefault(pstate, colDef->raw_expr,
|
||||||
typeTup->t_data->t_oid,
|
basetypeoid,
|
||||||
stmt->typename->typmod,
|
stmt->typename->typmod,
|
||||||
domainName);
|
domainName);
|
||||||
/*
|
/*
|
||||||
@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
TypeCreate(domainName, /* type name */
|
TypeCreate(domainName, /* type name */
|
||||||
domainNamespace, /* namespace */
|
domainNamespace, /* namespace */
|
||||||
InvalidOid, /* preassigned type oid (not done here) */
|
InvalidOid, /* preassigned type oid (none here) */
|
||||||
InvalidOid, /* relation oid (n/a here) */
|
InvalidOid, /* relation oid (n/a here) */
|
||||||
internalLength, /* internal size */
|
internalLength, /* internal size */
|
||||||
externalLength, /* external size */
|
externalLength, /* external size */
|
||||||
@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
receiveProcedure, /* receive procedure */
|
receiveProcedure, /* receive procedure */
|
||||||
sendProcedure, /* send procedure */
|
sendProcedure, /* send procedure */
|
||||||
basetypelem, /* element type ID */
|
basetypelem, /* element type ID */
|
||||||
typeTup->t_data->t_oid, /* base type ID */
|
basetypeoid, /* base type ID */
|
||||||
defaultValue, /* default type value (text) */
|
defaultValue, /* default type value (text) */
|
||||||
defaultValueBin, /* default type value (binary) */
|
defaultValueBin, /* default type value (binary) */
|
||||||
byValue, /* passed by value */
|
byValue, /* passed by value */
|
||||||
@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
/*
|
/*
|
||||||
* RemoveDomain
|
* RemoveDomain
|
||||||
* Removes a domain.
|
* Removes a domain.
|
||||||
|
*
|
||||||
|
* This is identical to RemoveType except we insist it be a domain.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RemoveDomain(List *names, DropBehavior behavior)
|
RemoveDomain(List *names, DropBehavior behavior)
|
||||||
{
|
{
|
||||||
TypeName *typename;
|
TypeName *typename;
|
||||||
Relation relation;
|
|
||||||
Oid typeoid;
|
Oid typeoid;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
char typtype;
|
char typtype;
|
||||||
|
ObjectAddress object;
|
||||||
/* CASCADE unsupported */
|
|
||||||
if (behavior == DROP_CASCADE)
|
|
||||||
elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
|
|
||||||
|
|
||||||
/* Make a TypeName so we can use standard type lookup machinery */
|
/* Make a TypeName so we can use standard type lookup machinery */
|
||||||
typename = makeNode(TypeName);
|
typename = makeNode(TypeName);
|
||||||
@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior)
|
|||||||
typename->typmod = -1;
|
typename->typmod = -1;
|
||||||
typename->arrayBounds = NIL;
|
typename->arrayBounds = NIL;
|
||||||
|
|
||||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
/* Use LookupTypeName here so that shell types can be removed. */
|
||||||
|
typeoid = LookupTypeName(typename);
|
||||||
typeoid = typenameTypeId(typename);
|
if (!OidIsValid(typeoid))
|
||||||
|
elog(ERROR, "Type \"%s\" does not exist",
|
||||||
|
TypeNameToString(typename));
|
||||||
|
|
||||||
tup = SearchSysCache(TYPEOID,
|
tup = SearchSysCache(TYPEOID,
|
||||||
ObjectIdGetDatum(typeoid),
|
ObjectIdGetDatum(typeoid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup))
|
if (!HeapTupleIsValid(tup))
|
||||||
elog(ERROR, "RemoveDomain: type '%s' does not exist",
|
elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
|
||||||
TypeNameToString(typename));
|
TypeNameToString(typename));
|
||||||
|
|
||||||
/* Permission check: must own type or its namespace */
|
/* Permission check: must own type or its namespace */
|
||||||
@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior)
|
|||||||
elog(ERROR, "%s is not a domain",
|
elog(ERROR, "%s is not a domain",
|
||||||
TypeNameToString(typename));
|
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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse)
|
|||||||
* RemoveView
|
* RemoveView
|
||||||
*
|
*
|
||||||
* Remove a view given its name
|
* Remove a view given its name
|
||||||
|
*
|
||||||
|
* We just have to drop the relation; the associated rules will be
|
||||||
|
* cleaned up automatically.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RemoveView(const RangeVar *view, DropBehavior behavior)
|
RemoveView(const RangeVar *view, DropBehavior behavior)
|
||||||
{
|
{
|
||||||
Oid viewOid;
|
Oid viewOid;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
viewOid = RangeVarGetRelid(view, false);
|
viewOid = RangeVarGetRelid(view, false);
|
||||||
/*
|
|
||||||
* We just have to drop the relation; the associated rules will be
|
object.classId = RelOid_pg_class;
|
||||||
* cleaned up automatically.
|
object.objectId = viewOid;
|
||||||
*/
|
object.objectSubId = 0;
|
||||||
heap_drop_with_catalog(viewOid, allowSystemTableMods);
|
|
||||||
|
performDeletion(&object, behavior);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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, pktable);
|
||||||
Node_Copy(from, newnode, fk_attrs);
|
Node_Copy(from, newnode, fk_attrs);
|
||||||
Node_Copy(from, newnode, pk_attrs);
|
Node_Copy(from, newnode, pk_attrs);
|
||||||
if (from->match_type)
|
newnode->fk_matchtype = from->fk_matchtype;
|
||||||
newnode->match_type = pstrdup(from->match_type);
|
newnode->fk_upd_action = from->fk_upd_action;
|
||||||
newnode->actions = from->actions;
|
newnode->fk_del_action = from->fk_del_action;
|
||||||
newnode->deferrable = from->deferrable;
|
newnode->deferrable = from->deferrable;
|
||||||
newnode->initdeferred = from->initdeferred;
|
newnode->initdeferred = from->initdeferred;
|
||||||
|
newnode->skip_validation = from->skip_validation;
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -2089,6 +2090,7 @@ _copyIndexStmt(IndexStmt *from)
|
|||||||
Node_Copy(from, newnode, rangetable);
|
Node_Copy(from, newnode, rangetable);
|
||||||
newnode->unique = from->unique;
|
newnode->unique = from->unique;
|
||||||
newnode->primary = from->primary;
|
newnode->primary = from->primary;
|
||||||
|
newnode->isconstraint = from->isconstraint;
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
return false;
|
||||||
if (a->primary != b->primary)
|
if (a->primary != b->primary)
|
||||||
return false;
|
return false;
|
||||||
|
if (a->isconstraint != b->isconstraint)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1734,14 +1736,18 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b)
|
|||||||
return false;
|
return false;
|
||||||
if (!equal(a->pk_attrs, b->pk_attrs))
|
if (!equal(a->pk_attrs, b->pk_attrs))
|
||||||
return false;
|
return false;
|
||||||
if (!equalstr(a->match_type, b->match_type))
|
if (a->fk_matchtype != b->fk_matchtype)
|
||||||
return false;
|
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;
|
return false;
|
||||||
if (a->deferrable != b->deferrable)
|
if (a->deferrable != b->deferrable)
|
||||||
return false;
|
return false;
|
||||||
if (a->initdeferred != b->initdeferred)
|
if (a->initdeferred != b->initdeferred)
|
||||||
return false;
|
return false;
|
||||||
|
if (a->skip_validation != b->skip_validation)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||||
@ -136,9 +136,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
|
|||||||
_outNode(str, node->whereClause);
|
_outNode(str, node->whereClause);
|
||||||
appendStringInfo(str, " :rangetable ");
|
appendStringInfo(str, " :rangetable ");
|
||||||
_outNode(str, node->rangetable);
|
_outNode(str, node->rangetable);
|
||||||
appendStringInfo(str, " :unique %s :primary %s ",
|
appendStringInfo(str, " :unique %s :primary %s :isconstraint %s ",
|
||||||
booltostr(node->unique),
|
booltostr(node->unique),
|
||||||
booltostr(node->primary));
|
booltostr(node->primary),
|
||||||
|
booltostr(node->isconstraint));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1447,12 +1448,13 @@ _outFkConstraint(StringInfo str, FkConstraint *node)
|
|||||||
_outNode(str, node->fk_attrs);
|
_outNode(str, node->fk_attrs);
|
||||||
appendStringInfo(str, " :pk_attrs ");
|
appendStringInfo(str, " :pk_attrs ");
|
||||||
_outNode(str, node->pk_attrs);
|
_outNode(str, node->pk_attrs);
|
||||||
appendStringInfo(str, " :match_type ");
|
appendStringInfo(str, " :fk_matchtype %c :fk_upd_action %c :fk_del_action %c :deferrable %s :initdeferred %s :skip_validation %s",
|
||||||
_outToken(str, node->match_type);
|
node->fk_matchtype,
|
||||||
appendStringInfo(str, " :actions %d :deferrable %s :initdeferred %s",
|
node->fk_upd_action,
|
||||||
node->actions,
|
node->fk_del_action,
|
||||||
booltostr(node->deferrable),
|
booltostr(node->deferrable),
|
||||||
booltostr(node->initdeferred));
|
booltostr(node->initdeferred),
|
||||||
|
booltostr(node->skip_validation));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* State shared by transformCreateSchemaStmt and its subroutines */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
|
const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
|
||||||
char *schemaname; /* name of schema */
|
char *schemaname; /* name of schema */
|
||||||
char *authid; /* owner of schema */
|
char *authid; /* owner of schema */
|
||||||
List *tables; /* CREATE TABLE items */
|
List *tables; /* CREATE TABLE items */
|
||||||
@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
|||||||
cxt->stmtType, (cxt->relation)->relname);
|
cxt->stmtType, (cxt->relation)->relname);
|
||||||
cxt->pkey = index;
|
cxt->pkey = index;
|
||||||
}
|
}
|
||||||
|
index->isconstraint = true;
|
||||||
|
|
||||||
if (constraint->name != NULL)
|
if (constraint->name != NULL)
|
||||||
index->idxname = pstrdup(constraint->name);
|
index->idxname = pstrdup(constraint->name);
|
||||||
@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
|||||||
static void
|
static void
|
||||||
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||||
{
|
{
|
||||||
CreateTrigStmt *fk_trigger;
|
|
||||||
List *fkactions = NIL;
|
List *fkactions = NIL;
|
||||||
List *fkclist;
|
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)
|
if (cxt->fkconstraints == NIL)
|
||||||
return;
|
return;
|
||||||
@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
|||||||
foreach(fkclist, cxt->fkconstraints)
|
foreach(fkclist, cxt->fkconstraints)
|
||||||
{
|
{
|
||||||
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
|
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
|
||||||
|
Oid pktypoid[INDEX_MAX_KEYS];
|
||||||
|
Oid fktypoid[INDEX_MAX_KEYS];
|
||||||
|
int i;
|
||||||
int attnum;
|
int attnum;
|
||||||
List *fkattrs;
|
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++)
|
for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
|
||||||
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
|
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
|
||||||
|
|
||||||
@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
|
* For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
|
||||||
* action.
|
* gin up an ALTER TABLE ADD CONSTRAINT command to execute after
|
||||||
|
* the basic CREATE TABLE is complete.
|
||||||
*/
|
*/
|
||||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
id = (Ident *) lfirst(fk_attr);
|
AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
|
||||||
fk_trigger->args = lappend(fk_trigger->args,
|
|
||||||
makeString(id->name));
|
|
||||||
|
|
||||||
id = (Ident *) lfirst(pk_attr);
|
alterstmt->subtype = 'c'; /* preprocessed add constraint */
|
||||||
fk_trigger->args = lappend(fk_trigger->args,
|
alterstmt->relation = cxt->relation;
|
||||||
makeString(id->name));
|
alterstmt->name = NULL;
|
||||||
|
alterstmt->def = (Node *) makeList1(fkconstraint);
|
||||||
|
|
||||||
fk_attr = lnext(fk_attr);
|
/* Don't need to scan the table contents in this case */
|
||||||
pk_attr = lnext(pk_attr);
|
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);
|
*extras_after = nconc(cxt.alist, *extras_after);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
/*
|
||||||
|
* Already-transformed ADD CONSTRAINT, so just make it look
|
||||||
|
* like the standard case.
|
||||||
|
*/
|
||||||
|
stmt->subtype = 'C';
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -309,8 +309,7 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <node> TableConstraint, TableLikeClause
|
%type <node> TableConstraint, TableLikeClause
|
||||||
%type <list> ColQualList
|
%type <list> ColQualList
|
||||||
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
|
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
|
||||||
%type <ival> key_actions, key_delete, key_update, key_reference
|
%type <ival> key_actions, key_delete, key_match, key_update, key_action
|
||||||
%type <str> key_match
|
|
||||||
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
|
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
|
||||||
ConstraintTimeSpec
|
ConstraintTimeSpec
|
||||||
|
|
||||||
@ -1594,8 +1593,9 @@ ColConstraintElem:
|
|||||||
n->pktable = $2;
|
n->pktable = $2;
|
||||||
n->fk_attrs = NIL;
|
n->fk_attrs = NIL;
|
||||||
n->pk_attrs = $3;
|
n->pk_attrs = $3;
|
||||||
n->match_type = $4;
|
n->fk_matchtype = $4;
|
||||||
n->actions = $5;
|
n->fk_upd_action = (char) ($5 >> 8);
|
||||||
|
n->fk_del_action = (char) ($5 & 0xFF);
|
||||||
n->deferrable = FALSE;
|
n->deferrable = FALSE;
|
||||||
n->initdeferred = FALSE;
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
@ -1714,16 +1714,16 @@ ConstraintElem:
|
|||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
||||||
opt_column_list
|
opt_column_list key_match key_actions ConstraintAttributeSpec
|
||||||
key_match key_actions ConstraintAttributeSpec
|
|
||||||
{
|
{
|
||||||
FkConstraint *n = makeNode(FkConstraint);
|
FkConstraint *n = makeNode(FkConstraint);
|
||||||
n->constr_name = NULL;
|
n->constr_name = NULL;
|
||||||
n->pktable = $7;
|
n->pktable = $7;
|
||||||
n->fk_attrs = $4;
|
n->fk_attrs = $4;
|
||||||
n->pk_attrs = $8;
|
n->pk_attrs = $8;
|
||||||
n->match_type = $9;
|
n->fk_matchtype = $9;
|
||||||
n->actions = $10;
|
n->fk_upd_action = (char) ($10 >> 8);
|
||||||
|
n->fk_del_action = (char) ($10 & 0xFF);
|
||||||
n->deferrable = ($11 & 1) != 0;
|
n->deferrable = ($11 & 1) != 0;
|
||||||
n->initdeferred = ($11 & 2) != 0;
|
n->initdeferred = ($11 & 2) != 0;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
@ -1750,45 +1750,54 @@ columnElem: ColId
|
|||||||
|
|
||||||
key_match: MATCH FULL
|
key_match: MATCH FULL
|
||||||
{
|
{
|
||||||
$$ = "FULL";
|
$$ = FKCONSTR_MATCH_FULL;
|
||||||
}
|
}
|
||||||
| MATCH PARTIAL
|
| MATCH PARTIAL
|
||||||
{
|
{
|
||||||
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
|
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
|
||||||
$$ = "PARTIAL";
|
$$ = FKCONSTR_MATCH_PARTIAL;
|
||||||
}
|
}
|
||||||
| MATCH SIMPLE
|
| MATCH SIMPLE
|
||||||
{
|
{
|
||||||
$$ = "UNSPECIFIED";
|
$$ = FKCONSTR_MATCH_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
| /*EMPTY*/
|
| /*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_actions:
|
||||||
key_delete { $$ = $1; }
|
key_update
|
||||||
| key_update { $$ = $1; }
|
{ $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
|
||||||
| key_delete key_update { $$ = $1 | $2; }
|
| key_delete
|
||||||
| key_update key_delete { $$ = $1 | $2; }
|
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
|
||||||
| /*EMPTY*/ { $$ = 0; }
|
| 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
|
key_update: ON UPDATE key_action { $$ = $3; }
|
||||||
{ $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
key_update: ON UPDATE key_reference
|
key_delete: ON DELETE_P key_action { $$ = $3; }
|
||||||
{ $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
key_reference:
|
key_action:
|
||||||
NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; }
|
NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
|
||||||
| RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; }
|
| RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
|
||||||
| CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; }
|
| CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
|
||||||
| SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; }
|
| SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
|
||||||
| SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; }
|
| SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
|
||||||
;
|
;
|
||||||
|
|
||||||
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
|
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
|
||||||
@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; }
|
|||||||
| INDEX { $$ = DROP_INDEX; }
|
| INDEX { $$ = DROP_INDEX; }
|
||||||
| TYPE_P { $$ = DROP_TYPE; }
|
| TYPE_P { $$ = DROP_TYPE; }
|
||||||
| DOMAIN_P { $$ = DROP_DOMAIN; }
|
| DOMAIN_P { $$ = DROP_DOMAIN; }
|
||||||
| CONVERSION_P { $$ = DROP_CONVERSION; }
|
| CONVERSION_P { $$ = DROP_CONVERSION; }
|
||||||
;
|
;
|
||||||
|
|
||||||
any_name_list:
|
any_name_list:
|
||||||
@ -6673,6 +6682,7 @@ unreserved_keyword:
|
|||||||
| COMMIT
|
| COMMIT
|
||||||
| COMMITTED
|
| COMMITTED
|
||||||
| CONSTRAINTS
|
| CONSTRAINTS
|
||||||
|
| CONVERSION_P
|
||||||
| COPY
|
| COPY
|
||||||
| CREATEDB
|
| CREATEDB
|
||||||
| CREATEUSER
|
| CREATEUSER
|
||||||
@ -6857,6 +6867,7 @@ col_name_keyword:
|
|||||||
| SUBSTRING
|
| SUBSTRING
|
||||||
| TIME
|
| TIME
|
||||||
| TIMESTAMP
|
| TIMESTAMP
|
||||||
|
| TREAT
|
||||||
| TRIM
|
| TRIM
|
||||||
| VARCHAR
|
| VARCHAR
|
||||||
;
|
;
|
||||||
@ -6963,7 +6974,6 @@ reserved_keyword:
|
|||||||
| THEN
|
| THEN
|
||||||
| TO
|
| TO
|
||||||
| TRAILING
|
| TRAILING
|
||||||
| TREAT
|
|
||||||
| TRUE_P
|
| TRUE_P
|
||||||
| UNION
|
| UNION
|
||||||
| UNIQUE
|
| UNIQUE
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "commands/view.h"
|
#include "commands/view.h"
|
||||||
@ -57,6 +58,8 @@ InsertRule(char *rulname,
|
|||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Oid rewriteObjectId;
|
Oid rewriteObjectId;
|
||||||
|
ObjectAddress myself,
|
||||||
|
referenced;
|
||||||
|
|
||||||
if (IsDefinedRewriteRule(eventrel_oid, rulname))
|
if (IsDefinedRewriteRule(eventrel_oid, rulname))
|
||||||
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
|
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
|
||||||
@ -103,6 +106,23 @@ InsertRule(char *rulname,
|
|||||||
|
|
||||||
heap_freetuple(tup);
|
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);
|
heap_close(pg_rewrite_desc, RowExclusiveLock);
|
||||||
|
|
||||||
return rewriteObjectId;
|
return rewriteObjectId;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/genam.h"
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "commands/comment.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "rewrite/rewriteRemove.h"
|
#include "rewrite/rewriteRemove.h"
|
||||||
#include "rewrite/rewriteSupport.h"
|
#include "rewrite/rewriteSupport.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
@ -34,15 +35,62 @@
|
|||||||
* Delete a rule given its name.
|
* Delete a rule given its name.
|
||||||
*/
|
*/
|
||||||
void
|
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;
|
Relation RewriteRelation;
|
||||||
|
ScanKeyData skey[1];
|
||||||
|
SysScanDesc rcscan;
|
||||||
Relation event_relation;
|
Relation event_relation;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Oid ruleId;
|
|
||||||
Oid eventRelationOid;
|
Oid eventRelationOid;
|
||||||
bool hasMoreRules;
|
bool hasMoreRules;
|
||||||
AclResult aclresult;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the pg_rewrite relation.
|
* Open the pg_rewrite relation.
|
||||||
@ -52,24 +100,18 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
|
|||||||
/*
|
/*
|
||||||
* Find the tuple for the target rule.
|
* Find the tuple for the target rule.
|
||||||
*/
|
*/
|
||||||
tuple = SearchSysCacheCopy(RULERELNAME,
|
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||||
ObjectIdGetDatum(owningRel),
|
ObjectIdAttributeNumber, F_OIDEQ,
|
||||||
PointerGetDatum(ruleName),
|
ObjectIdGetDatum(ruleOid));
|
||||||
0, 0);
|
|
||||||
|
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true,
|
||||||
|
SnapshotNow, 1, skey);
|
||||||
|
|
||||||
|
tuple = systable_getnext(rcscan);
|
||||||
|
|
||||||
/*
|
|
||||||
* complain if no rule with such name existed
|
|
||||||
*/
|
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "Rule \"%s\" not found", ruleName);
|
elog(ERROR, "RemoveRewriteRuleById: Rule %u does not exist",
|
||||||
|
ruleOid);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We had better grab AccessExclusiveLock so that we know no other
|
* 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
|
* cannot set relhasrules correctly. Besides, we don't want to be
|
||||||
* changing the ruleset while queries are executing on the rel.
|
* changing the ruleset while queries are executing on the rel.
|
||||||
*/
|
*/
|
||||||
|
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
|
||||||
event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
|
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 &&
|
hasMoreRules = event_relation->rd_rules != NULL &&
|
||||||
event_relation->rd_rules->numLocks > 1;
|
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
|
* Now delete the pg_rewrite tuple for the rule
|
||||||
*/
|
*/
|
||||||
simple_heap_delete(RewriteRelation, &tuple->t_self);
|
simple_heap_delete(RewriteRelation, &tuple->t_self);
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
systable_endscan(rcscan);
|
||||||
|
|
||||||
heap_close(RewriteRelation, RowExclusiveLock);
|
heap_close(RewriteRelation, RowExclusiveLock);
|
||||||
|
|
||||||
@ -120,49 +146,3 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
|
|||||||
/* Close rel, but keep lock till commit... */
|
/* Close rel, but keep lock till commit... */
|
||||||
heap_close(event_relation, NoLock);
|
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);
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/catname.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "rewrite/rewriteSupport.h"
|
#include "rewrite/rewriteSupport.h"
|
||||||
|
#include "utils/inval.h"
|
||||||
#include "utils/syscache.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
|
* NOTE: an important side-effect of this operation is that an SI invalidation
|
||||||
* message is sent out to all backends --- including me --- causing relcache
|
* 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.
|
* 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
|
* This must happen even if we find that no change is needed in the pg_class
|
||||||
* already. Possible future improvement: skip the disk update and just send
|
* row.
|
||||||
* an SI message in that case.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
||||||
@ -54,6 +54,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
|||||||
{
|
{
|
||||||
Relation relationRelation;
|
Relation relationRelation;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
Form_pg_class classForm;
|
||||||
Relation idescs[Num_pg_class_indices];
|
Relation idescs[Num_pg_class_indices];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -66,18 +67,28 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
|||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId);
|
elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId);
|
||||||
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
/* Do the update */
|
if (classForm->relhasrules != relHasRules ||
|
||||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
|
(relIsBecomingView && classForm->relkind != RELKIND_VIEW))
|
||||||
if (relIsBecomingView)
|
{
|
||||||
((Form_pg_class) GETSTRUCT(tuple))->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 */
|
/* Keep the catalog indices up to date */
|
||||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
|
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
|
||||||
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
|
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
|
||||||
CatalogCloseIndices(Num_pg_class_indices, idescs);
|
CatalogCloseIndices(Num_pg_class_indices, idescs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no need to change tuple, but force relcache rebuild anyway */
|
||||||
|
CacheInvalidateRelcache(relationId);
|
||||||
|
}
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
heap_close(relationRelation, RowExclusiveLock);
|
heap_close(relationRelation, RowExclusiveLock);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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->indexParams, /* parameters */
|
||||||
stmt->unique,
|
stmt->unique,
|
||||||
stmt->primary,
|
stmt->primary,
|
||||||
|
stmt->isconstraint,
|
||||||
(Expr *) stmt->whereClause,
|
(Expr *) stmt->whereClause,
|
||||||
stmt->rangetable);
|
stmt->rangetable);
|
||||||
}
|
}
|
||||||
@ -596,19 +597,11 @@ ProcessUtility(Node *parsetree,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RemoveAggrStmt:
|
case T_RemoveAggrStmt:
|
||||||
{
|
RemoveAggregate((RemoveAggrStmt *) parsetree);
|
||||||
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
|
|
||||||
|
|
||||||
RemoveAggregate(stmt->aggname, stmt->aggtype);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RemoveFuncStmt:
|
case T_RemoveFuncStmt:
|
||||||
{
|
RemoveFunction((RemoveFuncStmt *) parsetree);
|
||||||
RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
|
|
||||||
|
|
||||||
RemoveFunction(stmt->funcname, stmt->args);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RemoveOperStmt:
|
case T_RemoveOperStmt:
|
||||||
@ -719,7 +712,7 @@ ProcessUtility(Node *parsetree,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreateTrigStmt:
|
case T_CreateTrigStmt:
|
||||||
CreateTrigger((CreateTrigStmt *) parsetree);
|
CreateTrigger((CreateTrigStmt *) parsetree, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DropPropertyStmt:
|
case T_DropPropertyStmt:
|
||||||
@ -733,11 +726,13 @@ ProcessUtility(Node *parsetree,
|
|||||||
{
|
{
|
||||||
case DROP_RULE:
|
case DROP_RULE:
|
||||||
/* RemoveRewriteRule checks permissions */
|
/* RemoveRewriteRule checks permissions */
|
||||||
RemoveRewriteRule(relId, stmt->property);
|
RemoveRewriteRule(relId, stmt->property,
|
||||||
|
stmt->behavior);
|
||||||
break;
|
break;
|
||||||
case DROP_TRIGGER:
|
case DROP_TRIGGER:
|
||||||
/* DropTrigger checks permissions */
|
/* DropTrigger checks permissions */
|
||||||
DropTrigger(relId, stmt->property);
|
DropTrigger(relId, stmt->property,
|
||||||
|
stmt->behavior);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/backend/utils/cache/lsyscache.c
vendored
25
src/backend/utils/cache/lsyscache.c
vendored
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Eventually, the index information should go through here, too.
|
* Eventually, the index information should go through here, too.
|
||||||
@ -671,6 +671,25 @@ get_relname_relid(const char *relname, Oid relnamespace)
|
|||||||
0, 0);
|
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
|
#ifdef NOT_USED
|
||||||
/*
|
/*
|
||||||
* get_relnatts
|
* get_relnatts
|
||||||
@ -1060,7 +1079,7 @@ getBaseType(Oid typid)
|
|||||||
/*
|
/*
|
||||||
* getBaseTypeTypeMod
|
* getBaseTypeTypeMod
|
||||||
* If the given type is a domain, return its base type;
|
* 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
|
Oid
|
||||||
getBaseTypeTypeMod(Oid typid, int32 *typmod)
|
getBaseTypeTypeMod(Oid typid, int32 *typmod)
|
||||||
@ -1077,7 +1096,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
|
|||||||
ObjectIdGetDatum(typid),
|
ObjectIdGetDatum(typid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup))
|
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);
|
typTup = (Form_pg_type) GETSTRUCT(tup);
|
||||||
if (typTup->typtype != 'd')
|
if (typTup->typtype != 'd')
|
||||||
{
|
{
|
||||||
|
68
src/backend/utils/cache/relcache.c
vendored
68
src/backend/utils/cache/relcache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_amproc.h"
|
||||||
#include "catalog/pg_attrdef.h"
|
#include "catalog/pg_attrdef.h"
|
||||||
#include "catalog/pg_attribute.h"
|
#include "catalog/pg_attribute.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_index.h"
|
#include "catalog/pg_index.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_relcheck.h"
|
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
@ -296,7 +296,7 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
|||||||
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
||||||
Relation oldrelation);
|
Relation oldrelation);
|
||||||
static void AttrDefaultFetch(Relation relation);
|
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 List *insert_ordered_oid(List *list, Oid datum);
|
||||||
static void IndexSupportInitialize(Form_pg_index iform,
|
static void IndexSupportInitialize(Form_pg_index iform,
|
||||||
IndexStrategy indexStrategy,
|
IndexStrategy indexStrategy,
|
||||||
@ -451,7 +451,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
|
|||||||
* RelationBuildTupleDesc
|
* RelationBuildTupleDesc
|
||||||
*
|
*
|
||||||
* Form the relation's tuple descriptor from information in
|
* 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
|
static void
|
||||||
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
||||||
@ -603,7 +603,7 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
|||||||
MemoryContextAlloc(CacheMemoryContext,
|
MemoryContextAlloc(CacheMemoryContext,
|
||||||
constr->num_check * sizeof(ConstrCheck));
|
constr->num_check * sizeof(ConstrCheck));
|
||||||
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
|
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
|
||||||
RelCheckFetch(relation);
|
CheckConstraintFetch(relation);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
constr->num_check = 0;
|
constr->num_check = 0;
|
||||||
@ -2483,62 +2483,60 @@ AttrDefaultFetch(Relation relation)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
RelCheckFetch(Relation relation)
|
CheckConstraintFetch(Relation relation)
|
||||||
{
|
{
|
||||||
ConstrCheck *check = relation->rd_att->constr->check;
|
ConstrCheck *check = relation->rd_att->constr->check;
|
||||||
int ncheck = relation->rd_att->constr->num_check;
|
int ncheck = relation->rd_att->constr->num_check;
|
||||||
Relation rcrel;
|
Relation conrel;
|
||||||
SysScanDesc rcscan;
|
SysScanDesc conscan;
|
||||||
ScanKeyData skey;
|
ScanKeyData skey[1];
|
||||||
HeapTuple htup;
|
HeapTuple htup;
|
||||||
Name rcname;
|
|
||||||
Datum val;
|
Datum val;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
int found;
|
int found = 0;
|
||||||
|
|
||||||
ScanKeyEntryInitialize(&skey,
|
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||||
(bits16) 0x0,
|
Anum_pg_constraint_conrelid, F_OIDEQ,
|
||||||
(AttrNumber) Anum_pg_relcheck_rcrelid,
|
|
||||||
(RegProcedure) F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||||
|
|
||||||
rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
|
conrel = heap_openr(ConstraintRelationName, AccessShareLock);
|
||||||
rcscan = systable_beginscan(rcrel, RelCheckIndex, true,
|
conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
|
||||||
SnapshotNow,
|
SnapshotNow, 1, skey);
|
||||||
1, &skey);
|
|
||||||
found = 0;
|
|
||||||
|
|
||||||
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)
|
if (found == ncheck)
|
||||||
elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
|
elog(ERROR, "CheckConstraintFetch: unexpected record found for rel %s",
|
||||||
RelationGetRelationName(relation));
|
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,
|
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
|
||||||
NameStr(*rcname));
|
NameStr(conform->conname));
|
||||||
|
|
||||||
|
/* Grab and test conbin is actually set */
|
||||||
val = fastgetattr(htup,
|
val = fastgetattr(htup,
|
||||||
Anum_pg_relcheck_rcbin,
|
Anum_pg_constraint_conbin,
|
||||||
rcrel->rd_att, &isnull);
|
conrel->rd_att, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
|
elog(ERROR, "CheckConstraintFetch: conbin IS NULL for rel %s",
|
||||||
RelationGetRelationName(relation));
|
RelationGetRelationName(relation));
|
||||||
|
|
||||||
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
|
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
|
||||||
DatumGetCString(DirectFunctionCall1(textout,
|
DatumGetCString(DirectFunctionCall1(textout,
|
||||||
val)));
|
val)));
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(rcscan);
|
systable_endscan(conscan);
|
||||||
heap_close(rcrel, AccessShareLock);
|
heap_close(conrel, AccessShareLock);
|
||||||
|
|
||||||
if (found != ncheck)
|
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));
|
ncheck - found, RelationGetRelationName(relation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
# Portions Copyright (c) 1994, Regents of the University of California
|
# 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
|
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
|
||||||
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
|
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
|
||||||
|
ALTER TABLE pg_constraint CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_database CREATE TOAST TABLE;
|
ALTER TABLE pg_database CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_description CREATE TOAST TABLE;
|
ALTER TABLE pg_description CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_group CREATE TOAST TABLE;
|
ALTER TABLE pg_group CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_proc 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_rewrite CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_shadow CREATE TOAST TABLE;
|
ALTER TABLE pg_shadow CREATE TOAST TABLE;
|
||||||
ALTER TABLE pg_statistic CREATE TOAST TABLE;
|
ALTER TABLE pg_statistic CREATE TOAST TABLE;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
if (tbinfo->ncheck > 0)
|
||||||
{
|
{
|
||||||
PGresult *res2;
|
PGresult *res2;
|
||||||
int i_rcname,
|
int i_conname,
|
||||||
i_rcsrc;
|
i_consrc;
|
||||||
int ntups2;
|
int ntups2;
|
||||||
|
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
@ -4666,24 +4666,25 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
|
|||||||
|
|
||||||
resetPQExpBuffer(query);
|
resetPQExpBuffer(query);
|
||||||
if (g_fout->remoteVersion >= 70300)
|
if (g_fout->remoteVersion >= 70300)
|
||||||
appendPQExpBuffer(query, "SELECT rcname, rcsrc"
|
appendPQExpBuffer(query, "SELECT conname, consrc"
|
||||||
" from pg_catalog.pg_relcheck c1"
|
" from pg_catalog.pg_constraint c1"
|
||||||
" where rcrelid = '%s'::pg_catalog.oid "
|
" where conrelid = '%s'::pg_catalog.oid "
|
||||||
|
" and contype = 'c' "
|
||||||
" and not exists "
|
" and not exists "
|
||||||
" (select 1 from "
|
" (select 1 from "
|
||||||
" pg_catalog.pg_relcheck c2, "
|
" pg_catalog.pg_constraint c2, "
|
||||||
" pg_catalog.pg_inherits i "
|
" pg_catalog.pg_inherits i "
|
||||||
" where i.inhrelid = c1.rcrelid "
|
" where i.inhrelid = c1.conrelid "
|
||||||
" and (c2.rcname = c1.rcname "
|
" and (c2.conname = c1.conname "
|
||||||
" or (c2.rcname[0] = '$' "
|
" or (c2.conname[0] = '$' "
|
||||||
" and c1.rcname[0] = '$')"
|
" and c1.conname[0] = '$')"
|
||||||
" )"
|
" )"
|
||||||
" and c2.rcsrc = c1.rcsrc "
|
" and c2.consrc = c1.consrc "
|
||||||
" and c2.rcrelid = i.inhparent) "
|
" and c2.conrelid = i.inhparent) "
|
||||||
" order by rcname ",
|
" order by conname ",
|
||||||
tbinfo->oid);
|
tbinfo->oid);
|
||||||
else
|
else
|
||||||
appendPQExpBuffer(query, "SELECT rcname, rcsrc"
|
appendPQExpBuffer(query, "SELECT rcname as conname, rcsrc as consrc"
|
||||||
" from pg_relcheck c1"
|
" from pg_relcheck c1"
|
||||||
" where rcrelid = '%s'::oid "
|
" where rcrelid = '%s'::oid "
|
||||||
" and not exists "
|
" and not exists "
|
||||||
@ -4714,13 +4715,13 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
|
|||||||
exit_nicely();
|
exit_nicely();
|
||||||
}
|
}
|
||||||
|
|
||||||
i_rcname = PQfnumber(res2, "rcname");
|
i_conname = PQfnumber(res2, "conname");
|
||||||
i_rcsrc = PQfnumber(res2, "rcsrc");
|
i_consrc = PQfnumber(res2, "consrc");
|
||||||
|
|
||||||
for (j = 0; j < ntups2; j++)
|
for (j = 0; j < ntups2; j++)
|
||||||
{
|
{
|
||||||
const char *name = PQgetvalue(res2, j, i_rcname);
|
const char *name = PQgetvalue(res2, j, i_conname);
|
||||||
const char *expr = PQgetvalue(res2, j, i_rcsrc);
|
const char *expr = PQgetvalue(res2, j, i_consrc);
|
||||||
|
|
||||||
if (actual_atts + j > 0)
|
if (actual_atts + j > 0)
|
||||||
appendPQExpBuffer(q, ",\n\t");
|
appendPQExpBuffer(q, ",\n\t");
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2000 by PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "describe.h"
|
#include "describe.h"
|
||||||
@ -747,7 +747,7 @@ describeTableDetails(const char *name, bool desc)
|
|||||||
*result3 = NULL,
|
*result3 = NULL,
|
||||||
*result4 = NULL;
|
*result4 = NULL;
|
||||||
int index_count = 0,
|
int index_count = 0,
|
||||||
constr_count = 0,
|
check_count = 0,
|
||||||
rule_count = 0,
|
rule_count = 0,
|
||||||
trigger_count = 0;
|
trigger_count = 0;
|
||||||
int count_footers = 0;
|
int count_footers = 0;
|
||||||
@ -770,19 +770,20 @@ describeTableDetails(const char *name, bool desc)
|
|||||||
index_count = PQntuples(result1);
|
index_count = PQntuples(result1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* count table (and column) constraints */
|
/* count table (and column) check constraints */
|
||||||
if (tableinfo.checks)
|
if (tableinfo.checks)
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT rcsrc, rcname\n"
|
"SELECT consrc, conname\n"
|
||||||
"FROM pg_relcheck r, pg_class c\n"
|
"FROM pg_constraint r, pg_class c\n"
|
||||||
"WHERE c.relname='%s' AND c.oid = r.rcrelid",
|
"WHERE c.relname='%s' AND c.oid = r.conrelid\n"
|
||||||
|
"AND r.contype = 'c'",
|
||||||
name);
|
name);
|
||||||
result2 = PSQLexec(buf.data);
|
result2 = PSQLexec(buf.data);
|
||||||
if (!result2)
|
if (!result2)
|
||||||
goto error_return;
|
goto error_return;
|
||||||
else
|
else
|
||||||
constr_count = PQntuples(result2);
|
check_count = PQntuples(result2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* count rules */
|
/* count rules */
|
||||||
@ -815,7 +816,7 @@ describeTableDetails(const char *name, bool desc)
|
|||||||
trigger_count = PQntuples(result4);
|
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));
|
* sizeof(*footers));
|
||||||
|
|
||||||
/* print indexes */
|
/* print indexes */
|
||||||
@ -846,8 +847,8 @@ describeTableDetails(const char *name, bool desc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* print constraints */
|
/* print check constraints */
|
||||||
for (i = 0; i < constr_count; i++)
|
for (i = 0; i < check_count; i++)
|
||||||
{
|
{
|
||||||
char *s = _("Check constraints");
|
char *s = _("Check constraints");
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* 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
|
typedef struct tupleDesc
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 AccessMethodOperatorRelationName "pg_amop"
|
||||||
#define AccessMethodProcedureRelationName "pg_amproc"
|
#define AccessMethodProcedureRelationName "pg_amproc"
|
||||||
#define AttributeRelationName "pg_attribute"
|
#define AttributeRelationName "pg_attribute"
|
||||||
|
#define ConstraintRelationName "pg_constraint"
|
||||||
#define ConversionRelationName "pg_conversion"
|
#define ConversionRelationName "pg_conversion"
|
||||||
#define DatabaseRelationName "pg_database"
|
#define DatabaseRelationName "pg_database"
|
||||||
|
#define DependRelationName "pg_depend"
|
||||||
#define DescriptionRelationName "pg_description"
|
#define DescriptionRelationName "pg_description"
|
||||||
#define GroupRelationName "pg_group"
|
#define GroupRelationName "pg_group"
|
||||||
#define IndexRelationName "pg_index"
|
#define IndexRelationName "pg_index"
|
||||||
@ -40,7 +42,6 @@
|
|||||||
#define TypeRelationName "pg_type"
|
#define TypeRelationName "pg_type"
|
||||||
#define VersionRelationName "pg_version"
|
#define VersionRelationName "pg_version"
|
||||||
#define AttrDefaultRelationName "pg_attrdef"
|
#define AttrDefaultRelationName "pg_attrdef"
|
||||||
#define RelCheckRelationName "pg_relcheck"
|
|
||||||
#define TriggerRelationName "pg_trigger"
|
#define TriggerRelationName "pg_trigger"
|
||||||
|
|
||||||
#endif /* CATNAME_H */
|
#endif /* CATNAME_H */
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200207111
|
#define CATALOG_VERSION_NO 200207112
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
92
src/include/catalog/dependency.h
Normal file
92
src/include/catalog/dependency.h
Normal 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 */
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 relhasoids,
|
||||||
bool allow_system_table_mods);
|
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);
|
extern void heap_truncate(Oid rid);
|
||||||
|
|
||||||
@ -58,7 +58,8 @@ extern Node *cookDefault(ParseState *pstate,
|
|||||||
int32 atttypmod,
|
int32 atttypmod,
|
||||||
char *attname);
|
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,
|
extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
|
||||||
bool relhasoids);
|
bool relhasoids);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 accessMethodObjectId,
|
||||||
Oid *classObjectId,
|
Oid *classObjectId,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool isconstraint,
|
||||||
bool allow_system_table_mods);
|
bool allow_system_table_mods);
|
||||||
|
|
||||||
extern void index_drop(Oid indexId);
|
extern void index_drop(Oid indexId);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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_attr_indices 2
|
||||||
#define Num_pg_attrdef_indices 1
|
#define Num_pg_attrdef_indices 1
|
||||||
#define Num_pg_class_indices 2
|
#define Num_pg_class_indices 2
|
||||||
|
#define Num_pg_constraint_indices 3
|
||||||
#define Num_pg_conversion_indices 2
|
#define Num_pg_conversion_indices 2
|
||||||
#define Num_pg_database_indices 2
|
#define Num_pg_database_indices 2
|
||||||
|
#define Num_pg_depend_indices 2
|
||||||
#define Num_pg_description_indices 1
|
#define Num_pg_description_indices 1
|
||||||
#define Num_pg_group_indices 2
|
#define Num_pg_group_indices 2
|
||||||
#define Num_pg_index_indices 2
|
#define Num_pg_index_indices 2
|
||||||
@ -39,7 +41,6 @@
|
|||||||
#define Num_pg_opclass_indices 2
|
#define Num_pg_opclass_indices 2
|
||||||
#define Num_pg_operator_indices 2
|
#define Num_pg_operator_indices 2
|
||||||
#define Num_pg_proc_indices 2
|
#define Num_pg_proc_indices 2
|
||||||
#define Num_pg_relcheck_indices 1
|
|
||||||
#define Num_pg_rewrite_indices 2
|
#define Num_pg_rewrite_indices 2
|
||||||
#define Num_pg_shadow_indices 2
|
#define Num_pg_shadow_indices 2
|
||||||
#define Num_pg_statistic_indices 1
|
#define Num_pg_statistic_indices 1
|
||||||
@ -60,10 +61,15 @@
|
|||||||
#define AttributeRelidNumIndex "pg_attribute_relid_attnum_index"
|
#define AttributeRelidNumIndex "pg_attribute_relid_attnum_index"
|
||||||
#define ClassNameNspIndex "pg_class_relname_nsp_index"
|
#define ClassNameNspIndex "pg_class_relname_nsp_index"
|
||||||
#define ClassOidIndex "pg_class_oid_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 ConversionDefaultIndex "pg_conversion_default_index"
|
||||||
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
|
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
|
||||||
#define DatabaseNameIndex "pg_database_datname_index"
|
#define DatabaseNameIndex "pg_database_datname_index"
|
||||||
#define DatabaseOidIndex "pg_database_oid_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 DescriptionObjIndex "pg_description_o_c_o_index"
|
||||||
#define GroupNameIndex "pg_group_name_index"
|
#define GroupNameIndex "pg_group_name_index"
|
||||||
#define GroupSysidIndex "pg_group_sysid_index"
|
#define GroupSysidIndex "pg_group_sysid_index"
|
||||||
@ -81,7 +87,6 @@
|
|||||||
#define OperatorOidIndex "pg_operator_oid_index"
|
#define OperatorOidIndex "pg_operator_oid_index"
|
||||||
#define ProcedureNameNspIndex "pg_proc_proname_args_nsp_index"
|
#define ProcedureNameNspIndex "pg_proc_proname_args_nsp_index"
|
||||||
#define ProcedureOidIndex "pg_proc_oid_index"
|
#define ProcedureOidIndex "pg_proc_oid_index"
|
||||||
#define RelCheckIndex "pg_relcheck_rcrelid_index"
|
|
||||||
#define RewriteOidIndex "pg_rewrite_oid_index"
|
#define RewriteOidIndex "pg_rewrite_oid_index"
|
||||||
#define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index"
|
#define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index"
|
||||||
#define ShadowNameIndex "pg_shadow_usename_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_attr_indices[];
|
||||||
extern char *Name_pg_attrdef_indices[];
|
extern char *Name_pg_attrdef_indices[];
|
||||||
extern char *Name_pg_class_indices[];
|
extern char *Name_pg_class_indices[];
|
||||||
|
extern char *Name_pg_constraint_indices[];
|
||||||
extern char *Name_pg_conversion_indices[];
|
extern char *Name_pg_conversion_indices[];
|
||||||
extern char *Name_pg_database_indices[];
|
extern char *Name_pg_database_indices[];
|
||||||
|
extern char *Name_pg_depend_indices[];
|
||||||
extern char *Name_pg_description_indices[];
|
extern char *Name_pg_description_indices[];
|
||||||
extern char *Name_pg_group_indices[];
|
extern char *Name_pg_group_indices[];
|
||||||
extern char *Name_pg_index_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_opclass_indices[];
|
||||||
extern char *Name_pg_operator_indices[];
|
extern char *Name_pg_operator_indices[];
|
||||||
extern char *Name_pg_proc_indices[];
|
extern char *Name_pg_proc_indices[];
|
||||||
extern char *Name_pg_relcheck_indices[];
|
|
||||||
extern char *Name_pg_rewrite_indices[];
|
extern char *Name_pg_rewrite_indices[];
|
||||||
extern char *Name_pg_shadow_indices[];
|
extern char *Name_pg_shadow_indices[];
|
||||||
extern char *Name_pg_statistic_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_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));
|
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 */
|
/* 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_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_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_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));
|
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_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_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));
|
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_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));
|
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 */
|
/* 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_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_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));
|
DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops));
|
||||||
|
172
src/include/catalog/pg_constraint.h
Normal file
172
src/include/catalog/pg_constraint.h
Normal 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 */
|
93
src/include/catalog/pg_depend.h
Normal file
93
src/include/catalog/pg_depend.h
Normal 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 */
|
@ -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 */
|
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
extern void CommentObject(CommentStmt *stmt);
|
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);
|
extern void CreateComments(Oid oid, Oid classoid, int32 subid, char *comment);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,
|
List *attributeList,
|
||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool isconstraint,
|
||||||
Expr *predicate,
|
Expr *predicate,
|
||||||
List *rangetable);
|
List *rangetable);
|
||||||
extern void RemoveIndex(RangeVar *relation, DropBehavior behavior);
|
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 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 DefineOperator(List *names, List *parameters);
|
||||||
extern void RemoveOperator(RemoveOperStmt *stmt);
|
extern void RemoveOperator(RemoveOperStmt *stmt);
|
||||||
|
extern void RemoveOperatorById(Oid operOid);
|
||||||
|
|
||||||
extern void DefineAggregate(List *names, List *parameters);
|
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 DefineType(List *names, List *parameters);
|
||||||
extern void RemoveType(List *names, DropBehavior behavior);
|
extern void RemoveType(List *names, DropBehavior behavior);
|
||||||
|
extern void RemoveTypeById(Oid typeOid);
|
||||||
extern void DefineDomain(CreateDomainStmt *stmt);
|
extern void DefineDomain(CreateDomainStmt *stmt);
|
||||||
extern void RemoveDomain(List *names, DropBehavior behavior);
|
extern void RemoveDomain(List *names, DropBehavior behavior);
|
||||||
|
|
||||||
|
@ -13,5 +13,6 @@
|
|||||||
|
|
||||||
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
|
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
|
||||||
extern void DropProceduralLanguage(DropPLangStmt *stmt);
|
extern void DropProceduralLanguage(DropPLangStmt *stmt);
|
||||||
|
extern void DropProceduralLanguageById(Oid langOid);
|
||||||
|
|
||||||
#endif /* PROCLANG_H */
|
#endif /* PROCLANG_H */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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))
|
#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
|
||||||
|
|
||||||
|
|
||||||
extern void CreateTrigger(CreateTrigStmt *stmt);
|
extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint);
|
||||||
extern void DropTrigger(Oid relid, const char *trigname);
|
|
||||||
extern void RelationRemoveTriggers(Relation rel);
|
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);
|
extern void renametrig(Oid relid, const char *oldname, const char *newname);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* M = alter column storage
|
||||||
* D = drop column
|
* D = drop column
|
||||||
* C = add constraint
|
* C = add constraint
|
||||||
|
* c = pre-processed add constraint
|
||||||
|
* (local in parser/analyze.c)
|
||||||
* X = drop constraint
|
* X = drop constraint
|
||||||
* E = create toast table
|
* E = create toast table
|
||||||
* U = change owner
|
* U = change owner
|
||||||
@ -929,31 +931,41 @@ typedef struct Constraint
|
|||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Definitions for FOREIGN KEY constraints in CreateStmt
|
* 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_ACTION_NOACTION 'a'
|
||||||
#define FKCONSTR_ON_KEY_RESTRICT 0x0001
|
#define FKCONSTR_ACTION_RESTRICT 'r'
|
||||||
#define FKCONSTR_ON_KEY_CASCADE 0x0002
|
#define FKCONSTR_ACTION_CASCADE 'c'
|
||||||
#define FKCONSTR_ON_KEY_SETNULL 0x0004
|
#define FKCONSTR_ACTION_SETNULL 'n'
|
||||||
#define FKCONSTR_ON_KEY_SETDEFAULT 0x0008
|
#define FKCONSTR_ACTION_SETDEFAULT 'd'
|
||||||
|
|
||||||
#define FKCONSTR_ON_DELETE_MASK 0x000F
|
#define FKCONSTR_MATCH_FULL 'f'
|
||||||
#define FKCONSTR_ON_DELETE_SHIFT 0
|
#define FKCONSTR_MATCH_PARTIAL 'p'
|
||||||
|
#define FKCONSTR_MATCH_UNSPECIFIED 'u'
|
||||||
#define FKCONSTR_ON_UPDATE_MASK 0x00F0
|
|
||||||
#define FKCONSTR_ON_UPDATE_SHIFT 4
|
|
||||||
|
|
||||||
typedef struct FkConstraint
|
typedef struct FkConstraint
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *constr_name; /* Constraint name */
|
char *constr_name; /* Constraint name, or NULL if unnamed */
|
||||||
RangeVar *pktable; /* Primary key table */
|
RangeVar *pktable; /* Primary key table */
|
||||||
List *fk_attrs; /* Attributes of foreign key */
|
List *fk_attrs; /* Attributes of foreign key */
|
||||||
List *pk_attrs; /* Corresponding attrs in PK table */
|
List *pk_attrs; /* Corresponding attrs in PK table */
|
||||||
char *match_type; /* FULL or PARTIAL */
|
char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */
|
||||||
int32 actions; /* ON DELETE/UPDATE actions */
|
char fk_upd_action; /* ON UPDATE action */
|
||||||
|
char fk_del_action; /* ON DELETE action */
|
||||||
bool deferrable; /* DEFERRABLE */
|
bool deferrable; /* DEFERRABLE */
|
||||||
bool initdeferred; /* INITIALLY DEFERRED */
|
bool initdeferred; /* INITIALLY DEFERRED */
|
||||||
|
bool skip_validation; /* skip validation of existing rows? */
|
||||||
} FkConstraint;
|
} FkConstraint;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
@ -1201,6 +1213,7 @@ typedef struct IndexStmt
|
|||||||
* transformStmt() */
|
* transformStmt() */
|
||||||
bool unique; /* is index unique? */
|
bool unique; /* is index unique? */
|
||||||
bool primary; /* is index on primary key? */
|
bool primary; /* is index on primary key? */
|
||||||
|
bool isconstraint; /* is it from a CONSTRAINT clause? */
|
||||||
} IndexStmt;
|
} IndexStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
|
@ -7,14 +7,18 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
#ifndef REWRITEREMOVE_H
|
||||||
#define REWRITEREMOVE_H
|
#define REWRITEREMOVE_H
|
||||||
|
|
||||||
extern void RemoveRewriteRule(Oid owningRel, const char *ruleName);
|
#include "nodes/parsenodes.h"
|
||||||
extern void RelationRemoveRules(Oid relid);
|
|
||||||
|
|
||||||
|
extern void RemoveRewriteRule(Oid owningRel, const char *ruleName,
|
||||||
|
DropBehavior behavior);
|
||||||
|
extern void RemoveRewriteRuleById(Oid ruleOid);
|
||||||
|
|
||||||
#endif /* REWRITEREMOVE_H */
|
#endif /* REWRITEREMOVE_H */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 bool get_func_retset(Oid funcid);
|
||||||
extern char func_volatile(Oid funcid);
|
extern char func_volatile(Oid funcid);
|
||||||
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
|
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 char *get_rel_name(Oid relid);
|
||||||
extern Oid get_rel_namespace(Oid relid);
|
extern Oid get_rel_namespace(Oid relid);
|
||||||
extern Oid get_rel_type_id(Oid relid);
|
extern Oid get_rel_type_id(Oid relid);
|
||||||
|
@ -340,8 +340,6 @@ ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not fo
|
|||||||
DROP TABLE tmp5;
|
DROP TABLE tmp5;
|
||||||
DROP TABLE tmp4;
|
DROP TABLE tmp4;
|
||||||
DROP TABLE tmp3;
|
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;
|
DROP TABLE tmp2;
|
||||||
-- Foreign key adding test with mixed types
|
-- Foreign key adding test with mixed types
|
||||||
-- Note: these tables are TEMP to avoid name conflicts when this test
|
-- 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
|
-- As should this
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
||||||
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
DROP TABLE pktable;
|
DROP TABLE pktable cascade;
|
||||||
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
|
NOTICE: Drop cascades to constraint $2 on table fktable
|
||||||
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
|
NOTICE: Drop cascades to constraint $1 on table fktable
|
||||||
DROP TABLE fktable;
|
DROP TABLE fktable;
|
||||||
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
|
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
|
||||||
PRIMARY KEY(ptest1, ptest2));
|
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)
|
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
|
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
|
||||||
You will have to retype this query using an explicit cast
|
You will have to retype this query using an explicit cast
|
||||||
-- Again, so should this...
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
-- Again, so should this...
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
||||||
references pktable(ptest1, ptest2);
|
references pktable(ptest1, ptest2);
|
||||||
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
|
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
|
||||||
You will have to retype this query using an explicit cast
|
You will have to retype this query using an explicit cast
|
||||||
-- This fails because we mixed up the column ordering
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
-- This fails because we mixed up the column ordering
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
|
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
||||||
references pktable(ptest2, ptest1);
|
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);
|
alter table atacc1 add check (test2>test);
|
||||||
-- should fail for $2
|
-- should fail for $2
|
||||||
insert into atacc1 (test2, test) values (3, 4);
|
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;
|
drop table atacc1;
|
||||||
-- inheritance related tests
|
-- inheritance related tests
|
||||||
create table atacc1 (test int);
|
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'
|
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1'
|
||||||
alter table atacc1 alter column test drop not null;
|
alter table atacc1 alter column test drop not null;
|
||||||
ERROR: ALTER TABLE: Attribute "test" is in a primary key
|
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;
|
alter table atacc1 alter column test drop not null;
|
||||||
insert into atacc1 values (null);
|
insert into atacc1 values (null);
|
||||||
alter table atacc1 alter test set not null;
|
alter table atacc1 alter test set not null;
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
-- Test Comment / Drop
|
-- Test Comment / Drop
|
||||||
create domain domaindroptest int4;
|
create domain domaindroptest int4;
|
||||||
comment on domain domaindroptest is 'About to drop this..';
|
comment on domain domaindroptest is 'About to drop this..';
|
||||||
|
-- currently this will be disallowed
|
||||||
create domain basetypetest domaindroptest;
|
create domain basetypetest domaindroptest;
|
||||||
ERROR: DefineDomain: domaindroptest is not a basetype
|
ERROR: DefineDomain: domaindroptest is not a basetype
|
||||||
drop domain domaindroptest cascade;
|
|
||||||
ERROR: DROP DOMAIN does not support the CASCADE keyword
|
|
||||||
drop domain domaindroptest;
|
drop domain domaindroptest;
|
||||||
|
-- this should fail because already gone
|
||||||
|
drop domain domaindroptest cascade;
|
||||||
|
ERROR: Type "domaindroptest" does not exist
|
||||||
-- TEST Domains.
|
-- TEST Domains.
|
||||||
create domain domainvarchar varchar(5);
|
create domain domainvarchar varchar(5);
|
||||||
create domain domainnumeric numeric(8,2);
|
create domain domainnumeric numeric(8,2);
|
||||||
|
@ -22,7 +22,7 @@ INSERT INTO FKTABLE VALUES (3, 4);
|
|||||||
INSERT INTO FKTABLE VALUES (NULL, 1);
|
INSERT INTO FKTABLE VALUES (NULL, 1);
|
||||||
-- Insert a failed row into FK TABLE
|
-- Insert a failed row into FK TABLE
|
||||||
INSERT INTO FKTABLE VALUES (100, 2);
|
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
|
-- Check FKTABLE
|
||||||
SELECT * FROM FKTABLE;
|
SELECT * FROM FKTABLE;
|
||||||
ftest1 | ftest2
|
ftest1 | ftest2
|
||||||
@ -55,9 +55,8 @@ SELECT * FROM FKTABLE;
|
|||||||
1 | 3
|
1 | 3
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
|
||||||
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
DROP TABLE PKTABLE;
|
||||||
--
|
--
|
||||||
-- check set NULL and table constraint on multiple columns
|
-- check set NULL and table constraint on multiple columns
|
||||||
--
|
--
|
||||||
@ -138,8 +137,8 @@ SELECT * FROM FKTABLE;
|
|||||||
| | 8
|
| | 8
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
DROP TABLE PKTABLE CASCADE;
|
||||||
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
|
NOTICE: Drop cascades to constraint constrname on table fktable
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
--
|
--
|
||||||
-- check set default and table constraint on multiple columns
|
-- check set default and table constraint on multiple columns
|
||||||
@ -223,8 +222,13 @@ SELECT * FROM FKTABLE;
|
|||||||
-1 | -2 | 8
|
-1 | -2 | 8
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
|
-- this should fail for lack of CASCADE
|
||||||
DROP TABLE PKTABLE;
|
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;
|
DROP TABLE FKTABLE;
|
||||||
--
|
--
|
||||||
-- First test, check with no on delete or on update
|
-- 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 INTO FKTABLE VALUES (NULL, 1);
|
||||||
-- Insert a failed row into FK TABLE
|
-- Insert a failed row into FK TABLE
|
||||||
INSERT INTO FKTABLE VALUES (100, 2);
|
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
|
-- Check FKTABLE
|
||||||
SELECT * FROM FKTABLE;
|
SELECT * FROM FKTABLE;
|
||||||
ftest1 | ftest2
|
ftest1 | ftest2
|
||||||
@ -270,7 +274,7 @@ SELECT * FROM PKTABLE;
|
|||||||
|
|
||||||
-- Delete a row from PK TABLE (should fail)
|
-- Delete a row from PK TABLE (should fail)
|
||||||
DELETE FROM PKTABLE WHERE ptest1=1;
|
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 a row from PK TABLE (should succeed)
|
||||||
DELETE FROM PKTABLE WHERE ptest1=5;
|
DELETE FROM PKTABLE WHERE ptest1=5;
|
||||||
-- Check PKTABLE for deletes
|
-- Check PKTABLE for deletes
|
||||||
@ -285,7 +289,7 @@ SELECT * FROM PKTABLE;
|
|||||||
|
|
||||||
-- Update a row from PK TABLE (should fail)
|
-- Update a row from PK TABLE (should fail)
|
||||||
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2;
|
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 a row from PK TABLE (should succeed)
|
||||||
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
|
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
|
||||||
-- Check PKTABLE for updates
|
-- Check PKTABLE for updates
|
||||||
@ -298,9 +302,8 @@ SELECT * FROM PKTABLE;
|
|||||||
0 | Test4
|
0 | Test4
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
|
||||||
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
DROP TABLE PKTABLE;
|
||||||
-- MATCH unspecified
|
-- MATCH unspecified
|
||||||
-- Base test restricting update/delete
|
-- Base test restricting update/delete
|
||||||
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
|
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)
|
(5 rows)
|
||||||
|
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
-- cascade update/delete
|
-- cascade update/delete
|
||||||
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
|
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)
|
(4 rows)
|
||||||
|
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
-- set null update / set default delete
|
-- set null update / set default delete
|
||||||
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
|
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)
|
(6 rows)
|
||||||
|
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
-- set default update / set null delete
|
-- set default update / set null delete
|
||||||
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
|
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)
|
(7 rows)
|
||||||
|
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
||||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
|
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);
|
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
|
||||||
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
DROP TABLE FKTABLE;
|
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
|
-- As should this
|
||||||
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
|
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
|
||||||
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
-- Two columns, two tables
|
-- Two columns, two tables
|
||||||
CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2));
|
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));
|
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)
|
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
DROP TABLE FKTABLE;
|
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
|
-- As does this
|
||||||
CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2));
|
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)
|
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||||
DROP TABLE FKTABLE;
|
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;
|
||||||
-- Two columns, same table
|
-- Two columns, same table
|
||||||
-- Make sure this still works...
|
-- Make sure this still works...
|
||||||
@ -832,25 +819,23 @@ insert into pktable(base1) values (1);
|
|||||||
insert into pktable(base1) values (2);
|
insert into pktable(base1) values (2);
|
||||||
-- let's insert a non-existant fktable value
|
-- let's insert a non-existant fktable value
|
||||||
insert into fktable(ftest1) values (3);
|
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
|
-- let's make a valid row for that
|
||||||
insert into pktable(base1) values (3);
|
insert into pktable(base1) values (3);
|
||||||
insert into fktable(ftest1) values (3);
|
insert into fktable(ftest1) values (3);
|
||||||
-- let's try removing a row that should fail from pktable
|
-- let's try removing a row that should fail from pktable
|
||||||
delete from pktable where base1>2;
|
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
|
-- okay, let's try updating all of the base1 values to *4
|
||||||
-- which should fail.
|
-- which should fail.
|
||||||
update pktable set base1=base1*4;
|
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.
|
-- okay, let's try an update that should work.
|
||||||
update pktable set base1=base1*4 where base1<3;
|
update pktable set base1=base1*4 where base1<3;
|
||||||
-- and a delete that should work
|
-- and a delete that should work
|
||||||
delete from pktable where base1>3;
|
delete from pktable where base1>3;
|
||||||
-- cleanup
|
-- cleanup
|
||||||
drop table fktable;
|
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;
|
delete from pktable;
|
||||||
-- Now 2 columns 2 tables, matching types
|
-- Now 2 columns 2 tables, matching types
|
||||||
create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1));
|
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);
|
insert into pktable(base1, ptest1) values (2, 2);
|
||||||
-- let's insert a non-existant fktable value
|
-- let's insert a non-existant fktable value
|
||||||
insert into fktable(ftest1, ftest2) values (3, 1);
|
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
|
-- let's make a valid row for that
|
||||||
insert into pktable(base1,ptest1) values (3, 1);
|
insert into pktable(base1,ptest1) values (3, 1);
|
||||||
insert into fktable(ftest1, ftest2) values (3, 1);
|
insert into fktable(ftest1, ftest2) values (3, 1);
|
||||||
-- let's try removing a row that should fail from pktable
|
-- let's try removing a row that should fail from pktable
|
||||||
delete from pktable where base1>2;
|
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
|
-- okay, let's try updating all of the base1 values to *4
|
||||||
-- which should fail.
|
-- which should fail.
|
||||||
update pktable set base1=base1*4;
|
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.
|
-- okay, let's try an update that should work.
|
||||||
update pktable set base1=base1*4 where base1<3;
|
update pktable set base1=base1*4 where base1<3;
|
||||||
-- and a delete that should work
|
-- and a delete that should work
|
||||||
delete from pktable where base1>3;
|
delete from pktable where base1>3;
|
||||||
-- cleanup
|
-- cleanup
|
||||||
drop table fktable;
|
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;
|
||||||
drop table pktable_base;
|
drop table pktable_base;
|
||||||
-- Now we'll do one all in 1 table with 2 columns of matching types
|
-- 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);
|
insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2);
|
||||||
-- fails (3,2) isn't in base1, ptest1
|
-- fails (3,2) isn't in base1, ptest1
|
||||||
insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2);
|
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
|
-- fails (2,2) is being referenced
|
||||||
delete from pktable where base1=2;
|
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)
|
-- fails (1,1) is being referenced (twice)
|
||||||
update pktable set base1=3 where base1=1;
|
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
|
-- 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 base2=2;
|
||||||
delete from pktable where base1=2;
|
delete from pktable where base1=2;
|
||||||
|
@ -38,8 +38,10 @@ SELECT relname, relhasindex
|
|||||||
pg_attrdef | t
|
pg_attrdef | t
|
||||||
pg_attribute | t
|
pg_attribute | t
|
||||||
pg_class | t
|
pg_class | t
|
||||||
|
pg_constraint | t
|
||||||
pg_conversion | t
|
pg_conversion | t
|
||||||
pg_database | t
|
pg_database | t
|
||||||
|
pg_depend | t
|
||||||
pg_description | t
|
pg_description | t
|
||||||
pg_group | t
|
pg_group | t
|
||||||
pg_index | t
|
pg_index | t
|
||||||
@ -50,7 +52,6 @@ SELECT relname, relhasindex
|
|||||||
pg_opclass | t
|
pg_opclass | t
|
||||||
pg_operator | t
|
pg_operator | t
|
||||||
pg_proc | t
|
pg_proc | t
|
||||||
pg_relcheck | t
|
|
||||||
pg_rewrite | t
|
pg_rewrite | t
|
||||||
pg_shadow | t
|
pg_shadow | t
|
||||||
pg_statistic | t
|
pg_statistic | t
|
||||||
@ -61,5 +62,5 @@ SELECT relname, relhasindex
|
|||||||
shighway | t
|
shighway | t
|
||||||
tenk1 | t
|
tenk1 | t
|
||||||
tenk2 | t
|
tenk2 | t
|
||||||
(51 rows)
|
(52 rows)
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ INSERT INTO INSERT_TBL(y) VALUES ('Y');
|
|||||||
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
||||||
INSERT INTO INSERT_TBL(y) VALUES ('Y');
|
INSERT INTO INSERT_TBL(y) VALUES ('Y');
|
||||||
INSERT INTO INSERT_TBL(x,z) VALUES (1, -2);
|
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(z,x) VALUES (-7, 7);
|
||||||
INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5);
|
INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5);
|
||||||
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
||||||
@ -139,7 +139,7 @@ SELECT '' AS four, * FROM INSERT_TBL;
|
|||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4);
|
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');
|
INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed');
|
||||||
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
|
||||||
INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed');
|
INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed');
|
||||||
|
@ -250,7 +250,7 @@ CREATE TEMP TABLE FKTABLE (ftest1 varchar);
|
|||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
||||||
-- As should this
|
-- As should this
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
||||||
DROP TABLE pktable;
|
DROP TABLE pktable cascade;
|
||||||
DROP TABLE fktable;
|
DROP TABLE fktable;
|
||||||
|
|
||||||
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
|
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
|
-- This should fail, because we just chose really odd types
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
|
||||||
-- Again, so should this...
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
-- Again, so should this...
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
||||||
references pktable(ptest1, ptest2);
|
references pktable(ptest1, ptest2);
|
||||||
-- This fails because we mixed up the column ordering
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
-- This fails because we mixed up the column ordering
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
|
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
|
||||||
references pktable(ptest2, ptest1);
|
references pktable(ptest2, ptest1);
|
||||||
@ -486,7 +486,7 @@ alter table foo alter column bar drop not null;
|
|||||||
create table atacc1 (test int not null);
|
create table atacc1 (test int not null);
|
||||||
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
|
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
|
||||||
alter table atacc1 alter column test drop not null;
|
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;
|
alter table atacc1 alter column test drop not null;
|
||||||
insert into atacc1 values (null);
|
insert into atacc1 values (null);
|
||||||
alter table atacc1 alter test set not null;
|
alter table atacc1 alter test set not null;
|
||||||
|
@ -4,11 +4,14 @@
|
|||||||
create domain domaindroptest int4;
|
create domain domaindroptest int4;
|
||||||
comment on domain domaindroptest is 'About to drop this..';
|
comment on domain domaindroptest is 'About to drop this..';
|
||||||
|
|
||||||
|
-- currently this will be disallowed
|
||||||
create domain basetypetest domaindroptest;
|
create domain basetypetest domaindroptest;
|
||||||
|
|
||||||
drop domain domaindroptest cascade;
|
|
||||||
drop domain domaindroptest;
|
drop domain domaindroptest;
|
||||||
|
|
||||||
|
-- this should fail because already gone
|
||||||
|
drop domain domaindroptest cascade;
|
||||||
|
|
||||||
|
|
||||||
-- TEST Domains.
|
-- TEST Domains.
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
|
|||||||
-- Check FKTABLE for update of matched row
|
-- Check FKTABLE for update of matched row
|
||||||
SELECT * FROM FKTABLE;
|
SELECT * FROM FKTABLE;
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
DROP TABLE PKTABLE;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- check set NULL and table constraint on multiple columns
|
-- 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
|
-- Check FKTABLE for update of matched row
|
||||||
SELECT * FROM FKTABLE;
|
SELECT * FROM FKTABLE;
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
DROP TABLE PKTABLE CASCADE;
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -147,7 +147,9 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
|
|||||||
-- Check FKTABLE for update of matched row
|
-- Check FKTABLE for update of matched row
|
||||||
SELECT * FROM FKTABLE;
|
SELECT * FROM FKTABLE;
|
||||||
|
|
||||||
|
-- this should fail for lack of CASCADE
|
||||||
DROP TABLE PKTABLE;
|
DROP TABLE PKTABLE;
|
||||||
|
DROP TABLE PKTABLE CASCADE;
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
|
||||||
|
|
||||||
@ -197,8 +199,8 @@ UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
|
|||||||
-- Check PKTABLE for updates
|
-- Check PKTABLE for updates
|
||||||
SELECT * FROM PKTABLE;
|
SELECT * FROM PKTABLE;
|
||||||
|
|
||||||
DROP TABLE PKTABLE;
|
|
||||||
DROP TABLE FKTABLE;
|
DROP TABLE FKTABLE;
|
||||||
|
DROP TABLE PKTABLE;
|
||||||
|
|
||||||
|
|
||||||
-- MATCH unspecified
|
-- MATCH unspecified
|
||||||
|
Loading…
x
Reference in New Issue
Block a user