On Thu, Nov 15, 2018 at 10:52 AM Robert Haas <robertmh...@gmail.com> wrote:
> I think that the solution that you are proposing sorta sucks, but it's
> better than hacking objsubid to do it, which did in fact pass the
> laugh test, in that I laughed when I read it.  :-)

Probably a good idea to get another cup of coffee if I'm
pre-apologizing for my ideas.

> In a perfect world, it seems to me that what we ought to do is have
> some real logic in the server that figures out which of the various
> things we could report would be most likely to be useful to the user
> ... but that's probably a non-trivial project involving a fair amount
> of human judgement.  Reasonable people may differ about what is best,
> never mind unreasonable people.  I am inclined to think that your
> proposal here is good enough for now, and somebody who dislikes it
> (surely such a person will exist) can decide to look for ways to make
> it better.

Great. Actually, the on-disk size of the pg_depend heap relation is
*unchanged* in the attached WIP patch, since it fits in a hole
previously lost to alignment. And, as I said, the indexes end up
smaller with suffix truncation. Even if the only thing you care about
is the on-disk size of system catalogs, you'll still pretty reliably
come out ahead. The design here is squirrelly, but we're already
relying on scan order to reach objsubid = 0 entries first.

There is a single tiny behavioral change to the regression test output
with this patch applied. I think that that's just because there is one
place where this dependency management stuff interacts with pages full
of duplicates, and therefore stops putting duplicates in pg_depend
indexes in exactly DESC TID order. My other patches add a couple of
more tiny changes along similar lines, since of course I'm only doing
this with the pg_depend indexes, and not for every system catalog
index.

-- 
Peter Geoghegan
From 7158fa1f93447d1f9bb296605a65b0fecfd54210 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <p...@bowt.ie>
Date: Tue, 13 Nov 2018 18:14:23 -0800
Subject: [PATCH 01/18] Add pg_depend index scan tiebreaker column.

findDependentObjects() and other code that scans pg_depend evolved to
depend on pg_depend_depender_index and pg_depend_reference_index scan
order.  This is clear from running the regression tests with
"ignore_system_indexes=on": much of the test output changes for
regression tests that happen to have DROP diagnostic messages.  More
importantly, a small number of those test failures involve alternative
messages that are objectively useless or even harmful.  These
regressions all involve internal dependencies of one form or another.
For example, we can go from a HINT that suggests dropping a partitioning
parent table's trigger in order to drop the child's trigger to a HINT
that suggests simply dropping the child table.  This HINT is technically
still correct from the point of view of findDependentObjects(), and yet
it's clearly not the intended user-visible behavior.

Make dependency.c take responsibility for its dependency on scan order
by commenting on it directly.  Ensure that the behavior of
findDependentObjects() is deterministic in the event of duplicates by
adding a new per-backend sequentially assigned number column to
pg_depend.  Both indexes now use this new column as a final trailing
attribute, effectively making it a tiebreaker wherever processing order
happens to matter.  The new column is a per-backend sequentially
assigned number.  New values are assigned in decreasing order.  That
produces the behavior that's already expected from nbtree scans in the
event of duplicate index entries (nbtree usually leaves duplicate index
entries in reverse insertion order at present).  A similar new column is
not needed for pg_shdepend because the aforementioned harmful changes
only occur with cases involving internal dependencies.

The overall effect is to stabilize the behavior of DROP diagnostic
messages, making it possible to avoid the "\set VERBOSITY=terse" hack
that has been used to paper over test instability in the past.  We may
wish to go through the regression tests and remove existing instances of
the "\set VERBOSITY=terse" hack (see also: commit 8e753726), but that's
left for later.

An upcoming patch to make nbtree store duplicate entries in a
well-defined order by treating heap TID as a tiebreaker tuple attribute
more or less flips the order that duplicates appear (the order will
change from approximately descending to perfectly ascending).  That
would cause all sorts of problems for findDependentObjects() callers if
this groundwork was skipped.

The nbtree patch series will more than compensate for the overhead of
adding the new column: both pg_depend indexes end up slightly smaller
after the regression tests are run once suffix truncation is used.
There is no change in the on-disk size of pg_depend heap relations on
64-bit platforms, since the new column fits in a space that was
previously lost to alignment.  The MAXALIGN()'d size of pg_depend heap
tuples remains 56 bytes (including tuple header overhead).

Discussion: https://postgr.es/m/CAH2-Wzkypv1R+teZrr71U23J578NnTBt2X8+Y=odr4podw1...@mail.gmail.com
Discussion: https://postgr.es/m/11852.1501610262%40sss.pgh.pa.us
---
 doc/src/sgml/catalogs.sgml                | 17 ++++++++-
 src/backend/catalog/dependency.c          | 10 ++++++
 src/backend/catalog/pg_depend.c           | 10 +++++-
 src/bin/initdb/initdb.c                   | 44 +++++++++++------------
 src/include/catalog/indexing.h            |  4 +--
 src/include/catalog/pg_depend.h           |  1 +
 src/test/regress/expected/alter_table.out |  2 +-
 src/test/regress/expected/misc_sanity.out |  4 +--
 8 files changed, 63 insertions(+), 29 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8b7f169d50..af2514913e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2952,6 +2952,20 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>depcreate</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry></entry>
+      <entry>
+       A per-backend sequentially assigned number for this dependency
+       relationship.  Used as a tiebreaker in the event of multiple
+       internal dependency relationships of otherwise equal
+       precedence.  Identifiers are assigned in descending order to
+       ensure that the most recently entered dependency is the one
+       referenced by <literal>HINT</literal> fields.
+      </entry>
+     </row>
+
     </tbody>
    </tgroup>
   </table>
@@ -3069,7 +3083,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        that the system itself depends on the referenced object, and so
        that object must never be deleted.  Entries of this type are
        created only by <command>initdb</command>.  The columns for the
