diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index 38cee4041..dd456ec4d 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -14,6 +14,7 @@ #include "distributed/commands.h" #include "distributed/deparser.h" +#include "distributed/pg_version_constants.h" static DistributeObjectOps NoDistributeOps = { .deparse = NULL, @@ -414,6 +415,29 @@ static DistributeObjectOps Schema_Rename = { .postprocess = NULL, .address = AlterSchemaRenameStmtObjectAddress, }; +#if PG_VERSION_NUM >= PG_VERSION_13 +static DistributeObjectOps Statistics_Alter = { + .deparse = DeparseAlterStatisticsStmt, + .qualify = QualifyAlterStatisticsStmt, + .preprocess = PreprocessAlterStatisticsStmt, + .postprocess = NULL, + .address = NULL, +}; +#endif +static DistributeObjectOps Statistics_AlterObjectSchema = { + .deparse = DeparseAlterStatisticsSchemaStmt, + .qualify = QualifyAlterStatisticsSchemaStmt, + .preprocess = PreprocessAlterStatisticsSchemaStmt, + .postprocess = PostprocessAlterStatisticsSchemaStmt, + .address = AlterStatisticsSchemaStmtObjectAddress, +}; +static DistributeObjectOps Statistics_AlterOwner = { + .deparse = DeparseAlterStatisticsOwnerStmt, + .qualify = QualifyAlterStatisticsOwnerStmt, + .preprocess = PreprocessAlterStatisticsOwnerStmt, + .postprocess = NULL, + .address = NULL, +}; static DistributeObjectOps Statistics_Drop = { .deparse = NULL, .qualify = QualifyDropStatisticsStmt, @@ -421,6 +445,13 @@ static DistributeObjectOps Statistics_Drop = { .postprocess = NULL, .address = NULL, }; +static DistributeObjectOps Statistics_Rename = { + .deparse = DeparseAlterStatisticsRenameStmt, + .qualify = QualifyAlterStatisticsRenameStmt, + .preprocess = PreprocessAlterStatisticsRenameStmt, + .postprocess = NULL, + .address = NULL, +}; static DistributeObjectOps Table_AlterTable = { .deparse = NULL, .qualify = NULL, @@ -590,6 +621,11 @@ GetDistributeObjectOps(Node *node) return &Routine_AlterObjectSchema; } + case OBJECT_STATISTIC_EXT: + { + return &Statistics_AlterObjectSchema; + } + case OBJECT_TABLE: { return &Table_AlterObjectSchema; @@ -637,6 +673,11 @@ GetDistributeObjectOps(Node *node) return &Routine_AlterOwner; } + case OBJECT_STATISTIC_EXT: + { + return &Statistics_AlterOwner; + } + case OBJECT_TYPE: { return &Type_AlterOwner; @@ -664,6 +705,13 @@ GetDistributeObjectOps(Node *node) return &Any_AlterRoleSet; } +#if PG_VERSION_NUM >= PG_VERSION_13 + case T_AlterStatsStmt: + { + return &Statistics_Alter; + } + +#endif case T_AlterTableStmt: { AlterTableStmt *stmt = castNode(AlterTableStmt, node); @@ -907,6 +955,11 @@ GetDistributeObjectOps(Node *node) return &Schema_Rename; } + case OBJECT_STATISTIC_EXT: + { + return &Statistics_Rename; + } + case OBJECT_TYPE: { return &Type_Rename; diff --git a/src/backend/distributed/commands/statistics.c b/src/backend/distributed/commands/statistics.c index 46fd2ed33..c922a6f31 100644 --- a/src/backend/distributed/commands/statistics.c +++ b/src/backend/distributed/commands/statistics.c @@ -37,6 +37,7 @@ #include "distributed/relation_access_tracking.h" #include "distributed/resource_lock.h" #include "distributed/worker_transaction.h" +#include "miscadmin.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -45,7 +46,10 @@ static List * GetExplicitStatisticsIdList(Oid relationId); static Oid GetRelIdByStatsOid(Oid statsOid); - +static char * CreateAlterCommandIfOwnerNotDefault(Oid statsOid); +#if PG_VERSION_NUM >= PG_VERSION_13 +static char * CreateAlterCommandIfTargetNotDefault(Oid statsOid); +#endif /* * PreprocessCreateStatisticsStmt is called during the planning phase for @@ -190,6 +194,213 @@ PreprocessDropStatisticsStmt(Node *node, const char *queryString) } +/* + * PreprocessAlterStatisticsRenameStmt is called during the planning phase for + * ALTER STATISTICS RENAME. + */ +List * +PreprocessAlterStatisticsRenameStmt(Node *node, const char *queryString) +{ + RenameStmt *renameStmt = castNode(RenameStmt, node); + Assert(renameStmt->renameType == OBJECT_STATISTIC_EXT); + + Oid statsOid = get_statistics_object_oid((List *) renameStmt->object, false); + Oid relationId = GetRelIdByStatsOid(statsOid); + + if (!IsCitusTable(relationId) || !ShouldPropagate()) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) renameStmt); + + char *ddlCommand = DeparseTreeNode((Node *) renameStmt); + + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); + + ddlJob->targetRelationId = relationId; + ddlJob->concurrentIndexCmd = false; + ddlJob->startNewTransaction = false; + ddlJob->commandString = ddlCommand; + ddlJob->taskList = DDLTaskList(relationId, ddlCommand); + + List *ddlJobs = list_make1(ddlJob); + + return ddlJobs; +} + + +/* + * PreprocessAlterStatisticsSchemaStmt is called during the planning phase for + * ALTER STATISTICS SET SCHEMA. + */ +List * +PreprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString) +{ + AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + Oid statsOid = get_statistics_object_oid((List *) stmt->object, false); + Oid relationId = GetRelIdByStatsOid(statsOid); + + if (!IsCitusTable(relationId) || !ShouldPropagate()) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) stmt); + + char *ddlCommand = DeparseTreeNode((Node *) stmt); + + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); + + ddlJob->targetRelationId = relationId; + ddlJob->concurrentIndexCmd = false; + ddlJob->startNewTransaction = false; + ddlJob->commandString = ddlCommand; + ddlJob->taskList = DDLTaskList(relationId, ddlCommand); + + List *ddlJobs = list_make1(ddlJob); + + return ddlJobs; +} + + +/* + * PostprocessAlterStatisticsSchemaStmt is called after a ALTER STATISTICS SCHEMA + * command has been executed by standard process utility. + */ +List * +PostprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString) +{ + AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + Value *statName = llast((List *) stmt->object); + Oid statsOid = get_statistics_object_oid(list_make2(makeString(stmt->newschema), + statName), false); + Oid relationId = GetRelIdByStatsOid(statsOid); + + if (!IsCitusTable(relationId) || !ShouldPropagate()) + { + return NIL; + } + + bool missingOk = false; + ObjectAddress objectAddress = GetObjectAddressFromParseTree((Node *) stmt, missingOk); + + EnsureDependenciesExistOnAllNodes(&objectAddress); + + return NIL; +} + + +/* + * AlterStatisticsSchemaStmtObjectAddress finds the ObjectAddress for the statistics + * that is altered by given AlterObjectSchemaStmt. If missingOk is false and if + * the statistics does not exist, then it errors out. + * + * Never returns NULL, but the objid in the address can be invalid if missingOk + * was set to true. + */ +ObjectAddress +AlterStatisticsSchemaStmtObjectAddress(Node *node, bool missingOk) +{ + AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); + + ObjectAddress address = { 0 }; + Value *statName = llast((List *) stmt->object); + Oid statsOid = get_statistics_object_oid(list_make2(makeString(stmt->newschema), + statName), missingOk); + ObjectAddressSet(address, StatisticExtRelationId, statsOid); + + return address; +} + + +#if PG_VERSION_NUM >= PG_VERSION_13 + +/* + * PreprocessAlterStatisticsStmt is called during the planning phase for + * ALTER STATISTICS .. SET STATISTICS. + */ +List * +PreprocessAlterStatisticsStmt(Node *node, const char *queryString) +{ + AlterStatsStmt *stmt = castNode(AlterStatsStmt, node); + + Oid statsOid = get_statistics_object_oid(stmt->defnames, false); + Oid relationId = GetRelIdByStatsOid(statsOid); + + if (!IsCitusTable(relationId) || !ShouldPropagate()) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) stmt); + + char *ddlCommand = DeparseTreeNode((Node *) stmt); + + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); + + ddlJob->targetRelationId = relationId; + ddlJob->concurrentIndexCmd = false; + ddlJob->startNewTransaction = false; + ddlJob->commandString = ddlCommand; + ddlJob->taskList = DDLTaskList(relationId, ddlCommand); + + List *ddlJobs = list_make1(ddlJob); + + return ddlJobs; +} + + +#endif + +/* + * PreprocessAlterStatisticsOwnerStmt is called during the planning phase for + * ALTER STATISTICS .. OWNER TO. + */ +List * +PreprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString) +{ + AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + Oid statsOid = get_statistics_object_oid((List *) stmt->object, false); + Oid relationId = GetRelIdByStatsOid(statsOid); + + if (!IsCitusTable(relationId) || !ShouldPropagate()) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) stmt); + + char *ddlCommand = DeparseTreeNode((Node *) stmt); + + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); + + ddlJob->targetRelationId = relationId; + ddlJob->concurrentIndexCmd = false; + ddlJob->startNewTransaction = false; + ddlJob->commandString = ddlCommand; + ddlJob->taskList = DDLTaskList(relationId, ddlCommand); + + List *ddlJobs = list_make1(ddlJob); + + return ddlJobs; +} + + /* * GetExplicitStatisticsCommandList returns the list of DDL commands to create * statistics that are explicitly created for the table with relationId. See @@ -199,6 +410,7 @@ List * GetExplicitStatisticsCommandList(Oid relationId) { List *createStatisticsCommandList = NIL; + List *alterStatisticsCommandList = NIL; PushOverrideEmptySearchPath(CurrentMemoryContext); @@ -207,16 +419,44 @@ GetExplicitStatisticsCommandList(Oid relationId) Oid statisticsId = InvalidOid; foreach_oid(statisticsId, statisticsIdList) { - char *createStatisticsCommand = pg_get_statisticsobj_worker(statisticsId, false); + char *createStatisticsCommand = pg_get_statisticsobj_worker(statisticsId, + false); - createStatisticsCommandList = lappend( - createStatisticsCommandList, - makeTableDDLCommandString(createStatisticsCommand)); + createStatisticsCommandList = + lappend(createStatisticsCommandList, + makeTableDDLCommandString(createStatisticsCommand)); +#if PG_VERSION_NUM >= PG_VERSION_13 + + /* we need to alter stats' target if it's getting distributed after creation */ + char *alterStatisticsTargetCommand = + CreateAlterCommandIfTargetNotDefault(statisticsId); + + if (alterStatisticsTargetCommand != NULL) + { + alterStatisticsCommandList = + lappend(alterStatisticsCommandList, + makeTableDDLCommandString(alterStatisticsTargetCommand)); + } +#endif + + /* we need to alter stats' owner if it's getting distributed after creation */ + char *alterStatisticsOwnerCommand = + CreateAlterCommandIfOwnerNotDefault(statisticsId); + + if (alterStatisticsOwnerCommand != NULL) + { + alterStatisticsCommandList = + lappend(alterStatisticsCommandList, + makeTableDDLCommandString(alterStatisticsOwnerCommand)); + } } /* revert back to original search_path */ PopOverrideSearchPath(); + createStatisticsCommandList = list_concat(createStatisticsCommandList, + alterStatisticsCommandList); + return createStatisticsCommandList; } @@ -332,3 +572,84 @@ GetRelIdByStatsOid(Oid statsOid) return statisticsForm->stxrelid; } + + +/* + * CreateAlterCommandIfOwnerNotDefault returns an ALTER STATISTICS .. OWNER TO + * command if the stats object with given id has an owner different than the default one. + * Returns NULL otherwise. + */ +static char * +CreateAlterCommandIfOwnerNotDefault(Oid statsOid) +{ + HeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid)); + + if (!HeapTupleIsValid(tup)) + { + ereport(WARNING, (errmsg("No stats object found with id: %u", statsOid))); + return NULL; + } + + Form_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup); + ReleaseSysCache(tup); + + if (statisticsForm->stxowner == GetUserId()) + { + return NULL; + } + + char *schemaName = get_namespace_name(statisticsForm->stxnamespace); + char *statName = NameStr(statisticsForm->stxname); + char *ownerName = GetUserNameFromId(statisticsForm->stxowner, false); + + StringInfoData str; + initStringInfo(&str); + + appendStringInfo(&str, "ALTER STATISTICS %s OWNER TO %s", + NameListToQuotedString(list_make2(makeString(schemaName), + makeString(statName))), + quote_identifier(ownerName)); + + return str.data; +} + + +#if PG_VERSION_NUM >= PG_VERSION_13 + +/* + * CreateAlterCommandIfTargetNotDefault returns an ALTER STATISTICS .. SET STATISTICS + * command if the stats object with given id has a target different than the default one. + * Returns NULL otherwise. + */ +static char * +CreateAlterCommandIfTargetNotDefault(Oid statsOid) +{ + HeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid)); + + if (!HeapTupleIsValid(tup)) + { + ereport(WARNING, (errmsg("No stats object found with id: %u", statsOid))); + return NULL; + } + + Form_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup); + ReleaseSysCache(tup); + + if (statisticsForm->stxstattarget == -1) + { + return NULL; + } + + AlterStatsStmt *alterStatsStmt = makeNode(AlterStatsStmt); + + char *schemaName = get_namespace_name(statisticsForm->stxnamespace); + char *statName = NameStr(statisticsForm->stxname); + + alterStatsStmt->stxstattarget = statisticsForm->stxstattarget; + alterStatsStmt->defnames = list_make2(makeString(schemaName), makeString(statName)); + + return DeparseAlterStatisticsStmt((Node *) alterStatsStmt); +} + + +#endif diff --git a/src/backend/distributed/deparser/deparse_statistics_stmts.c b/src/backend/distributed/deparser/deparse_statistics_stmts.c index 58fd8f736..125d4494f 100644 --- a/src/backend/distributed/deparser/deparse_statistics_stmts.c +++ b/src/backend/distributed/deparser/deparse_statistics_stmts.c @@ -22,6 +22,12 @@ static void AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt *stmt); static void AppendDropStatisticsStmt(StringInfo buf, List *nameList, bool ifExists); +static void AppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt *stmt); +static void AppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); +#if PG_VERSION_NUM >= PG_VERSION_13 +static void AppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt *stmt); +#endif +static void AppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); static void AppendStatisticsName(StringInfo buf, CreateStatsStmt *stmt); static void AppendStatTypes(StringInfo buf, CreateStatsStmt *stmt); static void AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt); @@ -53,6 +59,65 @@ DeparseDropStatisticsStmt(List *nameList, bool ifExists) } +char * +DeparseAlterStatisticsRenameStmt(Node *node) +{ + RenameStmt *stmt = castNode(RenameStmt, node); + + StringInfoData str; + initStringInfo(&str); + + AppendAlterStatisticsRenameStmt(&str, stmt); + + return str.data; +} + + +char * +DeparseAlterStatisticsSchemaStmt(Node *node) +{ + AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); + + StringInfoData str; + initStringInfo(&str); + + AppendAlterStatisticsSchemaStmt(&str, stmt); + + return str.data; +} + + +#if PG_VERSION_NUM >= PG_VERSION_13 +char * +DeparseAlterStatisticsStmt(Node *node) +{ + AlterStatsStmt *stmt = castNode(AlterStatsStmt, node); + + StringInfoData str; + initStringInfo(&str); + + AppendAlterStatisticsStmt(&str, stmt); + + return str.data; +} + + +#endif +char * +DeparseAlterStatisticsOwnerStmt(Node *node) +{ + AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + StringInfoData str; + initStringInfo(&str); + + AppendAlterStatisticsOwnerStmt(&str, stmt); + + return str.data; +} + + static void AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt *stmt) { @@ -74,8 +139,6 @@ AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt *stmt) appendStringInfoString(buf, " FROM "); AppendTableName(buf, stmt); - - appendStringInfoString(buf, ";"); } @@ -93,6 +156,44 @@ AppendDropStatisticsStmt(StringInfo buf, List *nameList, bool ifExists) } +static void +AppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt *stmt) +{ + appendStringInfo(buf, "ALTER STATISTICS %s RENAME TO %s", + NameListToQuotedString((List *) stmt->object), quote_identifier( + stmt->newname)); +} + + +static void +AppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt) +{ + appendStringInfo(buf, "ALTER STATISTICS %s SET SCHEMA %s", + NameListToQuotedString((List *) stmt->object), quote_identifier( + stmt->newschema)); +} + + +#if PG_VERSION_NUM >= PG_VERSION_13 +static void +AppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt *stmt) +{ + appendStringInfo(buf, "ALTER STATISTICS %s SET STATISTICS %d", NameListToQuotedString( + stmt->defnames), stmt->stxstattarget); +} + + +#endif +static void +AppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) +{ + List *names = (List *) stmt->object; + appendStringInfo(buf, "ALTER STATISTICS %s OWNER TO %s", + NameListToQuotedString(names), + RoleSpecString(stmt->newowner, true)); +} + + static void AppendStatisticsName(StringInfo buf, CreateStatsStmt *stmt) { diff --git a/src/backend/distributed/deparser/qualify_statistics_stmt.c b/src/backend/distributed/deparser/qualify_statistics_stmt.c index 9e6d97fa8..2a8cb1eaa 100644 --- a/src/backend/distributed/deparser/qualify_statistics_stmt.c +++ b/src/backend/distributed/deparser/qualify_statistics_stmt.c @@ -78,3 +78,89 @@ QualifyDropStatisticsStmt(Node *node) dropStatisticsStmt->objects = objectNameListWithSchema; } + + +/* + * QualifyAlterStatisticsRenameStmt qualifies RenameStmt's with schema name for + * ALTER STATISTICS RENAME statements. + */ +void +QualifyAlterStatisticsRenameStmt(Node *node) +{ + RenameStmt *renameStmt = castNode(RenameStmt, node); + Assert(renameStmt->renameType == OBJECT_STATISTIC_EXT); + + List *nameList = (List *) renameStmt->object; + if (list_length(nameList) == 1) + { + RangeVar *stat = makeRangeVarFromNameList(nameList); + Oid schemaOid = RangeVarGetCreationNamespace(stat); + stat->schemaname = get_namespace_name(schemaOid); + renameStmt->object = (Node *) MakeNameListFromRangeVar(stat); + } +} + + +/* + * QualifyAlterStatisticsSchemaStmt qualifies AlterObjectSchemaStmt's with schema name for + * ALTER STATISTICS RENAME statements. + */ +void +QualifyAlterStatisticsSchemaStmt(Node *node) +{ + AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + List *nameList = (List *) stmt->object; + if (list_length(nameList) == 1) + { + RangeVar *stat = makeRangeVarFromNameList(nameList); + Oid schemaOid = RangeVarGetCreationNamespace(stat); + stat->schemaname = get_namespace_name(schemaOid); + stmt->object = (Node *) MakeNameListFromRangeVar(stat); + } +} + + +#if PG_VERSION_NUM >= PG_VERSION_13 + +/* + * QualifyAlterStatisticsStmt qualifies AlterStatsStmt's with schema name for + * ALTER STATISTICS .. SET STATISTICS statements. + */ +void +QualifyAlterStatisticsStmt(Node *node) +{ + AlterStatsStmt *stmt = castNode(AlterStatsStmt, node); + + if (list_length(stmt->defnames) == 1) + { + RangeVar *stat = makeRangeVarFromNameList(stmt->defnames); + Oid schemaOid = RangeVarGetCreationNamespace(stat); + stat->schemaname = get_namespace_name(schemaOid); + stmt->defnames = MakeNameListFromRangeVar(stat); + } +} + + +#endif + +/* + * QualifyAlterStatisticsStmt qualifies AlterOwnerStmt's with schema name for + * ALTER STATISTICS .. OWNER TO statements. + */ +void +QualifyAlterStatisticsOwnerStmt(Node *node) +{ + AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); + Assert(stmt->objectType == OBJECT_STATISTIC_EXT); + + List *nameList = (List *) stmt->object; + if (list_length(nameList) == 1) + { + RangeVar *stat = makeRangeVarFromNameList(nameList); + Oid schemaOid = RangeVarGetCreationNamespace(stat); + stat->schemaname = get_namespace_name(schemaOid); + stmt->object = (Node *) MakeNameListFromRangeVar(stat); + } +} diff --git a/src/backend/distributed/relay/relay_event_utility.c b/src/backend/distributed/relay/relay_event_utility.c index fd9f4de71..9e1ca2865 100644 --- a/src/backend/distributed/relay/relay_event_utility.c +++ b/src/backend/distributed/relay/relay_event_utility.c @@ -82,17 +82,48 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId) { AlterObjectSchemaStmt *alterObjectSchemaStmt = (AlterObjectSchemaStmt *) parseTree; - char **relationName = &(alterObjectSchemaStmt->relation->relname); - char **relationSchemaName = &(alterObjectSchemaStmt->relation->schemaname); + ObjectType objectType = alterObjectSchemaStmt->objectType; - /* prefix with schema name if it is not added already */ - SetSchemaNameIfNotExist(relationSchemaName, schemaName); + if (objectType == OBJECT_STATISTIC_EXT) + { + RangeVar *stat = makeRangeVarFromNameList( + (List *) alterObjectSchemaStmt->object); + + /* append shard id */ + AppendShardIdToName(&stat->relname, shardId); + + alterObjectSchemaStmt->object = (Node *) MakeNameListFromRangeVar(stat); + } + else + { + char **relationName = &(alterObjectSchemaStmt->relation->relname); + char **relationSchemaName = + &(alterObjectSchemaStmt->relation->schemaname); + + /* prefix with schema name if it is not added already */ + SetSchemaNameIfNotExist(relationSchemaName, schemaName); + + /* append shardId to base relation name */ + AppendShardIdToName(relationName, shardId); + } - /* append shardId to base relation name */ - AppendShardIdToName(relationName, shardId); break; } +#if PG_VERSION_NUM >= PG_VERSION_13 + case T_AlterStatsStmt: + { + AlterStatsStmt *alterStatsStmt = (AlterStatsStmt *) parseTree; + RangeVar *stat = makeRangeVarFromNameList(alterStatsStmt->defnames); + + AppendShardIdToName(&stat->relname, shardId); + + alterStatsStmt->defnames = MakeNameListFromRangeVar(stat); + + break; + } +#endif + case T_AlterTableStmt: { /* @@ -180,6 +211,22 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId) break; } + case T_AlterOwnerStmt: + { + AlterOwnerStmt *alterOwnerStmt = castNode(AlterOwnerStmt, parseTree); + + /* we currently extend names in alter owner statements only for statistics */ + Assert(alterOwnerStmt->objectType == OBJECT_STATISTIC_EXT); + + RangeVar *stat = makeRangeVarFromNameList((List *) alterOwnerStmt->object); + + AppendShardIdToName(&stat->relname, shardId); + + alterOwnerStmt->object = (Node *) MakeNameListFromRangeVar(stat); + + break; + } + case T_ClusterStmt: { ClusterStmt *clusterStmt = (ClusterStmt *) parseTree; @@ -530,6 +577,17 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId) { RenamePolicyEventExtendNames(renameStmt, schemaName, shardId); } + else if (objectType == OBJECT_STATISTIC_EXT) + { + RangeVar *stat = makeRangeVarFromNameList((List *) renameStmt->object); + + AppendShardIdToName(&stat->relname, shardId); + AppendShardIdToName(&renameStmt->newname, shardId); + + SetSchemaNameIfNotExist(&stat->schemaname, schemaName); + + renameStmt->object = (Node *) MakeNameListFromRangeVar(stat); + } else { ereport(WARNING, (errmsg("unsafe object type in rename statement"), diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index b33e5486e..338f3f25c 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -282,6 +282,12 @@ 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 * PreprocessDropStatisticsStmt(Node *node, const char *queryString); +extern List * PreprocessAlterStatisticsRenameStmt(Node *node, const char *queryString); +extern List * PreprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString); +extern List * PostprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString); +extern ObjectAddress AlterStatisticsSchemaStmtObjectAddress(Node *node, bool missingOk); +extern List * PreprocessAlterStatisticsStmt(Node *node, const char *queryString); +extern List * PreprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString); extern List * GetExplicitStatisticsCommandList(Oid relationId); extern List * GetExplicitStatisticsSchemaIdList(Oid relationId); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index d49797f05..7e264544c 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -58,9 +58,17 @@ extern char * DeparseAlterSchemaRenameStmt(Node *stmt); /* forward declarations for deparse_statistics_stmts.c */ extern char * DeparseCreateStatisticsStmt(Node *node); extern char * DeparseDropStatisticsStmt(List *nameList, bool ifExists); +extern char * DeparseAlterStatisticsRenameStmt(Node *node); +extern char * DeparseAlterStatisticsSchemaStmt(Node *node); +extern char * DeparseAlterStatisticsStmt(Node *node); +extern char * DeparseAlterStatisticsOwnerStmt(Node *node); extern void QualifyCreateStatisticsStmt(Node *node); extern void QualifyDropStatisticsStmt(Node *node); +extern void QualifyAlterStatisticsRenameStmt(Node *node); +extern void QualifyAlterStatisticsSchemaStmt(Node *node); +extern void QualifyAlterStatisticsStmt(Node *node); +extern void QualifyAlterStatisticsOwnerStmt(Node *node); /* forward declarations for deparse_type_stmts.c */ extern char * DeparseCompositeTypeStmt(Node *stmt); diff --git a/src/test/regress/expected/pg13_propagate_statistics.out b/src/test/regress/expected/pg13_propagate_statistics.out new file mode 100644 index 000000000..c75eb333f --- /dev/null +++ b/src/test/regress/expected/pg13_propagate_statistics.out @@ -0,0 +1,112 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int > 12 AS server_version_above_twelve +\gset +\if :server_version_above_twelve +\else +\q +\endif +CREATE SCHEMA "statistics'TestTarget"; +SET search_path TO "statistics'TestTarget"; +SET citus.next_shard_id TO 980000; +SET client_min_messages TO WARNING; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +CREATE TABLE t1 (a int, b int); +CREATE STATISTICS s1 ON a,b FROM t1; +CREATE STATISTICS s2 ON a,b FROM t1; +CREATE STATISTICS s3 ON a,b FROM t1; +CREATE STATISTICS s4 ON a,b FROM t1; +-- test altering stats target +-- test alter target before distribution +ALTER STATISTICS s1 SET STATISTICS 3; +-- since max value for target is 10000, this will automatically be lowered +ALTER STATISTICS s2 SET STATISTICS 999999; +WARNING: lowering statistics target to 10000 +SELECT create_distributed_table('t1', 'b'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- test alter target before distribution +ALTER STATISTICS s3 SET STATISTICS 46; +\c - - - :worker_1_port +SELECT stxstattarget, stxrelid::regclass +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('statistics''TestTarget') +) +ORDER BY stxstattarget, stxrelid::regclass ASC; + stxstattarget | stxrelid +--------------------------------------------------------------------- + -1 | "statistics'TestTarget".t1_980000 + -1 | "statistics'TestTarget".t1_980002 + -1 | "statistics'TestTarget".t1_980004 + -1 | "statistics'TestTarget".t1_980006 + -1 | "statistics'TestTarget".t1_980008 + -1 | "statistics'TestTarget".t1_980010 + -1 | "statistics'TestTarget".t1_980012 + -1 | "statistics'TestTarget".t1_980014 + -1 | "statistics'TestTarget".t1_980016 + -1 | "statistics'TestTarget".t1_980018 + -1 | "statistics'TestTarget".t1_980020 + -1 | "statistics'TestTarget".t1_980022 + -1 | "statistics'TestTarget".t1_980024 + -1 | "statistics'TestTarget".t1_980026 + -1 | "statistics'TestTarget".t1_980028 + -1 | "statistics'TestTarget".t1_980030 + 3 | "statistics'TestTarget".t1_980000 + 3 | "statistics'TestTarget".t1_980002 + 3 | "statistics'TestTarget".t1_980004 + 3 | "statistics'TestTarget".t1_980006 + 3 | "statistics'TestTarget".t1_980008 + 3 | "statistics'TestTarget".t1_980010 + 3 | "statistics'TestTarget".t1_980012 + 3 | "statistics'TestTarget".t1_980014 + 3 | "statistics'TestTarget".t1_980016 + 3 | "statistics'TestTarget".t1_980018 + 3 | "statistics'TestTarget".t1_980020 + 3 | "statistics'TestTarget".t1_980022 + 3 | "statistics'TestTarget".t1_980024 + 3 | "statistics'TestTarget".t1_980026 + 3 | "statistics'TestTarget".t1_980028 + 3 | "statistics'TestTarget".t1_980030 + 46 | "statistics'TestTarget".t1_980000 + 46 | "statistics'TestTarget".t1_980002 + 46 | "statistics'TestTarget".t1_980004 + 46 | "statistics'TestTarget".t1_980006 + 46 | "statistics'TestTarget".t1_980008 + 46 | "statistics'TestTarget".t1_980010 + 46 | "statistics'TestTarget".t1_980012 + 46 | "statistics'TestTarget".t1_980014 + 46 | "statistics'TestTarget".t1_980016 + 46 | "statistics'TestTarget".t1_980018 + 46 | "statistics'TestTarget".t1_980020 + 46 | "statistics'TestTarget".t1_980022 + 46 | "statistics'TestTarget".t1_980024 + 46 | "statistics'TestTarget".t1_980026 + 46 | "statistics'TestTarget".t1_980028 + 46 | "statistics'TestTarget".t1_980030 + 10000 | "statistics'TestTarget".t1_980000 + 10000 | "statistics'TestTarget".t1_980002 + 10000 | "statistics'TestTarget".t1_980004 + 10000 | "statistics'TestTarget".t1_980006 + 10000 | "statistics'TestTarget".t1_980008 + 10000 | "statistics'TestTarget".t1_980010 + 10000 | "statistics'TestTarget".t1_980012 + 10000 | "statistics'TestTarget".t1_980014 + 10000 | "statistics'TestTarget".t1_980016 + 10000 | "statistics'TestTarget".t1_980018 + 10000 | "statistics'TestTarget".t1_980020 + 10000 | "statistics'TestTarget".t1_980022 + 10000 | "statistics'TestTarget".t1_980024 + 10000 | "statistics'TestTarget".t1_980026 + 10000 | "statistics'TestTarget".t1_980028 + 10000 | "statistics'TestTarget".t1_980030 +(64 rows) + +\c - - - :master_port +SET client_min_messages TO WARNING; +DROP SCHEMA "statistics'TestTarget" CASCADE; diff --git a/src/test/regress/expected/pg13_propagate_statistics_0.out b/src/test/regress/expected/pg13_propagate_statistics_0.out new file mode 100644 index 000000000..e25fbb82d --- /dev/null +++ b/src/test/regress/expected/pg13_propagate_statistics_0.out @@ -0,0 +1,6 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int > 12 AS server_version_above_twelve +\gset +\if :server_version_above_twelve +\else +\q diff --git a/src/test/regress/expected/propagate_statistics.out b/src/test/regress/expected/propagate_statistics.out index 2a69cec89..80e501fff 100644 --- a/src/test/regress/expected/propagate_statistics.out +++ b/src/test/regress/expected/propagate_statistics.out @@ -70,6 +70,27 @@ DROP STATISTICS IF EXISTS s3, sc2.s4, s6; DROP STATISTICS s5,s6; ERROR: statistics object "statistics'Test.s6" does not exist DROP STATISTICS IF EXISTS s5,s5,s6,s6; +-- test renaming statistics +CREATE STATISTICS s6 ON a,b FROM test_stats4; +DROP STATISTICS s7; +ERROR: statistics object "statistics'Test.s7" does not exist +ALTER STATISTICS s6 RENAME TO s7; +ALTER STATISTICS sc1.st1 RENAME TO st1_new; +-- test altering stats schema +CREATE SCHEMA test_alter_schema; +ALTER STATISTICS s7 SET SCHEMA test_alter_schema; +-- test alter owner +ALTER STATISTICS sc2."neW'Stat" OWNER TO pg_monitor; +-- test alter owner before distribution +CREATE TABLE ownertest(a int, b int); +CREATE STATISTICS sc1.s9 ON a,b FROM ownertest; +ALTER STATISTICS sc1.s9 OWNER TO pg_signal_backend; +SELECT create_distributed_table('ownertest','a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + \c - - - :worker_1_port SELECT stxname FROM pg_statistic_ext @@ -129,23 +150,39 @@ ORDER BY stxname ASC; s2_980058 s2_980060 s2_980062 - st1_980064 - st1_980066 - st1_980068 - st1_980070 - st1_980072 - st1_980074 - st1_980076 - st1_980078 - st1_980080 - st1_980082 - st1_980084 - st1_980086 - st1_980088 - st1_980090 - st1_980092 - st1_980094 -(64 rows) + s9_980129 + s9_980131 + s9_980133 + s9_980135 + s9_980137 + s9_980139 + s9_980141 + s9_980143 + s9_980145 + s9_980147 + s9_980149 + s9_980151 + s9_980153 + s9_980155 + s9_980157 + s9_980159 + st1_new_980064 + st1_new_980066 + st1_new_980068 + st1_new_980070 + st1_new_980072 + st1_new_980074 + st1_new_980076 + st1_new_980078 + st1_new_980080 + st1_new_980082 + st1_new_980084 + st1_new_980086 + st1_new_980088 + st1_new_980090 + st1_new_980092 + st1_new_980094 +(80 rows) SELECT count(DISTINCT stxnamespace) FROM pg_statistic_ext @@ -159,8 +196,21 @@ WHERE stxnamespace IN ( 3 (1 row) +SELECT COUNT(DISTINCT stxowner) +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') +); + count +--------------------------------------------------------------------- + 3 +(1 row) + \c - - - :master_port SET client_min_messages TO WARNING; DROP SCHEMA "statistics'Test" CASCADE; +DROP SCHEMA test_alter_schema CASCADE; DROP SCHEMA sc1 CASCADE; DROP SCHEMA sc2 CASCADE; diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 5f862dc71..8a65fb475 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -94,6 +94,7 @@ test: tableam # Tests for statistics propagation # ---------- test: propagate_statistics +test: pg13_propagate_statistics # ---------- # Miscellaneous tests to check our query planning behavior diff --git a/src/test/regress/sql/pg13_propagate_statistics.sql b/src/test/regress/sql/pg13_propagate_statistics.sql new file mode 100644 index 000000000..9716d3289 --- /dev/null +++ b/src/test/regress/sql/pg13_propagate_statistics.sql @@ -0,0 +1,45 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int > 12 AS server_version_above_twelve +\gset +\if :server_version_above_twelve +\else +\q +\endif + +CREATE SCHEMA "statistics'TestTarget"; +SET search_path TO "statistics'TestTarget"; +SET citus.next_shard_id TO 980000; +SET client_min_messages TO WARNING; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; + +CREATE TABLE t1 (a int, b int); +CREATE STATISTICS s1 ON a,b FROM t1; +CREATE STATISTICS s2 ON a,b FROM t1; +CREATE STATISTICS s3 ON a,b FROM t1; +CREATE STATISTICS s4 ON a,b FROM t1; + +-- test altering stats target +-- test alter target before distribution +ALTER STATISTICS s1 SET STATISTICS 3; +-- since max value for target is 10000, this will automatically be lowered +ALTER STATISTICS s2 SET STATISTICS 999999; + +SELECT create_distributed_table('t1', 'b'); + +-- test alter target before distribution +ALTER STATISTICS s3 SET STATISTICS 46; + +\c - - - :worker_1_port +SELECT stxstattarget, stxrelid::regclass +FROM pg_statistic_ext +WHERE stxnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname IN ('statistics''TestTarget') +) +ORDER BY stxstattarget, stxrelid::regclass ASC; + +\c - - - :master_port +SET client_min_messages TO WARNING; +DROP SCHEMA "statistics'TestTarget" CASCADE; diff --git a/src/test/regress/sql/propagate_statistics.sql b/src/test/regress/sql/propagate_statistics.sql index 1c46b9644..8a0372cf7 100644 --- a/src/test/regress/sql/propagate_statistics.sql +++ b/src/test/regress/sql/propagate_statistics.sql @@ -57,6 +57,24 @@ DROP STATISTICS IF EXISTS s3, sc2.s4, s6; DROP STATISTICS s5,s6; DROP STATISTICS IF EXISTS s5,s5,s6,s6; +-- test renaming statistics +CREATE STATISTICS s6 ON a,b FROM test_stats4; +DROP STATISTICS s7; +ALTER STATISTICS s6 RENAME TO s7; +ALTER STATISTICS sc1.st1 RENAME TO st1_new; + +-- test altering stats schema +CREATE SCHEMA test_alter_schema; +ALTER STATISTICS s7 SET SCHEMA test_alter_schema; + +-- test alter owner +ALTER STATISTICS sc2."neW'Stat" OWNER TO pg_monitor; +-- test alter owner before distribution +CREATE TABLE ownertest(a int, b int); +CREATE STATISTICS sc1.s9 ON a,b FROM ownertest; +ALTER STATISTICS sc1.s9 OWNER TO pg_signal_backend; +SELECT create_distributed_table('ownertest','a'); + \c - - - :worker_1_port SELECT stxname FROM pg_statistic_ext @@ -75,8 +93,17 @@ WHERE stxnamespace IN ( WHERE nspname IN ('public', 'statistics''Test', 'sc1', 'sc2') ); +SELECT COUNT(DISTINCT stxowner) +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 test_alter_schema CASCADE; DROP SCHEMA sc1 CASCADE; DROP SCHEMA sc2 CASCADE;