DROP relation IF EXISTS Docs and Tests - Bug Fix

Started by David G. Johnstonover 5 years ago30 messages
#1David G. Johnston
david.g.johnston@gmail.com
1 attachment(s)

This is a follow-up to Bug # 16492 which also links to a thread sent to
-hackers back in 2018.

I'm firmly of the belief that the existing behavior of DROP relation IF
EXISTS is flawed - it should not be an error if there is a namespace
collision but the relkind of the existing relation doesn't match the
relkind set by the DROP command.

Since our documentation fails to elaborate on any additional behavior, and
uses the relkind in the description, our users (few as they may be) are
rightly calling this a bug. I loosely believe that any behavior change in
this area should not be back-patched thus for released versions this is a
documentation bug. I have attached a patch to fix that bug.

In putting together the patch I noticed that the existing drop_if_exists
regression tests exercise the DROP DOMAIN command. Out of curiosity I
included that in my namespace testing and discovered that DROP DOMAIN
thinks of itself as being a relation for purposes of IF EXISTS but DROP
TABLE does not. I modified both DROP DOMAIN and the Glossary in response
to this finding - though I suspect to find disagreement with my choice. I
looked at pg_class for some guidance but a quick search for RELKIND_
(DOMAIN) and finding nothing decided I didn't know enough and figured to
punt on any further exploration of this inconsistency.

The documentation and tests need to go in and be back-patched. After that
happens I'll see whether and/or how to go about trying to get my PoV on the
behavioral change committed.

David J.

Attachments:

drop-if-exists-docs-and-tests-v1.patchapplication/octet-stream; name=drop-if-exists-docs-and-tests-v1.patchDownload
commit 57a9256f4c3a61cdaca7315ab77e08e61338fbf9
Author: David G. Johnston <david.g.johnston@gmail.com>
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 @@
      <glossterm linkend="glossary-view">views</glossterm>,
      <glossterm linkend="glossary-foreign-table">foreign tables</glossterm>,
      <glossterm linkend="glossary-materialized-view">materialized views</glossterm>,
-     composite types, and
-     <glossterm linkend="glossary-index">indexes</glossterm> are all relations.
+     composite types,
+     <glossterm linkend="glossary-index">indexes</glossterm>, and
+     domains are all relations.
     </para>
     <para>
      <firstterm>Class</firstterm> 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 ] <replaceable class="parameter">name</replaceable> [, .
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceabl
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the foreign table does not exist.
-      A notice is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</r
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the index does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replac
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the materialized view does not exist. A notice
-      is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [,
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the sequence does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ..
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the table does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the type does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the view does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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);
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: David G. Johnston (#1)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

"David G. Johnston" <david.g.johnston@gmail.com> writes:

I'm firmly of the belief that the existing behavior of DROP relation IF
EXISTS is flawed - it should not be an error if there is a namespace
collision but the relkind of the existing relation doesn't match the
relkind set by the DROP command.

I don't particularly agree, as I said in the other thread. The core
point here is that it's not clear to me why the specific error of
"wrong relkind" deserves a pass, while other errors such as "you're
not the owner" don't. Both of those cases suggest that you're not
targeting the relation you think you are, and both of them would get
in the way of a subsequent CREATE. To me, success of DROP IF EXISTS
should mean "the coast is clear to do a CREATE". With an exception
like this, a success would mean nothing at all.

Another point here is that we have largely the same issue with respect
to different subclasses of routines (functions/procedures/aggregates)
and types (base types/composite types/domains). If we do change
something then I'd want to see it done consistently across all these
cases.

regards, tom lane

#3David G. Johnston
david.g.johnston@gmail.com
In reply to: Tom Lane (#2)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Wed, Jun 17, 2020 at 4:32 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

"David G. Johnston" <david.g.johnston@gmail.com> writes:

I'm firmly of the belief that the existing behavior of DROP relation IF
EXISTS is flawed - it should not be an error if there is a namespace
collision but the relkind of the existing relation doesn't match the
relkind set by the DROP command.

The other thread:

/messages/by-id/CAKFQuwY90=GSX_65cYdAm18TWCv4CvnPdHCuH92qfzKSYaFnxQ@mail.gmail.com

I don't particularly agree, as I said in the other thread. The core

point here is that it's not clear to me why the specific error of
"wrong relkind" deserves a pass, while other errors such as "you're
not the owner" don't.

Because if you're not the owner then by definition the expected target
exists and a drop is attempted - which can still fail.

Both of those cases suggest that you're not

targeting the relation you think you are, and both of them would get
in the way of a subsequent CREATE.

Agreed, as noted on the other thread we actually are not sufficiently
paranoid in this situation. Specifically, we allow dropping a relation
based upon a search_path search when the target it not on the first entry
in the search_path. I'd be glad to see that hole closed up - but this is
still broken even when the name is always schema qualified.

To me, success of DROP IF EXISTS

should mean "the coast is clear to do a CREATE". With an exception
like this, a success would mean nothing at all.

To me and at least some users DROP IF EXISTS means that the specific object
I specified no longer exists, period.

If you want access to the behavior you describe go and write DROP ROUTINE.
As noted on the other thread I think that is a bad option but hey, it does
have the benefit of doing exactly what you describe.

Users can write multiple the drop commands necessary to get their create
command to execute successfully. If the create command fails they can
react to that and figure out where their misunderstanding was. Is that
really so terrible?

Another point here is that we have largely the same issue with respect

to different subclasses of routines (functions/procedures/aggregates)
and types (base types/composite types/domains). If we do change
something then I'd want to see it done consistently across all these
cases.

Ok. I don't necessarily disagree. In fact the patch I submitted, which is
the on-topic discussion for this thread, brings up the very point that
domain behavior here is presently inconsistent.

At least for DROP TABLE IF EXISTS if we close up the hole with search_path
resolution by introducing an actual "found relation in the wrong location"
error then the risk will have been removed - which exists outside of the IF
EXISTS logic - and instead of not dropping a table and throwing an error we
just are not dropping a table.

So, in summary, this thread is to document the current behavior [actual doc
bug fix]. There is probably another thread buried in all of this for going
through and finding other undocumented behaviors for other object types
[potential doc bug fixes]. Then a thread for solidifying search_path
handling to actually fill in missing seemingly desirable safety features to
avoid drop target mis-identification (so we don't actually drop the wrong
object) [feature]. Then a thread to discuss whether or not dropping an
object that wasn't of the relkind that user specified should be an error
[bug fix held up due to insufficient safety features]. Then a thread to
discuss DROP ROUTINE [user choice of convenience over safety].

David J.

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: David G. Johnston (#1)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

Hi

čt 18. 6. 2020 v 0:47 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

This is a follow-up to Bug # 16492 which also links to a thread sent to
-hackers back in 2018.

I'm firmly of the belief that the existing behavior of DROP relation IF
EXISTS is flawed - it should not be an error if there is a namespace
collision but the relkind of the existing relation doesn't match the
relkind set by the DROP command.

Since our documentation fails to elaborate on any additional behavior, and
uses the relkind in the description, our users (few as they may be) are
rightly calling this a bug. I loosely believe that any behavior change in
this area should not be back-patched thus for released versions this is a
documentation bug. I have attached a patch to fix that bug.

In putting together the patch I noticed that the existing drop_if_exists
regression tests exercise the DROP DOMAIN command. Out of curiosity I
included that in my namespace testing and discovered that DROP DOMAIN
thinks of itself as being a relation for purposes of IF EXISTS but DROP
TABLE does not. I modified both DROP DOMAIN and the Glossary in response
to this finding - though I suspect to find disagreement with my choice. I
looked at pg_class for some guidance but a quick search for RELKIND_
(DOMAIN) and finding nothing decided I didn't know enough and figured to
punt on any further exploration of this inconsistency.

The documentation and tests need to go in and be back-patched. After that
happens I'll see whether and/or how to go about trying to get my PoV on the
behavioral change committed.

I am reading this patch. I don't think so text for domains and types are
correct (or minimally it is little bit messy)

+      This parameter instructs <productname>PostgreSQL</productname> 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.

"If no relations are found ...".

This case is a little bit more complex - domains are not subset of
relations. But relations (in Postgres) extends types.

So in this case maybe modified text can be better

+      This parameter instructs <productname>PostgreSQL</productname> to
search
+      for the first instance of any domain with the provided name in
pg_type catalog.
+      If no type is found a notice is issued and the command ends.
+      If a type is found then one of two things will happen:
+      if the type is a domain it is dropped, otherwise the command fails.
Postgres knows
+      base types, composite types, relation related types and domain types.

Regards

Pavel

Show quoted text

David J.

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#4)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

po 13. 7. 2020 v 11:11 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:

Hi

čt 18. 6. 2020 v 0:47 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

This is a follow-up to Bug # 16492 which also links to a thread sent to
-hackers back in 2018.

I'm firmly of the belief that the existing behavior of DROP relation IF
EXISTS is flawed - it should not be an error if there is a namespace
collision but the relkind of the existing relation doesn't match the
relkind set by the DROP command.

Since our documentation fails to elaborate on any additional behavior,
and uses the relkind in the description, our users (few as they may be) are
rightly calling this a bug. I loosely believe that any behavior change in
this area should not be back-patched thus for released versions this is a
documentation bug. I have attached a patch to fix that bug.

In putting together the patch I noticed that the existing drop_if_exists
regression tests exercise the DROP DOMAIN command. Out of curiosity I
included that in my namespace testing and discovered that DROP DOMAIN
thinks of itself as being a relation for purposes of IF EXISTS but DROP
TABLE does not. I modified both DROP DOMAIN and the Glossary in response
to this finding - though I suspect to find disagreement with my choice. I
looked at pg_class for some guidance but a quick search for RELKIND_
(DOMAIN) and finding nothing decided I didn't know enough and figured to
punt on any further exploration of this inconsistency.

The documentation and tests need to go in and be back-patched. After
that happens I'll see whether and/or how to go about trying to get my PoV
on the behavioral change committed.

I am reading this patch. I don't think so text for domains and types are
correct (or minimally it is little bit messy)

+      This parameter instructs <productname>PostgreSQL</productname> 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.

"If no relations are found ...".

This case is a little bit more complex - domains are not subset of
relations. But relations (in Postgres) extends types.

So in this case maybe modified text can be better

+      This parameter instructs <productname>PostgreSQL</productname> to
search
+      for the first instance of any domain with the provided name in
pg_type catalog.
+      If no type is found a notice is issued and the command ends.
+      If a type is found then one of two things will happen:
+      if the type is a domain it is dropped, otherwise the command fails.
Postgres knows
+      base types, composite types, relation related types and domain
types.

create type footyp as (a int, b int);
postgres=# drop domain if exists footyp;
ERROR: "footyp" is not a domain
postgres=#

Show quoted text

Regards

Pavel

David J.

#6David G. Johnston
david.g.johnston@gmail.com
In reply to: Pavel Stehule (#4)
1 attachment(s)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Mon, Jul 13, 2020 at 2:12 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

I am reading this patch. I don't think so text for domains and types are
correct (or minimally it is little bit messy)
This case is a little bit more complex - domains are not subset of
relations. But relations (in Postgres) extends types.

Yeah, though in further working on this I dislike the saying "A composite
type is a relation" (see Glossary and probably other spots). That a table
auto-creates a separate composite type, and depends on it, manifests a
certain link between the two but the type that represents the table is not
a relation as it doesn't hold data, it is just a definition. If a
composite type were a relation then whatever argument you use to justify
that would seem to apply to non-composite types as well.

I'm attaching version 2 as a plain diff (complete) instead of a patch.

New with this version is the addition of tests for drop domain and drop
type, and related documentation changes. Notably pointing out the fact
that DROP TYPE drops all types, including domains.

To recap, the interesting relation related behaviors these tests
demonstrate are:

A non-failure while performing a DROP "relation" IF EXISTS command means
that a subsequent CREATE "relation" command will not fail due to the name
already existing (other failures are of course possible).

In the presence of multiple schemas a failure of a DROP "relation" IF
EXISTS command does not necessarily mean that an corresponding CREATE
"relation" command would fail - the found entry could belong to a non-first
schema on the search_path while the creation will place the newly created
object always on the first schema.

The plain meaning of the opposite of "DROP IF EXISTS" (i.e., it's not an
error if the specified object doesn't exist, just move on) is not what
actually happens but rather we provide an additional test related to
namespace occupation that is now documented.

The latter two items are explicitly documented while the first is implicit
and self-evident.

David J.

Attachments:

drop-if-exists-docs-and-tasks-v2.diffapplication/octet-stream; name=drop-if-exists-docs-and-tasks-v2.diffDownload
diff --git a/doc/src/sgml/glossary.sgml b/doc/src/sgml/glossary.sgml
index 25b03f3b37..c517ff5b4a 100644
--- a/doc/src/sgml/glossary.sgml
+++ b/doc/src/sgml/glossary.sgml
@@ -1612,6 +1612,30 @@
    </glossdef>
   </glossentry>
 
+  <glossentry id="glossary-type">
+   <glossterm>Type</glossterm>
+   <glossdef>
+    <para>
+     All data stored within the database is typed.  Each type has both a name, and associated
+     properties, as well as an overarching Type, of which there are two variations. Extensible
+     types can created by users and comprise of: base, composite, domain, enum, and range. There
+     are also the system-only pseudo-types.  Type records are stored in pg_type and the
+     overarching type is found in pg_type.typtype.
+    </para>
+   </glossdef>
+  </glossentry>  
+
+  <glossentry id="glossary-type-definition">
+   <glossterm>Type Definition</glossterm>
+   <glossdef>
+    <para>
+     Collective term for the various <glossterm linkend="glossary-type">Type</glossterm>
+     variants that are allowed for pg_type.typtype catalog table column values;
+     specifically, ones that are extensible.
+    </para>
+   </glossdef>
+  </glossentry>
+
   <glossentry id="glossary-unique-constraint">
    <glossterm>Unique constraint</glossterm>
    <glossdef>
diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml
index b18faf3917..8b04b3818b 100644
--- a/doc/src/sgml/ref/drop_domain.sgml
+++ b/doc/src/sgml/ref/drop_domain.sgml
@@ -30,7 +30,9 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
 
   <para>
    <command>DROP DOMAIN</command> removes a domain.  Only the owner of
-   a domain can remove it.
+   a domain can remove it.  The duplicates the functionality provided by 
+   <xref linkend="sql-droptype"/> but restricts the object type to domain.
+
   </para>
  </refsect1>
 
@@ -42,8 +44,13 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any 
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
+      If no type definitions are found a notice is issued and the command ends.
+      If a type definition is found then one of two things will happen:
+      if the type definition is a domain it is dropped, otherwise the command fails.      
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceabl
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the foreign table does not exist.
-      A notice is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</r
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the index does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replac
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the materialized view does not exist. A notice
-      is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [,
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the sequence does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ..
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the table does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml
index 9e555c0624..305a62486f 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -42,8 +42,17 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the type does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any 
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
+      If no type definitions are found a notice is issued and the command ends.
+      If a type definition is found then it will be dropped. This includes
+      domains even though they have their own drop command.
+      
+      However, an attempt to drop the automatically created composite type of an
+      existing relation will fail as the relation has a dependency on the found
+      type definition.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the view does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index 5e44c2c3ce..b57ddc2e60 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -330,6 +330,167 @@ 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;
+-- Type & Domain 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 in first search_path schema
+--      does not prevent second search_path table dropping
+DROP TABLE test_rel_exists;
+--      cleanup
+DROP DOMAIN test_rel_exists;
+--      setup
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      type with same name exists as table creation creates a composite type
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+ERROR:  type "test_rel_exists" already exists
+--      domain creation with same name as table fails as auto-generated type conflicts
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+ERROR:  type "test_rel_exists" already exists
+--      cannot independently drop a composite type associated with a table
+DROP TYPE IF EXISTS test_if_exists_second.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.
+--      cleanup
+DROP TABLE test_if_exists_second.test_rel_exists;
+--      setup
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      auto-created type (which is added also as a relation) prevents table from being created
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+ERROR:  relation "test_rel_exists" already exists
+--      a composite type is not a domain so a conditional drop fails
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+--      can still create a domain in the first schema
+CREATE DOMAIN test_rel_exists int4;
+--      unlike the case with relations the fact that domains
+--      and other extensible types share the same namespace
+--      doesn't cause a DROP TYPE command that first
+--      finds a domain to fail - though it does prevent
+--      the search algorithm from looking in the second
+--      schema for an exact match.
+DROP TYPE IF EXISTS test_rel_exists; -- This removes the domain
+ --     type present, not a domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+--      Yep, domain seems to have been removed
+DROP TYPE IF EXISTS test_if_exists_first.test_rel_exists;
+NOTICE:  type "test_if_exists_first.test_rel_exists" does not exist, skipping
+--      Yep, domain removed, finding the type
+DROP DOMAIN test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+--      cleanup
+DROP TYPE test_rel_exists;
+--      test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      fails since the first matched name is not a domain
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+--      remove the matching type
+DROP TYPE IF EXISTS test_rel_exists;
+--      drop type cleans up domains...
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists; 
+--      ...so it no longer exists here
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+-- /Type & 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..5cb7f17875 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -296,6 +296,142 @@ 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;
+
+-- Type & Domain 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 in first search_path schema
+--      does not prevent second search_path table dropping
+DROP TABLE test_rel_exists;
+--      cleanup
+DROP DOMAIN test_rel_exists;
+
+--      setup
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      type with same name exists as table creation creates a composite type
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      domain creation with same name as table fails as auto-generated type conflicts
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      cannot independently drop a composite type associated with a table
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+--      cleanup
+DROP TABLE test_if_exists_second.test_rel_exists;
+
+--      setup
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      auto-created type (which is added also as a relation) prevents table from being created
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      a composite type is not a domain so a conditional drop fails
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+--      can still create a domain in the first schema
+CREATE DOMAIN test_rel_exists int4;
+--      unlike the case with relations the fact that domains
+--      and other extensible types share the same namespace
+--      doesn't cause a DROP TYPE command that first
+--      finds a domain to fail - though it does prevent
+--      the search algorithm from looking in the second
+--      schema for an exact match.
+DROP TYPE IF EXISTS test_rel_exists; -- This removes the domain
+ --     type present, not a domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+--      Yep, domain seems to have been removed
+DROP TYPE IF EXISTS test_if_exists_first.test_rel_exists;
+--      Yep, domain removed, finding the type
+DROP DOMAIN test_rel_exists;
+--      cleanup
+DROP TYPE test_rel_exists;
+
+--      test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      fails since the first matched name is not a domain
+DROP DOMAIN IF EXISTS test_rel_exists;
+--      remove the matching type
+DROP TYPE IF EXISTS test_rel_exists;
+--      drop type cleans up domains...
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+--      ...so it no longer exists here
+DROP DOMAIN IF EXISTS test_rel_exists;
+-- /Type & 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);
#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: David G. Johnston (#6)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

út 14. 7. 2020 v 0:37 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

On Mon, Jul 13, 2020 at 2:12 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

I am reading this patch. I don't think so text for domains and types are
correct (or minimally it is little bit messy)
This case is a little bit more complex - domains are not subset of
relations. But relations (in Postgres) extends types.

Yeah, though in further working on this I dislike the saying "A composite
type is a relation" (see Glossary and probably other spots). That a table
auto-creates a separate composite type, and depends on it, manifests a
certain link between the two but the type that represents the table is not
a relation as it doesn't hold data, it is just a definition. If a
composite type were a relation then whatever argument you use to justify
that would seem to apply to non-composite types as well.

I'm attaching version 2 as a plain diff (complete) instead of a patch.

New with this version is the addition of tests for drop domain and drop
type, and related documentation changes. Notably pointing out the fact
that DROP TYPE drops all types, including domains.

To recap, the interesting relation related behaviors these tests
demonstrate are:

A non-failure while performing a DROP "relation" IF EXISTS command means
that a subsequent CREATE "relation" command will not fail due to the name
already existing (other failures are of course possible).

In the presence of multiple schemas a failure of a DROP "relation" IF
EXISTS command does not necessarily mean that an corresponding CREATE
"relation" command would fail - the found entry could belong to a non-first
schema on the search_path while the creation will place the newly created
object always on the first schema.

The plain meaning of the opposite of "DROP IF EXISTS" (i.e., it's not an
error if the specified object doesn't exist, just move on) is not what
actually happens but rather we provide an additional test related to
namespace occupation that is now documented.

The latter two items are explicitly documented while the first is implicit
and self-evident.

I think so now all changes are correct and valuable. I''l mark this patch
as ready for commit

Thank you for patch

Regards

Pavel

Show quoted text

David J.

#8Justin Pryzby
pryzby@telsasoft.com
In reply to: Pavel Stehule (#7)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Jul 14, 2020 at 07:25:56AM +0200, Pavel Stehule wrote:

�t 14. 7. 2020 v 0:37 odes�latel David G. Johnston <david.g.johnston@gmail.com> napsal:

On Mon, Jul 13, 2020 at 2:12 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:

I think so now all changes are correct and valuable. I''l mark this patch
as ready for commit

This is failing relevant tests in cfbot:

drop_if_exists ... FAILED 450 ms

--
Justin

#9David G. Johnston
david.g.johnston@gmail.com
In reply to: Justin Pryzby (#8)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Jul 14, 2020 at 5:40 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Tue, Jul 14, 2020 at 07:25:56AM +0200, Pavel Stehule wrote:

út 14. 7. 2020 v 0:37 odesílatel David G. Johnston <

david.g.johnston@gmail.com> napsal:

On Mon, Jul 13, 2020 at 2:12 AM Pavel Stehule <pavel.stehule@gmail.com>

wrote:

I think so now all changes are correct and valuable. I''l mark this patch
as ready for commit

This is failing relevant tests in cfbot:

drop_if_exists ... FAILED 450 ms

Oops, did a minor whitespace cleanup in the test file and didn't re-copy
expected output. I'm actually going to try and clean up the commenting in
the test file a bit to make it easier to read, and split out the glossary
changes into their own diff so that the bulk of the changes can be
back-patched.

Further comments welcome so I'm putting it back into needs review for the
moment while I work on the refactor.

David J.

#10Pavel Stehule
pavel.stehule@gmail.com
In reply to: David G. Johnston (#9)
1 attachment(s)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

út 14. 7. 2020 v 15:55 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

On Tue, Jul 14, 2020 at 5:40 AM Justin Pryzby <pryzby@telsasoft.com>
wrote:

On Tue, Jul 14, 2020 at 07:25:56AM +0200, Pavel Stehule wrote:

út 14. 7. 2020 v 0:37 odesílatel David G. Johnston <

david.g.johnston@gmail.com> napsal:

On Mon, Jul 13, 2020 at 2:12 AM Pavel Stehule <

pavel.stehule@gmail.com> wrote:

I think so now all changes are correct and valuable. I''l mark this

patch

as ready for commit

This is failing relevant tests in cfbot:

drop_if_exists ... FAILED 450 ms

Oops, did a minor whitespace cleanup in the test file and didn't re-copy
expected output. I'm actually going to try and clean up the commenting in
the test file a bit to make it easier to read, and split out the glossary
changes into their own diff so that the bulk of the changes can be
back-patched.

Further comments welcome so I'm putting it back into needs review for the
moment while I work on the refactor.

attached fixed patch

all tests passed
doc build without problems

Show quoted text

David J.

Attachments:

drop-if-exists-docs-and-tasks-v3.difftext/x-patch; charset=US-ASCII; name=drop-if-exists-docs-and-tasks-v3.diffDownload
diff --git a/doc/src/sgml/glossary.sgml b/doc/src/sgml/glossary.sgml
index 76525c6302..1dac096f23 100644
--- a/doc/src/sgml/glossary.sgml
+++ b/doc/src/sgml/glossary.sgml
@@ -1689,6 +1689,30 @@
    </glossdef>
   </glossentry>
 
+  <glossentry id="glossary-type">
+   <glossterm>Type</glossterm>
+   <glossdef>
+    <para>
+     All data stored within the database is typed.  Each type has both a name, and associated
+     properties, as well as an overarching Type, of which there are two variations. Extensible
+     types can created by users and comprise of: base, composite, domain, enum, and range. There
+     are also the system-only pseudo-types.  Type records are stored in pg_type and the
+     overarching type is found in pg_type.typtype.
+    </para>
+   </glossdef>
+  </glossentry>  
+
+  <glossentry id="glossary-type-definition">
+   <glossterm>Type Definition</glossterm>
+   <glossdef>
+    <para>
+     Collective term for the various <glossterm linkend="glossary-type">Type</glossterm>
+     variants that are allowed for pg_type.typtype catalog table column values;
+     specifically, ones that are extensible.
+    </para>
+   </glossdef>
+  </glossentry>
+
   <glossentry id="glossary-unique-constraint">
    <glossterm>Unique constraint</glossterm>
    <glossdef>
diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml
index b18faf3917..8b04b3818b 100644
--- a/doc/src/sgml/ref/drop_domain.sgml
+++ b/doc/src/sgml/ref/drop_domain.sgml
@@ -30,7 +30,9 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
 
   <para>
    <command>DROP DOMAIN</command> removes a domain.  Only the owner of
-   a domain can remove it.
+   a domain can remove it.  The duplicates the functionality provided by 
+   <xref linkend="sql-droptype"/> but restricts the object type to domain.
+
   </para>
  </refsect1>
 
@@ -42,8 +44,13 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any 
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
+      If no type definitions are found a notice is issued and the command ends.
+      If a type definition is found then one of two things will happen:
+      if the type definition is a domain it is dropped, otherwise the command fails.      
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceabl
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the foreign table does not exist.
-      A notice is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</r
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the index does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replac
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the materialized view does not exist. A notice
-      is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [,
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the sequence does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ..
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the table does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml
index 9e555c0624..305a62486f 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -42,8 +42,17 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the type does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any 
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
+      If no type definitions are found a notice is issued and the command ends.
+      If a type definition is found then it will be dropped. This includes
+      domains even though they have their own drop command.
+      
+      However, an attempt to drop the automatically created composite type of an
+      existing relation will fail as the relation has a dependency on the found
+      type definition.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the view does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index 5e44c2c3ce..f328d94b9e 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -330,6 +330,167 @@ 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;
+-- Type & Domain 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 in first search_path schema
+--      does not prevent second search_path table dropping
+DROP TABLE test_rel_exists;
+--      cleanup
+DROP DOMAIN test_rel_exists;
+--      setup
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      type with same name exists as table creation creates a composite type
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+ERROR:  type "test_rel_exists" already exists
+--      domain creation with same name as table fails as auto-generated type conflicts
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+ERROR:  type "test_rel_exists" already exists
+--      cannot independently drop a composite type associated with a table
+DROP TYPE IF EXISTS test_if_exists_second.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.
+--      cleanup
+DROP TABLE test_if_exists_second.test_rel_exists;
+--      setup
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      auto-created type (which is added also as a relation) prevents table from being created
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+ERROR:  relation "test_rel_exists" already exists
+--      a composite type is not a domain so a conditional drop fails
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+--      can still create a domain in the first schema
+CREATE DOMAIN test_rel_exists int4;
+--      unlike the case with relations the fact that domains
+--      and other extensible types share the same namespace
+--      doesn't cause a DROP TYPE command that first
+--      finds a domain to fail - though it does prevent
+--      the search algorithm from looking in the second
+--      schema for an exact match.
+DROP TYPE IF EXISTS test_rel_exists; -- This removes the domain
+ --     type present, not a domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+--      Yep, domain seems to have been removed
+DROP TYPE IF EXISTS test_if_exists_first.test_rel_exists;
+NOTICE:  type "test_if_exists_first.test_rel_exists" does not exist, skipping
+--      Yep, domain removed, finding the type
+DROP DOMAIN test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+--      cleanup
+DROP TYPE test_rel_exists;
+--      test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      fails since the first matched name is not a domain
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+--      remove the matching type
+DROP TYPE IF EXISTS test_rel_exists;
+--      drop type cleans up domains...
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+--      ...so it no longer exists here
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+-- /Type & 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..5cb7f17875 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -296,6 +296,142 @@ 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;
+
+-- Type & Domain 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 in first search_path schema
+--      does not prevent second search_path table dropping
+DROP TABLE test_rel_exists;
+--      cleanup
+DROP DOMAIN test_rel_exists;
+
+--      setup
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      type with same name exists as table creation creates a composite type
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      domain creation with same name as table fails as auto-generated type conflicts
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      cannot independently drop a composite type associated with a table
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+--      cleanup
+DROP TABLE test_if_exists_second.test_rel_exists;
+
+--      setup
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+--      auto-created type (which is added also as a relation) prevents table from being created
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+--      a composite type is not a domain so a conditional drop fails
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+--      can still create a domain in the first schema
+CREATE DOMAIN test_rel_exists int4;
+--      unlike the case with relations the fact that domains
+--      and other extensible types share the same namespace
+--      doesn't cause a DROP TYPE command that first
+--      finds a domain to fail - though it does prevent
+--      the search algorithm from looking in the second
+--      schema for an exact match.
+DROP TYPE IF EXISTS test_rel_exists; -- This removes the domain
+ --     type present, not a domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+--      Yep, domain seems to have been removed
+DROP TYPE IF EXISTS test_if_exists_first.test_rel_exists;
+--      Yep, domain removed, finding the type
+DROP DOMAIN test_rel_exists;
+--      cleanup
+DROP TYPE test_rel_exists;
+
+--      test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+--      fails since the first matched name is not a domain
+DROP DOMAIN IF EXISTS test_rel_exists;
+--      remove the matching type
+DROP TYPE IF EXISTS test_rel_exists;
+--      drop type cleans up domains...
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+--      ...so it no longer exists here
+DROP DOMAIN IF EXISTS test_rel_exists;
+-- /Type & 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);
#11David G. Johnston
david.g.johnston@gmail.com
In reply to: Pavel Stehule (#10)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Jul 14, 2020 at 6:56 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

út 14. 7. 2020 v 15:55 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

Further comments welcome so I'm putting it back into needs review for the
moment while I work on the refactor.

attached fixed patch

all tests passed
doc build without problems

Thanks.

Actually, one question I didn't pose before, does the SQL standard define
DROP TYPE to target domains while also providing for a DROP DOMAIN
command? Do drop commands for the other types we have not exist because
those aren't SQL standard types (or the standard they are standard types
but the commands aren't defined)?

David J.

#12Pavel Stehule
pavel.stehule@gmail.com
In reply to: David G. Johnston (#11)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

út 14. 7. 2020 v 16:09 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

On Tue, Jul 14, 2020 at 6:56 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

út 14. 7. 2020 v 15:55 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

Further comments welcome so I'm putting it back into needs review for
the moment while I work on the refactor.

attached fixed patch

all tests passed
doc build without problems

Thanks.

Actually, one question I didn't pose before, does the SQL standard define
DROP TYPE to target domains while also providing for a DROP DOMAIN
command? Do drop commands for the other types we have not exist because
those aren't SQL standard types (or the standard they are standard types
but the commands aren't defined)?

It looks like Postgres user defined types are something else than ANSI SQL
- so CREATE TYPE and DROP TYPE did different work.

In the section DROP TYPE in ANSI SQL there is not mentioned any relation to
domains.

Show quoted text

David J.

#13David G. Johnston
david.g.johnston@gmail.com
In reply to: Pavel Stehule (#12)
2 attachment(s)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Jul 14, 2020 at 7:21 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

út 14. 7. 2020 v 16:09 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

On Tue, Jul 14, 2020 at 6:56 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

út 14. 7. 2020 v 15:55 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

Further comments welcome so I'm putting it back into needs review for
the moment while I work on the refactor.

attached fixed patch

all tests passed
doc build without problems

Thanks.

Actually, one question I didn't pose before, does the SQL standard define
DROP TYPE to target domains while also providing for a DROP DOMAIN
command? Do drop commands for the other types we have not exist because
those aren't SQL standard types (or the standard they are standard types
but the commands aren't defined)?

It looks like Postgres user defined types are something else than ANSI SQL
- so CREATE TYPE and DROP TYPE did different work.

In the section DROP TYPE in ANSI SQL there is not mentioned any relation
to domains.

Attaching a backpatch-able patch for the main docs and tests, v4
Added a head-only patch for the glossary changes, set to v4 as well.

I didn't try and address any SQL standard dynamics here.

David J.

Attachments:

001-drop-if-exists-docs-and-tests-v4.patchapplication/octet-stream; name=001-drop-if-exists-docs-and-tests-v4.patchDownload
commit d0f11aa4f8efe8a18ed3ffa4368c7245cc3c8deb
Author: David G. Johnston <david.g.johnston@gmail.com>
Date:   Wed Jun 17 22:24:08 2020 +0000

    Document DROP relation and DROP type 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 has merit 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.
    
    Due to the behavior of CREATE TABLE producing a composite type
    the behaviors for DROP TYPE and DROP DOMAIN are relevant here so
    add tests and documentation for those as well.  In particular
    document that DROP TYPE drops domains.
    
    One shortcoming tested here is that DROP IF EXISTS doesn't only
    look at the first search_path entry while CREATE IF NOT EXISTS
    will only consider the first search_path entry.
    
    As this patch addresses undocumented fundamental behavior for these
    commands backpatch to all supported versions.
    
    I (DGJ) have not tried to address the IMO confusing statement that
    "composite types are relations".  That material is better done against
    head only anyway as it should incorporate the newly created glossary.

diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml
index b18faf3917..cba665fcc7 100644
--- a/doc/src/sgml/ref/drop_domain.sgml
+++ b/doc/src/sgml/ref/drop_domain.sgml
@@ -30,7 +30,9 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
 
   <para>
    <command>DROP DOMAIN</command> removes a domain.  Only the owner of
-   a domain can remove it.
+   a domain can remove it.  This duplicates the functionality provided by 
+   <xref linkend="sql-droptype"/> but restricts the object type to domain.
+
   </para>
  </refsect1>
 
@@ -42,8 +44,11 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any type definition with the provided name.
+      If no type definition is found a notice is issued and the command ends.
+      If a type definition is found then one of two things will happen:
+      if the type definition is a domain it is dropped, otherwise the command fails.      
      </para>
     </listitem>
    </varlistentry>
@@ -108,6 +113,7 @@ DROP DOMAIN box;
   <simplelist type="inline">
    <member><xref linkend="sql-createdomain"/></member>
    <member><xref linkend="sql-alterdomain"/></member>
+   <member><xref linkend="sql-droptype"/></member>
   </simplelist>
  </refsect1>
 
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 ] <replaceable class="parameter">name</replaceabl
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the foreign table does not exist.
-      A notice is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</r
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the index does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replac
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the materialized view does not exist. A notice
-      is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [,
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the sequence does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ..
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the table does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml
index 9e555c0624..afaa2387fe 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -30,7 +30,9 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
 
   <para>
    <command>DROP TYPE</command> removes a user-defined data type.
-   Only the owner of a type can remove it.
+   Only the owner of a type can remove it.  This includes domains,
+   though they can be targeted specifically by using the
+   <xref linkend="sql-dropdomain"/> command.
   </para>
  </refsect1>
 
@@ -42,8 +44,15 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the type does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any type definition with the provided name.
+      If no type definition is found a notice is issued and the command ends.
+      If a type definition is found then it will be dropped. 
+     </para>
+     <para> 
+      However, an attempt to drop the automatically created composite type of an
+      existing relation will fail as the relation has a dependency on the found
+      type definition.
      </para>
     </listitem>
    </varlistentry>
@@ -110,6 +119,7 @@ DROP TYPE box;
   <simplelist type="inline">
    <member><xref linkend="sql-altertype"/></member>
    <member><xref linkend="sql-createtype"/></member>
+   <member><xref linkend="sql-dropdomain"/></member>
   </simplelist>
  </refsect1>
 
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the view does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index 5e44c2c3ce..b25886272b 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -330,6 +330,125 @@ 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);
+CREATE TABLE IF NOT EXISTS test_rel_exists (c text, d int); -- IF NOT EXISTS checks only the first schema
+DROP TABLE IF EXISTS test_rel_exists; -- Two matches but only the first is dropped
+-- 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
+-- 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 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.
+CREATE VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP VIEW to remove a view.
+DROP VIEW test_rel_exists;
+CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai);
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP INDEX to remove an index.
+DROP INDEX test_rel_exists;
+CREATE SEQUENCE test_rel_exists;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP SEQUENCE to remove a sequence.
+DROP SEQUENCE test_rel_exists;
+CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP MATERIALIZED VIEW to remove a materialized view.
+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;
+-- Type & Domain behavior is thus:
+-- If Exists Behavior
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+DROP TYPE IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+CREATE DOMAIN test_rel_exists int4;
+DROP TABLE test_rel_exists;
+DROP DOMAIN test_rel_exists;
+-- Test with domain first and type second; plus table tests
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text); -- type with same name exists as table creation creates a composite type
+ERROR:  type "test_rel_exists" already exists
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4; -- domain creation with same name as table fails as auto-generated type conflicts
+ERROR:  type "test_rel_exists" already exists
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists; -- cannot independently drop a composite type associated with a table
+ERROR:  cannot drop type test_rel_exists because table test_rel_exists requires it
+HINT:  You can drop table test_rel_exists instead.
+DROP TABLE test_if_exists_second.test_rel_exists;
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); -- auto-created type prevents table from being created
+ERROR:  relation "test_rel_exists" already exists
+DROP TABLE test_if_exists_second.test_rel_exists; -- a standalone composite type cannot be dropped using the drop table command
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP TYPE to remove a type.
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+CREATE DOMAIN test_rel_exists int4;
+DROP TYPE IF EXISTS test_rel_exists; -- DROP TYPE Drops the Domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+DROP DOMAIN test_rel_exists; -- double-checking the domain doesn't exist
+ERROR:  "test_rel_exists" is not a domain
+DROP TYPE test_rel_exists;
+-- Test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+DROP TYPE IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+-- /Type & 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..b95189c47c 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -296,6 +296,104 @@ 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);
+
+CREATE TABLE IF NOT EXISTS test_rel_exists (c text, d int); -- IF NOT EXISTS checks only the first schema
+DROP TABLE IF EXISTS test_rel_exists; -- Two matches but only the first is dropped
+
+-- A role is not a relation so this shouldn't be affected
+DROP ROLE IF EXISTS test_rel_exists;
+
+-- 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 SEQUENCE IF EXISTS test_rel_exists;
+DROP MATERIALIZED VIEW IF EXISTS test_rel_exists;
+DROP FOREIGN TABLE IF EXISTS test_rel_exists;
+
+CREATE VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+DROP VIEW test_rel_exists;
+
+CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai);
+DROP TABLE test_rel_exists;
+DROP INDEX test_rel_exists;
+
+CREATE SEQUENCE test_rel_exists;
+DROP TABLE test_rel_exists;
+DROP SEQUENCE test_rel_exists;
+
+CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+
+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;
+
+-- Type & Domain behavior is thus:
+-- If Exists Behavior
+DROP DOMAIN IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_rel_exists;
+
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+DROP DOMAIN IF EXISTS test_rel_exists;
+CREATE DOMAIN test_rel_exists int4;
+DROP TABLE test_rel_exists;
+DROP DOMAIN test_rel_exists;
+
+-- Test with domain first and type second; plus table tests
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text); -- type with same name exists as table creation creates a composite type
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4; -- domain creation with same name as table fails as auto-generated type conflicts
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists; -- cannot independently drop a composite type associated with a table
+DROP TABLE test_if_exists_second.test_rel_exists;
+
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); -- auto-created type prevents table from being created
+DROP TABLE test_if_exists_second.test_rel_exists; -- a standalone composite type cannot be dropped using the drop table command
+
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+CREATE DOMAIN test_rel_exists int4;
+DROP TYPE IF EXISTS test_rel_exists; -- DROP TYPE Drops the Domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+DROP DOMAIN test_rel_exists; -- double-checking the domain doesn't exist
+DROP TYPE test_rel_exists;
+
+-- Test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+DROP DOMAIN IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+
+DROP DOMAIN IF EXISTS test_rel_exists;
+-- /Type & 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);
002-types-glossary-v4.patchapplication/octet-stream; name=002-types-glossary-v4.patchDownload
commit 508dc751e2ed60e9ba6bb4849e9320543ae0f134
Author: David G. Johnston <david.g.johnston@gmail.com>
Date:   Tue Jul 14 15:38:30 2020 +0000

    Link CREATE/DROP TYPE and DOMAIN to the Glossary
    
    We have a generic term that encompasses tables, views, etc...,
    "relation".  Introduce the term "type definition" to encompass
    base, composite, domain, etc... types.  The individual types
    are the rows on pg_type while type definition represents the
    allowed values for the pg_type.typtype column (restricted to
    user-creatable ones).
    
    Update the TYPE and DOMAIN pages to refer to these
    glossary entries.

diff --git a/doc/src/sgml/glossary.sgml b/doc/src/sgml/glossary.sgml
index 25b03f3b37..c517ff5b4a 100644
--- a/doc/src/sgml/glossary.sgml
+++ b/doc/src/sgml/glossary.sgml
@@ -1612,6 +1612,30 @@
    </glossdef>
   </glossentry>
 
+  <glossentry id="glossary-type">
+   <glossterm>Type</glossterm>
+   <glossdef>
+    <para>
+     All data stored within the database is typed.  Each type has both a name, and associated
+     properties, as well as an overarching Type, of which there are two variations. Extensible
+     types can created by users and comprise of: base, composite, domain, enum, and range. There
+     are also the system-only pseudo-types.  Type records are stored in pg_type and the
+     overarching type is found in pg_type.typtype.
+    </para>
+   </glossdef>
+  </glossentry>  
+
+  <glossentry id="glossary-type-definition">
+   <glossterm>Type Definition</glossterm>
+   <glossdef>
+    <para>
+     Collective term for the various <glossterm linkend="glossary-type">Type</glossterm>
+     variants that are allowed for pg_type.typtype catalog table column values;
+     specifically, ones that are extensible.
+    </para>
+   </glossdef>
+  </glossentry>
+
   <glossentry id="glossary-unique-constraint">
    <glossterm>Unique constraint</glossterm>
    <glossdef>
diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 81a8924926..00e1544ac8 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -38,8 +38,8 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
 
   <para>
    <command>CREATE DOMAIN</command> creates a new domain.  A domain is
-   essentially a data type with optional constraints (restrictions on
-   the allowed set of values).
+   essentially a data <glossterm linkend="glossary-type">type</glossterm>
+   with optional constraints (restrictions on the allowed set of values).
    The user who defines a domain becomes its owner.
   </para>
 
@@ -64,6 +64,13 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
    To be able to create a domain, you must have <literal>USAGE</literal>
    privilege on the underlying type.
   </para>
+  
+  <para>
+   This emulates the functionality provided by 
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type definition</glossterm>
+   to domain.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 111f8e65d2..bbeeca7317 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -64,9 +64,10 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
   <title>Description</title>
 
   <para>
-   <command>CREATE TYPE</command> registers a new data type for use in
-   the current database.  The user who defines a type becomes its
-   owner.
+   <command>CREATE TYPE</command> registers a new data
+   <glossterm linkend="glossary-type">type</glossterm>
+   for use in the current database.  The user who defines
+   a type becomes its owner.
   </para>
 
   <para>
@@ -83,7 +84,10 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    syntax synopsis above.  They respectively create a <firstterm>composite
    type</firstterm>, an <firstterm>enum type</firstterm>, a <firstterm>range type</firstterm>, a
    <firstterm>base type</firstterm>, or a <firstterm>shell type</firstterm>.  The first four
-   of these are discussed in turn below.  A shell type is simply a placeholder
+   of these are discussed in turn below and represent four of the five
+   <glossterm linkend="glossary-type-definition">type definition</glossterm>s,
+   with the fifth, domain, created using the <xref linkend="sql-createdomain"/> command.
+   A shell type is simply a placeholder
    for a type to be defined later; it is created by issuing <command>CREATE
    TYPE</command> with no parameters except for the type name.  Shell types
    are needed as forward references when creating range types and base types,
diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml
index cba665fcc7..8375dd5849 100644
--- a/doc/src/sgml/ref/drop_domain.sgml
+++ b/doc/src/sgml/ref/drop_domain.sgml
@@ -29,10 +29,13 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
   <title>Description</title>
 
   <para>
-   <command>DROP DOMAIN</command> removes a domain.  Only the owner of
-   a domain can remove it.  This duplicates the functionality provided by 
-   <xref linkend="sql-droptype"/> but restricts the object type to domain.
-
+   <command>DROP DOMAIN</command> removes a domain
+   <glossterm linkend="glossary-type">type</glossterm>.
+   Only the owner of a domain can remove it.
+   This duplicates the functionality provided by 
+   <xref linkend="sql-droptype"/> but restricts the object
+   <glossterm linkend="glossary-type-definition">type definition</glossterm>
+   to domain.
   </para>
  </refsect1>
 
@@ -45,7 +48,9 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
     <listitem>
      <para>
       This parameter instructs <productname>PostgreSQL</productname> to search 
-      for the first instance of any type definition with the provided name.
+      for the first instance of any
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
       If no type definition is found a notice is issued and the command ends.
       If a type definition is found then one of two things will happen:
       if the type definition is a domain 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 afaa2387fe..393be721e3 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -29,10 +29,11 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
   <title>Description</title>
 
   <para>
-   <command>DROP TYPE</command> removes a user-defined data type.
-   Only the owner of a type can remove it.  This includes domains,
-   though they can be targeted specifically by using the
-   <xref linkend="sql-dropdomain"/> command.
+   <command>DROP TYPE</command> removes a user-defined data
+   <glossterm linkend="glossary-type">type</glossterm>.
+   Only the owner of a type can remove it.
+   This includes domains, though they can be targeted
+   specifically by using the <xref linkend="sql-dropdomain"/> command.
   </para>
  </refsect1>
 
@@ -45,7 +46,9 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
     <listitem>
      <para>
       This parameter instructs <productname>PostgreSQL</productname> to search 
-      for the first instance of any type definition with the provided name.
+      for the first instance of any 
+      <glossterm linkend="glossary-type-definition">type definition</glossterm>
+      with the provided name.
       If no type definition is found a notice is issued and the command ends.
       If a type definition is found then it will be dropped. 
      </para>
#14Alexander Korotkov
aekorotkov@gmail.com
In reply to: David G. Johnston (#13)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

Hi!

I've skimmed through the thread and checked the patchset. Everything
looks good, except one paragraph, which doesn't look completely clear.

+  <para>
+   This emulates the functionality provided by
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type definition</glossterm>
+   to domain.
+  </para>

As I get it states that CREATE DOMAIN somehow "emulates" CREATE TYPE.
Could you please, rephrase it? It looks confusing to me yet.

------
Regards,
Alexander Korotkov

#15David G. Johnston
david.g.johnston@gmail.com
In reply to: Alexander Korotkov (#14)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Sep 15, 2020 at 3:48 PM Alexander Korotkov <aekorotkov@gmail.com>
wrote:

Hi!

I've skimmed through the thread and checked the patchset. Everything
looks good, except one paragraph, which doesn't look completely clear.

+  <para>
+   This emulates the functionality provided by
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type
definition</glossterm>
+   to domain.
+  </para>

As I get it states that CREATE DOMAIN somehow "emulates" CREATE TYPE.
Could you please, rephrase it? It looks confusing to me yet.

I'll look at it.

My main point here is that writing "CREATE TYPE typename AS DOMAIN" would
be expected, with the appropriate sub-specification, similar to "CREATE
TYPE typename AS RANGE". While the syntax wasn't rolled up into "CREATE
TYPE" proper "CREATE DOMAIN" effectively does the same thing - creates a
type of domain (just ask CREATE TYPE AS RANGE creates a type of range).
I'm calling "a type of something" the type's "type domain". CREATE DOMAIN
emulates the non-existent "CREATE TYPE typename AS DOMAIN" command.

David J.

#16Tom Lane
tgl@sss.pgh.pa.us
In reply to: David G. Johnston (#15)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

"David G. Johnston" <david.g.johnston@gmail.com> writes:

My main point here is that writing "CREATE TYPE typename AS DOMAIN" would
be expected, with the appropriate sub-specification, similar to "CREATE
TYPE typename AS RANGE".

Well, that point seems entirely invented. CREATE DOMAIN is in the
SQL standard:

<domain definition> ::=
CREATE DOMAIN <domain name> [ AS ] <predefined type>
[ <default clause> ]
[ <domain constraint>... ]
[ <collate clause> ]

While SQL does also have a CREATE TYPE command, domains are not
among the kinds of type it can make. So that separation is
very much per spec.

I don't personally find the doc changes proposed here to be a good idea.
001 seems to add a lot of verbosity and not much else. 002 invents terms
used nowhere else in our docs, which seems more confusing than anything
else. It is very badly in need of copy-editing, as well.

Also, I think the phrase you are looking for might be "type category".
Using "type definition" to mean that seems completely wrong. Deciding
that capitalized Type means something special is something I might expect
to find in one of the more abstruse philosophers, but it's not a great
idea in the Postgres manual ... especially when you then use different
terminology elsewhere.

regards, tom lane

#17David G. Johnston
david.g.johnston@gmail.com
In reply to: Tom Lane (#16)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Wed, Sep 16, 2020 at 4:42 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

"David G. Johnston" <david.g.johnston@gmail.com> writes:

My main point here is that writing "CREATE TYPE typename AS DOMAIN" would
be expected, with the appropriate sub-specification, similar to "CREATE
TYPE typename AS RANGE".

Well, that point seems entirely invented. CREATE DOMAIN is in the
SQL standard:

<domain definition> ::=
CREATE DOMAIN <domain name> [ AS ] <predefined type>
[ <default clause> ]
[ <domain constraint>... ]
[ <collate clause> ]

While SQL does also have a CREATE TYPE command, domains are not
among the kinds of type it can make. So that separation is
very much per spec.

I don't personally find the doc changes proposed here to be a good idea.
001 seems to add a lot of verbosity and not much else.

The intent is to add accuracy, which means verbosity given the non-obvious
choice made in the current implementation.

002 invents terms
used nowhere else in our docs, which seems more confusing than anything
else.

Fair point - was hoping it would be discussion starter.

It is very badly in need of copy-editing, as well.

I'll look at it with fresh eyes...

Also, I think the phrase you are looking for might be "type category".

Actually what I want is "Type type (typtype)" according to pg_type but that
seemed like an implementation detail that would be undesirable to use here
so I tried to give it a different name. Type category (typcategory)
already has a meaning.

Using "type definition" to mean that seems completely wrong. Deciding

that capitalized Type means something special is something I might expect
to find in one of the more abstruse philosophers, but it's not a great
idea in the Postgres manual ... especially when you then use different
terminology elsewhere.

I very well may have been inconsistent but coupled with the above point
"type of the Type" seems easier to follow compared to "type of the type" if
I were to change "type definition" to "type of the Type".

David J.

#18David G. Johnston
david.g.johnston@gmail.com
In reply to: Tom Lane (#16)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Wed, Sep 16, 2020 at 4:42 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

"David G. Johnston" <david.g.johnston@gmail.com> writes:

My main point here is that writing "CREATE TYPE typename AS DOMAIN" would
be expected, with the appropriate sub-specification, similar to "CREATE
TYPE typename AS RANGE".

Well, that point seems entirely invented. CREATE DOMAIN is in the
SQL standard:

And I'm writing for the user who sees that both "CREATE DOMAIN" and "CREATE
TYPE AS RANGE" exist, and that there is no "CREATE RANGE", and wonders why
if domains are simply a variant of a type, like ranges are, why doesn't
CREATE TYPE just create those as well - or, rather, are there any material
differences. I choose to include an observation that, no, they are not
materially different in terms of being abstract types.

It struck me as odd that it wasn't just CREATE TYPE AS DOMAIN and so in my
patch I thought to comment upon the oddity - and in doing so emphasize that
the DROP behavior for DOMAINS is no different than the types created by the
CREATE TYPE command.

David J.

#19David G. Johnston
david.g.johnston@gmail.com
In reply to: Alexander Korotkov (#14)
1 attachment(s)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Sep 15, 2020 at 3:48 PM Alexander Korotkov <aekorotkov@gmail.com>
wrote:

Hi!

I've skimmed through the thread and checked the patchset. Everything
looks good, except one paragraph, which doesn't look completely clear.

+  <para>
+   This emulates the functionality provided by
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type
definition</glossterm>
+   to domain.
+  </para>

As I get it states that CREATE DOMAIN somehow "emulates" CREATE TYPE.
Could you please, rephrase it? It looks confusing to me yet.

v5 attached, looking at this fresh and with some comments to consider.

I ended up just combining both patches into one.

I did away with the glossary changes altogether, and the invention of the
new term. I ended up limiting "type's type" to just domain usage but did a
couple of a additional tweaks that tried to treat domains as not being
actual types even though, at least in PostgreSQL, they are (at least as far
as DROP TYPE is concerned - and since I don't have any understanding of the
SQL Standard's decision to separate out create domain and create type I'll
just stick to the implementation in front of me.

David J.

Attachments:

001-v5-drop-if-exists-docs-and-tests.patchapplication/octet-stream; name=001-v5-drop-if-exists-docs-and-tests.patchDownload
commit 59ac2f05f66906e3cba20351e4d3b32f1d635e29
Author: David G. Johnston <david.g.johnston@gmail.com>
Date:   Wed Sep 30 01:51:20 2020 +0000

    Document DROP relation and DROP type 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 has merit 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.
    
    Due to the behavior of CREATE TABLE producing a composite type
    the behaviors for DROP TYPE and DROP DOMAIN are relevant here so
    add tests and documentation for those as well.  In particular
    document that DROP TYPE drops domains.
    
    One shortcoming tested here is that DROP IF EXISTS doesn't only
    look at the first search_path entry while CREATE IF NOT EXISTS
    will only consider the first search_path entry.
    
    As this patch addresses undocumented fundamental behavior for these
    commands backpatch to all supported versions.

diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 81a8924926..612580c749 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -38,7 +38,7 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
 
   <para>
    <command>CREATE DOMAIN</command> creates a new domain.  A domain is
-   essentially a data type with optional constraints (restrictions on
+   a data type with optional constraints (restrictions on
    the allowed set of values).
    The user who defines a domain becomes its owner.
   </para>
@@ -47,8 +47,8 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
    If a schema name is given (for example, <literal>CREATE DOMAIN
    myschema.mydomain ...</literal>) then the domain is created in the
    specified schema.  Otherwise it is created in the current schema.
-   The domain name must be unique among the types and domains existing
-   in its schema.
+   The domain name must be unique among all types (not just domains)
+   existing in its schema.
   </para>
 
   <para>
@@ -281,6 +281,7 @@ CREATE TABLE us_snail_addy (
   <simplelist type="inline">
    <member><xref linkend="sql-alterdomain"/></member>
    <member><xref linkend="sql-dropdomain"/></member>
+   <member><xref linkend="sql-droptype"/></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 111f8e65d2..fe1bfe5906 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -72,8 +72,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
   <para>
    If a schema name is given then the type is created in the specified
    schema.  Otherwise it is created in the current schema.  The type
-   name must be distinct from the name of any existing type or domain
-   in the same schema.  (Because tables have associated data types,
+   name must be distinct from the name of any existing type (including
+   domains) in the same schema.  (Because tables have associated data types,
    the type name must also be distinct from the name of any existing
    table in the same schema.)
   </para>
@@ -83,7 +83,10 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    syntax synopsis above.  They respectively create a <firstterm>composite
    type</firstterm>, an <firstterm>enum type</firstterm>, a <firstterm>range type</firstterm>, a
    <firstterm>base type</firstterm>, or a <firstterm>shell type</firstterm>.  The first four
-   of these are discussed in turn below.  A shell type is simply a placeholder
+   of these are discussed in turn below and represent four of the five types of data types,
+   with the fifth, a <firstterm>domain type</firstterm>, created using the
+   <xref linkend="sql-createdomain"/> command.
+   A shell type is simply a placeholder
    for a type to be defined later; it is created by issuing <command>CREATE
    TYPE</command> with no parameters except for the type name.  Shell types
    are needed as forward references when creating range types and base types,
diff --git a/doc/src/sgml/ref/drop_domain.sgml b/doc/src/sgml/ref/drop_domain.sgml
index b18faf3917..9106838c68 100644
--- a/doc/src/sgml/ref/drop_domain.sgml
+++ b/doc/src/sgml/ref/drop_domain.sgml
@@ -29,8 +29,11 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
   <title>Description</title>
 
   <para>
-   <command>DROP DOMAIN</command> removes a domain.  Only the owner of
-   a domain can remove it.
+   <command>DROP DOMAIN</command> removes a domain type.
+   Only the owner of a domain can remove it.
+   This duplicates the functionality provided by 
+   <xref linkend="sql-droptype"/> but restricts
+   the type's type to domain.
   </para>
  </refsect1>
 
@@ -42,8 +45,11 @@ DROP DOMAIN [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, .
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search 
+      for the first instance of any type with the provided name.
+      If no type is found a notice is issued and the command ends.
+      If a type is found then one of two things will happen:
+      if the type is a domain it is dropped, otherwise the command fails.      
      </para>
     </listitem>
    </varlistentry>
@@ -108,6 +114,7 @@ DROP DOMAIN box;
   <simplelist type="inline">
    <member><xref linkend="sql-createdomain"/></member>
    <member><xref linkend="sql-alterdomain"/></member>
+   <member><xref linkend="sql-droptype"/></member>
   </simplelist>
  </refsect1>
 
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 ] <replaceable class="parameter">name</replaceabl
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the foreign table does not exist.
-      A notice is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml
index 85cf23bca2..b07627fb55 100644
--- a/doc/src/sgml/ref/drop_index.sgml
+++ b/doc/src/sgml/ref/drop_index.sgml
@@ -72,8 +72,11 @@ DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] <replaceable class="parameter">name</r
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the index does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replac
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the materialized view does not exist. A notice
-      is issued in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [,
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the sequence does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
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 ] <replaceable class="parameter">name</replaceable> [, ..
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the table does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml
index 9e555c0624..f88e6353ee 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -31,6 +31,8 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
   <para>
    <command>DROP TYPE</command> removes a user-defined data type.
    Only the owner of a type can remove it.
+   This includes domains, though they can be removed specifically
+   by using the <xref linkend="sql-dropdomain"/> command.
   </para>
  </refsect1>
 
@@ -42,8 +44,15 @@ DROP TYPE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the type does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search
+      for the first instance of any type with the provided name.
+      If no type is found a notice is issued and the command ends.
+      If a type is found it is dropped.
+     </para>
+     <para>
+      However, dropping the automatically created composite type of an
+      existing relation will fail as the relation has a dependency on the composite
+      type.
      </para>
     </listitem>
    </varlistentry>
@@ -110,6 +119,7 @@ DROP TYPE box;
   <simplelist type="inline">
    <member><xref linkend="sql-altertype"/></member>
    <member><xref linkend="sql-createtype"/></member>
+   <member><xref linkend="sql-dropdomain"/></member>
   </simplelist>
  </refsect1>
 
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 ] <replaceable class="parameter">name</replaceable> [, ...
     <term><literal>IF EXISTS</literal></term>
     <listitem>
      <para>
-      Do not throw an error if the view does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> 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.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index 5e44c2c3ce..b25886272b 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -330,6 +330,125 @@ 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);
+CREATE TABLE IF NOT EXISTS test_rel_exists (c text, d int); -- IF NOT EXISTS checks only the first schema
+DROP TABLE IF EXISTS test_rel_exists; -- Two matches but only the first is dropped
+-- 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
+-- 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 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.
+CREATE VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP VIEW to remove a view.
+DROP VIEW test_rel_exists;
+CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai);
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP INDEX to remove an index.
+DROP INDEX test_rel_exists;
+CREATE SEQUENCE test_rel_exists;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP SEQUENCE to remove a sequence.
+DROP SEQUENCE test_rel_exists;
+CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP MATERIALIZED VIEW to remove a materialized view.
+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;
+-- Type & Domain behavior is thus:
+-- If Exists Behavior
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+DROP TYPE IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+CREATE DOMAIN test_rel_exists int4;
+DROP TABLE test_rel_exists;
+DROP DOMAIN test_rel_exists;
+-- Test with domain first and type second; plus table tests
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text); -- type with same name exists as table creation creates a composite type
+ERROR:  type "test_rel_exists" already exists
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4; -- domain creation with same name as table fails as auto-generated type conflicts
+ERROR:  type "test_rel_exists" already exists
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists; -- cannot independently drop a composite type associated with a table
+ERROR:  cannot drop type test_rel_exists because table test_rel_exists requires it
+HINT:  You can drop table test_rel_exists instead.
+DROP TABLE test_if_exists_second.test_rel_exists;
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); -- auto-created type prevents table from being created
+ERROR:  relation "test_rel_exists" already exists
+DROP TABLE test_if_exists_second.test_rel_exists; -- a standalone composite type cannot be dropped using the drop table command
+ERROR:  "test_rel_exists" is not a table
+HINT:  Use DROP TYPE to remove a type.
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+CREATE DOMAIN test_rel_exists int4;
+DROP TYPE IF EXISTS test_rel_exists; -- DROP TYPE Drops the Domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+ERROR:  "test_if_exists_second.test_rel_exists" is not a domain
+DROP DOMAIN test_rel_exists; -- double-checking the domain doesn't exist
+ERROR:  "test_rel_exists" is not a domain
+DROP TYPE test_rel_exists;
+-- Test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+DROP DOMAIN IF EXISTS test_rel_exists;
+ERROR:  "test_rel_exists" is not a domain
+DROP TYPE IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+DROP DOMAIN IF EXISTS test_rel_exists;
+NOTICE:  type "test_rel_exists" does not exist, skipping
+-- /Type & 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..b95189c47c 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -296,6 +296,104 @@ 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);
+
+CREATE TABLE IF NOT EXISTS test_rel_exists (c text, d int); -- IF NOT EXISTS checks only the first schema
+DROP TABLE IF EXISTS test_rel_exists; -- Two matches but only the first is dropped
+
+-- A role is not a relation so this shouldn't be affected
+DROP ROLE IF EXISTS test_rel_exists;
+
+-- 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 SEQUENCE IF EXISTS test_rel_exists;
+DROP MATERIALIZED VIEW IF EXISTS test_rel_exists;
+DROP FOREIGN TABLE IF EXISTS test_rel_exists;
+
+CREATE VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+DROP VIEW test_rel_exists;
+
+CREATE INDEX test_rel_exists ON test_if_exists_first.test_rel_with_index (ai);
+DROP TABLE test_rel_exists;
+DROP INDEX test_rel_exists;
+
+CREATE SEQUENCE test_rel_exists;
+DROP TABLE test_rel_exists;
+DROP SEQUENCE test_rel_exists;
+
+CREATE MATERIALIZED VIEW test_if_exists_first.test_rel_exists AS
+    SELECT 1::bigint AS d, 'e'::text AS e;
+DROP TABLE test_rel_exists;
+
+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;
+
+-- Type & Domain behavior is thus:
+-- If Exists Behavior
+DROP DOMAIN IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_rel_exists;
+
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+DROP DOMAIN IF EXISTS test_rel_exists;
+CREATE DOMAIN test_rel_exists int4;
+DROP TABLE test_rel_exists;
+DROP DOMAIN test_rel_exists;
+
+-- Test with domain first and type second; plus table tests
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text);
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text); -- type with same name exists as table creation creates a composite type
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4; -- domain creation with same name as table fails as auto-generated type conflicts
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists; -- cannot independently drop a composite type associated with a table
+DROP TABLE test_if_exists_second.test_rel_exists;
+
+CREATE TYPE test_if_exists_second.test_rel_exists AS (c int, d text);
+CREATE TABLE test_if_exists_second.test_rel_exists (a int, b text); -- auto-created type prevents table from being created
+DROP TABLE test_if_exists_second.test_rel_exists; -- a standalone composite type cannot be dropped using the drop table command
+
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+CREATE DOMAIN test_rel_exists int4;
+DROP TYPE IF EXISTS test_rel_exists; -- DROP TYPE Drops the Domain
+DROP DOMAIN IF EXISTS test_if_exists_second.test_rel_exists;
+DROP DOMAIN test_rel_exists; -- double-checking the domain doesn't exist
+DROP TYPE test_rel_exists;
+
+-- Test with domain second and type first
+CREATE TYPE test_if_exists_first.test_rel_exists AS (c int, d text);
+CREATE DOMAIN test_if_exists_second.test_rel_exists int4;
+DROP DOMAIN IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_rel_exists;
+DROP TYPE IF EXISTS test_if_exists_second.test_rel_exists;
+
+DROP DOMAIN IF EXISTS test_rel_exists;
+-- /Type & 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);
#20Pavel Stehule
pavel.stehule@gmail.com
In reply to: David G. Johnston (#19)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

st 30. 9. 2020 v 4:01 odesílatel David G. Johnston <
david.g.johnston@gmail.com> napsal:

On Tue, Sep 15, 2020 at 3:48 PM Alexander Korotkov <aekorotkov@gmail.com>
wrote:

Hi!

I've skimmed through the thread and checked the patchset. Everything
looks good, except one paragraph, which doesn't look completely clear.

+  <para>
+   This emulates the functionality provided by
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type
definition</glossterm>
+   to domain.
+  </para>

As I get it states that CREATE DOMAIN somehow "emulates" CREATE TYPE.
Could you please, rephrase it? It looks confusing to me yet.

v5 attached, looking at this fresh and with some comments to consider.

I ended up just combining both patches into one.

I did away with the glossary changes altogether, and the invention of the
new term. I ended up limiting "type's type" to just domain usage but did a
couple of a additional tweaks that tried to treat domains as not being
actual types even though, at least in PostgreSQL, they are (at least as far
as DROP TYPE is concerned - and since I don't have any understanding of the
SQL Standard's decision to separate out create domain and create type I'll
just stick to the implementation in front of me.

+1

Pavel

Show quoted text

David J.

#21Anastasia Lubennikova
a.lubennikova@postgrespro.ru
In reply to: David G. Johnston (#19)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On 30.09.2020 05:00, David G. Johnston wrote:

On Tue, Sep 15, 2020 at 3:48 PM Alexander Korotkov
<aekorotkov@gmail.com <mailto:aekorotkov@gmail.com>> wrote:

Hi!

I've skimmed through the thread and checked the patchset. Everything
looks good, except one paragraph, which doesn't look completely clear.

+  <para>
+   This emulates the functionality provided by
+   <xref linkend="sql-createtype"/> but sets the created object's
+   <glossterm linkend="glossary-type-definition">type
definition</glossterm>
+   to domain.
+  </para>

As I get it states that CREATE DOMAIN somehow "emulates" CREATE TYPE.
Could you please, rephrase it?  It looks confusing to me yet.

v5 attached, looking at this fresh and with some comments to consider.

I ended up just combining both patches into one.

I did away with the glossary changes altogether, and the invention of
the new term.  I ended up limiting "type's type" to just domain usage
but did a couple of a additional tweaks that tried to treat domains as
not being actual types even though, at least in PostgreSQL, they are
(at least as far as DROP TYPE is concerned - and since I don't have
any understanding of the SQL Standard's decision to separate out
create domain and create type I'll just stick to the implementation in
front of me.

David J.

Reminder from a CF manager, as this thread was inactive for a while.
Alexander, I see you signed up as a committer for this entry. Are you
going to continue this work?

--
Anastasia Lubennikova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#22David Steele
david@pgmasters.net
In reply to: Anastasia Lubennikova (#21)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

Hi David,

On 11/23/20 3:31 PM, Anastasia Lubennikova wrote:

On 30.09.2020 05:00, David G. Johnston wrote:

v5 attached, looking at this fresh and with some comments to consider.

I ended up just combining both patches into one.

I did away with the glossary changes altogether, and the invention of
the new term.  I ended up limiting "type's type" to just domain usage
but did a couple of a additional tweaks that tried to treat domains as
not being actual types even though, at least in PostgreSQL, they are
(at least as far as DROP TYPE is concerned - and since I don't have
any understanding of the SQL Standard's decision to separate out
create domain and create type I'll just stick to the implementation in
front of me.

Reminder from a CF manager, as this thread was inactive for a while.
Alexander, I see you signed up as a committer for this entry. Are you
going to continue this work?

This patch was marked Ready for Committer on July 14 but received a
significant update on August 30. So, I have marked it Needs Review.

Further, I think we should close this entry at the end of the CF if it
does not attract committer interest. Tom is not in favor of the patch
and it appears Alexander decided not to commit it.

Regards,
--
-David
david@pgmasters.net

#23David G. Johnston
david.g.johnston@gmail.com
In reply to: David Steele (#22)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tuesday, March 9, 2021, David Steele <david@pgmasters.net> wrote:

Further, I think we should close this entry at the end of the CF if it
does not attract committer interest. Tom is not in favor of the patch and
it appears Alexander decided not to commit it.

Pavel re-reviewed it and was fine with ready-to-commit so that status seems
fine.

Frankly, I am hoping for a bit more constructive feedback and even
collaboration from a committer, specifically Tom, on this one given the
outstanding user complaints received on the topic, our disagreement
regarding fixing it (which motivates the patch to better document and add
tests), and professional courtesy given to a fellow consistent community
contributor.

So, no, making it just go away because one of the dozens of committers
can’t make time to try and make it work doesn’t sit well with me. If a
committer wants to actively reject the patch with an explanation then so be
it.

David J.

#24David Steele
david@pgmasters.net
In reply to: David G. Johnston (#23)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On 3/9/21 10:08 AM, David G. Johnston wrote:

On Tuesday, March 9, 2021, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:

Further, I think we should close this entry at the end of the CF if
it does not attract committer interest. Tom is not in favor of the
patch and it appears Alexander decided not to commit it.

Pavel re-reviewed it and was fine with ready-to-commit so that status
seems fine.

Ah yes, that was my mistake.

Regards,
--
-David
david@pgmasters.net

#25Ibrar Ahmed
ibrar.ahmad@gmail.com
In reply to: David Steele (#24)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Mar 9, 2021 at 9:01 PM David Steele <david@pgmasters.net> wrote:

On 3/9/21 10:08 AM, David G. Johnston wrote:

On Tuesday, March 9, 2021, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:

Further, I think we should close this entry at the end of the CF if
it does not attract committer interest. Tom is not in favor of the
patch and it appears Alexander decided not to commit it.

Pavel re-reviewed it and was fine with ready-to-commit so that status
seems fine.

Ah yes, that was my mistake.

Regards,
--
-David
david@pgmasters.net

The status of the patch is "Need Review" which was previously "Ready for
Committer ''. After @David G
and @David Steele <david@pgmasters.net> comments, it's not clear whether it
should be "Read for commit" or "Need Review".

--
Ibrar Ahmed

#26David G. Johnston
david.g.johnston@gmail.com
In reply to: Ibrar Ahmed (#25)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Jul 13, 2021 at 3:30 AM Ibrar Ahmed <ibrar.ahmad@gmail.com> wrote:

On Tue, Mar 9, 2021 at 9:01 PM David Steele <david@pgmasters.net> wrote:

On 3/9/21 10:08 AM, David G. Johnston wrote:

On Tuesday, March 9, 2021, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:

Further, I think we should close this entry at the end of the CF if
it does not attract committer interest. Tom is not in favor of the
patch and it appears Alexander decided not to commit it.

Pavel re-reviewed it and was fine with ready-to-commit so that status
seems fine.

Ah yes, that was my mistake.

Regards,
--
-David
david@pgmasters.net

The status of the patch is "Need Review" which was previously "Ready for
Committer ''. After @David G
and @David Steele <david@pgmasters.net> comments, it's not clear whether
it should be "Read for commit" or "Need Review".

I changed it to Ready to Commit based on the same logic as my reply to
David quoted above.

David J.

#27Robert Haas
robertmhaas@gmail.com
In reply to: David G. Johnston (#23)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Mar 9, 2021 at 10:09 AM David G. Johnston
<david.g.johnston@gmail.com> wrote:

Frankly, I am hoping for a bit more constructive feedback and even collaboration from a committer, specifically Tom, on this one given the outstanding user complaints received on the topic, our disagreement regarding fixing it (which motivates the patch to better document and add tests), and professional courtesy given to a fellow consistent community contributor.

So, no, making it just go away because one of the dozens of committers can’t make time to try and make it work doesn’t sit well with me. If a committer wants to actively reject the patch with an explanation then so be it.

I have reviewed this patch and my opinion is that we should reject it,
because in my opinion, the documentation changes are not improvements,
and there is no really clear need for the regression test additions.
According to my view of the situation, there are three kinds of
changes in this patch. The first set of hunks make minor wording
adjustments. Typical is this:

    <command>CREATE DOMAIN</command> creates a new domain.  A domain is
-   essentially a data type with optional constraints (restrictions on
+   a data type with optional constraints (restrictions on
    the allowed set of values).

So, the only change here is deleting the word "essentially." Now, it's
possible that if someone different had written the original text, they
might have chosen to leave that word out. Personally, I would have
chosen to include it, but it's a judgement call. However, I find it
extremely difficult to imagine that anybody will be confused because
of the presence of that word there.

-   The domain name must be unique among the types and domains existing
-   in its schema.
+   The domain name must be unique among all types (not just domains)
+   existing in its schema.

Similarly here. It is arguable which way the text reads better, but
the stated purpose of the patch is to make the behavior more clear,
and I cannot imagine someone reading the existing text and getting
confused, and then reading the patched text and being not confused.

-      Do not throw an error if the domain does not exist. A notice is issued
-      in this case.
+      This parameter instructs <productname>PostgreSQL</productname> to search
+      for the first instance of any type with the provided name.
+      If no type is found a notice is issued and the command ends.
+      If a type is found then one of two things will happen:
+      if the type is a domain it is dropped, otherwise the command fails.

This is the second kind of hunk that is present in this patch. There
are a whole bunch that are very similar, adjusting the documentation
for various object types. I appreciate that it does confuse users
sometimes that a DROP IF NOT EXISTS command can still fail for some
other reason, so maybe we should try to clarify that in some way, but
I find this explanation to be too complex and technical to be helpful.
If we feel it's necessary to be more clear here, I'd suggest keeping
the existing text and adding a sentence like: "Note that the command
can still fail for other reasons; for example, it is an error if a
type with the provided name exists but is not a domain."

I feel that it's unnecessary to try to clarify what the behavior of
the command is relative to search_path, but if it were agreed that we
need to do so, I still would not like this way of doing it. First, you
never actually use the term "search_path". I expect a lot of people
would be confused by the reference to searching "for the first
instance" because they won't know what is being searched. Second, this
entire bit of text is inside the description of "IF NOT EXISTS" but
the behavior in question is mostly stuff that applies whether or not
"IF NOT EXISTS" is specified. Moreover, it's not only not specific to
IF NOT EXISTS, it's not even specific to this particular command. The
behavior of looking through the search_path for the first instance of
some object type is one that we use practically everywhere in all
kinds of situations. I think we are going to go crazy if we try to
re-explain that in every place where it might be relevant.

The final portion of the patch adds new regression tests. I'm not
going to say that this is completely without merit, because it can be
useful to know if some patch changes the behavior, but I guess I don't
really see a whole lot of value in it, either. It's easy to end up
with a ton of tests that take up a little bit of time every time
someone runs 'make check' but only ever find behavior changes that the
developer was intentionally trying to make. I think it's possible that
these changes would end up falling into that category. I wouldn't
complaining about them getting committed, or about committing them
myself, if there were a chorus of people convinced that they were
worth having, but there isn't, and I don't find them valuable enough
myself to justify a commit.

--
Robert Haas
EDB: http://www.enterprisedb.com

#28David G. Johnston
david.g.johnston@gmail.com
In reply to: Robert Haas (#27)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Aug 10, 2021 at 12:04 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Mar 9, 2021 at 10:09 AM David G. Johnston
<david.g.johnston@gmail.com> wrote:

Frankly, I am hoping for a bit more constructive feedback and even

collaboration from a committer, specifically Tom, on this one given the
outstanding user complaints received on the topic, our disagreement
regarding fixing it (which motivates the patch to better document and add
tests), and professional courtesy given to a fellow consistent community
contributor.

So, no, making it just go away because one of the dozens of committers

can’t make time to try and make it work doesn’t sit well with me. If a
committer wants to actively reject the patch with an explanation then so be
it.

I have reviewed this patch and my opinion is that we should reject it,

Thank you for the feedback.

So, the only change here is deleting the word "essentially."

I do tend to find this wishy-washy language to be more annoying than the
community at large.

I'd suggest keeping
the existing text and adding a sentence like: "Note that the command
can still fail for other reasons; for example, it is an error if a
type with the provided name exists but is not a domain."

I would at least like to see this added in response to the various bug
reports that found the shared namespace among types, and the fact that it
causes an error, to be a surprise.

The final portion of the patch adds new regression tests. I'm not
going to say that this is completely without merit, because it can be
useful to know if some patch changes the behavior, but I guess I don't
really see a whole lot of value in it, either.

I'd say the Bug # 16492 tests warrant keeping independent of the opinion
that demonstrating the complicated interplay between 10+ SQL commands isn't
worth the test suite time. I'd say that probably half of the tests are
demonstrating non-intuitive behavior from my perspective. The bug test
noted above plus the one the demonstration that a table in the non-first
schema in a search_path will not prevent a create <type> command from
succeeding but will cause a DROP <type non-table> IF EXISTS to error out.
Does it need to test all 5 types, probably not, but it should at least test
DROP VIEW IF EXISTS test_rel_exists.

What about the inherent confusion that having both DROP DOMAIN when DROP
TYPE will also drop domains? The doc change for that doesn't really fit
into your buckets. It would include:

drop_domain.sgml
+   This duplicates the functionality provided by
+   <xref linkend="sql-droptype"/> but restricts
+   the type's type to domain.
drop_type.sgml
+   This includes domains, though they can be removed specifically
+   by using the <xref linkend="sql-dropdomain"/> command.

Adding sql-droptype to "See Also" on all the domain related command pages
as well.

After looking at this again I will say I do agree that the procedural
nature of the doc changes for the main issue were probably overkill and a
"oh-by-the-way" note as to the fact that we ERROR on a namespace conflict
would address that concern in a user-facing way adequately. Looking back
and what I went through to put the test script together I don't regret
doing the work and feel that someone like myself would benefit from its
existence as a whole. It's more useful than a README that would
communicate the same information.

David J.

#29Robert Haas
robertmhaas@gmail.com
In reply to: David G. Johnston (#28)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Tue, Aug 10, 2021 at 5:53 PM David G. Johnston
<david.g.johnston@gmail.com> wrote:

The final portion of the patch adds new regression tests. I'm not
going to say that this is completely without merit, because it can be
useful to know if some patch changes the behavior, but I guess I don't
really see a whole lot of value in it, either.

I'd say the Bug # 16492 tests warrant keeping independent of the opinion that demonstrating the complicated interplay between 10+ SQL commands isn't worth the test suite time. I'd say that probably half of the tests are demonstrating non-intuitive behavior from my perspective. The bug test noted above plus the one the demonstration that a table in the non-first schema in a search_path will not prevent a create <type> command from succeeding but will cause a DROP <type non-table> IF EXISTS to error out. Does it need to test all 5 types, probably not, but it should at least test DROP VIEW IF EXISTS test_rel_exists.

It's not the function of the regression tests to demonstrate behavior.
Nobody says "I wonder how the system works, I guess I'll go look at
the regression tests," or at least very few people. The job of the
regression test is to let us know when things get broken by future
changes to the source code, or in other words, to find regressions.

What about the inherent confusion that having both DROP DOMAIN when DROP TYPE will also drop domains? The doc change for that doesn't really fit into your buckets. It would include:

drop_domain.sgml
+   This duplicates the functionality provided by
+   <xref linkend="sql-droptype"/> but restricts
+   the type's type to domain.
drop_type.sgml
+   This includes domains, though they can be removed specifically
+   by using the <xref linkend="sql-dropdomain"/> command.

Maybe we could add a sentence to the end of the DROP DOMAIN synopsis,
like: Since a domain is a kind of type, they can alternatively be
dropped using DROP TYPE.

And for DROP TYPE, we could say something like: Since a domain is a
kind of type, it is possible to drop a domain using this command
rather than DROP DOMAIN.

I think that's similar to your proposal, but I prefer it because I
think the duplicate-but-restrict language makes it sound kind of dumb
that DROP DOMAIN exists at all, and I don't think it's dumb that we
have DROP commands matching the CREATE commands that we also have.

Adding sql-droptype to "See Also" on all the domain related command pages as well.

I probably wouldn't do this, but the notes above could include cross-links.

After looking at this again I will say I do agree that the procedural nature of the doc changes for the main issue were probably overkill and a "oh-by-the-way" note as to the fact that we ERROR on a namespace conflict would address that concern in a user-facing way adequately. Looking back and what I went through to put the test script together I don't regret doing the work and feel that someone like myself would benefit from its existence as a whole. It's more useful than a README that would communicate the same information.

Yeah. I tend to feel like this is the kind of thing where it's not
likely to be 100% obvious to users how it all works no matter what we
put in the documentation, and that some of the behavior of a system
has to be learned just by trying out the system. Now, that doesn't
mean that we should be immune to ideas about how to improve it, just
that someone can always have a question that isn't answered there.

--
Robert Haas
EDB: http://www.enterprisedb.com

#30David G. Johnston
david.g.johnston@gmail.com
In reply to: Robert Haas (#29)
Re: DROP relation IF EXISTS Docs and Tests - Bug Fix

On Wed, Aug 11, 2021 at 5:54 AM Robert Haas <robertmhaas@gmail.com> wrote:

Yeah. I tend to feel like this is the kind of thing where it's not
likely to be 100% obvious to users how it all works no matter what we
put in the documentation, and that some of the behavior of a system
has to be learned just by trying out the system.

I see where you are coming from and agree with a good portion of it, and
accept that you are accurately representing reality for most of the rest,
regardless of my feelings on that reality. I'm not invested in this enough
to try and change mindsets. I have withdrawn the patch from the commitfest
and do not plan on putting forth a replacement.

Thank you for your thorough review, I do appreciate it. It did reaffirm
some of the suspicions I had about the wording I had chosen at least.

I will add that when I finished the patch I felt it was of more value to
future developers than it would be to our end users. I never really
worried that a patch could be written to fill in the missing pieces that
prompted the various bug reports. I went to the extra effort because the
underlying interactions seemed complicated and I wanted to see in practice
exactly how they behaved and what that meant for usability. I also still
disagree with our choice to emit an error on a namespace collision, and
generally feel this area could use improvement. Thus I keep the tests
around, which basically communicate that "this is how things work and it is
intentional" and also are useful to have should future changes, however
unlikely to materialize, be made. Sure, that isn't "regression" but in my
unfamiliarity I didn't know of a better existing place to put them and
didn't think to figure out (or create) a better location, one that doesn't
run on every build.

David J.