mirror of https://github.com/citusdata/citus.git
Relax the colocation checks for distributed functions
As long as the types can be coerced, it is safe to pushdown functions.pull/3029/head
parent
7172c7f727
commit
18de78f386
|
@ -37,6 +37,7 @@
|
||||||
#include "distributed/multi_executor.h"
|
#include "distributed/multi_executor.h"
|
||||||
#include "distributed/relation_access_tracking.h"
|
#include "distributed/relation_access_tracking.h"
|
||||||
#include "distributed/worker_transaction.h"
|
#include "distributed/worker_transaction.h"
|
||||||
|
#include "parser/parse_coerce.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgrprotos.h"
|
#include "utils/fmgrprotos.h"
|
||||||
|
@ -318,14 +319,7 @@ GetFunctionColocationId(Oid functionOid, char *colocateWithTableName,
|
||||||
Oid distributionArgumentOid)
|
Oid distributionArgumentOid)
|
||||||
{
|
{
|
||||||
int colocationId = INVALID_COLOCATION_ID;
|
int colocationId = INVALID_COLOCATION_ID;
|
||||||
bool createdColocationGroup = false;
|
Relation pgDistColocation = heap_open(DistColocationRelationId(), ShareLock);
|
||||||
|
|
||||||
/*
|
|
||||||
* Get an exclusive lock on the colocation system catalog. Therefore, we
|
|
||||||
* can be sure that there will no modifications on the colocation table
|
|
||||||
* until this transaction is committed.
|
|
||||||
*/
|
|
||||||
Relation pgDistColocation = heap_open(DistColocationRelationId(), ExclusiveLock);
|
|
||||||
|
|
||||||
if (pg_strncasecmp(colocateWithTableName, "default", NAMEDATALEN) == 0)
|
if (pg_strncasecmp(colocateWithTableName, "default", NAMEDATALEN) == 0)
|
||||||
{
|
{
|
||||||
|
@ -335,11 +329,13 @@ GetFunctionColocationId(Oid functionOid, char *colocateWithTableName,
|
||||||
|
|
||||||
if (colocationId == INVALID_COLOCATION_ID)
|
if (colocationId == INVALID_COLOCATION_ID)
|
||||||
{
|
{
|
||||||
colocationId =
|
char *functionName = get_func_name(functionOid);
|
||||||
CreateColocationGroup(ShardCount, ShardReplicationFactor,
|
|
||||||
distributionArgumentOid);
|
|
||||||
|
|
||||||
createdColocationGroup = true;
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("cannot distribute the function \"%s\" since there "
|
||||||
|
"is no table to colocate with", functionName),
|
||||||
|
errhint("Provide a distributed table via \"colocate_with\" "
|
||||||
|
"option to create_distributed_function()")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -353,23 +349,8 @@ GetFunctionColocationId(Oid functionOid, char *colocateWithTableName,
|
||||||
colocationId = TableColocationId(sourceRelationId);
|
colocationId = TableColocationId(sourceRelationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* keep the lock */
|
||||||
* If we created a new colocation group then we need to keep the lock to
|
heap_close(pgDistColocation, NoLock);
|
||||||
* prevent a concurrent create_distributed_table call from creating another
|
|
||||||
* colocation group with the same parameters. If we're using an existing
|
|
||||||
* colocation group then other transactions will use the same one.
|
|
||||||
*/
|
|
||||||
if (createdColocationGroup)
|
|
||||||
{
|
|
||||||
/* keep the exclusive lock */
|
|
||||||
heap_close(pgDistColocation, NoLock);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* release the exclusive lock */
|
|
||||||
heap_close(pgDistColocation, ExclusiveLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return colocationId;
|
return colocationId;
|
||||||
}
|
}
|
||||||
|
@ -415,15 +396,31 @@ EnsureFunctionCanBeColocatedWithTable(Oid functionOid, Oid distributionColumnTyp
|
||||||
"\"citus.replication_model\" is set to \"streaming\"")));
|
"\"citus.replication_model\" is set to \"streaming\"")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the types are the same, we're good. If not, we still check if there
|
||||||
|
* is any coercion path between the types.
|
||||||
|
*/
|
||||||
sourceDistributionColumnType = sourceDistributionColumn->vartype;
|
sourceDistributionColumnType = sourceDistributionColumn->vartype;
|
||||||
if (sourceDistributionColumnType != distributionColumnType)
|
if (sourceDistributionColumnType != distributionColumnType)
|
||||||
{
|
{
|
||||||
char *functionName = get_func_name(functionOid);
|
Oid coercionFuncId = InvalidOid;
|
||||||
char *sourceRelationName = get_rel_name(sourceRelationId);
|
CoercionPathType coercionType = COERCION_PATH_NONE;
|
||||||
|
|
||||||
ereport(ERROR, (errmsg("cannot colocate function \"%s\" and table \"%s\" "
|
coercionType =
|
||||||
"because distribution column types don't match",
|
find_coercion_pathway(distributionColumnType, sourceDistributionColumnType,
|
||||||
sourceRelationName, functionName)));
|
COERCION_EXPLICIT, &coercionFuncId);
|
||||||
|
|
||||||
|
/* if there is no path for coercion, error out*/
|
||||||
|
if (coercionType == COERCION_PATH_NONE)
|
||||||
|
{
|
||||||
|
char *functionName = get_func_name(functionOid);
|
||||||
|
char *sourceRelationName = get_rel_name(sourceRelationId);
|
||||||
|
|
||||||
|
ereport(ERROR, (errmsg("cannot colocate function \"%s\" and table \"%s\" "
|
||||||
|
"because distribution column types don't match and "
|
||||||
|
"there is no coercion path", sourceRelationName,
|
||||||
|
functionName)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,21 @@ CREATE FUNCTION add(integer, integer) RETURNS integer
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
IMMUTABLE
|
IMMUTABLE
|
||||||
RETURNS NULL ON NULL INPUT;
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
CREATE FUNCTION add_numeric(numeric, numeric) RETURNS numeric
|
||||||
|
AS 'select $1 + $2;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
CREATE FUNCTION add_text(text, text) RETURNS int
|
||||||
|
AS 'select $1::int + $2::int;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
CREATE FUNCTION add_polygons(polygon, polygon) RETURNS int
|
||||||
|
AS 'select 1'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
-- Test some combination of functions without ddl propagation
|
-- Test some combination of functions without ddl propagation
|
||||||
-- This will prevent the workers from having those types created. They are
|
-- This will prevent the workers from having those types created. They are
|
||||||
-- created just-in-time on function distribution
|
-- created just-in-time on function distribution
|
||||||
|
@ -225,7 +240,8 @@ SELECT create_distributed_function('add_with_param_names(int, int)', '$1', coloc
|
||||||
ERROR: cannot colocate function "add_with_param_names" and table "replicated_table_func_test"
|
ERROR: cannot colocate function "add_with_param_names" and table "replicated_table_func_test"
|
||||||
DETAIL: Citus currently only supports colocating function with distributed tables that are created using streaming replication model.
|
DETAIL: Citus currently only supports colocating function with distributed tables that are created using streaming replication model.
|
||||||
HINT: When distributing tables make sure that "citus.replication_model" is set to "streaming"
|
HINT: When distributing tables make sure that "citus.replication_model" is set to "streaming"
|
||||||
-- a function cannot be colocated with a different distribution argument type
|
-- a function can be colocated with a different distribution argument type
|
||||||
|
-- as long as there is a coercion path
|
||||||
SET citus.shard_replication_factor TO 1;
|
SET citus.shard_replication_factor TO 1;
|
||||||
CREATE TABLE replicated_table_func_test_2 (a bigint);
|
CREATE TABLE replicated_table_func_test_2 (a bigint);
|
||||||
SET citus.replication_model TO "streaming";
|
SET citus.replication_model TO "streaming";
|
||||||
|
@ -236,7 +252,11 @@ SELECT create_distributed_table('replicated_table_func_test_2', 'a');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT create_distributed_function('add_with_param_names(int, int)', 'val1', colocate_with:='replicated_table_func_test_2');
|
SELECT create_distributed_function('add_with_param_names(int, int)', 'val1', colocate_with:='replicated_table_func_test_2');
|
||||||
ERROR: cannot colocate function "replicated_table_func_test_2" and table "add_with_param_names" because distribution column types don't match
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
-- colocate_with cannot be used without distribution key
|
-- colocate_with cannot be used without distribution key
|
||||||
SELECT create_distributed_function('add_with_param_names(int, int)', colocate_with:='replicated_table_func_test_2');
|
SELECT create_distributed_function('add_with_param_names(int, int)', colocate_with:='replicated_table_func_test_2');
|
||||||
ERROR: cannot distribute the function "add_with_param_names" since the distribution argument is not valid
|
ERROR: cannot distribute the function "add_with_param_names" since the distribution argument is not valid
|
||||||
|
@ -297,6 +317,50 @@ WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- function with a numeric dist. arg can be colocated with int
|
||||||
|
-- column of a distributed table. In general, if there is a coercion
|
||||||
|
-- path, we rely on postgres for implicit coersions, and users for explicit coersions
|
||||||
|
-- to coerce the values
|
||||||
|
SELECT create_distributed_function('add_numeric(numeric, numeric)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_dist_partition.colocationid = objects.colocationid as table_and_function_colocated
|
||||||
|
FROM pg_dist_partition, citus.pg_dist_object as objects
|
||||||
|
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
||||||
|
objects.objid = 'add_numeric(numeric, numeric)'::regprocedure;
|
||||||
|
table_and_function_colocated
|
||||||
|
------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_function('add_text(text, text)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_dist_partition.colocationid = objects.colocationid as table_and_function_colocated
|
||||||
|
FROM pg_dist_partition, citus.pg_dist_object as objects
|
||||||
|
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
||||||
|
objects.objid = 'add_text(text, text)'::regprocedure;
|
||||||
|
table_and_function_colocated
|
||||||
|
------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- cannot distribute function because there is no
|
||||||
|
-- coercion path from polygon to int
|
||||||
|
SELECT create_distributed_function('add_polygons(polygon,polygon)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
ERROR: cannot colocate function "replicated_table_func_test_4" and table "add_polygons" because distribution column types don't match and there is no coercion path
|
||||||
|
-- without the colocate_with, the function errors out since there is no
|
||||||
|
-- default colocation group
|
||||||
|
SET citus.shard_count TO 55;
|
||||||
|
SELECT create_distributed_function('add_with_param_names(int, int)', 'val1');
|
||||||
|
ERROR: cannot distribute the function "add_with_param_names" since there is no table to colocate with
|
||||||
|
HINT: Provide a distributed table via "colocate_with" option to create_distributed_function()
|
||||||
-- clear objects
|
-- clear objects
|
||||||
SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary';
|
SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary';
|
||||||
stop_metadata_sync_to_node
|
stop_metadata_sync_to_node
|
||||||
|
|
|
@ -15,6 +15,24 @@ CREATE FUNCTION add(integer, integer) RETURNS integer
|
||||||
IMMUTABLE
|
IMMUTABLE
|
||||||
RETURNS NULL ON NULL INPUT;
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
|
||||||
|
CREATE FUNCTION add_numeric(numeric, numeric) RETURNS numeric
|
||||||
|
AS 'select $1 + $2;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
|
||||||
|
CREATE FUNCTION add_text(text, text) RETURNS int
|
||||||
|
AS 'select $1::int + $2::int;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
|
||||||
|
CREATE FUNCTION add_polygons(polygon, polygon) RETURNS int
|
||||||
|
AS 'select 1'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
|
||||||
-- Test some combination of functions without ddl propagation
|
-- Test some combination of functions without ddl propagation
|
||||||
-- This will prevent the workers from having those types created. They are
|
-- This will prevent the workers from having those types created. They are
|
||||||
-- created just-in-time on function distribution
|
-- created just-in-time on function distribution
|
||||||
|
@ -127,7 +145,8 @@ SET citus.replication_model TO "statement";
|
||||||
SELECT create_distributed_table('replicated_table_func_test', 'a');
|
SELECT create_distributed_table('replicated_table_func_test', 'a');
|
||||||
SELECT create_distributed_function('add_with_param_names(int, int)', '$1', colocate_with:='replicated_table_func_test');
|
SELECT create_distributed_function('add_with_param_names(int, int)', '$1', colocate_with:='replicated_table_func_test');
|
||||||
|
|
||||||
-- a function cannot be colocated with a different distribution argument type
|
-- a function can be colocated with a different distribution argument type
|
||||||
|
-- as long as there is a coercion path
|
||||||
SET citus.shard_replication_factor TO 1;
|
SET citus.shard_replication_factor TO 1;
|
||||||
CREATE TABLE replicated_table_func_test_2 (a bigint);
|
CREATE TABLE replicated_table_func_test_2 (a bigint);
|
||||||
SET citus.replication_model TO "streaming";
|
SET citus.replication_model TO "streaming";
|
||||||
|
@ -166,6 +185,31 @@ FROM pg_dist_partition, citus.pg_dist_object as objects
|
||||||
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
||||||
objects.objid = 'add_with_param_names(int, int)'::regprocedure;
|
objects.objid = 'add_with_param_names(int, int)'::regprocedure;
|
||||||
|
|
||||||
|
-- function with a numeric dist. arg can be colocated with int
|
||||||
|
-- column of a distributed table. In general, if there is a coercion
|
||||||
|
-- path, we rely on postgres for implicit coersions, and users for explicit coersions
|
||||||
|
-- to coerce the values
|
||||||
|
SELECT create_distributed_function('add_numeric(numeric, numeric)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
SELECT pg_dist_partition.colocationid = objects.colocationid as table_and_function_colocated
|
||||||
|
FROM pg_dist_partition, citus.pg_dist_object as objects
|
||||||
|
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
||||||
|
objects.objid = 'add_numeric(numeric, numeric)'::regprocedure;
|
||||||
|
|
||||||
|
SELECT create_distributed_function('add_text(text, text)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
SELECT pg_dist_partition.colocationid = objects.colocationid as table_and_function_colocated
|
||||||
|
FROM pg_dist_partition, citus.pg_dist_object as objects
|
||||||
|
WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND
|
||||||
|
objects.objid = 'add_text(text, text)'::regprocedure;
|
||||||
|
|
||||||
|
-- cannot distribute function because there is no
|
||||||
|
-- coercion path from polygon to int
|
||||||
|
SELECT create_distributed_function('add_polygons(polygon,polygon)', '$1', colocate_with:='replicated_table_func_test_4');
|
||||||
|
|
||||||
|
-- without the colocate_with, the function errors out since there is no
|
||||||
|
-- default colocation group
|
||||||
|
SET citus.shard_count TO 55;
|
||||||
|
SELECT create_distributed_function('add_with_param_names(int, int)', 'val1');
|
||||||
|
|
||||||
-- clear objects
|
-- clear objects
|
||||||
SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary';
|
SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue