From 06fad759888d5060f9b4cf4d4f4fdec777350027 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 12 May 2017 17:16:26 -0300
Subject: [PATCH 1/2] fix dependencies problem

---
 src/backend/catalog/heap.c              | 74 ---------------------------------
 src/backend/catalog/objectaddress.c     | 20 +++++++++
 src/backend/commands/statscmds.c        | 18 ++++----
 src/test/regress/expected/stats_ext.out | 13 ++++--
 src/test/regress/sql/stats_ext.sql      |  9 ++--
 5 files changed, 45 insertions(+), 89 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ab3d83f29b..ba63939022 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1615,10 +1615,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	heap_close(attr_rel, RowExclusiveLock);
 
 	if (attnum > 0)
-	{
 		RemoveStatistics(relid, attnum);
-		RemoveStatisticsExt(relid, attnum);
-	}
 
 	relation_close(rel, NoLock);
 }
@@ -1873,7 +1870,6 @@ heap_drop_with_catalog(Oid relid)
 	 * delete statistics
 	 */
 	RemoveStatistics(relid, 0);
-	RemoveStatisticsExt(relid, 0);
 
 	/*
 	 * delete attribute tuples
@@ -2784,76 +2780,6 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
 	heap_close(pgstatistic, RowExclusiveLock);
 }
 
-
-/*
- * RemoveStatisticsExt --- remove entries in pg_statistic_ext for a relation
- *
- * If attnum is zero, remove all entries for rel; else remove only the
- * one(s) involving that column.
- */
-void
-RemoveStatisticsExt(Oid relid, AttrNumber attnum)
-{
-	Relation	pgstatisticext;
-	SysScanDesc scan;
-	ScanKeyData key;
-	HeapTuple	tuple;
-
-	/*
-	 * Scan pg_statistic_ext to delete relevant tuples
-	 */
-	pgstatisticext = heap_open(StatisticExtRelationId, RowExclusiveLock);
-
-	ScanKeyInit(&key,
-				Anum_pg_statistic_ext_stxrelid,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(relid));
-
-	scan = systable_beginscan(pgstatisticext,
-							  StatisticExtRelidIndexId,
-							  true, NULL, 1, &key);
-
-	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
-	{
-		bool		delete = false;
-
-		if (attnum == 0)
-			delete = true;
-		else if (attnum != 0)
-		{
-			Form_pg_statistic_ext	staForm;
-			int			i;
-
-			/*
-			 * Decode the stxkeys array and delete any stats that involve the
-			 * specified column.
-			 */
-			staForm = (Form_pg_statistic_ext) GETSTRUCT(tuple);
-			for (i = 0; i < staForm->stxkeys.dim1; i++)
-			{
-				if (staForm->stxkeys.values[i] == attnum)
-				{
-					delete = true;
-					break;
-				}
-			}
-		}
-
-		if (delete)
-		{
-			CatalogTupleDelete(pgstatisticext, &tuple->t_self);
-			deleteDependencyRecordsFor(StatisticExtRelationId,
-									   HeapTupleGetOid(tuple),
-									   false);
-		}
-	}
-
-	systable_endscan(scan);
-
-	heap_close(pgstatisticext, RowExclusiveLock);
-}
-
-
 /*
  * RelationTruncateIndexes - truncate all indexes associated
  * with the heap relation to zero tuples.
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a9e529fba0..9443996dc9 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3004,6 +3004,26 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_STATISTIC_EXT:
+			{
+				HeapTuple	stxTup;
+				Form_pg_statistic_ext stxForm;
+
+				stxTup = SearchSysCache1(STATEXTOID,
+										 ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(stxTup))
+					elog(ERROR, "could not find tuple for statistics object %u",
+						 object->objectId);
+
+				stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup);
+
+				appendStringInfo(&buffer, _("statistics object %s"),
+								 NameStr(stxForm->stxname));
+
+				ReleaseSysCache(stxTup);
+				break;
+			}
+
 		case OCLASS_TRANSFORM:
 			{
 				HeapTuple	trfTup;
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 662b4fa15d..d3b5430369 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -312,24 +312,24 @@ CreateStatistics(CreateStatsStmt *stmt)
 	relation_close(rel, NoLock);
 
 	/*
-	 * Add a dependency on the table, so that stats get dropped on DROP TABLE.
-	 *
-	 * XXX don't we need dependencies on the specific columns, instead?
+	 * Add a dependency on the columns of the table, so that stats get dropped
+	 * on DROP TABLE as well as ALTER TABLE .. DROP COLUMN.
 	 */
