diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index c3ed19e4b2a7d31de1d089399e5110918fa42fc2..602680aaacb2019d3346bf62d50a2ac43935d657 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ <!-- Documentation of the system catalogs, directed toward PostgreSQL developers - $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.44 2002/06/20 15:44:06 momjian Exp $ + $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.45 2002/07/12 18:43:12 tgl Exp $ --> <chapter id="catalogs"> @@ -71,11 +71,21 @@ <entry>tables, indexes, sequences (<quote>relations</quote>)</entry> </row> + <row> + <entry>pg_constraint</entry> + <entry>check constraints, unique / primary key constraints, foreign key constraints</entry> + </row> + <row> <entry>pg_database</entry> <entry>databases within this database cluster</entry> </row> + <row> + <entry>pg_depend</entry> + <entry>dependencies between database objects</entry> + </row> + <row> <entry>pg_description</entry> <entry>descriptions or comments on database objects</entry> @@ -131,11 +141,6 @@ <entry>functions and procedures</entry> </row> - <row> - <entry>pg_relcheck</entry> - <entry>check constraints</entry> - </row> - <row> <entry>pg_rewrite</entry> <entry>query rewriter rules</entry> @@ -680,7 +685,7 @@ <entry></entry> <entry> Number of check constraints on the table; see - <structname>pg_relcheck</structname> catalog + <structname>pg_constraint</structname> catalog </entry> </row> @@ -764,6 +769,167 @@ </table> </sect1> + <sect1 id="catalog-pg-constraint"> + <title>pg_constraint</title> + + <para> + This system catalog stores CHECK, PRIMARY KEY, UNIQUE, and FOREIGN KEY + constraints on tables. (Column + constraints are not treated specially. Every column constraint is + equivalent to some table constraint.) See under <command>CREATE + TABLE</command> for more information. + </para> + + <note> + <para> + NOT NULL constraints are represented in the <structname>pg_attribute</> + catalog. + </para> + </note> + + <para> + CHECK constraints on domains are stored here, too. Global ASSERTIONS + (a currently-unsupported SQL feature) may someday appear here as well. + </para> + + <table> + <title>pg_constraint Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry>conname</entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Constraint name (not necessarily unique!)</entry> + </row> + + <row> + <entry>connamespace</entry> + <entry><type>oid</type></entry> + <entry>pg_namespace.oid</entry> + <entry> + The OID of the namespace that contains this constraint + </entry> + </row> + + <row> + <entry>contype</entry> + <entry><type>char</type></entry> + <entry></entry> + <entry> + 'c' = check constraint, + 'f' = foreign key constraint, + 'p' = primary key constraint, + 'u' = unique constraint + </entry> + </row> + + <row> + <entry>condeferrable</entry> + <entry><type>boolean</type></entry> + <entry></entry> + <entry>Is the constraint deferrable?</entry> + </row> + + <row> + <entry>condeferred</entry> + <entry><type>boolean</type></entry> + <entry></entry> + <entry>Is the constraint deferred by default?</entry> + </row> + + <row> + <entry>conrelid</entry> + <entry><type>oid</type></entry> + <entry>pg_class.oid</entry> + <entry>The table this constraint is on; 0 if not a table constraint</entry> + </row> + + <row> + <entry>contypid</entry> + <entry><type>oid</type></entry> + <entry>pg_type.oid</entry> + <entry>The domain this constraint is on; 0 if not a domain constraint</entry> + </row> + + <row> + <entry>confrelid</entry> + <entry><type>oid</type></entry> + <entry>pg_class.oid</entry> + <entry>If a foreign key, the referenced table; else 0</entry> + </row> + + <row> + <entry>confupdtype</entry> + <entry><type>char</type></entry> + <entry></entry> + <entry>Foreign key update action code</entry> + </row> + + <row> + <entry>confdeltype</entry> + <entry><type>char</type></entry> + <entry></entry> + <entry>Foreign key deletion action code</entry> + </row> + + <row> + <entry>confmatchtype</entry> + <entry><type>char</type></entry> + <entry></entry> + <entry>Foreign key match type</entry> + </row> + + <row> + <entry>conkey</entry> + <entry><type>smallint[]</type></entry> + <entry>pg_attribute.attnum</entry> + <entry>If a table constraint, list of columns which the constraint constrains</entry> + </row> + + <row> + <entry>confkey</entry> + <entry><type>smallint[]</type></entry> + <entry>pg_attribute.attnum</entry> + <entry>If a foreign key, list of the referenced columns</entry> + </row> + + <row> + <entry>conbin</entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>If a check constraint, an internal representation of the expression</entry> + </row> + + <row> + <entry>consrc</entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>If a check constraint, a human-readable representation of the expression</entry> + </row> + </tbody> + </tgroup> + </table> + + <note> + <para> + <structname>pg_class</structname>.<structfield>relchecks</structfield> + needs to agree with the number of check-constraint entries found in this + table for the given relation. + </para> + </note> + + </sect1> <sect1 id="catalog-pg-database"> <title>pg_database</title> @@ -903,6 +1069,151 @@ </sect1> + <sect1 id="catalog-pg-depend"> + <title>pg_depend</title> + + <para> + The <structname>pg_depend</structname> table records the dependency + relationships between database objects. This information allows + <command>DROP</> commands to find which other objects must be dropped + by <command>DROP CASCADE</>, or prevent dropping in the <command>DROP + RESTRICT</> case. + </para> + + <table> + <title>pg_depend Columns</title> + + <tgroup cols=4> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry>classid</entry> + <entry><type>oid</type></entry> + <entry>pg_class.oid</entry> + <entry>The oid of the system catalog the dependent object is in</entry> + </row> + + <row> + <entry>objid</entry> + <entry><type>oid</type></entry> + <entry>any oid attribute</entry> + <entry>The oid of the specific dependent object</entry> + </row> + + <row> + <entry>objsubid</entry> + <entry><type>integer</type></entry> + <entry></entry> + <entry>For a table attribute, this is the attribute's + column number (the objid and classid refer to the table itself). + For all other object types, this field is presently zero. + </entry> + </row> + + <row> + <entry>refclassid</entry> + <entry><type>oid</type></entry> + <entry>pg_class.oid</entry> + <entry>The oid of the system catalog the referenced object is in</entry> + </row> + + <row> + <entry>refobjid</entry> + <entry><type>oid</type></entry> + <entry>any oid attribute</entry> + <entry>The oid of the specific referenced object</entry> + </row> + + <row> + <entry>refobjsubid</entry> + <entry><type>integer</type></entry> + <entry></entry> + <entry>For a table attribute, this is the attribute's + column number (the objid and classid refer to the table itself). + For all other object types, this field is presently zero. + </entry> + </row> + + <row> + <entry>deptype</entry> + <entry><type>char</type></entry> + <entry></entry> + <entry> + A code defining the specific semantics of this dependency relationship. + </entry> + </row> + + </tbody> + </tgroup> + </table> + + <para> + In all cases, a <structname>pg_depend</structname> entry indicates that the + referenced object may not be dropped without also dropping the dependent + object. However, there are several subflavors identified by + <structfield>deptype</>: + + <itemizedlist> + <listitem> + <para> + DEPENDENCY_NORMAL ('n'): normal relationship between separately-created + objects. The dependent object may be dropped without affecting the + referenced object. The referenced object may only be dropped by + specifying CASCADE, in which case the dependent object is dropped too. + Example: a table column has a normal dependency on its datatype. + </para> + </listitem> + + <listitem> + <para> + DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately + from the referenced object, and should be automatically dropped + (regardless of RESTRICT or CASCADE mode) if the referenced object + is dropped. + Example: a named constraint on a table is made auto-dependent on + the table, so that it will go away if the table is dropped. + </para> + </listitem> + + <listitem> + <para> + DEPENDENCY_INTERNAL ('i'): the dependent object was created as part + of creation of the referenced object, and is really just a part of + its internal implementation. A DROP of the dependent object will be + disallowed outright (we'll tell the user to issue a DROP against the + referenced object, instead). A DROP of the referenced object will be + propagated through to drop the dependent object whether CASCADE is + specified or not. + Example: a trigger that's created to enforce a foreign-key constraint + is made internally dependent on the constraint's pg_constraint entry. + </para> + </listitem> + + <listitem> + <para> + DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry + is a signal that the system itself depends on the referenced object, + and so that object must never be deleted. Entries of this type are + created only during initdb. The fields for the dependent object + contain zeroes. + </para> + </listitem> + </itemizedlist> + + Other dependency flavors may be needed in future. + </para> + + </sect1> + + <sect1 id="catalog-pg-description"> <title>pg_description</title> @@ -1866,72 +2177,6 @@ </sect1> - - <sect1 id="catalog-pg-relcheck"> - <title>pg_relcheck</title> - - <para> - This system catalog stores CHECK constraints on tables. (Column - constraints are not treated specially. Every column constraint is - equivalent to some table constraint.) See under <command>CREATE - TABLE</command> for more information. - </para> - - <table> - <title>pg_relcheck Columns</title> - - <tgroup cols=4> - <thead> - <row> - <entry>Name</entry> - <entry>Type</entry> - <entry>References</entry> - <entry>Description</entry> - </row> - </thead> - - <tbody> - <row> - <entry>rcrelid</entry> - <entry><type>oid</type></entry> - <entry>pg_class.oid</entry> - <entry>The table this check constraint is on</entry> - </row> - - <row> - <entry>rcname</entry> - <entry><type>name</type></entry> - <entry></entry> - <entry>Constraint name</entry> - </row> - - <row> - <entry>rcbin</entry> - <entry><type>text</type></entry> - <entry></entry> - <entry>An internal representation of the constraint expression</entry> - </row> - - <row> - <entry>rcsrc</entry> - <entry><type>text</type></entry> - <entry></entry> - <entry>A human-readable representation of the constraint expression</entry> - </row> - </tbody> - </tgroup> - </table> - - <note> - <para> - <structname>pg_class</structname>.<structfield>relchecks</structfield> - needs to match up with the entries in this table. - </para> - </note> - - </sect1> - - <sect1 id="catalog-pg-rewrite"> <title>pg_rewrite</title> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 4966eb757b8c527da568b9892ef5c781f987d124..9acd474203cce3384b25972de8bbdebfabe0b796 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.45 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.46 2002/07/12 18:43:12 tgl Exp $ PostgreSQL documentation --> @@ -39,7 +39,7 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable> ALTER TABLE <replaceable class="PARAMETER">table</replaceable> ADD <replaceable class="PARAMETER">table_constraint_definition</replaceable> ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> - DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> { RESTRICT | CASCADE } + DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ] ALTER TABLE <replaceable class="PARAMETER">table</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> </synopsis> @@ -315,26 +315,6 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable> form after you've entered non-null values for the column in all rows. </para> - <para> - In DROP CONSTRAINT, the RESTRICT keyword is required, although - dependencies are not yet checked. The CASCADE option is unsupported. - Currently DROP CONSTRAINT only handles CHECK constraints. - To remove a PRIMARY or UNIQUE constraint, drop the - relevant index using the <xref linkend="SQL-DROPINDEX" endterm="sql-dropindex-title"> command. - To remove FOREIGN KEY constraints you need to recreate - and reload the table, using other parameters to the - <xref linkend="SQL-CREATETABLE" endterm="sql-createtable-title"> command. - </para> - <para> - For example, to drop all constraints on a table <literal>distributors</literal>: - <programlisting> -CREATE TABLE temp AS SELECT * FROM distributors; -DROP TABLE distributors; -CREATE TABLE distributors AS SELECT * FROM temp; -DROP TABLE temp; - </programlisting> - </para> - <para> Changing any part of the schema of a system catalog is not permitted. @@ -395,7 +375,7 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); <para> To remove a check constraint from a table and all its children: <programlisting> -ALTER TABLE distributors DROP CONSTRAINT zipchk RESTRICT; +ALTER TABLE distributors DROP CONSTRAINT zipchk; </programlisting> </para> diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index 3d4041ba8eead3f8ebc7c0959c6017fae037189a..ea173e1741a28c47a5ea647d0921be915bb2c5ab 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.19 2002/05/13 17:45:30 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.20 2002/07/12 18:43:12 tgl Exp $ PostgreSQL documentation --> @@ -26,6 +26,7 @@ COMMENT ON TABLE <replaceable class="PARAMETER">object_name</replaceable> | COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> | AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable>) | + CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> | DATABASE <replaceable class="PARAMETER">object_name</replaceable> | DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1_type</replaceable>, <replaceable class="PARAMETER">arg2_type</replaceable>, ...) | @@ -52,7 +53,7 @@ COMMENT ON <variablelist> <varlistentry> <term><replaceable class="PARAMETER">object_name, - table_name.column_name, agg_name, func_name, op, rule_name, trigger_name</replaceable></term> + table_name.column_name, agg_name, constraint_name, func_name, op, rule_name, trigger_name</replaceable></term> <listitem> <para> The name of the object to be be commented. Names of tables, diff --git a/doc/src/sgml/ref/drop_aggregate.sgml b/doc/src/sgml/ref/drop_aggregate.sgml index 857a7f10f48c7fb16b82a984de1e34f274813ab5..9913e5d8ed363cc30da2c1e9fc8310cb4ecd1c4b 100644 --- a/doc/src/sgml/ref/drop_aggregate.sgml +++ b/doc/src/sgml/ref/drop_aggregate.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.18 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.19 2002/07/12 18:43:12 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> ) +DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">type</replaceable> ) [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPAGGREGATE-1"> @@ -54,6 +54,23 @@ DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the aggregate. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the aggregate if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml index 5bc73a065fab389c2436f6c0d5d13556cb3ed728..b39e249302111ed49f2f8f188e054c45e15a6741 100644 --- a/doc/src/sgml/ref/drop_domain.sgml +++ b/doc/src/sgml/ref/drop_domain.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_domain.sgml,v 1.6 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_domain.sgml,v 1.7 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -48,8 +48,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C <term><literal>CASCADE</></term> <listitem> <para> - Automatically drop objects that depend on the domain. This - behavior is not currently supported. + Automatically drop objects that depend on the domain + (such as table columns). </para> </listitem> </varlistentry> @@ -58,7 +58,8 @@ DROP DOMAIN <replaceable class="PARAMETER">domainname</replaceable> [, ...] [ C <term><literal>RESTRICT</></term> <listitem> <para> - Do not drop dependent objects. This is the default. + Refuse to drop the domain if there are any dependent objects. + This is the default. </para> </listitem> </varlistentry> @@ -143,19 +144,14 @@ DROP DOMAIN box; <refsect1 id="SQL-DROPDOMAIN-compatibility"> <title>Compatibility</title> + + <refsect2 id="R2-SQL-DROPDOMAIN-sql92"> + <title> + SQL92 + </title> - <para> - A <command>DROP DOMAIN</command> statement exists in SQL99. As with - most other <quote>drop</quote> commands, <command>DROP - DOMAIN</command> in SQL99 requires a <quote>drop behavior</quote> - clause to select between dropping all dependent objects or refusing - to drop if dependent objects exist: -<synopsis> -DROP DOMAIN <replaceable>name</replaceable> { CASCADE | RESTRICT } -</synopsis> - <productname>PostgreSQL</productname> accepts only the RESTRICT - option, and currently does not check for existence of dependent objects. - </para> + <para></para> + </refsect2> </refsect1> <refsect1 id="SQL-DROPDOMAIN-see-also"> diff --git a/doc/src/sgml/ref/drop_function.sgml b/doc/src/sgml/ref/drop_function.sgml index 268d8c66e8c60d67d529be55d365ceca9dfebfa7..5aa1b141badd767642995c87fba0a7466634b416 100644 --- a/doc/src/sgml/ref/drop_function.sgml +++ b/doc/src/sgml/ref/drop_function.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.20 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.21 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) +DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPFUNCTION-1"> @@ -49,6 +49,24 @@ DROP FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceable </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the function + (such as operators or triggers). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the function if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> @@ -136,15 +154,8 @@ DROP FUNCTION sqrt(integer); <title>Compatibility</title> <para> - A <command>DROP FUNCTION</command> statement is defined in SQL99. One of its syntax forms is: - -<synopsis> -DROP FUNCTION <replaceable class="parameter">name</replaceable> (<replaceable>arg</>, ...) { RESTRICT | CASCADE } -</synopsis> - - where <literal>CASCADE</> specifies dropping all objects that - depend on the function and <literal>RESTRICT</literal> refuses to - drop the function if dependent objects exist. + A <command>DROP FUNCTION</command> statement is defined in SQL99. One of + its syntax forms is similar to PostgreSQL's. </para> </refsect1> diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml index 0db2c6094613c6676df662757f72a7ab9f6a633d..149afff0779fd15121f4ac9609bc818c03ba626a 100644 --- a/doc/src/sgml/ref/drop_index.sgml +++ b/doc/src/sgml/ref/drop_index.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_index.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_index.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...] +DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...] [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPINDEX-1"> @@ -41,6 +41,23 @@ DROP INDEX <replaceable class="PARAMETER">index_name</replaceable> [, ...] </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the index. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the index if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> diff --git a/doc/src/sgml/ref/drop_language.sgml b/doc/src/sgml/ref/drop_language.sgml index 08badab80832ae78602e61e6c9d90cf8b091e42e..e0690a88ac69c30b36015076d9cc46a358109018 100644 --- a/doc/src/sgml/ref/drop_language.sgml +++ b/doc/src/sgml/ref/drop_language.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_language.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_language.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable> +DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable> [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPLANGUAGE-1"> @@ -43,7 +43,26 @@ DROP [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">name</replaceable> </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the language + (such as functions in the language). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the language if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> + </para> </refsect2> @@ -112,14 +131,6 @@ ERROR: Language "<replaceable class="parameter">name</replaceable>" doesn't exis <xref linkend="sql-createlanguage" endterm="sql-createlanguage-title"> for information on how to create procedural languages. </para> - - <para> - No checks are made if functions or trigger procedures registered - in this language still exist. To re-enable them without having - to drop and recreate all the functions, the pg_proc's prolang - attribute of the functions must be adjusted to the new object - ID of the recreated pg_language entry for the PL. - </para> </refsect2> </refsect1> diff --git a/doc/src/sgml/ref/drop_operator.sgml b/doc/src/sgml/ref/drop_operator.sgml index bc4657038fdbb42058e4ef0812a6217a3349e14a..1cfb824090c89b1f3e35ac1bbd46ad959693d4c0 100644 --- a/doc/src/sgml/ref/drop_operator.sgml +++ b/doc/src/sgml/ref/drop_operator.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_operator.sgml,v 1.16 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_operator.sgml,v 1.17 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -22,7 +22,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable class="PARAMETER">lefttype</replaceable> | NONE , <replaceable class="PARAMETER">righttype</replaceable> | NONE ) +DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable class="PARAMETER">lefttype</replaceable> | NONE , <replaceable class="PARAMETER">righttype</replaceable> | NONE ) [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPOPERATOR-1"> @@ -60,6 +60,23 @@ DROP OPERATOR <replaceable class="PARAMETER">id</replaceable> ( <replaceable cla </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the operator. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the operator if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> diff --git a/doc/src/sgml/ref/drop_rule.sgml b/doc/src/sgml/ref/drop_rule.sgml index 4f8ea97f88d324287cc3eaa668af759e5275638a..afb1b6a874bf954d161a4a414159dead3419188d 100644 --- a/doc/src/sgml/ref/drop_rule.sgml +++ b/doc/src/sgml/ref/drop_rule.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_rule.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_rule.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1998-09-22</date> </refsynopsisdivinfo> <synopsis> -DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">relation</replaceable> +DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">relation</replaceable> [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPRULE-1"> @@ -50,7 +50,25 @@ DROP RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable clas </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the rule. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the rule if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> + </para> </refsect2> diff --git a/doc/src/sgml/ref/drop_sequence.sgml b/doc/src/sgml/ref/drop_sequence.sgml index 4c371164e1e9f784f441197bf2157835d9f8373a..50623849342cce2eec68727550b7c6016a9a7f2a 100644 --- a/doc/src/sgml/ref/drop_sequence.sgml +++ b/doc/src/sgml/ref/drop_sequence.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,8 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...] +DROP SEQUENCE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ] + </synopsis> <refsect2 id="R2-SQL-DROPSEQUENCE-1"> @@ -75,7 +76,25 @@ ERROR: sequence "<replaceable class="parameter">name</replaceable>" does not exi </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the sequence. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the sequence if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> + </para> </refsect2> </refsynopsisdiv> diff --git a/doc/src/sgml/ref/drop_table.sgml b/doc/src/sgml/ref/drop_table.sgml index cae3275067d4785da8dd9eff5aa5eaf4e0f5eae2..23148ff827d54bd41b5b93b16901a7713943297f 100644 --- a/doc/src/sgml/ref/drop_table.sgml +++ b/doc/src/sgml/ref/drop_table.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_table.sgml,v 1.15 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_table.sgml,v 1.16 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,8 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...] +DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ] + </synopsis> <refsect2 id="R2-SQL-DROPTABLE-1"> @@ -41,6 +42,24 @@ DROP TABLE <replaceable class="PARAMETER">name</replaceable> [, ...] </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the table + (such as views). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the table if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> @@ -136,44 +155,11 @@ DROP TABLE films, distributors; </title> <refsect2 id="R2-SQL-DROPTABLE-4"> - <refsect2info> - <date>1998-09-22</date> - </refsect2info> <title> SQL92 </title> <para> - SQL92 specifies some additional capabilities for DROP TABLE: </para> - <synopsis> -DROP TABLE <replaceable class="parameter">table</replaceable> { RESTRICT | CASCADE } - </synopsis> - <variablelist> - <varlistentry> - <term>RESTRICT</term> - <listitem> - <para> - Ensures that only a table with no dependent views or - integrity constraints can be destroyed. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>CASCADE</term> - <listitem> - <para> - Any referencing views or integrity constraints - will also be dropped. - </para> - </listitem> - </varlistentry> - </variablelist> - <tip> - <para> - At present, to remove a referencing view you must drop - it explicitly. - </para> - </tip> </refsect2> </refsect1> </refentry> diff --git a/doc/src/sgml/ref/drop_trigger.sgml b/doc/src/sgml/ref/drop_trigger.sgml index e4f157b1d7c0d8c23ed9c32145a80c38b25f5e91..c154d8117db751170a879a0b1fcfd6f5260e63a9 100644 --- a/doc/src/sgml/ref/drop_trigger.sgml +++ b/doc/src/sgml/ref/drop_trigger.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_trigger.sgml,v 1.12 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_trigger.sgml,v 1.13 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1998-09-22</date> </refsynopsisdivinfo> <synopsis> -DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table</replaceable> +DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table</replaceable> [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPTRIGGER-1"> @@ -50,6 +50,23 @@ DROP TRIGGER <replaceable class="PARAMETER">name</replaceable> ON <replaceable c </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the trigger. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the trigger if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml index 3ac0f17ae9fee5f6f630add5ca91161afb9a9140..81cf010bb00e6d6f2ad2e3ec4a88b3f263ef7af6 100644 --- a/doc/src/sgml/ref/drop_type.sgml +++ b/doc/src/sgml/ref/drop_type.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_type.sgml,v 1.17 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_type.sgml,v 1.18 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,8 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...] +DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...] [ CASCADE | RESTRICT ] + </synopsis> <refsect2 id="R2-SQL-DROPTYPE-1"> @@ -41,6 +42,24 @@ DROP TYPE <replaceable class="PARAMETER">typename</replaceable> [, ...] </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the type + (such as table columns, functions, operators, etc). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the type if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> </para> </refsect2> @@ -75,6 +94,7 @@ ERROR: RemoveType: type '<replaceable class="parameter">typename</replaceable>' </listitem> </varlistentry> </variablelist> + </para> </refsect2> </refsynopsisdiv> @@ -132,19 +152,6 @@ DROP TYPE box; <refsect1 id="SQL-DROPTYPE-compatibility"> <title>Compatibility</title> - <para> - A <command>DROP TYPE</command> statement exists in SQL99. As with - most other <quote>drop</quote> commands, <command>DROP - TYPE</command> in SQL99 requires a <quote>drop behavior</quote> - clause to select between dropping all dependent objects or refusing - to drop if dependent objects exist: -<synopsis> -DROP TYPE <replaceable>name</replaceable> { CASCADE | RESTRICT } -</synopsis> - <productname>PostgreSQL</productname> currently ignores - dependencies altogether. - </para> - <para> Note that the <command>CREATE TYPE</command> command and the data type extension mechanisms in <productname>PostgreSQL</productname> diff --git a/doc/src/sgml/ref/drop_view.sgml b/doc/src/sgml/ref/drop_view.sgml index a789e38c4ce1c2ce07ba3c5a6c61a3eb8da96cbf..52711d984ef5e3a8371161f193033d7ea5ff11a6 100644 --- a/doc/src/sgml/ref/drop_view.sgml +++ b/doc/src/sgml/ref/drop_view.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_view.sgml,v 1.14 2002/05/18 15:44:47 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_view.sgml,v 1.15 2002/07/12 18:43:13 tgl Exp $ PostgreSQL documentation --> @@ -21,7 +21,7 @@ PostgreSQL documentation <date>1999-07-20</date> </refsynopsisdivinfo> <synopsis> -DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...] +DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ] </synopsis> <refsect2 id="R2-SQL-DROPVIEW-1"> @@ -42,7 +42,26 @@ DROP VIEW <replaceable class="PARAMETER">name</replaceable> [, ...] </para> </listitem> </varlistentry> + <varlistentry> + <term>CASCADE</term> + <listitem> + <para> + Automatically drop objects that depend on the view + (such as other views). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>RESTRICT</term> + <listitem> + <para> + Refuse to drop the view if there are any dependent objects. + This is the default. + </para> + </listitem> + </varlistentry> </variablelist> + </para> </refsect2> @@ -134,58 +153,7 @@ DROP VIEW kinds; SQL92 </title> <para> - <acronym>SQL92</acronym> specifies some additional capabilities for - <command>DROP VIEW</command>: - - <synopsis> -DROP VIEW <replaceable class="parameter">view</replaceable> { RESTRICT | CASCADE } - </synopsis> </para> - - <refsect3 id="R3-SQL-DROPVIEW-1"> - <refsect3info> - <date>1998-09-22</date> - </refsect3info> - <title> - Inputs - </title> - <para> - <variablelist> - <varlistentry> - <term>RESTRICT</term> - <listitem> - <para> - Ensures that only a view with no dependent views or - integrity constraints can be destroyed. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>CASCADE</term> - <listitem> - <para> - Any referencing views and integrity constraints - will be dropped as well. - </para> - </listitem> - </varlistentry> - </variablelist> - </para> - </refsect3> - - <refsect3 id="R3-SQL-DROPVIEW-2"> - <refsect3info> - <date>1998-09-22</date> - </refsect3info> - <title> - Notes - </title> - <para> - At present, to remove a referencing view from a - <productname>PostgreSQL</productname> database, - you must drop it explicitly. - </para> - </refsect3> </refsect2> </refsect1> </refentry> diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index 9cd74fae205ed68057c9eb24f02477cc54fb659c..7fd8c92e371b85bd131d6376f7d9993bb5291694 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.139 2002/06/11 15:32:33 thomas Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.140 2002/07/12 18:43:12 tgl Exp $ --> <appendix id="release"> @@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without worries about funny characters. --> <literallayout><![CDATA[ +Most forms of DROP now support RESTRICT and CASCADE options Recursive SQL functions can be defined User-defined procedural languages can register a validator function to check new functions as they are created Functions can be executed with the privileges of the owner diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 3039f49725cd841e8738844ad0a139fc4a9f2ccc..705a2b1fe80e98544f7d40f0d3b08cafcb0b5f7f 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.48 2002/06/20 20:29:25 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.49 2002/07/12 18:43:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -240,7 +240,8 @@ Boot_DeclareIndexStmt: DefineIndex(makeRangeVar(NULL, LexIDStr($5)), LexIDStr($3), LexIDStr($7), - $9, false, false, NULL, NIL); + $9, + false, false, false, NULL, NIL); do_end(); } ; @@ -253,7 +254,8 @@ Boot_DeclareUniqueIndexStmt: DefineIndex(makeRangeVar(NULL, LexIDStr($6)), LexIDStr($4), LexIDStr($8), - $10, true, false, NULL, NIL); + $10, + true, false, false, NULL, NIL); do_end(); } ; diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 0d021964b7a48a2dc3e8813e023e4914c4d1ee78..22e033524eeffe232e104533bac37837056026c1 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.40 2002/07/11 07:39:27 ishii Exp $ +# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.41 2002/07/12 18:43:13 tgl Exp $ # #------------------------------------------------------------------------- @@ -10,9 +10,9 @@ subdir = src/backend/catalog top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS = catalog.o heap.o index.o indexing.o namespace.o aclchk.o \ - pg_aggregate.o pg_largeobject.o pg_namespace.o \ - pg_operator.o pg_proc.o pg_type.o pg_conversion.o +OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \ + pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o BKIFILES = postgres.bki postgres.description @@ -27,12 +27,12 @@ SUBSYS.o: $(OBJS) POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_proc.h pg_type.h pg_attribute.h pg_class.h \ - pg_attrdef.h pg_relcheck.h pg_inherits.h pg_index.h \ + pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \ pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \ pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \ - indexing.h \ + pg_depend.h indexing.h \ ) pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c new file mode 100644 index 0000000000000000000000000000000000000000..b7c431d3727232c4cbff26be43f98efec0f1e606 --- /dev/null +++ b/src/backend/catalog/dependency.c @@ -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; +} diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 81fbee1fa583265ded15af57ddd9db6d91e7188c..207090ae70aba4e6df8f7196eba6f74776d5bd64 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.204 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.205 2002/07/12 18:43:13 tgl Exp $ * * * INTERFACE ROUTINES @@ -33,21 +33,20 @@ #include "access/genam.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" -#include "catalog/pg_relcheck.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "commands/trigger.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" -#include "optimizer/prep.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -69,8 +68,6 @@ static void AddNewRelationTuple(Relation pg_class_desc, char relkind, bool relhasoids); static void DeleteAttributeTuples(Relation rel); static void DeleteRelationTuple(Relation rel); -static void DeleteTypeTuple(Relation rel); -static void RelationRemoveIndexes(Relation relation); static void RelationRemoveInheritance(Relation relation); static void AddNewRelationType(const char *typeName, Oid typeNamespace, @@ -80,7 +77,7 @@ static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin); static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void SetRelationNumChecks(Relation rel, int numchecks); -static void RemoveConstraints(Relation rel); +static void RemoveDefaults(Relation rel); static void RemoveStatistics(Relation rel); @@ -760,106 +757,42 @@ heap_create_with_catalog(const char *relname, } -/* -------------------------------- +/* * RelationRemoveInheritance * - * Note: for now, we cause an exception if relation is a - * superclass. Someday, we may want to allow this and merge - * the type info into subclass procedures.... this seems like - * lots of work. - * -------------------------------- + * Formerly, this routine checked for child relations and aborted the + * deletion if any were found. Now we rely on the dependency mechanism + * to check for or delete child relations. By the time we get here, + * there are no children and we need only remove the pg_inherits rows. */ static void RelationRemoveInheritance(Relation relation) { Relation catalogRelation; HeapTuple tuple; - HeapScanDesc scan; + SysScanDesc scan; ScanKeyData entry; - bool found = false; - /* - * open pg_inherits - */ catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock); - /* - * form a scan key for the subclasses of this class and begin scanning - */ - ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent, - F_OIDEQ, + ScanKeyEntryInitialize(&entry, 0x0, + Anum_pg_inherits_inhrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); - scan = heap_beginscan(catalogRelation, - SnapshotNow, - 1, - &entry); - - /* - * 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); + scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true, + SnapshotNow, 1, &entry); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + while (HeapTupleIsValid(tuple = systable_getnext(scan))) { simple_heap_delete(catalogRelation, &tuple->t_self); - found = true; } - heap_endscan(scan); + systable_endscan(scan); heap_close(catalogRelation, RowExclusiveLock); } /* - * RelationRemoveIndexes - */ -static void -RelationRemoveIndexes(Relation relation) -{ - List *indexoidlist, - *indexoidscan; - - indexoidlist = RelationGetIndexList(relation); - - foreach(indexoidscan, indexoidlist) - { - Oid indexoid = lfirsti(indexoidscan); - - index_drop(indexoid); - } - - freeList(indexoidlist); -} - -/* -------------------------------- * DeleteRelationTuple - * - * -------------------------------- */ static void DeleteRelationTuple(Relation rel) @@ -1049,163 +982,34 @@ DeleteAttributeTuples(Relation rel) heap_close(pg_attribute_desc, RowExclusiveLock); } -/* -------------------------------- - * DeleteTypeTuple - * - * If the user attempts to destroy a relation and there - * exists attributes in other relations of type - * "relation we are deleting", then we have to do something - * special. presently we disallow the destroy. - * -------------------------------- - */ -static void -DeleteTypeTuple(Relation rel) -{ - Relation pg_type_desc; - HeapScanDesc pg_type_scan; - Relation pg_attribute_desc; - HeapScanDesc pg_attribute_scan; - ScanKeyData key; - ScanKeyData attkey; - HeapTuple tup; - HeapTuple atttup; - Oid typoid; - - /* - * open pg_type - */ - pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock); - - /* - * create a scan key to locate the type tuple corresponding to this - * relation. - */ - ScanKeyEntryInitialize(&key, 0, - Anum_pg_type_typrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - - pg_type_scan = heap_beginscan(pg_type_desc, - SnapshotNow, - 1, - &key); - - /* - * use heap_getnext() to fetch the pg_type tuple. If this tuple is - * not valid then something's wrong. - */ - tup = heap_getnext(pg_type_scan, ForwardScanDirection); - - if (!HeapTupleIsValid(tup)) - { - heap_endscan(pg_type_scan); - heap_close(pg_type_desc, RowExclusiveLock); - elog(ERROR, "DeleteTypeTuple: type \"%s\" does not exist", - RelationGetRelationName(rel)); - } - - /* - * now scan pg_attribute. if any other relations have attributes of - * the type of the relation we are deleteing then we have to disallow - * the deletion. should talk to stonebraker about this. -cim 6/19/90 - */ - typoid = tup->t_data->t_oid; - - pg_attribute_desc = heap_openr(AttributeRelationName, RowExclusiveLock); - - ScanKeyEntryInitialize(&attkey, - 0, - Anum_pg_attribute_atttypid, - F_OIDEQ, - ObjectIdGetDatum(typoid)); - - pg_attribute_scan = heap_beginscan(pg_attribute_desc, - SnapshotNow, - 1, - &attkey); - - /* - * try and get a pg_attribute tuple. if we succeed it means we can't - * delete the relation because something depends on the schema. - */ - atttup = heap_getnext(pg_attribute_scan, ForwardScanDirection); - - if (HeapTupleIsValid(atttup)) - { - Oid relid = ((Form_pg_attribute) GETSTRUCT(atttup))->attrelid; - - heap_endscan(pg_attribute_scan); - heap_close(pg_attribute_desc, RowExclusiveLock); - heap_endscan(pg_type_scan); - heap_close(pg_type_desc, RowExclusiveLock); - - elog(ERROR, "DeleteTypeTuple: column of type %s exists in relation %u", - RelationGetRelationName(rel), relid); - } - heap_endscan(pg_attribute_scan); - heap_close(pg_attribute_desc, RowExclusiveLock); - - /* - * Ok, it's safe so we delete the relation tuple from pg_type and - * finish up. - */ - simple_heap_delete(pg_type_desc, &tup->t_self); - - heap_endscan(pg_type_scan); - heap_close(pg_type_desc, RowExclusiveLock); -} - /* ---------------------------------------------------------------- - * heap_drop_with_catalog - removes all record of named relation from catalogs + * heap_drop_with_catalog - removes specified relation from catalogs * - * 1) open relation, check for existence, etc. - * 2) remove inheritance information - * 3) remove indexes - * 4) remove pg_class tuple - * 5) remove pg_attribute tuples and related descriptions - * 6) remove pg_description tuples - * 7) remove pg_type tuples - * 8) RemoveConstraints () - * 9) unlink relation + * 1) open relation, acquire exclusive lock. + * 2) flush relation buffers from bufmgr + * 3) remove inheritance information + * 4) remove pg_statistic tuples + * 5) remove pg_attribute tuples and related items + * 6) remove pg_class tuple + * 7) unlink relation file * - * old comments - * Except for vital relations, removes relation from - * relation catalog, and related attributes from - * attribute catalog (needed?). (Anything else?) - * - * get proper relation from relation catalog (if not arg) - * scan attribute catalog deleting attributes of reldesc - * (necessary?) - * delete relation from relation catalog - * (How are the tuples of the relation discarded?) - * - * XXX Must fix to work with indexes. - * There may be a better order for doing things. - * Problems with destroying a deleted database--cannot create - * a struct reldesc without having an open file descriptor. + * Note that this routine is not responsible for dropping objects that are + * linked to the pg_class entry via dependencies (for example, indexes and + * constraints). Those are deleted by the dependency-tracing logic in + * dependency.c before control gets here. In general, therefore, this routine + * should never be called directly; go through performDeletion() instead. * ---------------------------------------------------------------- */ void -heap_drop_with_catalog(Oid rid, - bool allow_system_table_mods) +heap_drop_with_catalog(Oid rid) { Relation rel; - Oid toasttableOid; int i; /* * Open and lock the relation. */ rel = heap_open(rid, AccessExclusiveLock); - toasttableOid = rel->rd_rel->reltoastrelid; - - /* - * prevent deletion of system relations - */ - if (!allow_system_table_mods && - IsSystemRelation(rel)) - elog(ERROR, "System relation \"%s\" may not be dropped", - RelationGetRelationName(rel)); /* * Release all buffers that belong to this relation, after writing any @@ -1216,43 +1020,22 @@ heap_drop_with_catalog(Oid rid, elog(ERROR, "heap_drop_with_catalog: FlushRelationBuffers returned %d", i); - /* - * remove rules if necessary - */ - if (rel->rd_rules != NULL) - RelationRemoveRules(rid); - - /* triggers */ - RelationRemoveTriggers(rel); - /* * remove inheritance information */ RelationRemoveInheritance(rel); /* - * remove indexes if necessary + * delete statistics */ - RelationRemoveIndexes(rel); + RemoveStatistics(rel); /* - * delete attribute tuples + * delete attribute tuples and associated defaults */ DeleteAttributeTuples(rel); - /* - * delete comments, statistics, and constraints - */ - DeleteComments(rid, RelOid_pg_class); - - RemoveStatistics(rel); - - RemoveConstraints(rel); - - /* - * delete type tuple - */ - DeleteTypeTuple(rel); + RemoveDefaults(rel); /* * delete relation tuple @@ -1276,10 +1059,6 @@ heap_drop_with_catalog(Oid rid, * flush the relation from the relcache */ RelationForgetRelation(rid); - - /* If it has a toast table, recurse to get rid of that too */ - if (OidIsValid(toasttableOid)) - heap_drop_with_catalog(toasttableOid, true); } @@ -1374,11 +1153,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) { Node *expr; char *ccsrc; - Relation rcrel; - Relation idescs[Num_pg_relcheck_indices]; - HeapTuple tuple; - Datum values[4]; - static char nulls[4] = {' ', ' ', ' ', ' '}; + List *varList; + int keycount; + int16 *attNos; /* * Convert condition to a normal boolean expression tree. @@ -1394,26 +1171,55 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) RelationGetRelid(rel)), false); - values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel); - values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein, - CStringGetDatum(ccname)); - values[Anum_pg_relcheck_rcbin - 1] = DirectFunctionCall1(textin, - CStringGetDatum(ccbin)); - values[Anum_pg_relcheck_rcsrc - 1] = DirectFunctionCall1(textin, - CStringGetDatum(ccsrc)); - rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); - tuple = heap_formtuple(rcrel->rd_att, values, nulls); - simple_heap_insert(rcrel, tuple); - CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices, - idescs); - CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple); - CatalogCloseIndices(Num_pg_relcheck_indices, idescs); - heap_close(rcrel, RowExclusiveLock); + /* + * Find columns of rel that are used in ccbin + */ + varList = pull_var_clause(expr, false); + keycount = length(varList); + + if (keycount > 0) + { + List *vl; + int i = 0; + + attNos = (int16 *) palloc(keycount * sizeof(int16)); + foreach(vl, varList) + { + Var *var = (Var *) lfirst(vl); + int j; + + for (j = 0; j < i; j++) + if (attNos[j] == var->varattno) + break; + if (j == i) + attNos[i++] = var->varattno; + } + keycount = i; + } + else + attNos = NULL; + + /* + * Create the Check Constraint + */ + CreateConstraintEntry(ccname, /* Constraint Name */ + RelationGetNamespace(rel), /* namespace */ + CONSTRAINT_CHECK, /* Constraint Type */ + false, /* Is Deferrable */ + false, /* Is Deferred */ + RelationGetRelid(rel), /* relation */ + attNos, /* List of attributes in the constraint */ + keycount, /* # attributes in the constraint */ + InvalidOid, /* not a domain constraint */ + InvalidOid, /* Foreign key fields */ + NULL, + 0, + ' ', + ' ', + ' ', + ccbin, /* Binary form check constraint */ + ccsrc); /* Source form check constraint */ - pfree(DatumGetPointer(values[Anum_pg_relcheck_rcname - 1])); - pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1])); - pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1])); - heap_freetuple(tuple); pfree(ccsrc); } @@ -1488,6 +1294,7 @@ AddRelationRawConstraints(Relation rel, ParseState *pstate; RangeTblEntry *rte; int numchecks; + int constr_name_ctr = 0; List *listptr; Node *expr; @@ -1549,18 +1356,17 @@ AddRelationRawConstraints(Relation rel, /* Check name uniqueness, or generate a new name */ if (cdef->name != NULL) { - int i; List *listptr2; ccname = cdef->name; - /* Check against old constraints */ - for (i = 0; i < numoldchecks; i++) - { - if (strcmp(oldchecks[i].ccname, ccname) == 0) - elog(ERROR, "Duplicate CHECK constraint name: '%s'", - ccname); - } + /* Check against pre-existing constraints */ + if (ConstraintNameIsUsed(RelationGetRelid(rel), + RelationGetNamespace(rel), + ccname)) + elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", + ccname, RelationGetRelationName(rel)); /* Check against other new constraints */ + /* Needed because we don't do CommandCounterIncrement in loop */ foreach(listptr2, rawConstraints) { Constraint *cdef2 = (Constraint *) lfirst(listptr2); @@ -1577,55 +1383,40 @@ AddRelationRawConstraints(Relation rel, } else { - int i; - int j; bool success; - List *listptr2; - ccname = (char *) palloc(NAMEDATALEN); - - /* Loop until we find a non-conflicting constraint name */ - /* What happens if this loops forever? */ - j = numchecks + 1; do { - success = true; - snprintf(ccname, NAMEDATALEN, "$%d", j); - - /* Check against old constraints */ - for (i = 0; i < numoldchecks; i++) - { - if (strcmp(oldchecks[i].ccname, ccname) == 0) - { - success = false; - break; - } - } + List *listptr2; /* - * Check against other new constraints, if the check - * hasn't already failed + * 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. */ - if (success) + success = true; + foreach(listptr2, rawConstraints) { - 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) { - 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; - } + success = false; + break; } } - - ++j; } while (!success); } @@ -1852,157 +1643,74 @@ RemoveAttrDefaults(Relation rel) heap_close(adrel, RowExclusiveLock); } -static void -RemoveRelChecks(Relation rel) -{ - Relation rcrel; - HeapScanDesc rcscan; - ScanKeyData key; - HeapTuple tup; - - rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); - - ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - - rcscan = heap_beginscan(rcrel, SnapshotNow, 1, &key); - - while ((tup = heap_getnext(rcscan, ForwardScanDirection)) != NULL) - simple_heap_delete(rcrel, &tup->t_self); - - heap_endscan(rcscan); - heap_close(rcrel, RowExclusiveLock); - -} - /* - * Removes all CHECK constraints on a relation that match the given name. - * It is the responsibility of the calling function to acquire a lock on - * the relation. - * Returns: The number of CHECK constraints removed. + * Removes all constraints on a relation that match the given name. + * + * It is the responsibility of the calling function to acquire a suitable + * lock on the relation. + * + * Returns: The number of constraints removed. */ int -RemoveCheckConstraint(Relation rel, const char *constrName, bool inh) +RemoveRelConstraints(Relation rel, const char *constrName, + DropBehavior behavior) { - Oid relid; - Relation rcrel; - TupleDesc tupleDesc; - TupleConstr *oldconstr; - int numoldchecks; - int numchecks; - HeapScanDesc rcscan; - ScanKeyData key[2]; - HeapTuple rctup; - int rel_deleted = 0; - int all_deleted = 0; + int ndeleted = 0; + Relation conrel; + SysScanDesc conscan; + ScanKeyData key[1]; + HeapTuple contup; + + /* Grab an appropriate lock on the pg_constraint relation */ + conrel = heap_openr(ConstraintRelationName, RowExclusiveLock); + + /* Use the index to scan only constraints of the target relation */ + ScanKeyEntryInitialize(&key[0], 0x0, + Anum_pg_constraint_conrelid, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); - /* Find id of the relation */ - relid = RelationGetRelid(rel); + conscan = systable_beginscan(conrel, ConstraintRelidIndex, true, + SnapshotNow, 1, key); /* - * Process child tables and remove constraints of the same name. + * Scan over the result set, removing any matching entries. */ - if (inh) + while ((contup = systable_getnext(conscan)) != NULL) { - List *child, - *children; - - /* This routine is actually in the planner */ - children = find_all_inheritors(relid); + Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup); - /* - * find_all_inheritors does the recursive search of the - * inheritance hierarchy, so all we have to do is process all of - * the relids in the list that it returns. - */ - foreach(child, children) + if (strcmp(NameStr(con->conname), constrName) == 0) { - Oid childrelid = lfirsti(child); - Relation inhrel; - - if (childrelid == relid) - continue; - inhrel = heap_open(childrelid, AccessExclusiveLock); - all_deleted += RemoveCheckConstraint(inhrel, constrName, false); - heap_close(inhrel, NoLock); - } - } - - /* - * Get number of existing constraints. - */ - tupleDesc = RelationGetDescr(rel); - oldconstr = tupleDesc->constr; - if (oldconstr) - numoldchecks = oldconstr->num_check; - else - numoldchecks = 0; + ObjectAddress conobj; - /* Grab an appropriate lock on the pg_relcheck relation */ - rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); + conobj.classId = RelationGetRelid(conrel); + conobj.objectId = contup->t_data->t_oid; + conobj.objectSubId = 0; - /* - * 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))); + performDeletion(&conobj, behavior); - 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; + ndeleted++; + } } /* Clean up after the scan */ - heap_endscan(rcscan); - heap_close(rcrel, RowExclusiveLock); + systable_endscan(conscan); + heap_close(conrel, RowExclusiveLock); - if (rel_deleted) - { - /* - * Update the count of constraints in the relation's pg_class tuple. - */ - numchecks = numoldchecks - rel_deleted; - if (numchecks < 0) - elog(ERROR, "check count became negative"); - - SetRelationNumChecks(rel, numchecks); - } - - /* Return the number of tuples deleted, including all children */ - return all_deleted; + return ndeleted; } static void -RemoveConstraints(Relation rel) +RemoveDefaults(Relation rel) { TupleConstr *constr = rel->rd_att->constr; - if (!constr) - return; - - if (constr->num_defval > 0) + /* + * We can skip looking at pg_attrdef if there are no defaults recorded + * in the Relation. + */ + if (constr && constr->num_defval > 0) RemoveAttrDefaults(rel); - - if (constr->num_check > 0) - RemoveRelChecks(rel); } static void diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 2b6f77594c6e788d38e690048d2127787a934ac1..30cef89feff6f7aa17a4bcfbf2198aeed62ffa5c 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.181 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.182 2002/07/12 18:43:13 tgl Exp $ * * * INTERFACE ROUTINES @@ -29,14 +29,15 @@ #include "bootstrap/bootstrap.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_index.h" #include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "executor/executor.h" #include "miscadmin.h" #include "optimizer/clauses.h" @@ -535,6 +536,7 @@ index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid *classObjectId, bool primary, + bool isconstraint, bool allow_system_table_mods) { Relation heapRelation; @@ -543,6 +545,7 @@ index_create(Oid heapRelationId, bool shared_relation; Oid namespaceId; Oid indexoid; + int i; SetReindexProcessing(false); @@ -660,7 +663,89 @@ index_create(Oid heapRelationId, classObjectId, primary); /* - * fill in the index strategy structure with information from the + * Register constraint and dependencies for the index. + * + * If the index is from a CONSTRAINT clause, construct a pg_constraint + * entry. The index is then linked to the constraint, which in turn is + * linked to the table. If it's not a CONSTRAINT, make the dependency + * directly on the table. + * + * During bootstrap we can't register any dependencies, and we don't + * try to make a constraint either. + */ + if (!IsBootstrapProcessingMode()) + { + ObjectAddress myself, + referenced; + + myself.classId = RelOid_pg_class; + myself.objectId = indexoid; + myself.objectSubId = 0; + + if (isconstraint) + { + char constraintType; + Oid conOid; + + if (primary) + constraintType = CONSTRAINT_PRIMARY; + else if (indexInfo->ii_Unique) + constraintType = CONSTRAINT_UNIQUE; + else + { + elog(ERROR, "index_create: constraint must be PRIMARY or UNIQUE"); + constraintType = 0; /* keep compiler quiet */ + } + + conOid = CreateConstraintEntry(indexRelationName, + namespaceId, + constraintType, + false, /* isDeferrable */ + false, /* isDeferred */ + heapRelationId, + indexInfo->ii_KeyAttrNumbers, + indexInfo->ii_NumIndexAttrs, + InvalidOid, /* no domain */ + InvalidOid, /* no foreign key */ + NULL, + 0, + ' ', + ' ', + ' ', + NULL, /* Constraint Bin & Src */ + NULL); + + referenced.classId = get_system_catalog_relid(ConstraintRelationName); + referenced.objectId = conOid; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + } + else + { + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = heapRelationId; + referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i]; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } + } + + /* Store the dependency on the function (if appropriate) */ + if (OidIsValid(indexInfo->ii_FuncOid)) + { + referenced.classId = RelOid_pg_proc; + referenced.objectId = indexInfo->ii_FuncOid; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + } + + /* + * Fill in the index strategy structure with information from the * catalogs. First we must advance the command counter so that we * will see the newly-entered index catalog tuples. */ @@ -691,11 +776,11 @@ index_create(Oid heapRelationId, return indexoid; } -/* ---------------------------------------------------------------- - * +/* * index_drop * - * ---------------------------------------------------------------- + * NOTE: this routine should now only be called through performDeletion(), + * else associated dependencies won't be cleaned up. */ void index_drop(Oid indexId) @@ -730,17 +815,6 @@ index_drop(Oid indexId) userIndexRelation = index_open(indexId); LockRelation(userIndexRelation, AccessExclusiveLock); - /* - * Note: unlike heap_drop_with_catalog, we do not need to prevent - * deletion of system indexes here; that's checked for upstream. If we - * did check it here, deletion of TOAST tables would fail... - */ - - /* - * fix DESCRIPTION relation - */ - DeleteComments(indexId, RelOid_pg_class); - /* * fix RELATION relation */ diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 5611edda85343827176b48aa1353bce71607dbcd..9925c39af1e1cac39d7b31d3c4d2e88763129f45 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.95 2002/07/11 07:39:27 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.96 2002/07/12 18:43:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,10 +45,14 @@ char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] = {AttrDefaultIndex}; char *Name_pg_class_indices[Num_pg_class_indices] = {ClassNameNspIndex, ClassOidIndex}; +char *Name_pg_constraint_indices[Num_pg_constraint_indices] = +{ConstraintNameNspIndex, ConstraintOidIndex, ConstraintRelidIndex}; char *Name_pg_conversion_indices[Num_pg_conversion_indices] = {ConversionNameNspIndex, ConversionDefaultIndex}; char *Name_pg_database_indices[Num_pg_database_indices] = {DatabaseNameIndex, DatabaseOidIndex}; +char *Name_pg_depend_indices[Num_pg_depend_indices] = +{DependDependerIndex, DependReferenceIndex}; char *Name_pg_group_indices[Num_pg_group_indices] = {GroupNameIndex, GroupSysidIndex}; char *Name_pg_index_indices[Num_pg_index_indices] = @@ -67,8 +71,6 @@ char *Name_pg_operator_indices[Num_pg_operator_indices] = {OperatorOidIndex, OperatorNameNspIndex}; char *Name_pg_proc_indices[Num_pg_proc_indices] = {ProcedureOidIndex, ProcedureNameNspIndex}; -char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] = -{RelCheckIndex}; char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] = {RewriteOidIndex, RewriteRelRulenameIndex}; char *Name_pg_shadow_indices[Num_pg_shadow_indices] = diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 2af7e1d00f00bae79babf221df2c40719b7ad7d7..ad5a0c0d6a3aedb9c982dd3efce3760e74d9c8b8 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.23 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.24 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,7 @@ #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/pg_inherits.h" @@ -128,25 +129,10 @@ static Oid mySpecialNamespace = InvalidOid; char *namespace_search_path = NULL; -/* - * Deletion ordering constraint item. - */ -typedef struct DelConstraint -{ - Oid referencer; /* table to delete first */ - Oid referencee; /* table to delete second */ - int pred; /* workspace for TopoSortRels */ - struct DelConstraint *link; /* workspace for TopoSortRels */ -} DelConstraint; - - /* Local functions */ static void recomputeNamespacePath(void); static void InitTempTableNamespace(void); static void RemoveTempRelations(Oid tempNamespaceId); -static List *FindTempRelations(Oid tempNamespaceId); -static List *FindDeletionConstraints(List *relOids); -static List *TopoSortRels(List *relOids, List *constraintList); static void RemoveTempRelationsCallback(void); static void NamespaceCallback(Datum arg, Oid relid); @@ -1531,56 +1517,22 @@ AtEOXact_Namespace(bool isCommit) static void RemoveTempRelations(Oid tempNamespaceId) { - List *tempRelList; - List *constraintList; - List *lptr; - - /* Get a list of relations to delete */ - tempRelList = FindTempRelations(tempNamespaceId); - - if (tempRelList == NIL) - return; /* nothing to do */ - - /* If more than one, sort them to respect any deletion-order constraints */ - if (length(tempRelList) > 1) - { - constraintList = FindDeletionConstraints(tempRelList); - if (constraintList != NIL) - tempRelList = TopoSortRels(tempRelList, constraintList); - } - - /* Scan the list and delete all entries */ - foreach(lptr, tempRelList) - { - Oid reloid = (Oid) lfirsti(lptr); - - heap_drop_with_catalog(reloid, true); - /* - * Advance cmd counter to make catalog changes visible, in case - * a later entry depends on this one. - */ - CommandCounterIncrement(); - } -} - -/* - * Find all relations in the specified temp namespace. - * - * Returns a list of relation OIDs. - */ -static List * -FindTempRelations(Oid tempNamespaceId) -{ - List *tempRelList = NIL; Relation pgclass; HeapScanDesc scan; HeapTuple tuple; ScanKeyData key; + ObjectAddress object; /* * Scan pg_class to find all the relations in the target namespace. * Ignore indexes, though, on the assumption that they'll go away * when their tables are deleted. + * + * NOTE: if there are deletion constraints between temp relations, + * then our CASCADE delete call may cause as-yet-unvisited objects + * to go away. This is okay because we are using SnapshotNow; when + * the scan does reach those pg_class tuples, they'll be ignored as + * already deleted. */ ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relnamespace, @@ -1597,7 +1549,10 @@ FindTempRelations(Oid tempNamespaceId) case RELKIND_RELATION: case RELKIND_SEQUENCE: case RELKIND_VIEW: - tempRelList = lconsi(tuple->t_data->t_oid, tempRelList); + object.classId = RelOid_pg_class; + object.objectId = tuple->t_data->t_oid; + object.objectSubId = 0; + performDeletion(&object, DROP_CASCADE); break; default: break; @@ -1606,164 +1561,6 @@ FindTempRelations(Oid tempNamespaceId) heap_endscan(scan); heap_close(pgclass, AccessShareLock); - - return tempRelList; -} - -/* - * Find deletion-order constraints involving the given relation OIDs. - * - * Returns a list of DelConstraint objects. - */ -static List * -FindDeletionConstraints(List *relOids) -{ - List *constraintList = NIL; - Relation inheritsrel; - HeapScanDesc scan; - HeapTuple tuple; - - /* - * Scan pg_inherits to find parents and children that are in the list. - */ - inheritsrel = heap_openr(InheritsRelationName, AccessShareLock); - scan = heap_beginscan(inheritsrel, SnapshotNow, 0, NULL); - - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - Oid inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; - Oid inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent; - - if (intMember(inhrelid, relOids) && intMember(inhparent, relOids)) - { - DelConstraint *item; - - item = (DelConstraint *) palloc(sizeof(DelConstraint)); - item->referencer = inhrelid; - item->referencee = inhparent; - constraintList = lcons(item, constraintList); - } - } - - heap_endscan(scan); - heap_close(inheritsrel, AccessShareLock); - - return constraintList; -} - -/* - * TopoSortRels -- topological sort of a list of rels to delete - * - * This is a lot simpler and slower than, for example, the topological sort - * algorithm shown in Knuth's Volume 1. However, we are not likely to be - * working with more than a few constraints, so the apparent slowness of the - * algorithm won't really matter. - */ -static List * -TopoSortRels(List *relOids, List *constraintList) -{ - int queue_size = length(relOids); - Oid *rels; - int *beforeConstraints; - DelConstraint **afterConstraints; - List *resultList = NIL; - List *lptr; - int i, - j, - k, - last; - - /* Allocate workspace */ - rels = (Oid *) palloc(queue_size * sizeof(Oid)); - beforeConstraints = (int *) palloc(queue_size * sizeof(int)); - afterConstraints = (DelConstraint **) - palloc(queue_size * sizeof(DelConstraint*)); - - /* Build an array of the target relation OIDs */ - i = 0; - foreach(lptr, relOids) - { - rels[i++] = (Oid) lfirsti(lptr); - } - - /* - * Scan the constraints, and for each rel in the array, generate a - * count of the number of constraints that say it must be before - * something else, plus a list of the constraints that say it must be - * after something else. The count for the j'th rel is stored in - * beforeConstraints[j], and the head of its list in - * afterConstraints[j]. Each constraint stores its list link in - * its link field (note any constraint will be in just one list). - * The array index for the before-rel of each constraint is - * remembered in the constraint's pred field. - */ - MemSet(beforeConstraints, 0, queue_size * sizeof(int)); - MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*)); - foreach(lptr, constraintList) - { - DelConstraint *constraint = (DelConstraint *) lfirst(lptr); - Oid rel; - - /* Find the referencer rel in the array */ - rel = constraint->referencer; - for (j = queue_size; --j >= 0;) - { - if (rels[j] == rel) - break; - } - Assert(j >= 0); /* should have found a match */ - /* Find the referencee rel in the array */ - rel = constraint->referencee; - for (k = queue_size; --k >= 0;) - { - if (rels[k] == rel) - break; - } - Assert(k >= 0); /* should have found a match */ - beforeConstraints[j]++; /* referencer must come before */ - /* add this constraint to list of after-constraints for referencee */ - constraint->pred = j; - constraint->link = afterConstraints[k]; - afterConstraints[k] = constraint; - } - /*-------------------- - * Now scan the rels array backwards. At each step, output the - * last rel that has no remaining before-constraints, and decrease - * the beforeConstraints count of each of the rels it was constrained - * against. (This is the right order since we are building the result - * list back-to-front.) - * i = counter for number of rels left to output - * j = search index for rels[] - * dc = temp for scanning constraint list for rel j - * last = last valid index in rels (avoid redundant searches) - *-------------------- - */ - last = queue_size - 1; - for (i = queue_size; --i >= 0;) - { - DelConstraint *dc; - - /* Find next candidate to output */ - while (rels[last] == InvalidOid) - last--; - for (j = last; j >= 0; j--) - { - if (rels[j] != InvalidOid && beforeConstraints[j] == 0) - break; - } - /* If no available candidate, topological sort fails */ - if (j < 0) - elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering"); - /* Output candidate, and mark it done by zeroing rels[] entry */ - resultList = lconsi(rels[j], resultList); - rels[j] = InvalidOid; - /* Update beforeConstraints counts of its predecessors */ - for (dc = afterConstraints[j]; dc; dc = dc->link) - beforeConstraints[dc->pred]--; - } - - /* Done */ - return resultList; } /* diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c new file mode 100644 index 0000000000000000000000000000000000000000..47712dde1d66997d855a613f0e8ba57a665a8eea --- /dev/null +++ b/src/backend/catalog/pg_constraint.c @@ -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); +} diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c new file mode 100644 index 0000000000000000000000000000000000000000..4057374069ae17482a7bc001b8633c4a8ab942a3 --- /dev/null +++ b/src/backend/catalog/pg_depend.c @@ -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; +} diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 5b8b9a88c223bd310d94a9d1654853ef4d346852..88f113beb01c6c645e8f7c4b2b9880d80e2bca53 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.72 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.73 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_type.h" #include "miscadmin.h" @@ -166,6 +167,8 @@ TypeCreate(const char *typeName, NameData name; TupleDesc tupDesc; int i; + ObjectAddress myself, + referenced; /* * validate size specifications: either positive (fixed-length) or -1 @@ -298,6 +301,77 @@ TypeCreate(const char *typeName, CatalogCloseIndices(Num_pg_type_indices, idescs); } + /* + * Create dependencies + */ + myself.classId = RelOid_pg_type; + myself.objectId = typeObjectId; + myself.objectSubId = 0; + + /* Normal dependencies on the I/O functions */ + referenced.classId = RelOid_pg_proc; + referenced.objectId = inputProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.classId = RelOid_pg_proc; + referenced.objectId = outputProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (receiveProcedure != inputProcedure) + { + referenced.classId = RelOid_pg_proc; + referenced.objectId = receiveProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + if (sendProcedure != outputProcedure) + { + referenced.classId = RelOid_pg_proc; + referenced.objectId = sendProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* + * If the type is a rowtype for a relation, mark it as internally + * dependent on the relation. This allows it to be auto-dropped + * when the relation is, and not otherwise. + */ + if (OidIsValid(relationOid)) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = relationOid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + } + + /* + * If the type is an array type, mark it auto-dependent on the + * base type. (This is a compromise between the typical case where the + * array type is automatically generated and the case where it is manually + * created: we'd prefer INTERNAL for the former case and NORMAL for the + * latter.) + */ + if (OidIsValid(elementType)) + { + referenced.classId = RelOid_pg_type; + referenced.objectId = elementType; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } + + /* Normal dependency from a domain to its base type. */ + if (OidIsValid(baseType)) + { + referenced.classId = RelOid_pg_type; + referenced.objectId = baseType; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + /* * finish up */ diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index c3c1ed16dfc4ea2680bbd0a6bfe126c76cb36089..1b83f03f48136b7b31995147322f77e409f8a701 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.3 2002/07/12 18:43:15 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -24,10 +24,10 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_func.h" @@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters) } +/* + * RemoveAggregate + * Deletes an aggregate. + */ void -RemoveAggregate(List *aggName, TypeName *aggType) +RemoveAggregate(RemoveAggrStmt *stmt) { - Relation relation; - HeapTuple tup; + List *aggName = stmt->aggname; + TypeName *aggType = stmt->aggtype; Oid basetypeID; Oid procOid; + HeapTuple tup; + ObjectAddress object; /* * if a basetype is passed in, then attempt to find an aggregate for @@ -164,8 +170,9 @@ RemoveAggregate(List *aggName, TypeName *aggType) procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID); - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - + /* + * Find the function tuple, do permissions and validity checks + */ tup = SearchSysCache(PROCOID, ObjectIdGetDatum(procOid), 0, 0, 0); @@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName)); - /* Delete any comments associated with this function */ - DeleteComments(procOid, RelationGetRelid(relation)); - - /* Remove the pg_proc tuple */ - simple_heap_delete(relation, &tup->t_self); + /* find_aggregate_func already checked it is an aggregate */ ReleaseSysCache(tup); - heap_close(relation, RowExclusiveLock); - - /* Remove the pg_aggregate tuple */ - - relation = heap_openr(AggregateRelationName, RowExclusiveLock); - - tup = SearchSysCache(AGGFNOID, - ObjectIdGetDatum(procOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s", - NameListToString(aggName)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); + /* + * Do the deletion + */ + object.classId = RelOid_pg_proc; + object.objectId = procOid; + object.objectSubId = 0; - heap_close(relation, RowExclusiveLock); + performDeletion(&object, stmt->behavior); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 3306943fb04875971f65c266547fe0204bf974c7..837390744fbee37b1e972401b499b87bdb6cca2f 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.82 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/pg_index.h" @@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname) OldIndex; char NewHeapName[NAMEDATALEN]; char NewIndexName[NAMEDATALEN]; + ObjectAddress object; /* * We grab exclusive access to the target rel and index for the @@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname) CommandCounterIncrement(); /* Destroy old heap (along with its index) and rename new. */ - heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods); + object.classId = RelOid_pg_class; + object.objectId = OIDOldHeap; + object.objectSubId = 0; - CommandCounterIncrement(); + /* XXX better to use DROP_CASCADE here? */ + performDeletion(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ renamerel(OIDNewHeap, oldrelation->relname); @@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName) OldIndex->rd_rel->relam, OldIndex->rd_index->indclass, OldIndex->rd_index->indisprimary, + false, /* XXX losing constraint status */ allowSystemTableMods); setRelhasindex(OIDNewHeap, true, diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 160a52246a165ba40dddad798e6c0b3003550979..b1ce1b2ce65b84323f31e0747b20bc946dc36d32 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * Copyright (c) 1996-2001, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.49 2002/06/20 20:51:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.50 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -225,38 +225,45 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment) } /* - * DeleteComments -- + * DeleteComments -- remove comments for an object * - * This routine is used to purge all comments associated with an object, - * regardless of their objsubid. It is called, for example, when a relation - * is destroyed. + * If subid is nonzero then only comments matching it will be removed. + * If subid is zero, all comments matching the oid/classoid will be removed + * (this corresponds to deleting a whole object). */ void -DeleteComments(Oid oid, Oid classoid) +DeleteComments(Oid oid, Oid classoid, int32 subid) { Relation description; - ScanKeyData skey[2]; + ScanKeyData skey[3]; + int nkeys; SysScanDesc sd; HeapTuple oldtuple; /* Use the index to search for all matching old tuples */ - ScanKeyEntryInitialize(&skey[0], - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_OIDEQ, + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_description_objoid, F_OIDEQ, ObjectIdGetDatum(oid)); - ScanKeyEntryInitialize(&skey[1], - (bits16) 0x0, - (AttrNumber) 2, - (RegProcedure) F_OIDEQ, + ScanKeyEntryInitialize(&skey[1], 0x0, + Anum_pg_description_classoid, F_OIDEQ, ObjectIdGetDatum(classoid)); + if (subid != 0) + { + ScanKeyEntryInitialize(&skey[2], 0x0, + Anum_pg_description_objsubid, F_INT4EQ, + Int32GetDatum(subid)); + nkeys = 3; + } + else + nkeys = 2; + description = heap_openr(DescriptionRelationName, RowExclusiveLock); sd = systable_beginscan(description, DescriptionObjIndex, true, - SnapshotNow, 2, skey); + SnapshotNow, nkeys, skey); while ((oldtuple = systable_getnext(sd)) != NULL) { @@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid) /* Done */ systable_endscan(sd); - heap_close(description, NoLock); + heap_close(description, RowExclusiveLock); } /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 3526b91b997d28a749336751b746fad9f56288e5..c6bbb37186098cb49da25b13e1f9d32881e1edf6 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.95 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.96 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -456,8 +456,13 @@ dropdb(const char *dbname) heap_endscan(pgdbscan); - /* Delete any comments associated with the database */ - DeleteComments(db_id, RelationGetRelid(pgdbrel)); + /* + * Delete any comments associated with the database + * + * NOTE: this is probably dead code since any such comments should have + * been in that database, not mine. + */ + DeleteComments(db_id, RelationGetRelid(pgdbrel), 0); /* * Close pg_database, but keep exclusive lock till commit to ensure diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 638fd19a8ebba8f4c9ded5867e61a99a3c98c810..9a33810b07351dac3d2c2281b3d41efb78bf2955 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.7 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -33,11 +33,11 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "optimizer/cost.h" @@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt) /* * RemoveFunction * Deletes a function. - * - * Exceptions: - * BadArg if name is invalid. - * "ERROR" if function nonexistent. - * ... */ void -RemoveFunction(List *functionName, /* function name to be removed */ - List *argTypes) /* list of TypeName nodes */ +RemoveFunction(RemoveFuncStmt *stmt) { + List *functionName = stmt->funcname; + List *argTypes = stmt->args; /* list of TypeName nodes */ Oid funcOid; - Relation relation; HeapTuple tup; + ObjectAddress object; + /* + * Find the function, do permissions and validity checks + */ funcOid = LookupFuncNameTypeNames(functionName, argTypes, true, "RemoveFunction"); - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - tup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcOid), 0, 0, 0); @@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */ NameListToString(functionName)); } - /* Delete any comments associated with this function */ - DeleteComments(funcOid, RelationGetRelid(relation)); + ReleaseSysCache(tup); + + /* + * Do the deletion + */ + object.classId = RelOid_pg_proc; + object.objectId = funcOid; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of function deletion. + * + * Note: this is also used for aggregate deletion, since the OIDs of + * both functions and aggregates point to pg_proc. + */ +void +RemoveFunctionById(Oid funcOid) +{ + Relation relation; + HeapTuple tup; + bool isagg; + + /* + * Delete the pg_proc tuple. + */ + relation = heap_openr(ProcedureRelationName, RowExclusiveLock); + + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveFunctionById: couldn't find tuple for function %u", + funcOid); + + isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg; simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); + + /* + * If there's a pg_aggregate tuple, delete that too. + */ + if (isagg) + { + relation = heap_openr(AggregateRelationName, RowExclusiveLock); + + tup = SearchSysCache(AGGFNOID, + ObjectIdGetDatum(funcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveFunctionById: couldn't find pg_aggregate tuple for %u", + funcOid); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); + } } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 1338f16bb59b5236a21e964915e51c17703b0b19..5cf03bd11aa4262c2665fcd54e671f6e79f978ea 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.76 2002/07/01 15:27:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.77 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_opclass.h" @@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation, List *attributeList, bool unique, bool primary, + bool isconstraint, Expr *predicate, List *rangetable) { @@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation, index_create(relationId, indexRelationName, indexInfo, accessMethodId, classObjectId, - primary, allowSystemTableMods); + primary, isconstraint, allowSystemTableMods); /* * We update the relation's pg_class tuple even if it already has @@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior) { Oid indOid; HeapTuple tuple; + ObjectAddress object; indOid = RangeVarGetRelid(relation, false); tuple = SearchSysCache(RELOID, @@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior) ReleaseSysCache(tuple); - index_drop(indOid); + object.classId = RelOid_pg_class; + object.objectId = indOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } /* diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index fcf96c5e9c17d58c3ce29ebde573cdeb5443eb4e..1c4e5f3bee324a9d75c4f9808e9aadc92af5eb2c 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.4 2002/07/01 15:27:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -36,9 +36,9 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_operator.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_oper.h" @@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt) TypeName *typeName1 = (TypeName *) lfirst(stmt->args); TypeName *typeName2 = (TypeName *) lsecond(stmt->args); Oid operOid; - Relation relation; HeapTuple tup; + ObjectAddress object; operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2, "RemoveOperator"); - relation = heap_openr(OperatorRelationName, RowExclusiveLock); - - tup = SearchSysCacheCopy(OPEROID, - ObjectIdGetDatum(operOid), - 0, 0, 0); + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'", NameListToString(operatorName)); @@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName)); - /* Delete any comments associated with this operator */ - DeleteComments(operOid, RelationGetRelid(relation)); + ReleaseSysCache(tup); + + /* + * Do the deletion + */ + object.classId = get_system_catalog_relid(OperatorRelationName); + object.objectId = operOid; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of operator deletion. + */ +void +RemoveOperatorById(Oid operOid) +{ + Relation relation; + HeapTuple tup; + + relation = heap_openr(OperatorRelationName, RowExclusiveLock); + + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u", + operOid); simple_heap_delete(relation, &tup->t_self); - heap_freetuple(tup); + ReleaseSysCache(tup); + heap_close(relation, RowExclusiveLock); } diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 158927067f110eddb20f33a921350675444fe007..56dc320e2ef4416db7fce6b742ee2365c8b9de38 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.34 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.35 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" @@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt) { char languageName[NAMEDATALEN]; HeapTuple langTup; - Relation rel; + ObjectAddress object; /* * Check permission @@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt) */ case_translate_language_name(stmt->plname, languageName); - rel = heap_openr(LanguageRelationName, RowExclusiveLock); - - langTup = SearchSysCacheCopy(LANGNAME, - PointerGetDatum(languageName), - 0, 0, 0); + langTup = SearchSysCache(LANGNAME, + CStringGetDatum(languageName), + 0, 0, 0); if (!HeapTupleIsValid(langTup)) elog(ERROR, "Language %s doesn't exist", languageName); @@ -167,8 +166,39 @@ DropProceduralLanguage(DropPLangStmt *stmt) elog(ERROR, "Language %s isn't a created procedural language", languageName); + object.classId = get_system_catalog_relid(LanguageRelationName); + object.objectId = langTup->t_data->t_oid; + object.objectSubId = 0; + + ReleaseSysCache(langTup); + + /* + * Do the deletion + */ + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of language dropping. + */ +void +DropProceduralLanguageById(Oid langOid) +{ + Relation rel; + HeapTuple langTup; + + rel = heap_openr(LanguageRelationName, RowExclusiveLock); + + langTup = SearchSysCache(LANGOID, + ObjectIdGetDatum(langOid), + 0, 0, 0); + if (!HeapTupleIsValid(langTup)) + elog(ERROR, "DropProceduralLanguageById: language %u not found", + langOid); + simple_heap_delete(rel, &langTup->t_self); - heap_freetuple(langTup); + ReleaseSysCache(langTup); + heap_close(rel, RowExclusiveLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 901090c70aca49f97db348352835b57039a2346d..0d4d277bae4e6faa10102e912018314b6853d4e1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.19 2002/07/06 20:16:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.20 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,11 +18,13 @@ #include "access/tuptoaster.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" @@ -36,6 +38,7 @@ #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" +#include "parser/gramparse.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" @@ -57,6 +60,13 @@ static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass); static void drop_default(Oid relid, int16 attnum); static void CheckTupleType(Form_pg_class tuple_class); static bool needs_toast_table(Relation rel); +static void validateForeignKeyConstraint(FkConstraint *fkconstraint, + Relation rel, Relation pkrel); +static Oid createForeignKeyConstraint(Relation rel, Relation pkrel, + FkConstraint *fkconstraint); +static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, + Oid constrOid); +static char *fkMatchTypeToString(char match_type); /* Used by attribute and relation renaming routines: */ @@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind) ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * sizeof(ConstrCheck)); int ncheck = 0; + int constr_name_ctr = 0; foreach(listptr, old_constraints) { @@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind) } else { + /* + * Generate a constraint name. NB: this should match the + * form of names that GenerateConstraintName() may produce + * for names added later. We are assured that there is + * no name conflict, because MergeAttributes() did not pass + * back any names of this form. + */ check[ncheck].ccname = (char *) palloc(NAMEDATALEN); - snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); + snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", + ++constr_name_ctr); } Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); check[ncheck].ccbin = pstrdup(cdef->cooked_expr); @@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind) /* * RemoveRelation * Deletes a relation. - * - * Exceptions: - * BadArg if name is invalid. - * - * Note: - * If the relation has indices defined on it, then the index relations - * themselves will be destroyed, too. */ void RemoveRelation(const RangeVar *relation, DropBehavior behavior) { Oid relOid; + ObjectAddress object; relOid = RangeVarGetRelid(relation, false); - heap_drop_with_catalog(relOid, allowSystemTableMods); + + object.classId = RelOid_pg_class; + object.objectId = relOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } /* @@ -580,7 +598,13 @@ MergeAttributes(List *schema, List *supers, bool istemp, Node *expr; cdef->contype = CONSTR_CHECK; - if (check[i].ccname[0] == '$') + /* + * Do not inherit generated constraint names, since they + * might conflict across multiple inheritance parents. + * (But conflicts between user-assigned names will cause + * an error.) + */ + if (ConstraintNameIsGenerated(check[i].ccname)) cdef->name = NULL; else cdef->name = pstrdup(check[i].ccname); @@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, /* * complementary static functions for MergeAttributes(). * - * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit + * Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit * constraints from parent classes, since the inherited attributes could * be given different column numbers in multiple-inheritance cases. * @@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers) return; /* - * Catalog INHERITS information using direct ancestors only. + * Store INHERITS information in pg_inherits using direct ancestors only. + * Also enter dependencies on the direct ancestors. */ relation = heap_openr(InheritsRelationName, RowExclusiveLock); desc = RelationGetDescr(relation); @@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers) Oid entryOid = lfirsti(entry); Datum datum[Natts_pg_inherits]; char nullarr[Natts_pg_inherits]; + ObjectAddress childobject, + parentobject; datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */ @@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers) heap_freetuple(tuple); + /* + * Store a dependency too + */ + parentobject.classId = RelOid_pg_class; + parentobject.objectId = entryOid; + parentobject.objectSubId = 0; + childobject.classId = RelOid_pg_class; + childobject.objectId = relationId; + childobject.objectSubId = 0; + + recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL); + seqNumber += 1; } @@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid, { Relation rel; List *listptr; + int counter = 0; /* * Grab an exclusive lock on the target table, which we will NOT @@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid, foreach(listptr, newConstraints) { - Node *newConstraint = lfirst(listptr); + /* + * copy is because we may destructively alter the node below + * by inserting a generated name; this name is not necessarily + * correct for children or parents. + */ + Node *newConstraint = copyObject(lfirst(listptr)); switch (nodeTag(newConstraint)) { @@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid, RangeTblEntry *rte; List *qual; Node *expr; - char *name; + /* + * Assign or validate constraint name + */ if (constr->name) - name = constr->name; + { + if (ConstraintNameIsUsed(RelationGetRelid(rel), + RelationGetNamespace(rel), + constr->name)) + elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", + constr->name, + RelationGetRelationName(rel)); + } else - name = "<unnamed>"; + constr->name = GenerateConstraintName(RelationGetRelid(rel), + RelationGetNamespace(rel), + &counter); /* * We need to make a parse state and range @@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid, pfree(slot); if (!successful) - elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name); + elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", + constr->name); /* * Call AddRelationRawConstraints to do @@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid, { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; Relation pkrel; - HeapScanDesc scan; - HeapTuple tuple; - Trigger trig; - List *list; - int count; + Oid constrOid; + + /* + * Assign or validate constraint name + */ + if (fkconstraint->constr_name) + { + if (ConstraintNameIsUsed(RelationGetRelid(rel), + RelationGetNamespace(rel), + fkconstraint->constr_name)) + elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", + fkconstraint->constr_name, + RelationGetRelationName(rel)); + } + else + fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel), + RelationGetNamespace(rel), + &counter); /* * Grab an exclusive lock on the pk table, so that * someone doesn't delete rows out from under us. - * - * XXX wouldn't a lesser lock be sufficient? + * (Although a lesser lock would do for that purpose, + * we'll need exclusive lock anyway to add triggers + * to the pk table; trying to start with a lesser lock + * will just create a risk of deadlock.) */ pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); @@ -2500,119 +2572,471 @@ AlterTableAddConstraint(Oid myrelid, * Validity checks */ if (pkrel->rd_rel->relkind != RELKIND_RELATION) - elog(ERROR, "referenced table \"%s\" not a relation", - fkconstraint->pktable->relname); + elog(ERROR, "referenced relation \"%s\" is not a table", + RelationGetRelationName(pkrel)); + + if (!allowSystemTableMods + && IsSystemRelation(pkrel)) + elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", + RelationGetRelationName(pkrel)); + + /* XXX shouldn't there be a permission check too? */ if (isTempNamespace(RelationGetNamespace(pkrel)) && !isTempNamespace(RelationGetNamespace(rel))) - elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint"); /* - * First we check for limited correctness of the - * constraint. + * Check that the constraint is satisfied by existing + * rows (we can skip this during table creation). * * NOTE: we assume parser has already checked for * existence of an appropriate unique index on the * referenced relation, and that the column datatypes * are comparable. - * - * Scan through each tuple, calling RI_FKey_check_ins - * (insert trigger) as if that tuple had just been - * inserted. If any of those fail, it should - * elog(ERROR) and that's that. */ - MemSet(&trig, 0, sizeof(trig)); - trig.tgoid = InvalidOid; - if (fkconstraint->constr_name) - trig.tgname = fkconstraint->constr_name; - else - trig.tgname = "<unknown>"; - trig.tgenabled = TRUE; - trig.tgisconstraint = TRUE; - trig.tgconstrrelid = RelationGetRelid(pkrel); - trig.tgdeferrable = FALSE; - trig.tginitdeferred = FALSE; - - 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] = fkconstraint->match_type; - count = 4; - foreach(list, fkconstraint->fk_attrs) - { - Ident *fk_at = lfirst(list); + if (!fkconstraint->skip_validation) + validateForeignKeyConstraint(fkconstraint, rel, pkrel); - trig.tgargs[count] = fk_at->name; - count += 2; - } - count = 5; - foreach(list, fkconstraint->pk_attrs) - { - Ident *pk_at = lfirst(list); + /* + * Record the FK constraint in pg_constraint. + */ + constrOid = createForeignKeyConstraint(rel, pkrel, + fkconstraint); - trig.tgargs[count] = pk_at->name; - count += 2; - } - trig.tgnargs = count - 1; + /* + * Create the triggers that will enforce the constraint. + */ + createForeignKeyTriggers(rel, fkconstraint, constrOid); - scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + /* + * Close pk table, but keep lock until we've committed. + */ + heap_close(pkrel, NoLock); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - /* Make a call to the check function */ + break; + } + default: + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed"); + } + } - /* - * No parameters are passed, but we do set a - * context - */ - FunctionCallInfoData fcinfo; - TriggerData trigdata; + /* Close rel, but keep lock till commit */ + heap_close(rel, NoLock); +} - MemSet(&fcinfo, 0, sizeof(fcinfo)); +/* + * 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); - /* - * We assume RI_FKey_check_ins won't look at - * flinfo... - */ + trig.tgargs[count] = fk_at->name; + count += 2; + } + count = 5; + foreach(list, fkconstraint->pk_attrs) + { + Ident *pk_at = lfirst(list); - 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; + trig.tgargs[count] = pk_at->name; + count += 2; + } + trig.tgnargs = count - 1; - fcinfo.context = (Node *) &trigdata; + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); - RI_FKey_check_ins(&fcinfo); - } - heap_endscan(scan); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + FunctionCallInfoData fcinfo; + TriggerData trigdata; - pfree(trig.tgargs); + /* + * Make a call to the trigger function + * + * No parameters are passed, but we do set a context + */ + MemSet(&fcinfo, 0, sizeof(fcinfo)); - heap_close(pkrel, NoLock); + /* + * 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; - break; - } - default: - elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed"); - } + fcinfo.context = (Node *) &trigdata; + + RI_FKey_check_ins(&fcinfo); } - /* Close rel, but keep lock till commit */ - heap_close(rel, NoLock); + heap_endscan(scan); + + pfree(trig.tgargs); } +/* + * Record an FK constraint in pg_constraint. + */ +static Oid +createForeignKeyConstraint(Relation rel, Relation pkrel, + FkConstraint *fkconstraint) +{ + int16 *fkattr; + int16 *pkattr; + int fkcount; + int pkcount; + List *l; + int i; + + /* Convert foreign-key attr names to attr number array */ + fkcount = length(fkconstraint->fk_attrs); + fkattr = (int16 *) palloc(fkcount * sizeof(int16)); + i = 0; + foreach(l, fkconstraint->fk_attrs) + { + Ident *id = (Ident *) lfirst(l); + + fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name); + } + + /* The same for the referenced primary key attrs */ + pkcount = length(fkconstraint->pk_attrs); + pkattr = (int16 *) palloc(pkcount * sizeof(int16)); + i = 0; + foreach(l, fkconstraint->pk_attrs) + { + Ident *id = (Ident *) lfirst(l); + + pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name); + } + + /* Now we can make the pg_constraint entry */ + return CreateConstraintEntry(fkconstraint->constr_name, + RelationGetNamespace(rel), + CONSTRAINT_FOREIGN, + fkconstraint->deferrable, + fkconstraint->initdeferred, + RelationGetRelid(rel), + fkattr, + fkcount, + InvalidOid, /* not a domain constraint */ + RelationGetRelid(pkrel), + pkattr, + pkcount, + fkconstraint->fk_upd_action, + fkconstraint->fk_del_action, + fkconstraint->fk_matchtype, + NULL, + NULL); +} + +/* + * Create the triggers that implement an FK constraint. + */ +static void +createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, + Oid constrOid) +{ + RangeVar *myRel; + CreateTrigStmt *fk_trigger; + List *fk_attr; + List *pk_attr; + Ident *id; + ObjectAddress trigobj, + constrobj; + + /* + * Reconstruct a RangeVar for my relation (not passed in, unfortunately). + */ + myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), + RelationGetRelationName(rel)); + + /* + * Preset objectAddress fields + */ + constrobj.classId = get_system_catalog_relid(ConstraintRelationName); + constrobj.objectId = constrOid; + constrobj.objectSubId = 0; + trigobj.classId = get_system_catalog_relid(TriggerRelationName); + trigobj.objectSubId = 0; + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * CHECK action. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = myRel; + fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins"); + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'i'; + fk_trigger->actions[1] = 'u'; + fk_trigger->actions[2] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = fkconstraint->pktable; + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + if (length(fk_attr) != length(pk_attr)) + elog(ERROR, "number of key attributes in referenced table must be equal to foreign key" + "\n\tIllegal FOREIGN KEY definition references \"%s\"", + fkconstraint->pktable->relname); + + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * ON DELETE action on the referenced table. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = fkconstraint->pktable; + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'd'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = myRel; + switch (fkconstraint->fk_del_action) + { + case FKCONSTR_ACTION_NOACTION: + fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del"); + break; + case FKCONSTR_ACTION_RESTRICT: + fk_trigger->deferrable = false; + fk_trigger->initdeferred = false; + fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del"); + break; + case FKCONSTR_ACTION_CASCADE: + fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del"); + break; + case FKCONSTR_ACTION_SETNULL: + fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del"); + break; + case FKCONSTR_ACTION_SETDEFAULT: + fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del"); + break; + default: + elog(ERROR, "Unrecognized ON DELETE action for FOREIGN KEY constraint"); + break; + } + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * ON UPDATE action on the referenced table. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = fkconstraint->pktable; + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'u'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = myRel; + switch (fkconstraint->fk_upd_action) + { + case FKCONSTR_ACTION_NOACTION: + fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd"); + break; + case FKCONSTR_ACTION_RESTRICT: + fk_trigger->deferrable = false; + fk_trigger->initdeferred = false; + fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd"); + break; + case FKCONSTR_ACTION_CASCADE: + fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd"); + break; + case FKCONSTR_ACTION_SETNULL: + fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd"); + break; + case FKCONSTR_ACTION_SETDEFAULT: + fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd"); + break; + default: + elog(ERROR, "Unrecognized ON UPDATE action for FOREIGN KEY constraint"); + break; + } + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); +} + +/* + * fkMatchTypeToString - + * convert FKCONSTR_MATCH_xxx code to string to use in trigger args + */ +static char * +fkMatchTypeToString(char match_type) +{ + switch (match_type) + { + case FKCONSTR_MATCH_FULL: + return pstrdup("FULL"); + case FKCONSTR_MATCH_PARTIAL: + return pstrdup("PARTIAL"); + case FKCONSTR_MATCH_UNSPECIFIED: + return pstrdup("UNSPECIFIED"); + default: + elog(ERROR, "fkMatchTypeToString: Unknown MATCH TYPE '%c'", + match_type); + } + return NULL; /* can't get here */ +} /* * ALTER TABLE DROP CONSTRAINT - * Note: It is legal to remove a constraint with name "" as it is possible - * to add a constraint with name "". - * Christopher Kings-Lynne */ void AlterTableDropConstraint(Oid myrelid, @@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid, DropBehavior behavior) { Relation rel; - int deleted; - - /* - * We don't support CASCADE yet - in fact, RESTRICT doesn't work to - * the spec either! - */ - if (behavior == DROP_CASCADE) - elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword"); + int deleted = 0; /* * Acquire an exclusive lock on the target relation for the duration @@ -2649,26 +3066,39 @@ AlterTableDropConstraint(Oid myrelid, aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* - * Since all we have is the name of the constraint, we have to look - * through all catalogs that could possibly contain a constraint for - * this relation. We also keep a count of the number of constraints - * removed. + * Process child tables if requested. */ + if (inh) + { + List *child, + *children; - deleted = 0; + /* This routine is actually in the planner */ + children = find_all_inheritors(myrelid); - /* - * First, we remove all CHECK constraints with the given name - */ + /* + * 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; - deleted += RemoveCheckConstraint(rel, constrName, inh); + if (childrelid == myrelid) + continue; + inhrel = heap_open(childrelid, AccessExclusiveLock); + /* do NOT count child constraints in deleted. */ + RemoveRelConstraints(inhrel, constrName, behavior); + heap_close(inhrel, NoLock); + } + } /* - * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY - * constraints. - * - * Unimplemented. + * Now do the thing on this relation. */ + deleted += RemoveRelConstraints(rel, constrName, behavior); /* Close the target relation */ heap_close(rel, NoLock); @@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent) char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid classObjectId[2]; + ObjectAddress baseobject, + toastobject; /* * Grab an exclusive lock on the target table, which we will NOT @@ -2957,7 +3389,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent) toast_idxid = index_create(toast_relid, toast_idxname, indexInfo, BTREE_AM_OID, classObjectId, - true, true); + true, false, true); /* * Update toast rel's pg_class entry to show that it has an index. The @@ -2981,6 +3413,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent) heap_freetuple(reltup); + /* + * Register dependency from the toast table to the master, so that + * the toast table will be deleted if the master is. + */ + baseobject.classId = RelOid_pg_class; + baseobject.objectId = relOid; + baseobject.objectSubId = 0; + toastobject.classId = RelOid_pg_class; + toastobject.objectId = toast_relid; + toastobject.objectSubId = 0; + + recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); + /* * Close relations and make changes visible */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 18484372ed5e8d35127b880997df78a784f2c428..b27dd0f4380f5aa56b1ab2c9a450e90f4dc53306 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.120 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.121 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,12 +17,12 @@ #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" -#include "commands/comment.h" #include "commands/trigger.h" #include "executor/executor.h" #include "miscadmin.h" @@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno, MemoryContext per_tuple_context); -void -CreateTrigger(CreateTrigStmt *stmt) +Oid +CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) { int16 tgtype; int16 tgattr[FUNC_MAX_ARGS]; @@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt) Oid fargtypes[FUNC_MAX_ARGS]; Oid funcoid; Oid funclang; + Oid trigoid; int found = 0; int i; char constrtrigname[NAMEDATALEN]; - char *constrname = ""; - Oid constrrelid = InvalidOid; + char *trigname; + char *constrname; + Oid constrrelid; + ObjectAddress myself, + referenced; rel = heap_openrv(stmt->relation, AccessExclusiveLock); @@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt) aclcheck_error(aclresult, RelationGetRelationName(rel)); /* - * If trigger is an RI constraint, use trigger name as constraint name - * and build a unique trigger name instead. + * If trigger is an RI constraint, use specified trigger name as + * constraint name and build a unique trigger name instead. + * This is mainly for backwards compatibility with CREATE CONSTRAINT + * TRIGGER commands. */ if (stmt->isconstraint) { - constrname = stmt->trigname; snprintf(constrtrigname, sizeof(constrtrigname), "RI_ConstraintTrigger_%u", newoid()); - stmt->trigname = constrtrigname; - - if (stmt->constrrel != NULL) - constrrelid = RangeVarGetRelid(stmt->constrrel, false); - else - constrrelid = InvalidOid; + trigname = constrtrigname; + constrname = stmt->trigname; } + else + { + trigname = stmt->trigname; + constrname = ""; + } + + if (stmt->constrrel != NULL) + constrrelid = RangeVarGetRelid(stmt->constrrel, false); + else + constrrelid = InvalidOid; TRIGGER_CLEAR_TYPE(tgtype); if (stmt->before) @@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); - if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + if (namestrcmp(&(pg_trigger->tgname), trigname) == 0) elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s", - stmt->trigname, stmt->relation->relname); + trigname, stmt->relation->relname); found++; } systable_endscan(tgscan); @@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt) values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel)); values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein, - CStringGetDatum(stmt->trigname)); + CStringGetDatum(trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid); values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true); values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint); - values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname); + values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein, + CStringGetDatum(constrname)); values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid); values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable); values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred); @@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt) /* * Insert tuple into pg_trigger. */ - simple_heap_insert(tgrel, tuple); + trigoid = simple_heap_insert(tgrel, tuple); + CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple); CatalogCloseIndices(Num_pg_trigger_indices, idescs); + + myself.classId = RelationGetRelid(tgrel); + myself.objectId = trigoid; + myself.objectSubId = 0; + heap_freetuple(tuple); heap_close(tgrel, RowExclusiveLock); @@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt) stmt->relation->relname); ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; + simple_heap_update(pgrel, &tuple->t_self, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); + heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); @@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt) * upcoming CommandCounterIncrement... */ + /* + * Record dependencies for trigger. Always place a normal dependency + * on the function. If we are doing this in response to an explicit + * CREATE TRIGGER command, also make trigger be auto-dropped if its + * relation is dropped or if the FK relation is dropped. (Auto drop + * is compatible with our pre-7.3 behavior.) If the trigger is being + * made for a constraint, we can skip the relation links; the dependency + * on the constraint will indirectly depend on the relations. + */ + referenced.classId = RelOid_pg_proc; + referenced.objectId = funcoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (!forConstraint) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = RelationGetRelid(rel); + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + if (constrrelid != InvalidOid) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = constrrelid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } + } + /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); + + return trigoid; } /* * DropTrigger - drop an individual trigger by name */ void -DropTrigger(Oid relid, const char *trigname) +DropTrigger(Oid relid, const char *trigname, DropBehavior behavior) +{ + Relation tgrel; + ScanKeyData skey[2]; + SysScanDesc tgscan; + HeapTuple tup; + ObjectAddress object; + + /* + * Find the trigger, verify permissions, set up object address + */ + tgrel = heap_openr(TriggerRelationName, AccessShareLock); + + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_trigger_tgrelid, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ScanKeyEntryInitialize(&skey[1], 0x0, + Anum_pg_trigger_tgname, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, + SnapshotNow, 2, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", + trigname, get_rel_name(relid)); + + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid)); + + object.classId = RelationGetRelid(tgrel); + object.objectId = tup->t_data->t_oid; + object.objectSubId = 0; + + systable_endscan(tgscan); + heap_close(tgrel, AccessShareLock); + + /* + * Do the deletion + */ + performDeletion(&object, behavior); +} + +/* + * Guts of trigger deletion. + */ +void +RemoveTriggerById(Oid trigOid) { - Relation rel; Relation tgrel; SysScanDesc tgscan; - ScanKeyData key; + ScanKeyData skey[1]; + HeapTuple tup; + Oid relid; + Relation rel; Relation pgrel; HeapTuple tuple; + Form_pg_class classForm; Relation ridescs[Num_pg_class_indices]; - int remaining = 0; - int found = 0; + + tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); + + /* + * Find the trigger to delete. + */ + ScanKeyEntryInitialize(&skey[0], 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(trigOid)); + + tgscan = systable_beginscan(tgrel, TriggerOidIndex, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "RemoveTriggerById: Trigger %u does not exist", + trigOid); + + /* + * Open and exclusive-lock the relation the trigger belongs to. + */ + relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid; rel = heap_open(relid, AccessExclusiveLock); @@ -337,55 +462,22 @@ DropTrigger(Oid relid, const char *trigname) elog(ERROR, "DropTrigger: can't drop trigger for system relation %s", RelationGetRelationName(rel)); - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); - /* - * Search pg_trigger, delete target trigger, count remaining triggers - * for relation. (Although we could fetch and delete the target - * trigger directly, we'd still have to scan the remaining triggers, - * so we may as well do both in one indexscan.) - * - * Note this is OK only because we have AccessExclusiveLock on the rel, - * so no one else is creating/deleting triggers on this rel at the same - * time. + * Delete the pg_trigger tuple. */ - tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgrelid, - F_OIDEQ, - ObjectIdGetDatum(relid)); - tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, - SnapshotNow, 1, &key); - while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + simple_heap_delete(tgrel, &tup->t_self); - if (namestrcmp(&(pg_trigger->tgname), trigname) == 0) - { - /* Delete any comments associated with this trigger */ - DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel)); - - simple_heap_delete(tgrel, &tuple->t_self); - found++; - } - else - remaining++; - } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); - if (found == 0) - elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", - trigname, RelationGetRelationName(rel)); - if (found > 1) /* shouldn't happen */ - elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s", - found, trigname, RelationGetRelationName(rel)); - /* * Update relation's pg_class entry. Crucial side-effect: other * backends (and this one too!) are sent SI message to make them * rebuild relcache entries. + * + * Note this is OK only because we have AccessExclusiveLock on the rel, + * so no one else is creating/deleting triggers on this rel at the same + * time. */ pgrel = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, @@ -394,115 +486,27 @@ DropTrigger(Oid relid, const char *trigname) if (!HeapTupleIsValid(tuple)) elog(ERROR, "DropTrigger: relation %s not found in pg_class", RelationGetRelationName(rel)); + classForm = (Form_pg_class) GETSTRUCT(tuple); + + if (classForm->reltriggers == 0) + elog(ERROR, "DropTrigger: relation %s has reltriggers = 0", + RelationGetRelationName(rel)); + classForm->reltriggers--; - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining; simple_heap_update(pgrel, &tuple->t_self, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); + heap_freetuple(tuple); + heap_close(pgrel, RowExclusiveLock); - /* Keep lock on target rel until end of xact */ + /* Keep lock on trigger's rel until end of xact */ heap_close(rel, NoLock); } -/* - * Remove all triggers for a relation that's being deleted. - */ -void -RelationRemoveTriggers(Relation rel) -{ - Relation tgrel; - SysScanDesc tgscan; - ScanKeyData key; - HeapTuple tup; - bool found = false; - - tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, - SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tup = systable_getnext(tgscan))) - { - /* Delete any comments associated with this trigger */ - DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel)); - - simple_heap_delete(tgrel, &tup->t_self); - - found = true; - } - - systable_endscan(tgscan); - - /* - * If we deleted any triggers, must update pg_class entry and advance - * command counter to make the updated entry visible. This is fairly - * annoying, since we'e just going to drop the durn thing later, but - * it's necessary to have a consistent state in case we do - * CommandCounterIncrement() below --- if RelationBuildTriggers() - * runs, it will complain otherwise. Perhaps RelationBuildTriggers() - * shouldn't be so picky... - */ - if (found) - { - Relation pgrel; - Relation ridescs[Num_pg_class_indices]; - - pgrel = heap_openr(RelationRelationName, RowExclusiveLock); - tup = SearchSysCacheCopy(RELOID, - ObjectIdGetDatum(RelationGetRelid(rel)), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class", - RelationGetRelid(rel)); - - ((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0; - simple_heap_update(pgrel, &tup->t_self, tup); - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup); - CatalogCloseIndices(Num_pg_class_indices, ridescs); - heap_freetuple(tup); - heap_close(pgrel, RowExclusiveLock); - CommandCounterIncrement(); - } - - /* - * Also drop all constraint triggers referencing this relation - */ - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgconstrrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true, - SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tup = systable_getnext(tgscan))) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup); - - elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"", - get_rel_name(pg_trigger->tgrelid)); - - DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname)); - - /* - * Need to do a command counter increment here to show up new - * pg_class.reltriggers in the next loop iteration (in case there - * are multiple referential integrity action triggers for the same - * FK table defined on the PK table). - */ - CommandCounterIncrement(); - } - systable_endscan(tgscan); - - heap_close(tgrel, RowExclusiveLock); -} - /* * renametrig - changes the name of a trigger on a relation * diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index a9b468558107807c82b37e4466db8cf2c59465a9..f148ff658918db7401d530d87d1f4cc65f492d0e 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.4 2002/07/01 15:27:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -33,10 +33,10 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_func.h" @@ -262,17 +262,14 @@ DefineType(List *names, List *parameters) /* * RemoveType * Removes a datatype. - * - * NOTE: since this tries to remove the associated array type too, it'll - * only work on scalar types. */ void RemoveType(List *names, DropBehavior behavior) { TypeName *typename; - Relation relation; Oid typeoid; HeapTuple tup; + ObjectAddress object; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeNode(TypeName); @@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior) typename->typmod = -1; typename->arrayBounds = NIL; - relation = heap_openr(TypeRelationName, RowExclusiveLock); - /* Use LookupTypeName here so that shell types can be removed. */ typeoid = LookupTypeName(typename); if (!OidIsValid(typeoid)) @@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); + ReleaseSysCache(tup); - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); + /* + * Do the deletion + */ + object.classId = RelOid_pg_type; + object.objectId = typeoid; + object.objectSubId = 0; - ReleaseSysCache(tup); + performDeletion(&object, behavior); +} - /* Now, delete the "array of" that type */ - typename->arrayBounds = makeList1(makeInteger(1)); - typeoid = LookupTypeName(typename); - 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, - ObjectIdGetDatum(typeoid), + ObjectIdGetDatum(typeOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - DeleteComments(typeoid, RelationGetRelid(relation)); + elog(ERROR, "RemoveTypeById: type %u not found", + typeOid); simple_heap_delete(relation, &tup->t_self); @@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt) HeapTuple typeTup; List *schema = stmt->constraints; List *listptr; + Oid basetypeoid; + Form_pg_type baseType; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, @@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt) */ typeTup = typenameType(stmt->typename); + baseType = (Form_pg_type) GETSTRUCT(typeTup); + basetypeoid = typeTup->t_data->t_oid; + /* * What we really don't want is domains of domains. This could cause all sorts * of neat issues if we allow that. * * With testing, we may determine complex types should be allowed */ - typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype; + typtype = baseType->typtype; if (typtype != 'b') elog(ERROR, "DefineDomain: %s is not a basetype", TypeNameToString(stmt->typename)); /* passed by value */ - byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval; + byValue = baseType->typbyval; /* Required Alignment */ - alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign; + alignment = baseType->typalign; /* TOAST Strategy */ - storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage; + storage = baseType->typstorage; /* Storage Length */ - internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen; + internalLength = baseType->typlen; /* External Length (unused) */ - externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen; + externalLength = baseType->typprtlen; /* Array element Delimiter */ - delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim; + delimiter = baseType->typdelim; /* I/O Functions */ - inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; - outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput; - receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive; - sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend; + inputProcedure = baseType->typinput; + outputProcedure = baseType->typoutput; + receiveProcedure = baseType->typreceive; + sendProcedure = baseType->typsend; /* Inherited default value */ datum = SysCacheGetAttr(TYPEOID, typeTup, @@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt) * * This is what enables us to make a domain of an array */ - basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; + basetypelem = baseType->typelem; /* * Run through constraints manually to avoid the additional @@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt) * Note: Name is strictly for error message */ expr = cookDefault(pstate, colDef->raw_expr, - typeTup->t_data->t_oid, + basetypeoid, stmt->typename->typmod, domainName); /* @@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt) */ TypeCreate(domainName, /* type name */ domainNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* preassigned type oid (none here) */ InvalidOid, /* relation oid (n/a here) */ internalLength, /* internal size */ externalLength, /* external size */ @@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt) receiveProcedure, /* receive procedure */ sendProcedure, /* send procedure */ basetypelem, /* element type ID */ - typeTup->t_data->t_oid, /* base type ID */ + basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ byValue, /* passed by value */ @@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt) /* * RemoveDomain * Removes a domain. + * + * This is identical to RemoveType except we insist it be a domain. */ void RemoveDomain(List *names, DropBehavior behavior) { TypeName *typename; - Relation relation; Oid typeoid; HeapTuple tup; char typtype; - - /* CASCADE unsupported */ - if (behavior == DROP_CASCADE) - elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword"); + ObjectAddress object; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeNode(TypeName); @@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior) typename->typmod = -1; typename->arrayBounds = NIL; - relation = heap_openr(TypeRelationName, RowExclusiveLock); - - typeoid = typenameTypeId(typename); + /* Use LookupTypeName here so that shell types can be removed. */ + typeoid = LookupTypeName(typename); + if (!OidIsValid(typeoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); tup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "RemoveDomain: type '%s' does not exist", + elog(ERROR, "RemoveDomain: type \"%s\" does not exist", TypeNameToString(typename)); /* Permission check: must own type or its namespace */ @@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior) elog(ERROR, "%s is not a domain", TypeNameToString(typename)); - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); - - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); - ReleaseSysCache(tup); - /* At present, domains don't have associated array types */ + /* + * Do the deletion + */ + object.classId = RelOid_pg_type; + object.objectId = typeoid; + object.objectSubId = 0; - heap_close(relation, RowExclusiveLock); + performDeletion(&object, behavior); } diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index d27350fd4677d11b288ced85e3697b251f8aa674..519df157184960b1192f06be935e5db9e0393ae9 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -6,13 +6,14 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.65 2002/07/01 15:27:49 tgl Exp $ + * $Id: view.c,v 1.66 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/xact.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "commands/tablecmds.h" @@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse) * RemoveView * * Remove a view given its name + * + * We just have to drop the relation; the associated rules will be + * cleaned up automatically. */ void RemoveView(const RangeVar *view, DropBehavior behavior) { Oid viewOid; + ObjectAddress object; viewOid = RangeVarGetRelid(view, false); - /* - * We just have to drop the relation; the associated rules will be - * cleaned up automatically. - */ - heap_drop_with_catalog(viewOid, allowSystemTableMods); + + object.classId = RelOid_pg_class; + object.objectId = viewOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 739161b17919f3f88318e7c11ccd4ee46e645198..4e568a3c531ba5daa9bbb13a6b55d631dfe51dd4 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.192 2002/07/01 15:27:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.193 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1505,11 +1505,12 @@ _copyFkConstraint(FkConstraint *from) Node_Copy(from, newnode, pktable); Node_Copy(from, newnode, fk_attrs); Node_Copy(from, newnode, pk_attrs); - if (from->match_type) - newnode->match_type = pstrdup(from->match_type); - newnode->actions = from->actions; + newnode->fk_matchtype = from->fk_matchtype; + newnode->fk_upd_action = from->fk_upd_action; + newnode->fk_del_action = from->fk_del_action; newnode->deferrable = from->deferrable; newnode->initdeferred = from->initdeferred; + newnode->skip_validation = from->skip_validation; return newnode; } @@ -2089,6 +2090,7 @@ _copyIndexStmt(IndexStmt *from) Node_Copy(from, newnode, rangetable); newnode->unique = from->unique; newnode->primary = from->primary; + newnode->isconstraint = from->isconstraint; return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 214493449b96c9ab678be81d0f2fb3ac5af84eaf..e15870b2c800b4f4fc4534317e51a59136a4e0bb 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.139 2002/07/01 15:27:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.140 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -912,6 +912,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b) return false; if (a->primary != b->primary) return false; + if (a->isconstraint != b->isconstraint) + return false; return true; } @@ -1734,14 +1736,18 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b) return false; if (!equal(a->pk_attrs, b->pk_attrs)) return false; - if (!equalstr(a->match_type, b->match_type)) + if (a->fk_matchtype != b->fk_matchtype) return false; - if (a->actions != b->actions) + if (a->fk_upd_action != b->fk_upd_action) + return false; + if (a->fk_del_action != b->fk_del_action) return false; if (a->deferrable != b->deferrable) return false; if (a->initdeferred != b->initdeferred) return false; + if (a->skip_validation != b->skip_validation) + return false; return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2b292635186a5e98b76939e396416dba5a9e37a8..ae3139a6ea65e06f396b25ff5958b90a3b25bd5d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.161 2002/07/04 15:23:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.162 2002/07/12 18:43:16 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -136,9 +136,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node) _outNode(str, node->whereClause); appendStringInfo(str, " :rangetable "); _outNode(str, node->rangetable); - appendStringInfo(str, " :unique %s :primary %s ", + appendStringInfo(str, " :unique %s :primary %s :isconstraint %s ", booltostr(node->unique), - booltostr(node->primary)); + booltostr(node->primary), + booltostr(node->isconstraint)); } static void @@ -1447,12 +1448,13 @@ _outFkConstraint(StringInfo str, FkConstraint *node) _outNode(str, node->fk_attrs); appendStringInfo(str, " :pk_attrs "); _outNode(str, node->pk_attrs); - appendStringInfo(str, " :match_type "); - _outToken(str, node->match_type); - appendStringInfo(str, " :actions %d :deferrable %s :initdeferred %s", - node->actions, + appendStringInfo(str, " :fk_matchtype %c :fk_upd_action %c :fk_del_action %c :deferrable %s :initdeferred %s :skip_validation %s", + node->fk_matchtype, + node->fk_upd_action, + node->fk_del_action, booltostr(node->deferrable), - booltostr(node->initdeferred)); + booltostr(node->initdeferred), + booltostr(node->skip_validation)); } static void diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b1b94dc6fdacb85abe40dd2b06f5c61ae6b040e2..2fbca505987b7b84e164d526cdb9dd717cb14e0b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.237 2002/06/20 20:29:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.238 2002/07/12 18:43:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,7 +47,7 @@ /* State shared by transformCreateSchemaStmt and its subroutines */ typedef struct { - const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */ + const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */ char *schemaname; /* name of schema */ char *authid; /* owner of schema */ List *tables; /* CREATE TABLE items */ @@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) cxt->stmtType, (cxt->relation)->relname); cxt->pkey = index; } + index->isconstraint = true; if (constraint->name != NULL) index->idxname = pstrdup(constraint->name); @@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) static void transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt) { - CreateTrigStmt *fk_trigger; List *fkactions = NIL; List *fkclist; - List *fk_attr; - List *pk_attr; - Ident *id; - Oid pktypoid[INDEX_MAX_KEYS]; - Oid fktypoid[INDEX_MAX_KEYS]; - int i; if (cxt->fkconstraints == NIL) return; @@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt) foreach(fkclist, cxt->fkconstraints) { FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist); + Oid pktypoid[INDEX_MAX_KEYS]; + Oid fktypoid[INDEX_MAX_KEYS]; + int i; int attnum; List *fkattrs; - /* - * If the constraint has no name, set it to <unnamed> - */ - if (fkconstraint->constr_name == NULL) - fkconstraint->constr_name = "<unnamed>"; - for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++) pktypoid[attnum] = fktypoid[attnum] = InvalidOid; @@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt) } /* - * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK - * action. + * For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE, + * gin up an ALTER TABLE ADD CONSTRAINT command to execute after + * the basic CREATE TABLE is complete. */ - fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt); - fk_trigger->trigname = fkconstraint->constr_name; - fk_trigger->relation = cxt->relation; - fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins"); - fk_trigger->before = false; - fk_trigger->row = true; - fk_trigger->actions[0] = 'i'; - fk_trigger->actions[1] = 'u'; - fk_trigger->actions[2] = '\0'; - fk_trigger->lang = NULL; - fk_trigger->text = NULL; - - fk_trigger->attr = NIL; - fk_trigger->when = NULL; - fk_trigger->isconstraint = true; - fk_trigger->deferrable = fkconstraint->deferrable; - fk_trigger->initdeferred = fkconstraint->initdeferred; - fk_trigger->constrrel = fkconstraint->pktable; - - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString((cxt->relation)->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->match_type)); - fk_attr = fkconstraint->fk_attrs; - pk_attr = fkconstraint->pk_attrs; - if (length(fk_attr) != length(pk_attr)) - elog(ERROR, "number of key attributes in referenced table must be equal to foreign key" - "\n\tIllegal FOREIGN KEY definition references \"%s\"", - fkconstraint->pktable->relname); - - while (fk_attr != NIL) + if (strcmp(cxt->stmtType, "CREATE TABLE") == 0) { - id = (Ident *) lfirst(fk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); - - id = (Ident *) lfirst(pk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); + AlterTableStmt *alterstmt = makeNode(AlterTableStmt); - fk_attr = lnext(fk_attr); - pk_attr = lnext(pk_attr); - } + alterstmt->subtype = 'c'; /* preprocessed add constraint */ + alterstmt->relation = cxt->relation; + alterstmt->name = NULL; + alterstmt->def = (Node *) makeList1(fkconstraint); - fkactions = lappend(fkactions, (Node *) fk_trigger); + /* Don't need to scan the table contents in this case */ + fkconstraint->skip_validation = true; - /* - * 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; + fkactions = lappend(fkactions, (Node *) alterstmt); } - - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString((cxt->relation)->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->match_type)); - fk_attr = fkconstraint->fk_attrs; - pk_attr = fkconstraint->pk_attrs; - while (fk_attr != NIL) - { - id = (Ident *) lfirst(fk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); - - id = (Ident *) lfirst(pk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); - - fk_attr = lnext(fk_attr); - pk_attr = lnext(pk_attr); - } - - fkactions = lappend(fkactions, (Node *) fk_trigger); - - /* - * Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE - * action fired on the PK table !!! - */ - fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt); - fk_trigger->trigname = fkconstraint->constr_name; - fk_trigger->relation = fkconstraint->pktable; - fk_trigger->before = false; - fk_trigger->row = true; - fk_trigger->actions[0] = 'u'; - fk_trigger->actions[1] = '\0'; - fk_trigger->lang = NULL; - fk_trigger->text = NULL; - - fk_trigger->attr = NIL; - fk_trigger->when = NULL; - fk_trigger->isconstraint = true; - fk_trigger->deferrable = fkconstraint->deferrable; - fk_trigger->initdeferred = fkconstraint->initdeferred; - fk_trigger->constrrel = cxt->relation; - switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK) - >> FKCONSTR_ON_UPDATE_SHIFT) - { - case FKCONSTR_ON_KEY_NOACTION: - fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd"); - break; - case FKCONSTR_ON_KEY_RESTRICT: - fk_trigger->deferrable = false; - fk_trigger->initdeferred = false; - fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd"); - break; - case FKCONSTR_ON_KEY_CASCADE: - fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd"); - break; - case FKCONSTR_ON_KEY_SETNULL: - fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd"); - break; - case FKCONSTR_ON_KEY_SETDEFAULT: - fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd"); - break; - default: - elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint"); - break; - } - - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString((cxt->relation)->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->match_type)); - fk_attr = fkconstraint->fk_attrs; - pk_attr = fkconstraint->pk_attrs; - while (fk_attr != NIL) - { - id = (Ident *) lfirst(fk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); - - id = (Ident *) lfirst(pk_attr); - fk_trigger->args = lappend(fk_trigger->args, - makeString(id->name)); - - fk_attr = lnext(fk_attr); - pk_attr = lnext(pk_attr); - } - - fkactions = lappend(fkactions, (Node *) fk_trigger); } /* @@ -2642,6 +2454,14 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt, *extras_after = nconc(cxt.alist, *extras_after); break; + case 'c': + /* + * Already-transformed ADD CONSTRAINT, so just make it look + * like the standard case. + */ + stmt->subtype = 'C'; + break; + default: break; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 18b349c4ee2f211cc18ba29813ec7f12113fba28..17128a814e534411613e99244e9d411ad4334c4a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.338 2002/07/11 07:39:25 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.339 2002/07/12 18:43:17 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -309,8 +309,7 @@ static void doNegateFloat(Value *v); %type <node> TableConstraint, TableLikeClause %type <list> ColQualList %type <node> ColConstraint, ColConstraintElem, ConstraintAttr -%type <ival> key_actions, key_delete, key_update, key_reference -%type <str> key_match +%type <ival> key_actions, key_delete, key_match, key_update, key_action %type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec, ConstraintTimeSpec @@ -1594,8 +1593,9 @@ ColConstraintElem: n->pktable = $2; n->fk_attrs = NIL; n->pk_attrs = $3; - n->match_type = $4; - n->actions = $5; + n->fk_matchtype = $4; + n->fk_upd_action = (char) ($5 >> 8); + n->fk_del_action = (char) ($5 & 0xFF); n->deferrable = FALSE; n->initdeferred = FALSE; $$ = (Node *)n; @@ -1714,16 +1714,16 @@ ConstraintElem: $$ = (Node *)n; } | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name - opt_column_list - key_match key_actions ConstraintAttributeSpec + opt_column_list key_match key_actions ConstraintAttributeSpec { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; n->pktable = $7; n->fk_attrs = $4; n->pk_attrs = $8; - n->match_type = $9; - n->actions = $10; + n->fk_matchtype = $9; + n->fk_upd_action = (char) ($10 >> 8); + n->fk_del_action = (char) ($10 & 0xFF); n->deferrable = ($11 & 1) != 0; n->initdeferred = ($11 & 2) != 0; $$ = (Node *)n; @@ -1750,45 +1750,54 @@ columnElem: ColId key_match: MATCH FULL { - $$ = "FULL"; + $$ = FKCONSTR_MATCH_FULL; } | MATCH PARTIAL { elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented"); - $$ = "PARTIAL"; + $$ = FKCONSTR_MATCH_PARTIAL; } | MATCH SIMPLE { - $$ = "UNSPECIFIED"; + $$ = FKCONSTR_MATCH_UNSPECIFIED; } | /*EMPTY*/ { - $$ = "UNSPECIFIED"; + $$ = FKCONSTR_MATCH_UNSPECIFIED; } ; +/* + * We combine the update and delete actions into one value temporarily + * for simplicity of parsing, and then break them down again in the + * calling production. update is in the left 8 bits, delete in the right. + * Note that NOACTION is the default. + */ key_actions: - key_delete { $$ = $1; } - | key_update { $$ = $1; } - | key_delete key_update { $$ = $1 | $2; } - | key_update key_delete { $$ = $1 | $2; } - | /*EMPTY*/ { $$ = 0; } + key_update + { $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); } + | key_delete + { $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); } + | key_update key_delete + { $$ = ($1 << 8) | ($2 & 0xFF); } + | key_delete key_update + { $$ = ($2 << 8) | ($1 & 0xFF); } + | /*EMPTY*/ + { $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); } ; -key_delete: ON DELETE_P key_reference - { $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; } +key_update: ON UPDATE key_action { $$ = $3; } ; -key_update: ON UPDATE key_reference - { $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; } +key_delete: ON DELETE_P key_action { $$ = $3; } ; -key_reference: - NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; } - | RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; } - | CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; } - | SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; } - | SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; } +key_action: + NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; } + | RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; } + | CASCADE { $$ = FKCONSTR_ACTION_CASCADE; } + | SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; } + | SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; } ; OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } @@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; } | INDEX { $$ = DROP_INDEX; } | TYPE_P { $$ = DROP_TYPE; } | DOMAIN_P { $$ = DROP_DOMAIN; } - | CONVERSION_P { $$ = DROP_CONVERSION; } + | CONVERSION_P { $$ = DROP_CONVERSION; } ; any_name_list: @@ -6673,6 +6682,7 @@ unreserved_keyword: | COMMIT | COMMITTED | CONSTRAINTS + | CONVERSION_P | COPY | CREATEDB | CREATEUSER @@ -6857,6 +6867,7 @@ col_name_keyword: | SUBSTRING | TIME | TIMESTAMP + | TREAT | TRIM | VARCHAR ; @@ -6963,7 +6974,6 @@ reserved_keyword: | THEN | TO | TRAILING - | TREAT | TRUE_P | UNION | UNIQUE diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 43f8ffd6f1f8f1bb5cf5cb1d569cd4c2e05caecd..5922cb4ab5b67ff1766ab780aaf0b740fc068924 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.73 2002/06/20 20:29:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.74 2002/07/12 18:43:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_rewrite.h" #include "commands/view.h" @@ -57,6 +58,8 @@ InsertRule(char *rulname, TupleDesc tupDesc; HeapTuple tup; Oid rewriteObjectId; + ObjectAddress myself, + referenced; if (IsDefinedRewriteRule(eventrel_oid, rulname)) elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists", @@ -103,6 +106,23 @@ InsertRule(char *rulname, heap_freetuple(tup); + /* + * Install dependency on rule's relation to ensure it will go away + * on relation deletion. If the rule is ON SELECT, make the dependency + * implicit --- this prevents deleting a view's SELECT rule. Other + * kinds of rules can be AUTO. + */ + myself.classId = RelationGetRelid(pg_rewrite_desc); + myself.objectId = rewriteObjectId; + myself.objectSubId = 0; + + referenced.classId = RelOid_pg_class; + referenced.objectId = eventrel_oid; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, + (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); + heap_close(pg_rewrite_desc, RowExclusiveLock); return rewriteObjectId; diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index 71fd4d5ac407c7bd9ad0f112442393d86cda86b8..4ce9f3f2b908aa605648cf31746ae57404eaf0ed 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.50 2002/06/20 20:29:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.51 2002/07/12 18:43:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,14 +17,15 @@ #include "access/genam.h" #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_rewrite.h" -#include "commands/comment.h" #include "miscadmin.h" #include "rewrite/rewriteRemove.h" #include "rewrite/rewriteSupport.h" #include "utils/acl.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" @@ -34,77 +35,102 @@ * Delete a rule given its name. */ void -RemoveRewriteRule(Oid owningRel, const char *ruleName) +RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior) { - Relation RewriteRelation; - Relation event_relation; HeapTuple tuple; - Oid ruleId; Oid eventRelationOid; - bool hasMoreRules; AclResult aclresult; - - /* - * Open the pg_rewrite relation. - */ - RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock); + ObjectAddress object; /* * Find the tuple for the target rule. */ - tuple = SearchSysCacheCopy(RULERELNAME, - ObjectIdGetDatum(owningRel), - PointerGetDatum(ruleName), - 0, 0); + tuple = SearchSysCache(RULERELNAME, + ObjectIdGetDatum(owningRel), + PointerGetDatum(ruleName), + 0, 0); /* - * complain if no rule with such name existed + * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) elog(ERROR, "Rule \"%s\" not found", ruleName); /* - * Save the OID of the rule (i.e. the tuple's OID) and the event - * relation's OID + * Verify user has appropriate permissions. */ - ruleId = tuple->t_data->t_oid; 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)); /* - * We had better grab AccessExclusiveLock so that we know no other - * rule additions/deletions are going on for this relation. Else we - * cannot set relhasrules correctly. Besides, we don't want to be - * changing the ruleset while queries are executing on the rel. + * Do the deletion */ - event_relation = heap_open(eventRelationOid, AccessExclusiveLock); + object.classId = get_system_catalog_relid(RewriteRelationName); + object.objectId = tuple->t_data->t_oid; + object.objectSubId = 0; + + ReleaseSysCache(tuple); + + performDeletion(&object, behavior); +} + + +/* + * Guts of rule deletion. + */ +void +RemoveRewriteRuleById(Oid ruleOid) +{ + Relation RewriteRelation; + ScanKeyData skey[1]; + SysScanDesc rcscan; + Relation event_relation; + HeapTuple tuple; + Oid eventRelationOid; + bool hasMoreRules; /* - * Verify user has appropriate permissions. + * Open the pg_rewrite relation. */ - aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, RelationGetRelationName(event_relation)); + RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock); + + /* + * Find the tuple for the target rule. + */ + ScanKeyEntryInitialize(&skey[0], 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(ruleOid)); - /* 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"); + rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true, + SnapshotNow, 1, skey); - hasMoreRules = event_relation->rd_rules != NULL && - event_relation->rd_rules->numLocks > 1; + tuple = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "RemoveRewriteRuleById: Rule %u does not exist", + ruleOid); /* - * Delete any comments associated with this rule + * We had better grab AccessExclusiveLock so that we know no other + * rule additions/deletions are going on for this relation. Else we + * cannot set relhasrules correctly. Besides, we don't want to be + * changing the ruleset while queries are executing on the rel. */ - DeleteComments(ruleId, RelationGetRelid(RewriteRelation)); + eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; + event_relation = heap_open(eventRelationOid, AccessExclusiveLock); + + hasMoreRules = event_relation->rd_rules != NULL && + event_relation->rd_rules->numLocks > 1; /* * Now delete the pg_rewrite tuple for the rule */ simple_heap_delete(RewriteRelation, &tuple->t_self); - heap_freetuple(tuple); + systable_endscan(rcscan); heap_close(RewriteRelation, RowExclusiveLock); @@ -120,49 +146,3 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName) /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); } - -/* - * RelationRemoveRules - - * removes all rules associated with the relation when the relation is - * being removed. - */ -void -RelationRemoveRules(Oid relid) -{ - Relation RewriteRelation; - SysScanDesc scanDesc; - ScanKeyData scanKeyData; - HeapTuple tuple; - - /* - * Open the pg_rewrite relation. - */ - RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock); - - /* - * Scan pg_rewrite for all the tuples that have the same ev_class - * as relid (the relation to be removed). - */ - ScanKeyEntryInitialize(&scanKeyData, - 0, - Anum_pg_rewrite_ev_class, - F_OIDEQ, - ObjectIdGetDatum(relid)); - - scanDesc = systable_beginscan(RewriteRelation, - RewriteRelRulenameIndex, - true, SnapshotNow, - 1, &scanKeyData); - - while (HeapTupleIsValid(tuple = systable_getnext(scanDesc))) - { - /* Delete any comments associated with this rule */ - DeleteComments(tuple->t_data->t_oid, RelationGetRelid(RewriteRelation)); - - simple_heap_delete(RewriteRelation, &tuple->t_self); - } - - systable_endscan(scanDesc); - - heap_close(RewriteRelation, RowExclusiveLock); -} diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c index ba590b8cc6693a856f684b4c73b1aace2f46b343..3f4c7f23871962a7b587a40982d0f784a7f5fe75 100644 --- a/src/backend/rewrite/rewriteSupport.c +++ b/src/backend/rewrite/rewriteSupport.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.52 2002/06/20 20:29:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.53 2002/07/12 18:43:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "catalog/catname.h" #include "catalog/indexing.h" #include "rewrite/rewriteSupport.h" +#include "utils/inval.h" #include "utils/syscache.h" @@ -44,9 +45,8 @@ IsDefinedRewriteRule(Oid owningRel, const char *ruleName) * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache * entries to be flushed or updated with the new set of rules for the table. - * Therefore, we execute the update even if relhasrules has the right value - * already. Possible future improvement: skip the disk update and just send - * an SI message in that case. + * This must happen even if we find that no change is needed in the pg_class + * row. */ void SetRelationRuleStatus(Oid relationId, bool relHasRules, @@ -54,6 +54,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules, { Relation relationRelation; HeapTuple tuple; + Form_pg_class classForm; Relation idescs[Num_pg_class_indices]; /* @@ -66,18 +67,28 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules, 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId); + classForm = (Form_pg_class) GETSTRUCT(tuple); - /* Do the update */ - ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules; - if (relIsBecomingView) - ((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW; + if (classForm->relhasrules != relHasRules || + (relIsBecomingView && classForm->relkind != RELKIND_VIEW)) + { + /* Do the update */ + classForm->relhasrules = relHasRules; + if (relIsBecomingView) + classForm->relkind = RELKIND_VIEW; - simple_heap_update(relationRelation, &tuple->t_self, tuple); + simple_heap_update(relationRelation, &tuple->t_self, tuple); - /* Keep the catalog indices up to date */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple); - CatalogCloseIndices(Num_pg_class_indices, idescs); + /* Keep the catalog indices up to date */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + } + else + { + /* no need to change tuple, but force relcache rebuild anyway */ + CacheInvalidateRelcache(relationId); + } heap_freetuple(tuple); heap_close(relationRelation, RowExclusiveLock); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index e5e57d4a6de8ce8f6685ba12e242a283f0e9af22..8637394366703bee1a5a7d8459c90f832eeda8b7 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.161 2002/07/11 07:39:26 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.162 2002/07/12 18:43:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -582,6 +582,7 @@ ProcessUtility(Node *parsetree, stmt->indexParams, /* parameters */ stmt->unique, stmt->primary, + stmt->isconstraint, (Expr *) stmt->whereClause, stmt->rangetable); } @@ -596,19 +597,11 @@ ProcessUtility(Node *parsetree, break; case T_RemoveAggrStmt: - { - RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree; - - RemoveAggregate(stmt->aggname, stmt->aggtype); - } + RemoveAggregate((RemoveAggrStmt *) parsetree); break; case T_RemoveFuncStmt: - { - RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree; - - RemoveFunction(stmt->funcname, stmt->args); - } + RemoveFunction((RemoveFuncStmt *) parsetree); break; case T_RemoveOperStmt: @@ -719,7 +712,7 @@ ProcessUtility(Node *parsetree, break; case T_CreateTrigStmt: - CreateTrigger((CreateTrigStmt *) parsetree); + CreateTrigger((CreateTrigStmt *) parsetree, false); break; case T_DropPropertyStmt: @@ -733,11 +726,13 @@ ProcessUtility(Node *parsetree, { case DROP_RULE: /* RemoveRewriteRule checks permissions */ - RemoveRewriteRule(relId, stmt->property); + RemoveRewriteRule(relId, stmt->property, + stmt->behavior); break; case DROP_TRIGGER: /* DropTrigger checks permissions */ - DropTrigger(relId, stmt->property); + DropTrigger(relId, stmt->property, + stmt->behavior); break; } } diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 5063225afce29cd00eaad96f08248726d850d54c..e383ab892d1420b8b695dd1f26a75c3adf252063 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.75 2002/07/06 20:16:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -671,6 +671,25 @@ get_relname_relid(const char *relname, Oid relnamespace) 0, 0); } +/* + * get_system_catalog_relid + * Get the OID of a system catalog identified by name. + */ +Oid +get_system_catalog_relid(const char *catname) +{ + Oid relid; + + relid = GetSysCacheOid(RELNAMENSP, + PointerGetDatum(catname), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE), + 0, 0); + if (!OidIsValid(relid)) + elog(ERROR, "get_system_catalog_relid: cannot find %s", catname); + + return relid; +} + #ifdef NOT_USED /* * get_relnatts @@ -1060,7 +1079,7 @@ getBaseType(Oid typid) /* * getBaseTypeTypeMod * If the given type is a domain, return its base type; - * otherwise return the type's own OID. + * otherwise return the type's own OID. Also return base typmod. */ Oid getBaseTypeTypeMod(Oid typid, int32 *typmod) @@ -1077,7 +1096,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod) ObjectIdGetDatum(typid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "getBaseType: failed to lookup type %u", typid); + elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid); typTup = (Form_pg_type) GETSTRUCT(tup); if (typTup->typtype != 'd') { diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index ec70be31b6742e9387ed35e07e982c9a5131e09f..9714f4ba3aa7ea4596c6446d8d27075f6b6c2737 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.165 2002/06/20 20:29:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.166 2002/07/12 18:43:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,11 +43,11 @@ #include "catalog/pg_amproc.h" #include "catalog/pg_attrdef.h" #include "catalog/pg_attribute.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_index.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" -#include "catalog/pg_relcheck.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_type.h" #include "commands/trigger.h" @@ -296,7 +296,7 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo, Relation oldrelation); static void AttrDefaultFetch(Relation relation); -static void RelCheckFetch(Relation relation); +static void CheckConstraintFetch(Relation relation); static List *insert_ordered_oid(List *list, Oid datum); static void IndexSupportInitialize(Form_pg_index iform, IndexStrategy indexStrategy, @@ -451,7 +451,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) * RelationBuildTupleDesc * * Form the relation's tuple descriptor from information in - * the pg_attribute, pg_attrdef & pg_relcheck system catalogs. + * the pg_attribute, pg_attrdef & pg_constraint system catalogs. */ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, @@ -603,7 +603,7 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, MemoryContextAlloc(CacheMemoryContext, constr->num_check * sizeof(ConstrCheck)); MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck)); - RelCheckFetch(relation); + CheckConstraintFetch(relation); } else constr->num_check = 0; @@ -2483,62 +2483,60 @@ AttrDefaultFetch(Relation relation) } static void -RelCheckFetch(Relation relation) +CheckConstraintFetch(Relation relation) { ConstrCheck *check = relation->rd_att->constr->check; int ncheck = relation->rd_att->constr->num_check; - Relation rcrel; - SysScanDesc rcscan; - ScanKeyData skey; + Relation conrel; + SysScanDesc conscan; + ScanKeyData skey[1]; HeapTuple htup; - Name rcname; Datum val; bool isnull; - int found; + int found = 0; - ScanKeyEntryInitialize(&skey, - (bits16) 0x0, - (AttrNumber) Anum_pg_relcheck_rcrelid, - (RegProcedure) F_OIDEQ, + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_constraint_conrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); - rcrel = heap_openr(RelCheckRelationName, AccessShareLock); - rcscan = systable_beginscan(rcrel, RelCheckIndex, true, - SnapshotNow, - 1, &skey); - found = 0; + conrel = heap_openr(ConstraintRelationName, AccessShareLock); + conscan = systable_beginscan(conrel, ConstraintRelidIndex, true, + SnapshotNow, 1, skey); - while (HeapTupleIsValid(htup = systable_getnext(rcscan))) + while (HeapTupleIsValid(htup = systable_getnext(conscan))) { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup); + + /* We want check constraints only */ + if (conform->contype != CONSTRAINT_CHECK) + continue; + if (found == ncheck) - elog(ERROR, "RelCheckFetch: unexpected record found for rel %s", + elog(ERROR, "CheckConstraintFetch: unexpected record found for rel %s", RelationGetRelationName(relation)); - rcname = (Name) fastgetattr(htup, - Anum_pg_relcheck_rcname, - rcrel->rd_att, &isnull); - if (isnull) - elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s", - RelationGetRelationName(relation)); check[found].ccname = MemoryContextStrdup(CacheMemoryContext, - NameStr(*rcname)); + NameStr(conform->conname)); + + /* Grab and test conbin is actually set */ val = fastgetattr(htup, - Anum_pg_relcheck_rcbin, - rcrel->rd_att, &isnull); + Anum_pg_constraint_conbin, + conrel->rd_att, &isnull); if (isnull) - elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s", + elog(ERROR, "CheckConstraintFetch: conbin IS NULL for rel %s", RelationGetRelationName(relation)); + check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, DatumGetCString(DirectFunctionCall1(textout, val))); found++; } - systable_endscan(rcscan); - heap_close(rcrel, AccessShareLock); + systable_endscan(conscan); + heap_close(conrel, AccessShareLock); if (found != ncheck) - elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s", + elog(ERROR, "CheckConstraintFetch: %d record(s) not found for rel %s", ncheck - found, RelationGetRelationName(relation)); } diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 3e7ccceae9c2fb43ecdb221826bb325f2e10a103..144dad4dbab9ac19a129ee999d904886263f144c 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.156 2002/06/20 20:29:41 momjian Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.157 2002/07/12 18:43:18 tgl Exp $ # #------------------------------------------------------------------------- @@ -682,11 +682,11 @@ $ECHO_N "enabling unlimited row size for system tables... "$ECHO_C "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF ALTER TABLE pg_attrdef CREATE TOAST TABLE; +ALTER TABLE pg_constraint CREATE TOAST TABLE; ALTER TABLE pg_database CREATE TOAST TABLE; ALTER TABLE pg_description CREATE TOAST TABLE; ALTER TABLE pg_group CREATE TOAST TABLE; ALTER TABLE pg_proc CREATE TOAST TABLE; -ALTER TABLE pg_relcheck CREATE TOAST TABLE; ALTER TABLE pg_rewrite CREATE TOAST TABLE; ALTER TABLE pg_shadow CREATE TOAST TABLE; ALTER TABLE pg_statistic CREATE TOAST TABLE; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 01ec480e3f1089cd6bf5532a555ac1fa98343611..dd6aad5503df8dc10e0a2d7ac30d14baf9eafff4 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.270 2002/07/04 15:35:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.271 2002/07/12 18:43:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4656,8 +4656,8 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo) if (tbinfo->ncheck > 0) { PGresult *res2; - int i_rcname, - i_rcsrc; + int i_conname, + i_consrc; int ntups2; if (g_verbose) @@ -4666,24 +4666,25 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo) resetPQExpBuffer(query); if (g_fout->remoteVersion >= 70300) - appendPQExpBuffer(query, "SELECT rcname, rcsrc" - " from pg_catalog.pg_relcheck c1" - " where rcrelid = '%s'::pg_catalog.oid " + appendPQExpBuffer(query, "SELECT conname, consrc" + " from pg_catalog.pg_constraint c1" + " where conrelid = '%s'::pg_catalog.oid " + " and contype = 'c' " " and not exists " " (select 1 from " - " pg_catalog.pg_relcheck c2, " + " pg_catalog.pg_constraint c2, " " pg_catalog.pg_inherits i " - " where i.inhrelid = c1.rcrelid " - " and (c2.rcname = c1.rcname " - " or (c2.rcname[0] = '$' " - " and c1.rcname[0] = '$')" + " where i.inhrelid = c1.conrelid " + " and (c2.conname = c1.conname " + " or (c2.conname[0] = '$' " + " and c1.conname[0] = '$')" " )" - " and c2.rcsrc = c1.rcsrc " - " and c2.rcrelid = i.inhparent) " - " order by rcname ", + " and c2.consrc = c1.consrc " + " and c2.conrelid = i.inhparent) " + " order by conname ", tbinfo->oid); else - appendPQExpBuffer(query, "SELECT rcname, rcsrc" + appendPQExpBuffer(query, "SELECT rcname as conname, rcsrc as consrc" " from pg_relcheck c1" " where rcrelid = '%s'::oid " " and not exists " @@ -4714,13 +4715,13 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo) exit_nicely(); } - i_rcname = PQfnumber(res2, "rcname"); - i_rcsrc = PQfnumber(res2, "rcsrc"); + i_conname = PQfnumber(res2, "conname"); + i_consrc = PQfnumber(res2, "consrc"); for (j = 0; j < ntups2; j++) { - const char *name = PQgetvalue(res2, j, i_rcname); - const char *expr = PQgetvalue(res2, j, i_rcsrc); + const char *name = PQgetvalue(res2, j, i_conname); + const char *expr = PQgetvalue(res2, j, i_consrc); if (actual_atts + j > 0) appendPQExpBuffer(q, ",\n\t"); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 297875e0becaab36eaca1e780a432bfc8c96dd78..d3c386284b42fc36cde9d0dcd815b03217934f91 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.54 2002/05/13 17:45:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.55 2002/07/12 18:43:19 tgl Exp $ */ #include "postgres_fe.h" #include "describe.h" @@ -747,7 +747,7 @@ describeTableDetails(const char *name, bool desc) *result3 = NULL, *result4 = NULL; int index_count = 0, - constr_count = 0, + check_count = 0, rule_count = 0, trigger_count = 0; int count_footers = 0; @@ -770,19 +770,20 @@ describeTableDetails(const char *name, bool desc) index_count = PQntuples(result1); } - /* count table (and column) constraints */ + /* count table (and column) check constraints */ if (tableinfo.checks) { printfPQExpBuffer(&buf, - "SELECT rcsrc, rcname\n" - "FROM pg_relcheck r, pg_class c\n" - "WHERE c.relname='%s' AND c.oid = r.rcrelid", + "SELECT consrc, conname\n" + "FROM pg_constraint r, pg_class c\n" + "WHERE c.relname='%s' AND c.oid = r.conrelid\n" + "AND r.contype = 'c'", name); result2 = PSQLexec(buf.data); if (!result2) goto error_return; else - constr_count = PQntuples(result2); + check_count = PQntuples(result2); } /* count rules */ @@ -815,7 +816,7 @@ describeTableDetails(const char *name, bool desc) trigger_count = PQntuples(result4); } - footers = xmalloc((index_count + constr_count + rule_count + trigger_count + 1) + footers = xmalloc((index_count + check_count + rule_count + trigger_count + 1) * sizeof(*footers)); /* print indexes */ @@ -846,8 +847,8 @@ describeTableDetails(const char *name, bool desc) } - /* print constraints */ - for (i = 0; i < constr_count; i++) + /* print check constraints */ + for (i = 0; i < check_count; i++) { char *s = _("Check constraints"); diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index e6e0b1aaebb846b96d120b3d12ca0e803273e8fd..0f277d1c9e8ebaa859a4c8710c347c30930d00ef 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: tupdesc.h,v 1.35 2002/06/20 20:29:43 momjian Exp $ + * $Id: tupdesc.h,v 1.36 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,7 +43,7 @@ typedef struct tupleConstr /* * This structure contains all information (i.e. from Classes - * pg_attribute, pg_attrdef, pg_relcheck) for a tuple. + * pg_attribute, pg_attrdef, pg_constraint) for a tuple. */ typedef struct tupleDesc { diff --git a/src/include/catalog/catname.h b/src/include/catalog/catname.h index 6f755c9f9dc77302d845db3274cdaf10260abf1f..0e452a9ca3e7c3f27fac9ac3e96b5487fd74158d 100644 --- a/src/include/catalog/catname.h +++ b/src/include/catalog/catname.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catname.h,v 1.27 2002/07/11 07:39:27 ishii Exp $ + * $Id: catname.h,v 1.28 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,8 +20,10 @@ #define AccessMethodOperatorRelationName "pg_amop" #define AccessMethodProcedureRelationName "pg_amproc" #define AttributeRelationName "pg_attribute" +#define ConstraintRelationName "pg_constraint" #define ConversionRelationName "pg_conversion" #define DatabaseRelationName "pg_database" +#define DependRelationName "pg_depend" #define DescriptionRelationName "pg_description" #define GroupRelationName "pg_group" #define IndexRelationName "pg_index" @@ -40,7 +42,6 @@ #define TypeRelationName "pg_type" #define VersionRelationName "pg_version" #define AttrDefaultRelationName "pg_attrdef" -#define RelCheckRelationName "pg_relcheck" #define TriggerRelationName "pg_trigger" #endif /* CATNAME_H */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 81f52bd9b34565ea2aaf031840ff6ba1ade38733..9d3acbe35f558ebbf77884997d6af9c5fb69a9e5 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.138 2002/07/11 07:39:27 ishii Exp $ + * $Id: catversion.h,v 1.139 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200207111 +#define CATALOG_VERSION_NO 200207112 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h new file mode 100644 index 0000000000000000000000000000000000000000..c890acb687a9ca7b56cf8b7bcc10d23319d47808 --- /dev/null +++ b/src/include/catalog/dependency.h @@ -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 */ diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index a9570dd3ab510c367fb969dd3d0af1a30f0ecf33..5cf87e2631f4af9f95822ede4351fec3c8668c1c 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: heap.h,v 1.51 2002/06/20 20:29:43 momjian Exp $ + * $Id: heap.h,v 1.52 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,7 +44,7 @@ extern Oid heap_create_with_catalog(const char *relname, bool relhasoids, bool allow_system_table_mods); -extern void heap_drop_with_catalog(Oid rid, bool allow_system_table_mods); +extern void heap_drop_with_catalog(Oid rid); extern void heap_truncate(Oid rid); @@ -58,7 +58,8 @@ extern Node *cookDefault(ParseState *pstate, int32 atttypmod, char *attname); -extern int RemoveCheckConstraint(Relation rel, const char *constrName, bool inh); +extern int RemoveRelConstraints(Relation rel, const char *constrName, + DropBehavior behavior); extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, bool relhasoids); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index fb4afac8a2d4a4eff3167c7eb3e9cee3d0e89009..738f1079dc27c9b8542ff8be8513983363737015 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: index.h,v 1.48 2002/06/20 20:29:43 momjian Exp $ + * $Id: index.h,v 1.49 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,6 +35,7 @@ extern Oid index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid *classObjectId, bool primary, + bool isconstraint, bool allow_system_table_mods); extern void index_drop(Oid indexId); diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 9413e5c8586dd27a7ed006e6bd575848c7b438fd..d4ca744172b621279ac10331b7ffa37008db20b6 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: indexing.h,v 1.68 2002/07/11 07:39:27 ishii Exp $ + * $Id: indexing.h,v 1.69 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,8 +27,10 @@ #define Num_pg_attr_indices 2 #define Num_pg_attrdef_indices 1 #define Num_pg_class_indices 2 +#define Num_pg_constraint_indices 3 #define Num_pg_conversion_indices 2 #define Num_pg_database_indices 2 +#define Num_pg_depend_indices 2 #define Num_pg_description_indices 1 #define Num_pg_group_indices 2 #define Num_pg_index_indices 2 @@ -39,7 +41,6 @@ #define Num_pg_opclass_indices 2 #define Num_pg_operator_indices 2 #define Num_pg_proc_indices 2 -#define Num_pg_relcheck_indices 1 #define Num_pg_rewrite_indices 2 #define Num_pg_shadow_indices 2 #define Num_pg_statistic_indices 1 @@ -60,10 +61,15 @@ #define AttributeRelidNumIndex "pg_attribute_relid_attnum_index" #define ClassNameNspIndex "pg_class_relname_nsp_index" #define ClassOidIndex "pg_class_oid_index" +#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index" +#define ConstraintOidIndex "pg_constraint_oid_index" +#define ConstraintRelidIndex "pg_constraint_conrelid_index" #define ConversionDefaultIndex "pg_conversion_default_index" #define ConversionNameNspIndex "pg_conversion_name_nsp_index" #define DatabaseNameIndex "pg_database_datname_index" #define DatabaseOidIndex "pg_database_oid_index" +#define DependDependerIndex "pg_depend_depender_index" +#define DependReferenceIndex "pg_depend_reference_index" #define DescriptionObjIndex "pg_description_o_c_o_index" #define GroupNameIndex "pg_group_name_index" #define GroupSysidIndex "pg_group_sysid_index" @@ -81,7 +87,6 @@ #define OperatorOidIndex "pg_operator_oid_index" #define ProcedureNameNspIndex "pg_proc_proname_args_nsp_index" #define ProcedureOidIndex "pg_proc_oid_index" -#define RelCheckIndex "pg_relcheck_rcrelid_index" #define RewriteOidIndex "pg_rewrite_oid_index" #define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index" #define ShadowNameIndex "pg_shadow_usename_index" @@ -102,8 +107,10 @@ extern char *Name_pg_amproc_indices[]; extern char *Name_pg_attr_indices[]; extern char *Name_pg_attrdef_indices[]; extern char *Name_pg_class_indices[]; +extern char *Name_pg_constraint_indices[]; extern char *Name_pg_conversion_indices[]; extern char *Name_pg_database_indices[]; +extern char *Name_pg_depend_indices[]; extern char *Name_pg_description_indices[]; extern char *Name_pg_group_indices[]; extern char *Name_pg_index_indices[]; @@ -114,7 +121,6 @@ extern char *Name_pg_namespace_indices[]; extern char *Name_pg_opclass_indices[]; extern char *Name_pg_operator_indices[]; extern char *Name_pg_proc_indices[]; -extern char *Name_pg_relcheck_indices[]; extern char *Name_pg_rewrite_indices[]; extern char *Name_pg_shadow_indices[]; extern char *Name_pg_statistic_indices[]; @@ -160,10 +166,19 @@ DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnum_index on pg_attribute using btree DECLARE_UNIQUE_INDEX(pg_class_oid_index on pg_class using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname name_ops, relnamespace oid_ops)); /* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops)); +/* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops)); +DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops)); +/* This following index is not used for a cache and is not unique */ DECLARE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops)); DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops)); DECLARE_UNIQUE_INDEX(pg_database_datname_index on pg_database using btree(datname name_ops)); DECLARE_UNIQUE_INDEX(pg_database_oid_index on pg_database using btree(oid oid_ops)); +/* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_depend_depender_index on pg_depend using btree(classid oid_ops, objid oid_ops, objsubid int4_ops)); +/* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_depend_reference_index on pg_depend using btree(refclassid oid_ops, refobjid oid_ops, refobjsubid int4_ops)); DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index on pg_description using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops)); DECLARE_UNIQUE_INDEX(pg_group_name_index on pg_group using btree(groname name_ops)); DECLARE_UNIQUE_INDEX(pg_group_sysid_index on pg_group using btree(grosysid int4_ops)); @@ -183,7 +198,6 @@ DECLARE_UNIQUE_INDEX(pg_operator_oprname_l_r_n_index on pg_operator using btree( DECLARE_UNIQUE_INDEX(pg_proc_oid_index on pg_proc using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index on pg_proc using btree(proname name_ops, pronargs int2_ops, proargtypes oidvector_ops, pronamespace oid_ops)); /* This following index is not used for a cache and is not unique */ -DECLARE_INDEX(pg_relcheck_rcrelid_index on pg_relcheck using btree(rcrelid oid_ops)); DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index on pg_rewrite using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index on pg_rewrite using btree(ev_class oid_ops, rulename name_ops)); DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops)); diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h new file mode 100644 index 0000000000000000000000000000000000000000..ec493146524662f750f51c01644bc476eaee8794 --- /dev/null +++ b/src/include/catalog/pg_constraint.h @@ -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 */ diff --git a/src/include/catalog/pg_depend.h b/src/include/catalog/pg_depend.h new file mode 100644 index 0000000000000000000000000000000000000000..169a98fc2cce219593c295eff52588721655aba9 --- /dev/null +++ b/src/include/catalog/pg_depend.h @@ -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 */ diff --git a/src/include/catalog/pg_relcheck.h b/src/include/catalog/pg_relcheck.h deleted file mode 100644 index f37cbe85b08bdf3de2b625bc383d682d36f7f1ea..0000000000000000000000000000000000000000 --- a/src/include/catalog/pg_relcheck.h +++ /dev/null @@ -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 */ diff --git a/src/include/commands/comment.h b/src/include/commands/comment.h index bf2acfcfa0ed9479f862b2247b0749b179c4a09f..0972c13486a0441db966aee733fdcd516495956a 100644 --- a/src/include/commands/comment.h +++ b/src/include/commands/comment.h @@ -27,7 +27,7 @@ extern void CommentObject(CommentStmt *stmt); -extern void DeleteComments(Oid oid, Oid classoid); +extern void DeleteComments(Oid oid, Oid classoid, int32 subid); extern void CreateComments(Oid oid, Oid classoid, int32 subid, char *comment); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 551deae0956d811be8cc1799faeb5660e0ce7242..169ec3f3dff1dc132428a0600ac5ceaa750c32c1 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: defrem.h,v 1.40 2002/07/01 15:27:56 tgl Exp $ + * $Id: defrem.h,v 1.41 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,7 @@ extern void DefineIndex(RangeVar *heapRelation, List *attributeList, bool unique, bool primary, + bool isconstraint, Expr *predicate, List *rangetable); extern void RemoveIndex(RangeVar *relation, DropBehavior behavior); @@ -39,16 +40,19 @@ extern void ReindexDatabase(const char *databaseName, bool force, bool all); */ extern void CreateFunction(CreateFunctionStmt *stmt); -extern void RemoveFunction(List *functionName, List *argTypes); +extern void RemoveFunction(RemoveFuncStmt *stmt); +extern void RemoveFunctionById(Oid funcOid); extern void DefineOperator(List *names, List *parameters); extern void RemoveOperator(RemoveOperStmt *stmt); +extern void RemoveOperatorById(Oid operOid); extern void DefineAggregate(List *names, List *parameters); -extern void RemoveAggregate(List *aggName, TypeName *aggType); +extern void RemoveAggregate(RemoveAggrStmt *stmt); extern void DefineType(List *names, List *parameters); extern void RemoveType(List *names, DropBehavior behavior); +extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); extern void RemoveDomain(List *names, DropBehavior behavior); diff --git a/src/include/commands/proclang.h b/src/include/commands/proclang.h index 25cf7ab5ef8d590471e50215022235daebd6f5dc..f4fde9b1083eb5907d4d7ee2e63244b47b137afd 100644 --- a/src/include/commands/proclang.h +++ b/src/include/commands/proclang.h @@ -13,5 +13,6 @@ extern void CreateProceduralLanguage(CreatePLangStmt *stmt); extern void DropProceduralLanguage(DropPLangStmt *stmt); +extern void DropProceduralLanguageById(Oid langOid); #endif /* PROCLANG_H */ diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 9ff6c8e1eac8a451bd5661d640a6c81dc6e82c99..b9308cd57f3b7b1e54ed24e5264ad0ec14707632 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: trigger.h,v 1.36 2002/06/20 20:29:49 momjian Exp $ + * $Id: trigger.h,v 1.37 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -101,9 +101,11 @@ typedef struct TriggerData #define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2)) -extern void CreateTrigger(CreateTrigStmt *stmt); -extern void DropTrigger(Oid relid, const char *trigname); -extern void RelationRemoveTriggers(Relation rel); +extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint); + +extern void DropTrigger(Oid relid, const char *trigname, + DropBehavior behavior); +extern void RemoveTriggerById(Oid trigOid); extern void renametrig(Oid relid, const char *oldname, const char *newname); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 19693d57b3affbd60f91e1c79d09cffd1108ea6b..2fec7f66fbd9defc694620bcce3df2c212d7f5b2 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.184 2002/07/11 07:39:27 ishii Exp $ + * $Id: parsenodes.h,v 1.185 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -762,6 +762,8 @@ typedef struct AlterTableStmt * M = alter column storage * D = drop column * C = add constraint + * c = pre-processed add constraint + * (local in parser/analyze.c) * X = drop constraint * E = create toast table * U = change owner @@ -929,31 +931,41 @@ typedef struct Constraint /* ---------- * Definitions for FOREIGN KEY constraints in CreateStmt + * + * Note: FKCONSTR_ACTION_xxx values are stored into pg_constraint.confupdtype + * and pg_constraint.confdeltype columns; FKCONSTR_MATCH_xxx values are + * stored into pg_constraint.confmatchtype. Changing the code values may + * require an initdb! + * + * If skip_validation is true then we skip checking that the existing rows + * in the table satisfy the constraint, and just install the catalog entries + * for the constraint. This is currently used only during CREATE TABLE + * (when we know the table must be empty). * ---------- */ -#define FKCONSTR_ON_KEY_NOACTION 0x0000 -#define FKCONSTR_ON_KEY_RESTRICT 0x0001 -#define FKCONSTR_ON_KEY_CASCADE 0x0002 -#define FKCONSTR_ON_KEY_SETNULL 0x0004 -#define FKCONSTR_ON_KEY_SETDEFAULT 0x0008 - -#define FKCONSTR_ON_DELETE_MASK 0x000F -#define FKCONSTR_ON_DELETE_SHIFT 0 +#define FKCONSTR_ACTION_NOACTION 'a' +#define FKCONSTR_ACTION_RESTRICT 'r' +#define FKCONSTR_ACTION_CASCADE 'c' +#define FKCONSTR_ACTION_SETNULL 'n' +#define FKCONSTR_ACTION_SETDEFAULT 'd' -#define FKCONSTR_ON_UPDATE_MASK 0x00F0 -#define FKCONSTR_ON_UPDATE_SHIFT 4 +#define FKCONSTR_MATCH_FULL 'f' +#define FKCONSTR_MATCH_PARTIAL 'p' +#define FKCONSTR_MATCH_UNSPECIFIED 'u' typedef struct FkConstraint { NodeTag type; - char *constr_name; /* Constraint name */ + char *constr_name; /* Constraint name, or NULL if unnamed */ RangeVar *pktable; /* Primary key table */ List *fk_attrs; /* Attributes of foreign key */ List *pk_attrs; /* Corresponding attrs in PK table */ - char *match_type; /* FULL or PARTIAL */ - int32 actions; /* ON DELETE/UPDATE actions */ + char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */ + char fk_upd_action; /* ON UPDATE action */ + char fk_del_action; /* ON DELETE action */ bool deferrable; /* DEFERRABLE */ bool initdeferred; /* INITIALLY DEFERRED */ + bool skip_validation; /* skip validation of existing rows? */ } FkConstraint; /* ---------------------- @@ -1201,6 +1213,7 @@ typedef struct IndexStmt * transformStmt() */ bool unique; /* is index unique? */ bool primary; /* is index on primary key? */ + bool isconstraint; /* is it from a CONSTRAINT clause? */ } IndexStmt; /* ---------------------- diff --git a/src/include/rewrite/rewriteRemove.h b/src/include/rewrite/rewriteRemove.h index 3af9c15048173d9afb63bdda3a8210f121fde063..664625982d51d863ad686a35dadf84083be2f7b1 100644 --- a/src/include/rewrite/rewriteRemove.h +++ b/src/include/rewrite/rewriteRemove.h @@ -7,14 +7,18 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rewriteRemove.h,v 1.14 2002/06/20 20:29:52 momjian Exp $ + * $Id: rewriteRemove.h,v 1.15 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef REWRITEREMOVE_H #define REWRITEREMOVE_H -extern void RemoveRewriteRule(Oid owningRel, const char *ruleName); -extern void RelationRemoveRules(Oid relid); +#include "nodes/parsenodes.h" + + +extern void RemoveRewriteRule(Oid owningRel, const char *ruleName, + DropBehavior behavior); +extern void RemoveRewriteRuleById(Oid ruleOid); #endif /* REWRITEREMOVE_H */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index ca361ebc2cca1339d9e7a5d410e9b1848078fbfd..dcc310aab5edcee9f9134164aad0cf0af5bb55ce 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.54 2002/07/06 20:16:36 momjian Exp $ + * $Id: lsyscache.h,v 1.55 2002/07/12 18:43:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,7 @@ extern Oid get_func_rettype(Oid funcid); extern bool get_func_retset(Oid funcid); extern char func_volatile(Oid funcid); extern Oid get_relname_relid(const char *relname, Oid relnamespace); +extern Oid get_system_catalog_relid(const char *catname); extern char *get_rel_name(Oid relid); extern Oid get_rel_namespace(Oid relid); extern Oid get_rel_type_id(Oid relid); diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index f2e31919cc5e1556ed555e767bf52e98d4bee729..2dc64f3ccfa58d29a85e5bbd05ed97c788a53985 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -340,8 +340,6 @@ ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not fo DROP TABLE tmp5; DROP TABLE tmp4; DROP TABLE tmp3; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2" DROP TABLE tmp2; -- Foreign key adding test with mixed types -- Note: these tables are TEMP to avoid name conflicts when this test @@ -369,9 +367,9 @@ NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s) -- As should this ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s) -DROP TABLE pktable; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" +DROP TABLE pktable cascade; +NOTICE: Drop cascades to constraint $2 on table fktable +NOTICE: Drop cascades to constraint $1 on table fktable DROP TABLE fktable; CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2)); @@ -382,16 +380,16 @@ ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s) ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer' You will have to retype this query using an explicit cast --- Again, so should this... DROP TABLE FKTABLE; +-- Again, so should this... CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest1, ptest2); NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s) ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer' You will have to retype this query using an explicit cast --- This fails because we mixed up the column ordering DROP TABLE FKTABLE; +-- This fails because we mixed up the column ordering CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest2, ptest1); @@ -445,7 +443,7 @@ create table atacc1 (test int check (test>3), test2 int); alter table atacc1 add check (test2>test); -- should fail for $2 insert into atacc1 (test2, test) values (3, 4); -ERROR: ExecInsert: rejected due to CHECK constraint $2 +ERROR: ExecInsert: rejected due to CHECK constraint $1 drop table atacc1; -- inheritance related tests create table atacc1 (test int); @@ -628,7 +626,7 @@ alter table atacc1 add constraint "atacc1_pkey" primary key (test); NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1' alter table atacc1 alter column test drop not null; ERROR: ALTER TABLE: Attribute "test" is in a primary key -drop index atacc1_pkey; +alter table atacc1 drop constraint "atacc1_pkey"; alter table atacc1 alter column test drop not null; insert into atacc1 values (null); alter table atacc1 alter test set not null; diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 75c83fc7ebf6c24dff7978215c507ef23586b744..915b420b9a28f2e69740af85feb714a1a480457d 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -1,11 +1,13 @@ -- Test Comment / Drop create domain domaindroptest int4; comment on domain domaindroptest is 'About to drop this..'; +-- currently this will be disallowed create domain basetypetest domaindroptest; ERROR: DefineDomain: domaindroptest is not a basetype -drop domain domaindroptest cascade; -ERROR: DROP DOMAIN does not support the CASCADE keyword drop domain domaindroptest; +-- this should fail because already gone +drop domain domaindroptest cascade; +ERROR: Type "domaindroptest" does not exist -- TEST Domains. create domain domainvarchar varchar(5); create domain domainnumeric numeric(8,2); diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index b8be87228bba5eca3c7e457f62fae8482d3e4c29..f5272891b9f1fae1e133c6fd1de62793696d6942 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -22,7 +22,7 @@ INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable -- Check FKTABLE SELECT * FROM FKTABLE; ftest1 | ftest2 @@ -55,9 +55,8 @@ SELECT * FROM FKTABLE; 1 | 3 (3 rows) -DROP TABLE PKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" DROP TABLE FKTABLE; +DROP TABLE PKTABLE; -- -- check set NULL and table constraint on multiple columns -- @@ -138,8 +137,8 @@ SELECT * FROM FKTABLE; | | 8 (5 rows) -DROP TABLE PKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" +DROP TABLE PKTABLE CASCADE; +NOTICE: Drop cascades to constraint constrname on table fktable DROP TABLE FKTABLE; -- -- check set default and table constraint on multiple columns @@ -223,8 +222,13 @@ SELECT * FROM FKTABLE; -1 | -2 | 8 (5 rows) +-- this should fail for lack of CASCADE DROP TABLE PKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" +NOTICE: constraint constrname2 on table fktable depends on table pktable +ERROR: Cannot drop table pktable because other objects depend on it + Use DROP ... CASCADE to drop the dependent objects too +DROP TABLE PKTABLE CASCADE; +NOTICE: Drop cascades to constraint constrname2 on table fktable DROP TABLE FKTABLE; -- -- First test, check with no on delete or on update @@ -246,7 +250,7 @@ INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable -- Check FKTABLE SELECT * FROM FKTABLE; ftest1 | ftest2 @@ -270,7 +274,7 @@ SELECT * FROM PKTABLE; -- Delete a row from PK TABLE (should fail) DELETE FROM PKTABLE WHERE ptest1=1; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- Delete a row from PK TABLE (should succeed) DELETE FROM PKTABLE WHERE ptest1=5; -- Check PKTABLE for deletes @@ -285,7 +289,7 @@ SELECT * FROM PKTABLE; -- Update a row from PK TABLE (should fail) UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- Update a row from PK TABLE (should succeed) UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4; -- Check PKTABLE for updates @@ -298,9 +302,8 @@ SELECT * FROM PKTABLE; 0 | Test4 (4 rows) -DROP TABLE PKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" DROP TABLE FKTABLE; +DROP TABLE PKTABLE; -- MATCH unspecified -- Base test restricting update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); @@ -363,8 +366,6 @@ SELECT * from FKTABLE; (5 rows) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; -- cascade update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); @@ -462,8 +463,6 @@ SELECT * from FKTABLE; (4 rows) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; -- set null update / set default delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); @@ -568,8 +567,6 @@ SELECT * from FKTABLE; (6 rows) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; -- set default update / set null delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); @@ -687,8 +684,6 @@ SELECT * from FKTABLE; (7 rows) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' @@ -734,14 +729,10 @@ ERROR: Unable to identify an operator '=' for types 'inet' and 'integer' CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable); NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -- As should this CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1)); NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; -- Two columns, two tables CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2)); @@ -775,14 +766,10 @@ ERROR: Unable to identify an operator '=' for types 'integer' and 'inet' CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1)); NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -- As does this CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) DROP TABLE FKTABLE; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" DROP TABLE PKTABLE; -- Two columns, same table -- Make sure this still works... @@ -832,25 +819,23 @@ insert into pktable(base1) values (1); insert into pktable(base1) values (2); -- let's insert a non-existant fktable value insert into fktable(ftest1) values (3); -ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable -- let's make a valid row for that insert into pktable(base1) values (3); insert into fktable(ftest1) values (3); -- let's try removing a row that should fail from pktable delete from pktable where base1>2; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- okay, let's try updating all of the base1 values to *4 -- which should fail. update pktable set base1=base1*4; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- okay, let's try an update that should work. update pktable set base1=base1*4 where base1<3; -- and a delete that should work delete from pktable where base1>3; -- cleanup drop table fktable; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" delete from pktable; -- Now 2 columns 2 tables, matching types create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1)); @@ -860,25 +845,23 @@ insert into pktable(base1, ptest1) values (1, 1); insert into pktable(base1, ptest1) values (2, 2); -- let's insert a non-existant fktable value insert into fktable(ftest1, ftest2) values (3, 1); -ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable -- let's make a valid row for that insert into pktable(base1,ptest1) values (3, 1); insert into fktable(ftest1, ftest2) values (3, 1); -- let's try removing a row that should fail from pktable delete from pktable where base1>2; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- okay, let's try updating all of the base1 values to *4 -- which should fail. update pktable set base1=base1*4; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable +ERROR: $1 referential integrity violation - key in pktable still referenced from fktable -- okay, let's try an update that should work. update pktable set base1=base1*4 where base1<3; -- and a delete that should work delete from pktable where base1>3; -- cleanup drop table fktable; -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" -NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" drop table pktable; drop table pktable_base; -- Now we'll do one all in 1 table with 2 columns of matching types @@ -893,13 +876,13 @@ insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1); insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2); -- fails (3,2) isn't in base1, ptest1 insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2); -ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable -- fails (2,2) is being referenced delete from pktable where base1=2; -ERROR: <unnamed> referential integrity violation - key in pktable still referenced from pktable +ERROR: $1 referential integrity violation - key in pktable still referenced from pktable -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; -ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable +ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable -- this sequence of two deletes will work, since after the first there will be no (2,*) references delete from pktable where base2=2; delete from pktable where base1=2; diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 37544dd0ccb1f1956d212145cd8c60506f12668a..2e0cf3a03300daa482a2aba217aa99510c74f103 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -38,8 +38,10 @@ SELECT relname, relhasindex pg_attrdef | t pg_attribute | t pg_class | t + pg_constraint | t pg_conversion | t pg_database | t + pg_depend | t pg_description | t pg_group | t pg_index | t @@ -50,7 +52,6 @@ SELECT relname, relhasindex pg_opclass | t pg_operator | t pg_proc | t - pg_relcheck | t pg_rewrite | t pg_shadow | t pg_statistic | t @@ -61,5 +62,5 @@ SELECT relname, relhasindex shighway | t tenk1 | t tenk2 | t -(51 rows) +(52 rows) diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 3dafd9a87719bcc26895f6ffd05d4869c5d24830..8935dae873468b710d4107d17a5f90a488e879a1 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -123,7 +123,7 @@ INSERT INTO INSERT_TBL(y) VALUES ('Y'); ERROR: ExecInsert: rejected due to CHECK constraint insert_con INSERT INTO INSERT_TBL(y) VALUES ('Y'); INSERT INTO INSERT_TBL(x,z) VALUES (1, -2); -ERROR: ExecInsert: rejected due to CHECK constraint $2 +ERROR: ExecInsert: rejected due to CHECK constraint $1 INSERT INTO INSERT_TBL(z,x) VALUES (-7, 7); INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5); ERROR: ExecInsert: rejected due to CHECK constraint insert_con @@ -139,7 +139,7 @@ SELECT '' AS four, * FROM INSERT_TBL; (4 rows) INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4); -ERROR: ExecInsert: rejected due to CHECK constraint $2 +ERROR: ExecInsert: rejected due to CHECK constraint $1 INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed'); ERROR: ExecInsert: rejected due to CHECK constraint insert_con INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed'); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index a46e5aaa22fe94229871fe959866b3e2623c73fe..f39998073d78e1776e21dd40b7aed6b8ba956d31 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -250,7 +250,7 @@ CREATE TEMP TABLE FKTABLE (ftest1 varchar); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -- As should this ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); -DROP TABLE pktable; +DROP TABLE pktable cascade; DROP TABLE fktable; CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, @@ -258,13 +258,13 @@ CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, -- This should fail, because we just chose really odd types CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; --- Again, so should this... DROP TABLE FKTABLE; +-- Again, so should this... CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest1, ptest2); --- This fails because we mixed up the column ordering DROP TABLE FKTABLE; +-- This fails because we mixed up the column ordering CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest2, ptest1); @@ -486,7 +486,7 @@ alter table foo alter column bar drop not null; create table atacc1 (test int not null); alter table atacc1 add constraint "atacc1_pkey" primary key (test); alter table atacc1 alter column test drop not null; -drop index atacc1_pkey; +alter table atacc1 drop constraint "atacc1_pkey"; alter table atacc1 alter column test drop not null; insert into atacc1 values (null); alter table atacc1 alter test set not null; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 055c377decb872883f2ce60c19be3864b07d7afa..77dccb2aaca430888010c763b9c9dfc9452e3223 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -4,11 +4,14 @@ create domain domaindroptest int4; comment on domain domaindroptest is 'About to drop this..'; +-- currently this will be disallowed create domain basetypetest domaindroptest; -drop domain domaindroptest cascade; drop domain domaindroptest; +-- this should fail because already gone +drop domain domaindroptest cascade; + -- TEST Domains. diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index a7cb5842233a92827414c477a29f91c4168ce02a..c6b50e4b32edd1316bb16243d75ab8a3692da881 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -40,8 +40,8 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -DROP TABLE PKTABLE; DROP TABLE FKTABLE; +DROP TABLE PKTABLE; -- -- check set NULL and table constraint on multiple columns @@ -92,7 +92,7 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -DROP TABLE PKTABLE; +DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; -- @@ -147,7 +147,9 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; +-- this should fail for lack of CASCADE DROP TABLE PKTABLE; +DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; @@ -197,8 +199,8 @@ UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4; -- Check PKTABLE for updates SELECT * FROM PKTABLE; -DROP TABLE PKTABLE; DROP TABLE FKTABLE; +DROP TABLE PKTABLE; -- MATCH unspecified