Have combined logic for dependency check

velioglu/cyclic_dep_2
Burak Velioglu 2022-03-09 12:52:27 +03:00
parent f94e46560a
commit 47da090878
No known key found for this signature in database
GPG Key ID: F6827E620F6549C6
8 changed files with 121 additions and 158 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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