diff --git a/src/backend/distributed/commands/create_citus_local_table.c b/src/backend/distributed/commands/create_citus_local_table.c index 264cfd324..41f988450 100644 --- a/src/backend/distributed/commands/create_citus_local_table.c +++ b/src/backend/distributed/commands/create_citus_local_table.c @@ -17,6 +17,7 @@ #include "access/genam.h" #include "access/htup_details.h" #include "catalog/pg_constraint.h" +#include "catalog/pg_statistic_ext.h" #include "catalog/pg_trigger.h" #include "distributed/coordinator_protocol.h" #include "distributed/citus_ruleutils.h" @@ -49,14 +50,18 @@ static List * GetConstraintNameList(Oid relationId); static char * GetRenameShardConstraintCommand(Oid relationId, char *constraintName, uint64 shardId); static void RenameShardRelationIndexes(Oid shardRelationId, uint64 shardId); +static void RenameShardRelationStatistics(Oid shardRelationId, uint64 shardId); static char * GetDropTriggerCommand(Oid relationId, char *triggerName); static char * GetRenameShardIndexCommand(char *indexName, uint64 shardId); +static char * GetRenameShardStatsCommand(char *statSchema, char *statsName, + char *statsNameWithShardId); static void RenameShardRelationNonTruncateTriggers(Oid shardRelationId, uint64 shardId); static char * GetRenameShardTriggerCommand(Oid shardRelationId, char *triggerName, uint64 shardId); static void DropRelationTruncateTriggers(Oid relationId); static char * GetDropTriggerCommand(Oid relationId, char *triggerName); static List * GetExplicitIndexNameList(Oid relationId); +static List * GetRenameStatsCommandList(List *statsOidList, uint64 shardId); static void DropAndMoveDefaultSequenceOwnerships(Oid sourceRelationId, Oid targetRelationId); static void DropDefaultColumnDefinition(Oid relationId, char *columnName); @@ -358,6 +363,7 @@ ConvertLocalTableToShard(Oid relationId) RenameRelationToShardRelation(relationId, shardId); RenameShardRelationConstraints(relationId, shardId); RenameShardRelationIndexes(relationId, shardId); + RenameShardRelationStatistics(relationId, shardId); /* * We do not create truncate triggers on shard relation. This is @@ -537,6 +543,43 @@ GetRenameShardIndexCommand(char *indexName, uint64 shardId) } +/* + * RenameShardRelationStatistics appends given shardId to the end of the names + * of shard relation statistics. This function utilizes GetExplicitStatsNameList + * to pick the statistics to be renamed, see more details in function's comment. + */ +static void +RenameShardRelationStatistics(Oid shardRelationId, uint64 shardId) +{ + List *statsOidList = GetExplicitStatisticsIdList(shardRelationId); + List *statsCommandList = GetRenameStatsCommandList(statsOidList, shardId); + + char *command = NULL; + foreach_ptr(command, statsCommandList) + { + ExecuteAndLogDDLCommand(command); + } +} + + +/* + * GetRenameShardStatsCommand returns DDL command to append given shardId to + * the statistics with statName. + */ +static char * +GetRenameShardStatsCommand(char *statSchema, char *statsName, char *statsNameWithShardId) +{ + const char *quotedStatsNameWithShardId = quote_identifier(statsNameWithShardId); + char *qualifiedStatsName = quote_qualified_identifier(statSchema, statsName); + + StringInfo renameCommand = makeStringInfo(); + appendStringInfo(renameCommand, "ALTER STATISTICS %s RENAME TO %s;", + qualifiedStatsName, quotedStatsNameWithShardId); + + return renameCommand->data; +} + + /* * RenameShardRelationNonTruncateTriggers appends given shardId to the end of * the names of shard relation INSERT/DELETE/UPDATE triggers that are explicitly @@ -708,6 +751,44 @@ GetExplicitIndexNameList(Oid relationId) } +/* + * GetRenameStatsCommandList returns a list of "ALTER STATISTICS ... + * RENAME TO ..._shardId" commands for given statistics oid list. + */ +static List * +GetRenameStatsCommandList(List *statsOidList, uint64 shardId) +{ + List *statsCommandList = NIL; + Oid statsOid; + foreach_oid(statsOid, statsOidList) + { + HeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid)); + + if (!HeapTupleIsValid(tup)) + { + ereport(WARNING, (errmsg("No stats object found with id: %u", statsOid))); + continue; + } + + Form_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup); + + char *statsName = statisticsForm->stxname.data; + Oid statsSchemaOid = statisticsForm->stxnamespace; + char *statsSchema = get_namespace_name(statsSchemaOid); + + char *statsNameWithShardId = pstrdup(statsName); + AppendShardIdToName(&statsNameWithShardId, shardId); + + char *renameShardStatsCommand = GetRenameShardStatsCommand(statsSchema, statsName, + statsNameWithShardId); + statsCommandList = lappend(statsCommandList, renameShardStatsCommand); + ReleaseSysCache(tup); + } + + return statsCommandList; +} + + /* * DropAndMoveDefaultSequenceOwnerships drops default column definitions for * relation with sourceRelationId. Also, for each column that defaults to an diff --git a/src/backend/distributed/commands/statistics.c b/src/backend/distributed/commands/statistics.c index 16053bdc4..f7c65167f 100644 --- a/src/backend/distributed/commands/statistics.c +++ b/src/backend/distributed/commands/statistics.c @@ -49,7 +49,6 @@ #define ALTER_INDEX_COLUMN_SET_STATS_COMMAND \ "ALTER INDEX %s ALTER COLUMN %d SET STATISTICS %d" -static List * GetExplicitStatisticsIdList(Oid relationId); static char * GenerateAlterIndexColumnSetStatsCommand(char *indexNameWithSchema, int16 attnum, int32 attstattarget); @@ -573,7 +572,7 @@ GetAlterIndexStatisticsCommands(Oid indexOid) * that are explicitly created on the relation with relationId. That means, * this function discards internal statistics implicitly created by postgres. */ -static List * +List * GetExplicitStatisticsIdList(Oid relationId) { List *statisticsIdList = NIL; diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index d83574d99..c452a90b6 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -349,6 +349,7 @@ extern List * PreprocessAlterStatisticsOwnerStmt(Node *node, const char *querySt extern List * GetExplicitStatisticsCommandList(Oid relationId); extern List * GetExplicitStatisticsSchemaIdList(Oid relationId); extern List * GetAlterIndexStatisticsCommands(Oid indexOid); +extern List * GetExplicitStatisticsIdList(Oid relationId); /* subscription.c - forward declarations */ extern Node * ProcessCreateSubscriptionStmt(CreateSubscriptionStmt *createSubStmt); diff --git a/src/test/regress/expected/citus_local_tables.out b/src/test/regress/expected/citus_local_tables.out index 45beceeef..08f35c41d 100644 --- a/src/test/regress/expected/citus_local_tables.out +++ b/src/test/regress/expected/citus_local_tables.out @@ -672,6 +672,26 @@ SELECT create_citus_local_table('referenced_table'); ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a); NOTICE: executing the command locally: SELECT worker_apply_inter_shard_ddl_command (xxxxx, 'citus_local_tables_test_schema', xxxxx, 'citus_local_tables_test_schema', 'ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a);') +-- verify creating citus local table with extended statistics +CREATE TABLE test_citus_local_table_with_stats(a int, b int); +CREATE STATISTICS stx1 ON a, b FROM test_citus_local_table_with_stats; +SELECT create_citus_local_table('test_citus_local_table_with_stats'); + create_citus_local_table +--------------------------------------------------------------------- + +(1 row) + +CREATE STATISTICS "CiTUS!LocalTables"."Bad\'StatName" ON a, b FROM test_citus_local_table_with_stats; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1504042, 'citus_local_tables_test_schema', E'CREATE STATISTICS "CiTUS!LocalTables"."Bad\\''StatName" ON a, b FROM citus_local_tables_test_schema.test_citus_local_table_with_stats') +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + stxname +--------------------------------------------------------------------- + Bad\'StatName + Bad\'StatName_1504042 + stx1 + stx1_1504042 +(4 rows) + -- observe the debug messages telling that we switch to sequential -- execution when truncating a citus local table that is referenced -- by another table @@ -688,4 +708,4 @@ RESET client_min_messages; \set VERBOSITY terse -- cleanup at exit DROP SCHEMA citus_local_tables_test_schema, "CiTUS!LocalTables" CASCADE; -NOTICE: drop cascades to 22 other objects +NOTICE: drop cascades to 24 other objects diff --git a/src/test/regress/expected/citus_local_tables_mx.out b/src/test/regress/expected/citus_local_tables_mx.out index 1ec067d17..551a144dc 100644 --- a/src/test/regress/expected/citus_local_tables_mx.out +++ b/src/test/regress/expected/citus_local_tables_mx.out @@ -137,6 +137,36 @@ SELECT create_citus_local_table('citus_local_table_4'); ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_local_to_local FOREIGN KEY(l1) REFERENCES citus_local_table_4(l1) ON UPDATE SET NULL; NOTICE: executing the command locally: SELECT worker_apply_inter_shard_ddl_command (1508005, 'citus_local_tables_mx', 1508006, 'citus_local_tables_mx', 'ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_local_to_local FOREIGN KEY(l1) REFERENCES citus_local_table_4(l1) ON UPDATE SET NULL;') +-- check stats creation +CREATE TABLE citus_local_table_stats(a int, b int); +CREATE STATISTICS stx1 ON a, b FROM citus_local_table_stats; +SELECT create_citus_local_table('citus_local_table_stats'); + create_citus_local_table +--------------------------------------------------------------------- + +(1 row) + +CREATE STATISTICS stx2 ON a, b FROM citus_local_table_stats; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1508007, 'citus_local_tables_mx', 'CREATE STATISTICS citus_local_tables_mx.stx2 ON a, b FROM citus_local_tables_mx.citus_local_table_stats') +CREATE STATISTICS stx3 ON a, b FROM citus_local_table_stats; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1508007, 'citus_local_tables_mx', 'CREATE STATISTICS citus_local_tables_mx.stx3 ON a, b FROM citus_local_tables_mx.citus_local_table_stats') +CREATE STATISTICS stx4 ON a, b FROM citus_local_table_stats; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1508007, 'citus_local_tables_mx', 'CREATE STATISTICS citus_local_tables_mx.stx4 ON a, b FROM citus_local_tables_mx.citus_local_table_stats') +ALTER STATISTICS stx3 RENAME TO stx5; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1508007, 'citus_local_tables_mx', 'ALTER STATISTICS citus_local_tables_mx.stx3 RENAME TO stx5') +DROP STATISTICS stx4; +NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1508007, 'citus_local_tables_mx', 'DROP STATISTICS citus_local_tables_mx.stx4') +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + stxname +--------------------------------------------------------------------- + stx1 + stx1_1508007 + stx2 + stx2_1508007 + stx5 + stx5_1508007 +(6 rows) + -- and switch to worker1 \c - - - :worker_1_port SET search_path TO citus_local_tables_mx; @@ -188,6 +218,24 @@ SELECT * FROM citus_local_table_3; -- finally show that we do not allow defining foreign key in mx nodes ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_local_to_local_2 FOREIGN KEY(l1) REFERENCES citus_local_table_4(l1); ERROR: operation is not allowed on this node +-- check stats creation +CREATE TABLE citus_local_table_stats2(a int, b int); +CREATE STATISTICS stx8 ON a, b FROM citus_local_table_stats2; +SELECT create_citus_local_table('citus_local_table_stats2'); +ERROR: operation is not allowed on this node +CREATE STATISTICS stx9 ON a, b FROM citus_local_table_stats2; +DROP STATISTICS stx8; +DROP STATISTICS stx4; +ERROR: statistics object "citus_local_tables_mx.stx4" does not exist +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + stxname +--------------------------------------------------------------------- + stx1 + stx2 + stx5 + stx9 +(4 rows) + \c - - - :master_port SET search_path TO citus_local_tables_mx; SELECT master_remove_distributed_table_metadata_from_workers('citus_local_table_4'::regclass::oid, 'citus_local_tables_mx', 'citus_local_table_4'); @@ -210,4 +258,4 @@ $$); -- cleanup at exit DROP SCHEMA citus_local_tables_mx CASCADE; -NOTICE: drop cascades to 17 other objects +NOTICE: drop cascades to 19 other objects diff --git a/src/test/regress/sql/citus_local_tables.sql b/src/test/regress/sql/citus_local_tables.sql index e01534f34..e616dc5ba 100644 --- a/src/test/regress/sql/citus_local_tables.sql +++ b/src/test/regress/sql/citus_local_tables.sql @@ -125,6 +125,7 @@ SELECT create_citus_local_table('child_table'); CREATE UNLOGGED TABLE unlogged_table (a int primary key); SELECT create_citus_local_table('unlogged_table'); + -- show that we allow triggers -- BEGIN; @@ -446,6 +447,13 @@ SELECT create_citus_local_table('referenced_table'); ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a); +-- verify creating citus local table with extended statistics +CREATE TABLE test_citus_local_table_with_stats(a int, b int); +CREATE STATISTICS stx1 ON a, b FROM test_citus_local_table_with_stats; +SELECT create_citus_local_table('test_citus_local_table_with_stats'); +CREATE STATISTICS "CiTUS!LocalTables"."Bad\'StatName" ON a, b FROM test_citus_local_table_with_stats; +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + -- observe the debug messages telling that we switch to sequential -- execution when truncating a citus local table that is referenced -- by another table diff --git a/src/test/regress/sql/citus_local_tables_mx.sql b/src/test/regress/sql/citus_local_tables_mx.sql index ac766cbab..8fb9ba1ef 100644 --- a/src/test/regress/sql/citus_local_tables_mx.sql +++ b/src/test/regress/sql/citus_local_tables_mx.sql @@ -91,6 +91,18 @@ SELECT create_citus_local_table('citus_local_table_4'); ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_local_to_local FOREIGN KEY(l1) REFERENCES citus_local_table_4(l1) ON UPDATE SET NULL; +-- check stats creation +CREATE TABLE citus_local_table_stats(a int, b int); +CREATE STATISTICS stx1 ON a, b FROM citus_local_table_stats; +SELECT create_citus_local_table('citus_local_table_stats'); +CREATE STATISTICS stx2 ON a, b FROM citus_local_table_stats; +CREATE STATISTICS stx3 ON a, b FROM citus_local_table_stats; +CREATE STATISTICS stx4 ON a, b FROM citus_local_table_stats; +ALTER STATISTICS stx3 RENAME TO stx5; +DROP STATISTICS stx4; + +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + -- and switch to worker1 \c - - - :worker_1_port SET search_path TO citus_local_tables_mx; @@ -143,6 +155,16 @@ SELECT * FROM citus_local_table_3; -- finally show that we do not allow defining foreign key in mx nodes ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_local_to_local_2 FOREIGN KEY(l1) REFERENCES citus_local_table_4(l1); +-- check stats creation +CREATE TABLE citus_local_table_stats2(a int, b int); +CREATE STATISTICS stx8 ON a, b FROM citus_local_table_stats2; +SELECT create_citus_local_table('citus_local_table_stats2'); +CREATE STATISTICS stx9 ON a, b FROM citus_local_table_stats2; +DROP STATISTICS stx8; +DROP STATISTICS stx4; + +SELECT stxname FROM pg_statistic_ext ORDER BY stxname; + \c - - - :master_port SET search_path TO citus_local_tables_mx;