-	ObjectAddressSet(parentobject, RelationRelationId, relid);
 	ObjectAddressSet(childobject, StatisticExtRelationId, statoid);
-	recordDependencyOn(&childobject, &parentobject, DEPENDENCY_AUTO);
+	for (i = 0; i < numcols; i++)
+	{
+		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+		recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
+	}
 
 	/*
 	 * Also add dependency on the schema.  This is required to ensure that we
 	 * drop the statistics on DROP SCHEMA.  This is not handled automatically
 	 * by DROP TABLE because the statistics might be in a different schema
-	 * from the table itself.  (This definition is a bit bizarre for the
-	 * single-table case, but it will make more sense if/when we support
-	 * extended stats across multiple tables.)
+	 * from the table itself.
 	 */
 	ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
-	recordDependencyOn(&childobject, &parentobject, DEPENDENCY_AUTO);
+	recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
 
 	/* Return stats object's address */
 	ObjectAddressSet(address, StatisticExtRelationId, statoid);
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 4ccdf21a01..f2ae0ab80f 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -48,7 +48,11 @@ DROP STATISTICS regress_schema_2.ab1_a_b_stats;
 CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1;
 CREATE STATISTICS ab1_a_b_c_stats ON a, b, c FROM ab1;
 CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
-ALTER TABLE ab1 DROP COLUMN a;
+\set VERBOSITY terse
+ALTER TABLE ab1 DROP COLUMN a; -- fail
+ERROR:  cannot drop table ab1 column a because other objects depend on it
+ALTER TABLE ab1 DROP COLUMN a CASCADE;
+NOTICE:  drop cascades to 2 other objects
 \d ab1
                 Table "public.ab1"
  Column |  Type   | Collation | Nullable | Default 
@@ -58,7 +62,9 @@ ALTER TABLE ab1 DROP COLUMN a;
 Statistics:
     "public"."ab1_b_c_stats" (ndistinct, dependencies) ON b, c FROM ab1
 
-DROP TABLE ab1;
+DROP TABLE ab1 CASCADE;
+NOTICE:  drop cascades to statistics object ab1_b_c_stats
+\set VERBOSITY default
 -- Ensure things work sanely with SET STATISTICS 0
 CREATE TABLE ab1 (a INTEGER, b INTEGER);
 ALTER TABLE ab1 ALTER a SET STATISTICS 0;
@@ -71,7 +77,8 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1;
 ANALYZE ab1 (a);
 WARNING:  extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1
 ANALYZE ab1;
-DROP TABLE ab1;
+DROP TABLE ab1 CASCADE;
+NOTICE:  drop cascades to statistics object ab1_a_b_stats
 -- Verify supported object types for extended statistics
 CREATE schema tststats;
 CREATE TABLE tststats.t (a int, b int, c text);
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 4050f33c08..df9f47a3b6 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -35,9 +35,12 @@ DROP STATISTICS regress_schema_2.ab1_a_b_stats;
 CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1;
 CREATE STATISTICS ab1_a_b_c_stats ON a, b, c FROM ab1;
 CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
-ALTER TABLE ab1 DROP COLUMN a;
+\set VERBOSITY terse
+ALTER TABLE ab1 DROP COLUMN a; -- fail
+ALTER TABLE ab1 DROP COLUMN a CASCADE;
 \d ab1
-DROP TABLE ab1;
+DROP TABLE ab1 CASCADE;
+\set VERBOSITY default
 
 -- Ensure things work sanely with SET STATISTICS 0
 CREATE TABLE ab1 (a INTEGER, b INTEGER);
@@ -49,7 +52,7 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1;
 -- partial analyze doesn't build stats either
 ANALYZE ab1 (a);
 ANALYZE ab1;
-DROP TABLE ab1;
+DROP TABLE ab1 CASCADE;
 
 -- Verify supported object types for extended statistics
 CREATE schema tststats;
-- 
2.11.0

