commit 57a9256f4c3a61cdaca7315ab77e08e61338fbf9 Author: David G. Johnston Date: Wed Jun 17 22:24:08 2020 +0000 Document existing DROP relation IF EXISTS namespace behavior Bug# 16492 takes offense to the fact that DROP VIEW IF EXISTS name errors if a table with the same name exists but a view with that name does not. While I believe the bug is real there is no consensus to actually fix the behavior to work as it is documented. Therefore fix the documentation to match the behavior and encode that behavior in the regression tests. It is unclear whether Domains are supposed to be considered relations here. The presence of a table will cause DROP DOMAIN IF EXISTS to fail so it is thinking of itself as a relation. However, the table doesn't consider it to be one since DROP TABLE will happily ignore the presence of a domain with the table name earlier in the search_path; something the other relation types will not do. Include DROP DOMAIN in the documenation fixes, and add it to the glossary for consistency. diff --git a/doc/src/sgml/glossary.sgml b/doc/src/sgml/glossary.sgml index 25b03f3b37..5971268614 100644 --- a/doc/src/sgml/glossary.sgml +++ b/doc/src/sgml/glossary.sgml @@ -1111,8 +1111,9 @@ views, foreign tables, materialized views, - composite types, and - indexes are all relations. + composite types, + indexes, and + domains are all relations. Class is an archaic synonym for diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml index b18faf3917..9c492709a6 100644 --- a/doc/src/sgml/ref/drop_domain.sgml +++ b/doc/src/sgml/ref/drop_domain.sgml @@ -42,8 +42,11 @@ DROP DOMAIN [ IF EXISTS ] name [, . IF EXISTS - Do not throw an error if the domain does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is an domain it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_foreign_table.sgml b/doc/src/sgml/ref/drop_foreign_table.sgml index 07b3fd4251..0288fb2062 100644 --- a/doc/src/sgml/ref/drop_foreign_table.sgml +++ b/doc/src/sgml/ref/drop_foreign_table.sgml @@ -42,8 +42,11 @@ DROP FOREIGN TABLE [ IF EXISTS ] nameIF EXISTS - Do not throw an error if the foreign table does not exist. - A notice is issued in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a foreign table it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml index 0aedd71bd6..dff437cf9b 100644 --- a/doc/src/sgml/ref/drop_index.sgml +++ b/doc/src/sgml/ref/drop_index.sgml @@ -70,8 +70,11 @@ DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] nameIF EXISTS - Do not throw an error if the index does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is an index it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_materialized_view.sgml b/doc/src/sgml/ref/drop_materialized_view.sgml index c8f3bc5b0d..6647a0db0d 100644 --- a/doc/src/sgml/ref/drop_materialized_view.sgml +++ b/doc/src/sgml/ref/drop_materialized_view.sgml @@ -43,8 +43,11 @@ DROP MATERIALIZED VIEW [ IF EXISTS ] nameIF EXISTS - Do not throw an error if the materialized view does not exist. A notice - is issued in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a materialized view it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_sequence.sgml b/doc/src/sgml/ref/drop_sequence.sgml index 387c98edbc..b3209d6d01 100644 --- a/doc/src/sgml/ref/drop_sequence.sgml +++ b/doc/src/sgml/ref/drop_sequence.sgml @@ -42,8 +42,11 @@ DROP SEQUENCE [ IF EXISTS ] name [, IF EXISTS - Do not throw an error if the sequence does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a sequence it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_table.sgml b/doc/src/sgml/ref/drop_table.sgml index bf8996d198..9c9147f2ef 100644 --- a/doc/src/sgml/ref/drop_table.sgml +++ b/doc/src/sgml/ref/drop_table.sgml @@ -55,8 +55,11 @@ DROP TABLE [ IF EXISTS ] name [, .. IF EXISTS - Do not throw an error if the table does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a table it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml index 9e555c0624..516313b5be 100644 --- a/doc/src/sgml/ref/drop_type.sgml +++ b/doc/src/sgml/ref/drop_type.sgml @@ -42,8 +42,11 @@ DROP TYPE [ IF EXISTS ] name [, ... IF EXISTS - Do not throw an error if the type does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a type it is dropped, otherwise the command fails. diff --git a/doc/src/sgml/ref/drop_view.sgml b/doc/src/sgml/ref/drop_view.sgml index a1c550ec3e..ff75410cf1 100644 --- a/doc/src/sgml/ref/drop_view.sgml +++ b/doc/src/sgml/ref/drop_view.sgml @@ -42,8 +42,11 @@ DROP VIEW [ IF EXISTS ] name [, ... IF EXISTS - Do not throw an error if the view does not exist. A notice is issued - in this case. + This parameter instructs PostgreSQL to search + for the first instance of any relation with the provided name. + If no relations are found a notice is issued and the command ends. + If a relation is found then one of two things will happen: + if the relation is a view it is dropped, otherwise the command fails. diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out index 5e44c2c3ce..b9933e5fb5 100644 --- a/src/test/regress/expected/drop_if_exists.out +++ b/src/test/regress/expected/drop_if_exists.out @@ -330,6 +330,111 @@ HINT: Specify the argument list to select the routine unambiguously. -- cleanup DROP PROCEDURE test_ambiguous_procname(int); DROP PROCEDURE test_ambiguous_procname(text); +-- Demonstrate namespace collision behavior +CREATE SCHEMA test_if_exists_first; +CREATE SCHEMA test_if_exists_second; +SET search_path TO test_if_exists_first, test_if_exists_second; +DROP TABLE test_if_exists_second.test_rel_exists; +ERROR: table "test_rel_exists" does not exist +DROP TABLE IF EXISTS test_if_exists_second.test_rel_exists; +NOTICE: table "test_rel_exists" does not exist, skipping +CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); +CREATE TABLE test_if_exists_first.test_rel_with_index (ai int, bi text); +-- table presence in the second schema causes a failure here +-- even though a corresponding non-schema-qualified create +-- statement would succeed. +DROP VIEW IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not a view +HINT: Use DROP TABLE to remove a table. +DROP INDEX IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not an index +HINT: Use DROP TABLE to remove a table. +DROP TYPE IF EXISTS test_rel_exists; +ERROR: cannot drop type test_rel_exists because table test_rel_exists requires it +HINT: You can drop table test_rel_exists instead. +DROP SEQUENCE IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not a sequence +HINT: Use DROP TABLE to remove a table. +DROP MATERIALIZED VIEW IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not a materialized view +HINT: Use DROP TABLE to remove a table. +DROP FOREIGN TABLE IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not a foreign table +HINT: Use DROP TABLE to remove a table. +-- a role is not a relation so this shouldn't be affected +DROP ROLE IF EXISTS test_rel_exists; +NOTICE: role "test_rel_exists" does not exist, skipping +-- type affirmation (this isn't the same as the implicit type created with the table) +CREATE TYPE test_if_exists_first.test_rel_exists AS (c text, d int); +-- existence of type prevents finding the table +DROP TABLE test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP TYPE to remove a type. +-- cleanup +DROP TYPE IF EXISTS test_rel_exists; +-- view affirmation +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::bigint AS d, 'e'::text AS e; +-- existence of view prevents finding the table +DROP TABLE test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP VIEW to remove a view. +-- cleanup +DROP VIEW test_rel_exists; +-- index affirmation +CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai); +-- existence of index prevents finding the table +DROP TABLE test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP INDEX to remove an index. +-- cleanup +DROP INDEX test_rel_exists; +-- sequence affirmation +CREATE SEQUENCE test_rel_exists; +-- existence of sequence prevents finding the table +DROP TABLE test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP SEQUENCE to remove a sequence. +-- cleanup +DROP SEQUENCE test_rel_exists; +-- materialized affirmation +CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::bigint AS d, 'e'::text AS e; +-- existence of materialized view prevents finding the table +DROP TABLE test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP MATERIALIZED VIEW to remove a materialized view. +-- schema qualification works though (and cleanup) +DROP TABLE test_if_exists_second.test_rel_exists; +DROP MATERIALIZED VIEW test_rel_exists; +DROP TABLE test_if_exists_first.test_rel_with_index; +-- Domain if exists behavior is thus: +CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); +-- domain dropping errors with presence of table; no hint provided +DROP DOMAIN IF EXISTS test_rel_exists; +ERROR: "test_rel_exists" is not a domain +-- domain affirmation +create domain test_rel_exists int4; +-- existence of domain does not prevent table dropping +DROP TABLE test_rel_exists; +-- cleanup +DROP DOMAIN test_rel_exists; +-- /Domain +-- Bug # 16492 - this script produces an error, arguably it should not +CREATE TABLE test_if_exists_first.test_rel_exists (a int, b text); +DROP TABLE IF EXISTS test_if_exists_first.test_rel_exists; +DROP VIEW IF EXISTS test_if_exists_first.test_rel_exists; +NOTICE: view "test_rel_exists" does not exist, skipping +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::int AS a, 'one'::text AS b; +DROP TABLE IF EXISTS test_if_exists_first.test_rel_exists; +ERROR: "test_rel_exists" is not a table +HINT: Use DROP VIEW to remove a view. +DROP VIEW IF EXISTS test_if_exists_first.test_rel_exists; +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::int AS a, 'one'::text AS b; +DROP VIEW test_if_exists_first.test_rel_exists; +-- /Bug # 16492 -- This test checks both the functionality of 'if exists' and the syntax -- of the drop database command. drop database test_database_exists (force); diff --git a/src/test/regress/sql/drop_if_exists.sql b/src/test/regress/sql/drop_if_exists.sql index ac6168b91f..f08bb19c84 100644 --- a/src/test/regress/sql/drop_if_exists.sql +++ b/src/test/regress/sql/drop_if_exists.sql @@ -296,6 +296,94 @@ DROP ROUTINE IF EXISTS test_ambiguous_procname; DROP PROCEDURE test_ambiguous_procname(int); DROP PROCEDURE test_ambiguous_procname(text); +-- Demonstrate namespace collision behavior +CREATE SCHEMA test_if_exists_first; +CREATE SCHEMA test_if_exists_second; +SET search_path TO test_if_exists_first, test_if_exists_second; + +DROP TABLE test_if_exists_second.test_rel_exists; +DROP TABLE IF EXISTS test_if_exists_second.test_rel_exists; +CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); +CREATE TABLE test_if_exists_first.test_rel_with_index (ai int, bi text); + +-- table presence in the second schema causes a failure here +-- even though a corresponding non-schema-qualified create +-- statement would succeed. +DROP VIEW IF EXISTS test_rel_exists; +DROP INDEX IF EXISTS test_rel_exists; +DROP TYPE IF EXISTS test_rel_exists; +DROP SEQUENCE IF EXISTS test_rel_exists; +DROP MATERIALIZED VIEW IF EXISTS test_rel_exists; +DROP FOREIGN TABLE IF EXISTS test_rel_exists; + +-- a role is not a relation so this shouldn't be affected +DROP ROLE IF EXISTS test_rel_exists; + +-- type affirmation (this isn't the same as the implicit type created with the table) +CREATE TYPE test_if_exists_first.test_rel_exists AS (c text, d int); +-- existence of type prevents finding the table +DROP TABLE test_rel_exists; +-- cleanup +DROP TYPE IF EXISTS test_rel_exists; + +-- view affirmation +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::bigint AS d, 'e'::text AS e; +-- existence of view prevents finding the table +DROP TABLE test_rel_exists; +-- cleanup +DROP VIEW test_rel_exists; + +-- index affirmation +CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai); +-- existence of index prevents finding the table +DROP TABLE test_rel_exists; +-- cleanup +DROP INDEX test_rel_exists; + +-- sequence affirmation +CREATE SEQUENCE test_rel_exists; +-- existence of sequence prevents finding the table +DROP TABLE test_rel_exists; +-- cleanup +DROP SEQUENCE test_rel_exists; + +-- materialized affirmation +CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::bigint AS d, 'e'::text AS e; +-- existence of materialized view prevents finding the table +DROP TABLE test_rel_exists; + +-- schema qualification works though (and cleanup) +DROP TABLE test_if_exists_second.test_rel_exists; +DROP MATERIALIZED VIEW test_rel_exists; +DROP TABLE test_if_exists_first.test_rel_with_index; + +-- Domain if exists behavior is thus: +CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); +-- domain dropping errors with presence of table; no hint provided +DROP DOMAIN IF EXISTS test_rel_exists; +-- domain affirmation +create domain test_rel_exists int4; +-- existence of domain does not prevent table dropping +DROP TABLE test_rel_exists; +-- cleanup +DROP DOMAIN test_rel_exists; +-- /Domain + +-- Bug # 16492 - this script produces an error, arguably it should not +CREATE TABLE test_if_exists_first.test_rel_exists (a int, b text); +DROP TABLE IF EXISTS test_if_exists_first.test_rel_exists; +DROP VIEW IF EXISTS test_if_exists_first.test_rel_exists; +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::int AS a, 'one'::text AS b; +DROP TABLE IF EXISTS test_if_exists_first.test_rel_exists; +DROP VIEW IF EXISTS test_if_exists_first.test_rel_exists; +CREATE VIEW test_if_exists_first.test_rel_exists AS + SELECT 1::int AS a, 'one'::text AS b; +DROP VIEW test_if_exists_first.test_rel_exists; +-- /Bug # 16492 + -- This test checks both the functionality of 'if exists' and the syntax -- of the drop database command. drop database test_database_exists (force);