diff --git a/src/backend/distributed/commands/statistics.c b/src/backend/distributed/commands/statistics.c index c7b7069a8..40b60343a 100644 --- a/src/backend/distributed/commands/statistics.c +++ b/src/backend/distributed/commands/statistics.c @@ -144,10 +144,6 @@ GetExplicitStatisticsCommandList(Oid relationId) Oid statisticsId = InvalidOid; foreach_oid(statisticsId, statisticsIdList) { - ObjectAddress address = { 0 }; - ObjectAddressSet(address, StatisticExtRelationId, statisticsId); - EnsureDependenciesExistOnAllNodes(&address); - char *createStatisticsCommand = pg_get_statisticsobj_worker(statisticsId, false); createStatisticsCommandList = lappend( @@ -162,6 +158,51 @@ GetExplicitStatisticsCommandList(Oid relationId) } +/* + * GetExplicitStatisticsSchemaIdList returns the list of schema ids of statistics' + * which are created on relation with given relation id. + */ +List * +GetExplicitStatisticsSchemaIdList(Oid relationId) +{ + List *schemaIdList = NIL; + + Relation pgStatistics = table_open(StatisticExtRelationId, AccessShareLock); + + int scanKeyCount = 1; + ScanKeyData scanKey[1]; + + ScanKeyInit(&scanKey[0], Anum_pg_statistic_ext_stxrelid, + BTEqualStrategyNumber, F_OIDEQ, relationId); + + bool useIndex = true; + SysScanDesc scanDescriptor = systable_beginscan(pgStatistics, + StatisticExtRelidIndexId, + useIndex, NULL, scanKeyCount, + scanKey); + + HeapTuple heapTuple = systable_getnext(scanDescriptor); + while (HeapTupleIsValid(heapTuple)) + { + FormData_pg_statistic_ext *statisticsForm = + (FormData_pg_statistic_ext *) GETSTRUCT(heapTuple); + + Oid schemaId = statisticsForm->stxnamespace; + if (!list_member_oid(schemaIdList, schemaId)) + { + schemaIdList = lappend_oid(schemaIdList, schemaId); + } + + heapTuple = systable_getnext(scanDescriptor); + } + + systable_endscan(scanDescriptor); + table_close(pgStatistics, NoLock); + + return schemaIdList; +} + + /* * GetExplicitStatisticsIdList returns a list of OIDs corresponding to the statistics * that are explicitly created on the relation with relationId. That means, diff --git a/src/backend/distributed/commands/type.c b/src/backend/distributed/commands/type.c index 7ce8b814c..09042130f 100644 --- a/src/backend/distributed/commands/type.c +++ b/src/backend/distributed/commands/type.c @@ -258,7 +258,7 @@ PreprocessCreateEnumStmt(Node *node, const char *queryString) QualifyTreeNode(node); /* reconstruct creation statement in a portable fashion */ - const char *createEnumStmtSql = DeparseTreeNode((Node *) node); + const char *createEnumStmtSql = DeparseCreateEnumStmt(node); createEnumStmtSql = WrapCreateOrReplace(createEnumStmtSql); /* diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index 81106207b..6ffffb98f 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -21,6 +21,7 @@ #include "catalog/indexing.h" #include "catalog/pg_class.h" #include "catalog/pg_depend.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_proc_d.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_rewrite_d.h" @@ -119,6 +120,7 @@ typedef struct ViewDependencyNode static List * GetRelationTriggerFunctionDepencyList(Oid relationId); +static List * GetRelationStatsSchemaDependencyList(Oid relationId); static DependencyDefinition * CreateObjectAddressDependencyDef(Oid classId, Oid objectId); static ObjectAddress DependencyDefinitionObjectAddress(DependencyDefinition *definition); @@ -926,6 +928,17 @@ ExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress targe List *triggerFunctionDepencyList = GetRelationTriggerFunctionDepencyList(relationId); result = list_concat(result, triggerFunctionDepencyList); + + /* + * Statistics' both depend to the relations and to the schemas they belong + * to. Also, pg_depend records dependencies from statistics to their schemas + * but not from relations to their statistics' schemas. Given above two, + * we directly expand dependencies for the relations to schemas of + * statistics. + */ + List *statisticsSchemaDependencyList = + GetRelationStatsSchemaDependencyList(relationId); + result = list_concat(result, statisticsSchemaDependencyList); } default: @@ -938,6 +951,28 @@ ExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress targe } +/* + * GetRelationStatsSchemaDependencyList returns a list of DependencyDefinition + * objects for the schemas that statistics' of the relation with relationId depends. + */ +static List * +GetRelationStatsSchemaDependencyList(Oid relationId) +{ + List *dependencyList = NIL; + + List *schemaIds = GetExplicitStatisticsSchemaIdList(relationId); + Oid schemaId = InvalidOid; + foreach_oid(schemaId, schemaIds) + { + DependencyDefinition *dependency = + CreateObjectAddressDependencyDef(NamespaceRelationId, schemaId); + dependencyList = lappend(dependencyList, dependency); + } + + return dependencyList; +} + + /* * GetRelationTriggerFunctionDepencyList returns a list of DependencyDefinition * objects for the functions that triggers of the relation with relationId depends. diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 4f109ad9c..db0bf08b9 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -244,6 +244,7 @@ extern List * PreprocessCreateStatisticsStmt(Node *node, const char *queryString extern List * PostprocessCreateStatisticsStmt(Node *node, const char *queryString); extern ObjectAddress CreateStatisticsStmtObjectAddress(Node *node, bool missingOk); extern List * GetExplicitStatisticsCommandList(Oid relationId); +extern List * GetExplicitStatisticsSchemaIdList(Oid relationId); /* subscription.c - forward declarations */ extern Node * ProcessCreateSubscriptionStmt(CreateSubscriptionStmt *createSubStmt); diff --git a/src/test/regress/expected/propagate_statistics.out b/src/test/regress/expected/propagate_statistics.out index 867961ad8..260d233f2 100644 --- a/src/test/regress/expected/propagate_statistics.out +++ b/src/test/regress/expected/propagate_statistics.out @@ -1,5 +1,5 @@ -CREATE SCHEMA "statistics'test"; -SET search_path TO "statistics'test"; +CREATE SCHEMA "statistics'Test"; +SET search_path TO "statistics'Test"; SET citus.next_shard_id TO 980000; SET client_min_messages TO WARNING; SET citus.shard_count TO 32; @@ -17,12 +17,12 @@ SELECT create_distributed_table('test_stats', 'a'); CREATE STATISTICS s1 (dependencies) ON a, b FROM test_stats; -- test for distributing an already existing statistics -CREATE TABLE test_stats2 ( +CREATE TABLE "test'stats2" ( a int, b int ); -CREATE STATISTICS s2 (dependencies) ON a, b FROM test_stats2; -SELECT create_distributed_table('test_stats2', 'a'); +CREATE STATISTICS s2 (dependencies) ON a, b FROM "test'stats2"; +SELECT create_distributed_table('test''stats2', 'a'); create_distributed_table --------------------------------------------------------------------- @@ -30,18 +30,54 @@ SELECT create_distributed_table('test_stats2', 'a'); -- test when stats is on a different schema CREATE SCHEMA sc1; -CREATE TABLE tbl (a int, b int); +CREATE TABLE tbl (a int, "B" text); SELECT create_distributed_table ('tbl', 'a'); create_distributed_table --------------------------------------------------------------------- (1 row) -CREATE STATISTICS sc1.st1 ON a, b FROM tbl; -\c - - - :worker_1_port -SELECT stxname FROM pg_statistic_ext ORDER BY stxname ASC; - stxname +CREATE STATISTICS sc1.st1 ON a, "B" FROM tbl; +-- test distributing table with already created stats on a new schema +CREATE TABLE test_stats3 ( + a int, + b int +); +CREATE SCHEMA sc2; +CREATE STATISTICS sc2."neW'Stat" ON a,b FROM test_stats3; +SELECT create_distributed_table ('test_stats3','a'); + create_distributed_table --------------------------------------------------------------------- + +(1 row) + +\c - - - :worker_1_port +SELECT stxname +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') +) +ORDER BY stxname ASC; + stxname +--------------------------------------------------------------------- + neW'Stat_980096 + neW'Stat_980098 + neW'Stat_980100 + neW'Stat_980102 + neW'Stat_980104 + neW'Stat_980106 + neW'Stat_980108 + neW'Stat_980110 + neW'Stat_980112 + neW'Stat_980114 + neW'Stat_980116 + neW'Stat_980118 + neW'Stat_980120 + neW'Stat_980122 + neW'Stat_980124 + neW'Stat_980126 s1_980000 s1_980002 s1_980004 @@ -90,15 +126,22 @@ SELECT stxname FROM pg_statistic_ext ORDER BY stxname ASC; st1_980090 st1_980092 st1_980094 -(48 rows) +(64 rows) -SELECT count(DISTINCT stxnamespace) FROM pg_statistic_ext; +SELECT count(DISTINCT stxnamespace) +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') +); count --------------------------------------------------------------------- - 2 + 3 (1 row) \c - - - :master_port SET client_min_messages TO WARNING; -DROP SCHEMA "statistics'test" CASCADE; +DROP SCHEMA "statistics'Test" CASCADE; DROP SCHEMA sc1 CASCADE; +DROP SCHEMA sc2 CASCADE; diff --git a/src/test/regress/sql/propagate_statistics.sql b/src/test/regress/sql/propagate_statistics.sql index aa5f5f3dd..87a0e7f48 100644 --- a/src/test/regress/sql/propagate_statistics.sql +++ b/src/test/regress/sql/propagate_statistics.sql @@ -1,6 +1,6 @@ -CREATE SCHEMA "statistics'test"; +CREATE SCHEMA "statistics'Test"; -SET search_path TO "statistics'test"; +SET search_path TO "statistics'Test"; SET citus.next_shard_id TO 980000; SET client_min_messages TO WARNING; SET citus.shard_count TO 32; @@ -17,27 +17,51 @@ SELECT create_distributed_table('test_stats', 'a'); CREATE STATISTICS s1 (dependencies) ON a, b FROM test_stats; -- test for distributing an already existing statistics -CREATE TABLE test_stats2 ( +CREATE TABLE "test'stats2" ( a int, b int ); -CREATE STATISTICS s2 (dependencies) ON a, b FROM test_stats2; +CREATE STATISTICS s2 (dependencies) ON a, b FROM "test'stats2"; -SELECT create_distributed_table('test_stats2', 'a'); +SELECT create_distributed_table('test''stats2', 'a'); -- test when stats is on a different schema CREATE SCHEMA sc1; -CREATE TABLE tbl (a int, b int); +CREATE TABLE tbl (a int, "B" text); SELECT create_distributed_table ('tbl', 'a'); -CREATE STATISTICS sc1.st1 ON a, b FROM tbl; +CREATE STATISTICS sc1.st1 ON a, "B" FROM tbl; + +-- test distributing table with already created stats on a new schema +CREATE TABLE test_stats3 ( + a int, + b int +); +CREATE SCHEMA sc2; +CREATE STATISTICS sc2."neW'Stat" ON a,b FROM test_stats3; +SELECT create_distributed_table ('test_stats3','a'); \c - - - :worker_1_port -SELECT stxname FROM pg_statistic_ext ORDER BY stxname ASC; -SELECT count(DISTINCT stxnamespace) FROM pg_statistic_ext; +SELECT stxname +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') +) +ORDER BY stxname ASC; + +SELECT count(DISTINCT stxnamespace) +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') +); \c - - - :master_port SET client_min_messages TO WARNING; -DROP SCHEMA "statistics'test" CASCADE; +DROP SCHEMA "statistics'Test" CASCADE; DROP SCHEMA sc1 CASCADE; +DROP SCHEMA sc2 CASCADE;