diff --git a/src/backend/distributed/commands/aggregate.c b/src/backend/distributed/commands/aggregate.c index 4c25f41bf..f6161df49 100644 --- a/src/backend/distributed/commands/aggregate.c +++ b/src/backend/distributed/commands/aggregate.c @@ -57,42 +57,12 @@ PostprocessDefineAggregateStmt(Node *node, const char *queryString) EnsureSequentialMode(OBJECT_AGGREGATE); - ObjectAddress *undistributableDependency = GetUndistributableDependency( - &address); - if (undistributableDependency != NULL) + /* If the aggregate has any unsupported dependency, create it locally */ + DeferredErrorMessage *depError = DeferErrorIfHasUnsupportedDependency(&address); + + if (depError != NULL) { - if (SupportedDependencyByCitus(undistributableDependency)) - { - /* - * Citus can't distribute some relations as dependency, although those - * types as supported by Citus. So we can use get_rel_name directly - */ - RangeVar *aggRangeVar = makeRangeVarFromNameList(stmt->defnames); - char *aggName = aggRangeVar->relname; - char *dependentRelationName = - get_rel_name(undistributableDependency->objectId); - - ereport(WARNING, (errmsg("Citus can't distribute aggregate \"%s\" having " - "dependency on non-distributed relation \"%s\"", - aggName, dependentRelationName), - errdetail("Aggregate will be created only locally"), - errhint("To distribute aggregate, distribute dependent " - "relations first. Then, re-create the aggregate"))); - } - else - { - char *objectType = NULL; - #if PG_VERSION_NUM >= PG_VERSION_14 - objectType = getObjectTypeDescription(undistributableDependency, false); - #else - objectType = getObjectTypeDescription(undistributableDependency); - #endif - ereport(WARNING, (errmsg("Citus can't distribute functions having " - "dependency on unsupported object of type \"%s\"", - objectType), - errdetail("Aggregate will be created only locally"))); - } - + RaiseDeferredError(depError, WARNING); return NIL; } diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index bdfaeb39a..01f561b65 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -32,7 +32,6 @@ typedef bool (*AddressPredicate)(const ObjectAddress *); static void EnsureDependenciesCanBeDistributed(const ObjectAddress *relationAddress); -static void ErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress); static void ErrorIfCircularDependencyExists(const ObjectAddress *objectAddress); static int ObjectAddressComparator(const void *a, const void *b); static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency); @@ -155,58 +154,11 @@ EnsureDependenciesCanBeDistributed(const ObjectAddress *objectAddress) ErrorIfCircularDependencyExists(objectAddress); /* If the object has any unsupported dependency, error out */ - ErrorIfHasUnsupportedDependency(objectAddress); -} + DeferredErrorMessage *depError = DeferErrorIfHasUnsupportedDependency(objectAddress); - -/* - * ErrorIfHasUnsupportedDependency ensures object doesn't have any dependency unsupported - * by Citus. - */ -static void -ErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress) -{ - ObjectAddress *undistributableDependency = GetUndistributableDependency( - objectAddress); - - if (undistributableDependency != NULL) + if (depError != NULL) { - if (SupportedDependencyByCitus(undistributableDependency)) - { - /* - * Citus can't distribute some relations as dependency, although those - * types as supported by Citus. So we can use get_rel_name directly - * - * For now the relations are the only type that is supported by Citus - * but can not be distributed as dependency, though we've added an - * explicit check below as well to not to break the logic here in case - * GetUndistributableDependency changes. - */ - if (getObjectClass(undistributableDependency) == OCLASS_CLASS) - { - char *tableName = get_rel_name(objectAddress->objectId); - char *dependentRelationName = get_rel_name( - undistributableDependency->objectId); - - ereport(ERROR, (errmsg("Relation \"%s\" has dependency to a table" - " \"%s\" that is not in Citus' metadata", - tableName, dependentRelationName), - errhint("Distribute dependent relation first."))); - } - } - - char *dependencyDescription = NULL; - char *objectDescription = NULL; - #if PG_VERSION_NUM >= PG_VERSION_14 - dependencyDescription = getObjectDescription(undistributableDependency, false); - objectDescription = getObjectDescription(objectAddress, false); - #else - dependencyDescription = getObjectDescription(undistributableDependency); - objectDescription = getObjectDescription(objectAddress); - #endif - ereport(ERROR, (errmsg("Object \"%s\" has dependency on unsupported " - "object \"%s\"", objectDescription, - dependencyDescription))); + RaiseDeferredError(depError, ERROR); } } diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index 11729a21f..2c0dfd6bb 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -1365,47 +1365,12 @@ PostprocessCreateFunctionStmt(Node *node, const char *queryString) return NIL; } - /* - * This check should have been valid for all objects not only for functions. Though, - * we do this limited check for now as functions are more likely to be used with - * such dependencies, and we want to scope it for now. - */ - ObjectAddress *undistributableDependency = GetUndistributableDependency( - &functionAddress); - if (undistributableDependency != NULL) + /* If the function has any unsupported dependency, create it locally */ + DeferredErrorMessage *errMsg = DeferErrorIfHasUnsupportedDependency(&functionAddress); + + if (errMsg != NULL) { - if (SupportedDependencyByCitus(undistributableDependency)) - { - /* - * Citus can't distribute some relations as dependency, although those - * types as supported by Citus. So we can use get_rel_name directly - */ - RangeVar *functionRangeVar = makeRangeVarFromNameList(stmt->funcname); - char *functionName = functionRangeVar->relname; - char *dependentRelationName = - get_rel_name(undistributableDependency->objectId); - - ereport(WARNING, (errmsg("Citus can't distribute function \"%s\" having " - "dependency on non-distributed relation \"%s\"", - functionName, dependentRelationName), - errdetail("Function will be created only locally"), - errhint("To distribute function, distribute dependent " - "relations first. Then, re-create the function"))); - } - else - { - char *objectType = NULL; - #if PG_VERSION_NUM >= PG_VERSION_14 - objectType = getObjectTypeDescription(undistributableDependency, false); - #else - objectType = getObjectTypeDescription(undistributableDependency); - #endif - ereport(WARNING, (errmsg("Citus can't distribute functions having " - "dependency on unsupported object of type \"%s\"", - objectType), - errdetail("Function will be created only locally"))); - } - + RaiseDeferredError(errMsg, WARNING); return NIL; } diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index d7bff1ae1..57ff4f082 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -136,6 +136,7 @@ static void CollectObjectAddress(ObjectAddressCollector *collector, const ObjectAddress *address); static bool IsObjectAddressCollected(ObjectAddress findAddress, ObjectAddressCollector *collector); +static ObjectAddress * GetUndistributableDependency(const ObjectAddress *objectAddress); static void MarkObjectVisited(ObjectAddressCollector *collector, ObjectAddress target); static bool TargetObjectVisited(ObjectAddressCollector *collector, @@ -741,11 +742,83 @@ SupportedDependencyByCitus(const ObjectAddress *address) } +/* + * DeferErrorIfHasUnsupportedDependency returns deferred error message if the given + * object has any undistributable dependency. + */ +DeferredErrorMessage * +DeferErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress) +{ + ObjectAddress *undistributableDependency = GetUndistributableDependency( + objectAddress); + + if (undistributableDependency == NULL) + { + return NULL; + } + + char *objectDescription = NULL; + char *dependencyDescription = NULL; + StringInfo errorMessage = makeStringInfo(); + StringInfo detailMessage = makeStringInfo(); + + #if PG_VERSION_NUM >= PG_VERSION_14 + objectDescription = getObjectDescription(objectAddress, false); + dependencyDescription = getObjectDescription(undistributableDependency, false); + #else + objectDescription = getObjectDescription(objectAddress); + dependencyDescription = getObjectDescription(undistributableDependency); + #endif + + /* + * If the given object is a procedure, we want to create it locally, + * so provide that information in the error detail. + */ + if (getObjectClass(objectAddress) == OCLASS_PROC) + { + appendStringInfo(detailMessage, "\"%s\" will be created only locally", + objectDescription); + } + else + { + detailMessage->data = NULL; + } + + if (SupportedDependencyByCitus(undistributableDependency)) + { + StringInfo hintMessage = makeStringInfo(); + + appendStringInfo(errorMessage, "\"%s\" has dependency to \"%s\" that is not in " + "Citus' metadata", + objectDescription, + dependencyDescription); + + appendStringInfo(hintMessage, "Distribute \"%s\" first to distribute \"%s\"", + dependencyDescription, + objectDescription); + + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + errorMessage->data, + detailMessage->data, + hintMessage->data); + } + + appendStringInfo(errorMessage, "\"%s\" has dependency on unsupported " + "object \"%s\"", objectDescription, + dependencyDescription); + + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + errorMessage->data, + detailMessage->data, + NULL); +} + + /* * GetUndistributableDependency checks whether object has any non-distributable * dependency. If any one found, it will be returned. */ -ObjectAddress * +static ObjectAddress * GetUndistributableDependency(const ObjectAddress *objectAddress) { List *dependencies = GetAllDependenciesForObject(objectAddress); diff --git a/src/include/distributed/metadata/dependency.h b/src/include/distributed/metadata/dependency.h index 79a8575d9..af11c5f2a 100644 --- a/src/include/distributed/metadata/dependency.h +++ b/src/include/distributed/metadata/dependency.h @@ -15,13 +15,16 @@ #include "postgres.h" #include "catalog/objectaddress.h" +#include "distributed/errormessage.h" #include "nodes/pg_list.h" extern List * GetUniqueDependenciesList(List *objectAddressesList); extern List * GetDependenciesForObject(const ObjectAddress *target); extern List * GetAllSupportedDependenciesForObject(const ObjectAddress *target); extern List * GetAllDependenciesForObject(const ObjectAddress *target); -extern ObjectAddress * GetUndistributableDependency(const ObjectAddress *target); +extern DeferredErrorMessage * DeferErrorIfHasUnsupportedDependency(const + ObjectAddress * + objectAddress); extern List * OrderObjectAddressListInDependencyOrder(List *objectAddressList); extern bool SupportedDependencyByCitus(const ObjectAddress *address); extern List * GetPgDependTuplesForDependingObjects(Oid targetObjectClassId, diff --git a/src/test/regress/expected/aggregate_support.out b/src/test/regress/expected/aggregate_support.out index 80ac1bb7a..1459c139b 100644 --- a/src/test/regress/expected/aggregate_support.out +++ b/src/test/regress/expected/aggregate_support.out @@ -1089,14 +1089,14 @@ LEFT JOIN ref_table ON TRUE; create table dummy_tbl (a int); create function dummy_fnc(a dummy_tbl, d double precision) RETURNS dummy_tbl AS $$SELECT 1;$$ LANGUAGE sql; -WARNING: Citus can't distribute function "dummy_fnc" having dependency on non-distributed relation "dummy_tbl" -DETAIL: Function will be created only locally -HINT: To distribute function, distribute dependent relations first. Then, re-create the function +WARNING: "function dummy_fnc(dummy_tbl,double precision)" has dependency to "table dummy_tbl" that is not in Citus' metadata +DETAIL: "function dummy_fnc(dummy_tbl,double precision)" will be created only locally +HINT: Distribute "table dummy_tbl" first to distribute "function dummy_fnc(dummy_tbl,double precision)" -- should give warning and create aggregate local only create aggregate dependent_agg (float8) (stype=dummy_tbl, sfunc=dummy_fnc); -WARNING: Citus can't distribute aggregate "dependent_agg" having dependency on non-distributed relation "dummy_tbl" -DETAIL: Aggregate will be created only locally -HINT: To distribute aggregate, distribute dependent relations first. Then, re-create the aggregate +WARNING: "function dependent_agg(double precision)" has dependency to "table dummy_tbl" that is not in Citus' metadata +DETAIL: "function dependent_agg(double precision)" will be created only locally +HINT: Distribute "table dummy_tbl" first to distribute "function dependent_agg(double precision)" -- clear and try again with distributed table DROP TABLE dummy_tbl CASCADE; NOTICE: drop cascades to 2 other objects diff --git a/src/test/regress/expected/function_propagation.out b/src/test/regress/expected/function_propagation.out index ba39dacb5..dc6cf5d69 100644 --- a/src/test/regress/expected/function_propagation.out +++ b/src/test/regress/expected/function_propagation.out @@ -134,9 +134,9 @@ BEGIN return 1; END; $$; -WARNING: Citus can't distribute function "func_4" having dependency on non-distributed relation "function_prop_table" -DETAIL: Function will be created only locally -HINT: To distribute function, distribute dependent relations first. Then, re-create the function +WARNING: "function func_4(function_prop_table)" has dependency to "table function_prop_table" that is not in Citus' metadata +DETAIL: "function func_4(function_prop_table)" will be created only locally +HINT: Distribute "table function_prop_table" first to distribute "function func_4(function_prop_table)" CREATE OR REPLACE FUNCTION func_5(param_1 int) RETURNS function_prop_table LANGUAGE plpgsql AS @@ -145,9 +145,9 @@ BEGIN return 1; END; $$; -WARNING: Citus can't distribute function "func_5" having dependency on non-distributed relation "function_prop_table" -DETAIL: Function will be created only locally -HINT: To distribute function, distribute dependent relations first. Then, re-create the function +WARNING: "function func_5(integer)" has dependency to "table function_prop_table" that is not in Citus' metadata +DETAIL: "function func_5(integer)" will be created only locally +HINT: Distribute "table function_prop_table" first to distribute "function func_5(integer)" -- Functions can be created with distributed table dependency SELECT create_distributed_table('function_prop_table', 'a'); create_distributed_table @@ -186,8 +186,8 @@ BEGIN return 1; END; $$; -WARNING: Citus can't distribute functions having dependency on unsupported object of type "view" -DETAIL: Function will be created only locally +WARNING: "function func_7(function_prop_view)" has dependency on unsupported object "view function_prop_view" +DETAIL: "function func_7(function_prop_view)" will be created only locally CREATE OR REPLACE FUNCTION func_8(param_1 int) RETURNS function_prop_view LANGUAGE plpgsql AS @@ -196,8 +196,8 @@ BEGIN return 1; END; $$; -WARNING: Citus can't distribute functions having dependency on unsupported object of type "view" -DETAIL: Function will be created only locally +WARNING: "function func_8(integer)" has dependency on unsupported object "view function_prop_view" +DETAIL: "function func_8(integer)" will be created only locally -- Check within transaction BEGIN; CREATE TYPE type_in_transaction AS (a int, b int); @@ -435,8 +435,8 @@ BEGIN; CREATE TABLE table_to_prop_func_3(id int, col_1 int default func_in_transaction_3(NULL::non_dist_table)); -- It should error out as there is a non-distributed table dependency SELECT create_distributed_table('table_to_prop_func_3','id'); -ERROR: Relation "table_to_prop_func_3" has dependency to a table "non_dist_table" that is not in Citus' metadata -HINT: Distribute dependent relation first. +ERROR: "table table_to_prop_func_3" has dependency to "table non_dist_table" that is not in Citus' metadata +HINT: Distribute "table non_dist_table" first to distribute "table table_to_prop_func_3" COMMIT; -- Adding a column with default value should propagate the function BEGIN; @@ -497,8 +497,8 @@ BEGIN; (1 row) ALTER TABLE table_to_dist ADD COLUMN col_1 int default function_propagation_schema.non_dist_func(NULL::non_dist_table_for_function); -ERROR: Relation "table_to_dist" has dependency to a table "non_dist_table_for_function" that is not in Citus' metadata -HINT: Distribute dependent relation first. +ERROR: "table table_to_dist" has dependency to "table non_dist_table_for_function" that is not in Citus' metadata +HINT: Distribute "table non_dist_table_for_function" first to distribute "table table_to_dist" ROLLBACK; -- Adding multiple columns with default values should propagate the function BEGIN; @@ -723,8 +723,8 @@ BEGIN; CREATE TABLE table_to_prop_func_9(id int, col_1 int check (func_in_transaction_11(col_1, NULL::local_table_for_const))); -- It should error out since there is non-distributed table dependency exists SELECT create_distributed_table('table_to_prop_func_9', 'id'); -ERROR: Relation "table_to_prop_func_9" has dependency to a table "local_table_for_const" that is not in Citus' metadata -HINT: Distribute dependent relation first. +ERROR: "table table_to_prop_func_9" has dependency to "table local_table_for_const" that is not in Citus' metadata +HINT: Distribute "table local_table_for_const" first to distribute "table table_to_prop_func_9" COMMIT; -- Show that function as a part of generated always is supporte BEGIN; @@ -1268,9 +1268,9 @@ BEGIN return param_1 > 5; END; $$; -WARNING: Citus can't distribute function "func_for_circ_dep_3" having dependency on non-distributed relation "table_1_for_circ_dep_3" -DETAIL: Function will be created only locally -HINT: To distribute function, distribute dependent relations first. Then, re-create the function +WARNING: "function func_for_circ_dep_3(integer,table_1_for_circ_dep_3)" has dependency to "table table_1_for_circ_dep_3" that is not in Citus' metadata +DETAIL: "function func_for_circ_dep_3(integer,table_1_for_circ_dep_3)" will be created only locally +HINT: Distribute "table table_1_for_circ_dep_3" first to distribute "function func_for_circ_dep_3(integer,table_1_for_circ_dep_3)" CREATE TABLE table_2_for_circ_dep_3(id int, col_1 int check (func_for_circ_dep_3(col_1, NULL::table_1_for_circ_dep_3))); ALTER TABLE table_1_for_circ_dep_3 ADD COLUMN col_2 table_2_for_circ_dep_3; -- It should error out due to circular dependency diff --git a/src/test/regress/expected/pg13.out b/src/test/regress/expected/pg13.out index bf0337a83..cceab4418 100644 --- a/src/test/regress/expected/pg13.out +++ b/src/test/regress/expected/pg13.out @@ -145,13 +145,13 @@ CREATE TYPE myvarchar; CREATE FUNCTION myvarcharin(cstring, oid, integer) RETURNS myvarchar LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'varcharin'; NOTICE: return type myvarchar is only a shell -WARNING: Citus can't distribute functions having dependency on unsupported object of type "type" -DETAIL: Function will be created only locally +WARNING: "function myvarcharin(cstring,oid,integer)" has dependency on unsupported object "type myvarchar" +DETAIL: "function myvarcharin(cstring,oid,integer)" will be created only locally CREATE FUNCTION myvarcharout(myvarchar) RETURNS cstring LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'varcharout'; NOTICE: argument type myvarchar is only a shell -WARNING: Citus can't distribute functions having dependency on unsupported object of type "type" -DETAIL: Function will be created only locally +WARNING: "function myvarcharout(myvarchar)" has dependency on unsupported object "type myvarchar" +DETAIL: "function myvarcharout(myvarchar)" will be created only locally CREATE TYPE myvarchar ( input = myvarcharin, output = myvarcharout, @@ -164,7 +164,7 @@ CREATE TABLE my_table (a int, b myvarchar); -- """Add ALTER TYPE options useful for extensions, -- like TOAST and I/O functions control (Tomas Vondra, Tom Lane)""" SELECT create_distributed_table('my_table', 'a'); -ERROR: Object "table my_table" has dependency on unsupported object "type myvarchar" +ERROR: "table my_table" has dependency on unsupported object "type myvarchar" CREATE TABLE test_table(a int, b tsvector); SELECT create_distributed_table('test_table', 'a'); create_distributed_table