-       dependent object contain zeroes.
+       dependent object and the <structfield>depcreate</structfield>
+       column contain zeroes.
       </para>
      </listitem>
     </varlistentry>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 7dfa3278a5..d7889c2cd0 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -532,6 +532,11 @@ findDependentObjects(const ObjectAddress *object,
 	else
 		nkeys = 2;
 
+	/*
+	 * Note that we rely on DependDependerIndexId scan order to make
+	 * diagnostic messages deterministic.  (e.g., objsubid = 0 entries will be
+	 * processed before other entries for the same dependent object.)
+	 */
 	scan = systable_beginscan(*depRel, DependDependerIndexId, true,
 							  NULL, nkeys, key);
 
@@ -727,6 +732,11 @@ findDependentObjects(const ObjectAddress *object,
 	else
 		nkeys = 2;
 
+	/*
+	 * Note that we rely on DependReferenceIndexId scan order to make
+	 * diagnostic messages deterministic.  (e.g., refobjsubid = 0 entries will
+	 * be processed before other entries for the same referenced object.)
+	 */
 	scan = systable_beginscan(*depRel, DependReferenceIndexId, true,
 							  NULL, nkeys, key);
 
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 2ea05f350b..695330c2af 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -64,6 +64,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	int			i;
 	bool		nulls[Natts_pg_depend];
 	Datum		values[Natts_pg_depend];
+	static int32 depcreate = INT32_MAX;
 
 	if (nreferenced <= 0)
 		return;					/* nothing to do */
@@ -93,7 +94,10 @@ recordMultipleDependencies(const ObjectAddress *depender,
 		{
 			/*
 			 * Record the Dependency.  Note we don't bother to check for
-			 * duplicate dependencies; there's no harm in them.
+			 * duplicate dependencies; there's no harm in them.  Note that
+			 * depcreate ensures deterministic processing among dependencies
+			 * of otherwise equal precedence (e.g., among multiple entries of
+			 * the same refclassid + refobjid + refobjsubid).
 			 */
 			values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
 			values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
@@ -104,12 +108,16 @@ recordMultipleDependencies(const ObjectAddress *depender,
 			values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
 
 			values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
+			values[Anum_pg_depend_depcreate - 1] = Int32GetDatum(depcreate--);
 
 			tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
 
 			/* fetch index info only when we know we need it */
 			if (indstate == NULL)
 				indstate = CatalogOpenIndexes(dependDesc);
+			/* avoid signed underflow */
+			if (depcreate == INT_MIN)
+				depcreate = INT_MAX;
 
 			CatalogTupleInsertWithInfo(dependDesc, tup, indstate);
 
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ab5cb7f0c1..d013d184c7 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1619,55 +1619,55 @@ setup_depend(FILE *cmdfd)
 		"DELETE FROM pg_shdepend;\n\n",
 		"VACUUM pg_shdepend;\n\n",
 
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_class;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_proc;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_type;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_cast;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_constraint;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_conversion;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_attrdef;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_language;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_operator;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_opclass;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_opfamily;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_am;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_amop;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_amproc;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_rewrite;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_trigger;\n\n",
 
 		/*
 		 * restriction here to avoid pinning the public namespace
 		 */
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_namespace "
 		"    WHERE nspname LIKE 'pg%';\n\n",
 
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_ts_parser;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_ts_dict;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_ts_template;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_ts_config;\n\n",
-		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
+		"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p',0 "
 		" FROM pg_collation;\n\n",
 		"INSERT INTO pg_shdepend SELECT 0,0,0,0, tableoid,oid, 'p' "
 		" FROM pg_authid;\n\n",
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 254fbef1f7..d3e7699003 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -142,9 +142,9 @@ DECLARE_UNIQUE_INDEX(pg_database_datname_index, 2671, on pg_database using btree
 DECLARE_UNIQUE_INDEX(pg_database_oid_index, 2672, on pg_database using btree(oid oid_ops));
 #define DatabaseOidIndexId	2672
 
-DECLARE_INDEX(pg_depend_depender_index, 2673, on pg_depend using btree(classid oid_ops, objid oid_ops, objsubid int4_ops));
+DECLARE_INDEX(pg_depend_depender_index, 2673, on pg_depend using btree(classid oid_ops, objid oid_ops, objsubid int4_ops, depcreate int4_ops));
 #define DependDependerIndexId  2673
-DECLARE_INDEX(pg_depend_reference_index, 2674, on pg_depend using btree(refclassid oid_ops, refobjid oid_ops, refobjsubid int4_ops));
+DECLARE_INDEX(pg_depend_reference_index, 2674, on pg_depend using btree(refclassid oid_ops, refobjid oid_ops, refobjsubid int4_ops, depcreate int4_ops));
 #define DependReferenceIndexId	2674
 
 DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index, 2675, on pg_description using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops));
diff --git a/src/include/catalog/pg_depend.h b/src/include/catalog/pg_depend.h
index 482b8bd251..1e748b909e 100644
--- a/src/include/catalog/pg_depend.h
+++ b/src/include/catalog/pg_depend.h
@@ -61,6 +61,7 @@ CATALOG(pg_depend,2608,DependRelationId) BKI_WITHOUT_OIDS
 	 * field.  See DependencyType in catalog/dependency.h.
 	 */
 	char		deptype;		/* see codes in dependency.h */
+	int32		depcreate;		/* per-backend identifier; tiebreaker */
 } FormData_pg_depend;
 
 /* ----------------
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 0aa13b3cec..a8a77c44fd 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2688,10 +2688,10 @@ DETAIL:  drop cascades to table alter2.t1
 drop cascades to view alter2.v1
 drop cascades to function alter2.plus1(integer)
 drop cascades to type alter2.posint
-drop cascades to operator family alter2.ctype_hash_ops for access method hash
 drop cascades to type alter2.ctype
 drop cascades to function alter2.same(alter2.ctype,alter2.ctype)
 drop cascades to operator alter2.=(alter2.ctype,alter2.ctype)
+drop cascades to operator family alter2.ctype_hash_ops for access method hash
 drop cascades to conversion alter2.ascii_to_utf8
 drop cascades to text search parser alter2.prs
 drop cascades to text search configuration alter2.cfg
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b500..8dcdc1aebe 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -18,8 +18,8 @@ WHERE refclassid = 0 OR refobjid = 0 OR
       deptype NOT IN ('a', 'e', 'i', 'n', 'p') OR
       (deptype != 'p' AND (classid = 0 OR objid = 0)) OR
       (deptype = 'p' AND (classid != 0 OR objid != 0 OR objsubid != 0));
- classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype 
----------+-------+----------+------------+----------+-------------+---------
+ classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype | depcreate 
+---------+-------+----------+------------+----------+-------------+---------+-----------
 (0 rows)
 
 -- **************** pg_shdepend ****************
-- 
2.17.1

Reply via email to