From 4f8042085c4fb1df06cce7109b89c27a035b233c Mon Sep 17 00:00:00 2001 From: Murat Tuncer Date: Mon, 8 Oct 2018 12:34:20 -0700 Subject: [PATCH] Fix drop schema in mx with partitioned tables Drop schema command fails in mx mode if there is a partitioned table with active partitions. This is due to fact that sql drop trigger receives all the dropped objects including partitions. When we call drop table on parent partition, it also drops the partitions on the mx node. This causes the drop table command on partitions to fail on mx node because they are already dropped when the partition parent was dropped. With this work we did not require the table to exist on worker_drop_distributed_table. --- citus.control | 2 +- src/backend/distributed/Makefile | 4 ++- .../distributed/citus--8.0-7--8.0-8.sql | 15 +++++++++ src/backend/distributed/citus.control | 2 +- .../distributed/metadata/metadata_sync.c | 2 +- .../distributed/worker/worker_drop_protocol.c | 13 ++++++-- src/include/distributed/metadata_sync.h | 2 +- src/test/regress/expected/multi_extension.out | 1 + .../regress/expected/multi_metadata_sync.out | 12 +++---- .../expected/multi_mx_partitioning.out | 19 ++++++++++++ .../expected/multi_mx_partitioning_0.out | 31 +++++++++++++++++++ .../multi_unsupported_worker_operations.out | 4 +-- src/test/regress/sql/multi_extension.sql | 1 + .../regress/sql/multi_mx_partitioning.sql | 17 ++++++++++ .../multi_unsupported_worker_operations.sql | 4 +-- 15 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 src/backend/distributed/citus--8.0-7--8.0-8.sql diff --git a/citus.control b/citus.control index 22a7c1eb4..86f89c819 100644 --- a/citus.control +++ b/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '8.0-7' +default_version = '8.0-8' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/Makefile b/src/backend/distributed/Makefile index f126a903c..4b7f93ca5 100644 --- a/src/backend/distributed/Makefile +++ b/src/backend/distributed/Makefile @@ -17,7 +17,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \ 7.3-1 7.3-2 7.3-3 \ 7.4-1 7.4-2 7.4-3 \ 7.5-1 7.5-2 7.5-3 7.5-4 7.5-5 7.5-6 7.5-7 \ - 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 + 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 # All citus--*.sql files in the source directory DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql)) @@ -229,6 +229,8 @@ $(EXTENSION)--8.0-6.sql: $(EXTENSION)--8.0-5.sql $(EXTENSION)--8.0-5--8.0-6.sql cat $^ > $@ $(EXTENSION)--8.0-7.sql: $(EXTENSION)--8.0-6.sql $(EXTENSION)--8.0-6--8.0-7.sql cat $^ > $@ +$(EXTENSION)--8.0-8.sql: $(EXTENSION)--8.0-7.sql $(EXTENSION)--8.0-7--8.0-8.sql + cat $^ > $@ NO_PGXS = 1 diff --git a/src/backend/distributed/citus--8.0-7--8.0-8.sql b/src/backend/distributed/citus--8.0-7--8.0-8.sql new file mode 100644 index 000000000..a84b07924 --- /dev/null +++ b/src/backend/distributed/citus--8.0-7--8.0-8.sql @@ -0,0 +1,15 @@ +/* citus--8.0-7--8.0-8 */ +SET search_path = 'pg_catalog'; + +DROP FUNCTION IF EXISTS pg_catalog.worker_drop_distributed_table(logicalrelid Oid); + + +CREATE FUNCTION worker_drop_distributed_table(table_name text) + RETURNS VOID + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$worker_drop_distributed_table$$; + +COMMENT ON FUNCTION worker_drop_distributed_table(table_name text) + IS 'drop the distributed table and its reference from metadata tables'; + +RESET search_path; diff --git a/src/backend/distributed/citus.control b/src/backend/distributed/citus.control index 22a7c1eb4..86f89c819 100644 --- a/src/backend/distributed/citus.control +++ b/src/backend/distributed/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '8.0-7' +default_version = '8.0-8' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 4adde5852..fa6f2ee6e 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -583,7 +583,7 @@ DistributionDeleteCommand(char *schemaName, char *tableName) distributedRelationName = quote_qualified_identifier(schemaName, tableName); appendStringInfo(deleteDistributionCommand, - "SELECT worker_drop_distributed_table(%s::regclass)", + "SELECT worker_drop_distributed_table(%s)", quote_literal_cstr(distributedRelationName)); return deleteDistributionCommand->data; diff --git a/src/backend/distributed/worker/worker_drop_protocol.c b/src/backend/distributed/worker/worker_drop_protocol.c index 92ecee7a4..ae826595e 100644 --- a/src/backend/distributed/worker/worker_drop_protocol.c +++ b/src/backend/distributed/worker/worker_drop_protocol.c @@ -21,8 +21,10 @@ #include "distributed/citus_ruleutils.h" #include "distributed/distribution_column.h" #include "distributed/master_metadata_utility.h" +#include "distributed/master_protocol.h" #include "distributed/metadata_cache.h" #include "foreign/foreign.h" +#include "utils/builtins.h" #include "utils/fmgroids.h" @@ -45,8 +47,8 @@ PG_FUNCTION_INFO_V1(worker_drop_distributed_table); Datum worker_drop_distributed_table(PG_FUNCTION_ARGS) { - Datum relationIdDatum = PG_GETARG_OID(0); - Oid relationId = DatumGetObjectId(relationIdDatum); + text *relationName = PG_GETARG_TEXT_P(0); + Oid relationId = ResolveRelationId(relationName, true); ObjectAddress distributedTableObject = { InvalidOid, InvalidOid, 0 }; Relation distributedRelation = NULL; @@ -57,6 +59,13 @@ worker_drop_distributed_table(PG_FUNCTION_ARGS) CheckCitusVersion(ERROR); EnsureSuperUser(); + if (!OidIsValid(relationId)) + { + ereport(NOTICE, (errmsg("relation %s does not exist, skipping", + text_to_cstring(relationName)))); + PG_RETURN_VOID(); + } + shardList = LoadShardList(relationId); /* first check the relation type */ diff --git a/src/include/distributed/metadata_sync.h b/src/include/distributed/metadata_sync.h index 41b346ae9..05f569a05 100644 --- a/src/include/distributed/metadata_sync.h +++ b/src/include/distributed/metadata_sync.h @@ -41,7 +41,7 @@ extern void CreateTableMetadataOnWorkers(Oid relationId); #define DELETE_ALL_NODES "TRUNCATE pg_dist_node CASCADE" #define REMOVE_ALL_CLUSTERED_TABLES_COMMAND \ - "SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition" + "SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition" #define DISABLE_DDL_PROPAGATION "SET citus.enable_ddl_propagation TO 'off'" #define ENABLE_DDL_PROPAGATION "SET citus.enable_ddl_propagation TO 'on'" #define WORKER_APPLY_SEQUENCE_COMMAND "SELECT worker_apply_sequence_command (%s)" diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 09c841924..b191afe2e 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -150,6 +150,7 @@ ALTER EXTENSION citus UPDATE TO '8.0-4'; ALTER EXTENSION citus UPDATE TO '8.0-5'; ALTER EXTENSION citus UPDATE TO '8.0-6'; ALTER EXTENSION citus UPDATE TO '8.0-7'; +ALTER EXTENSION citus UPDATE TO '8.0-8'; -- show running version SHOW citus.version; citus.version diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index a007a9e7e..44e9e42f6 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -29,7 +29,7 @@ SELECT * FROM pg_dist_partition WHERE partmethod='h' AND repmodel='s'; SELECT unnest(master_metadata_snapshot()); unnest ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') (3 rows) @@ -55,7 +55,7 @@ UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::re SELECT unnest(master_metadata_snapshot()); unnest -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_test_table_col_3_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 NO CYCLE') @@ -74,7 +74,7 @@ CREATE INDEX mx_index ON mx_test_table(col_2); SELECT unnest(master_metadata_snapshot()); unnest -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_test_table_col_3_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 NO CYCLE') @@ -97,7 +97,7 @@ HINT: Connect to worker nodes directly to manually change schemas of affected o SELECT unnest(master_metadata_snapshot()); unnest ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') CREATE SCHEMA IF NOT EXISTS mx_testing_schema AUTHORIZATION postgres @@ -126,7 +126,7 @@ UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='non_mx_test_table' SELECT unnest(master_metadata_snapshot()); unnest ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') CREATE SCHEMA IF NOT EXISTS mx_testing_schema AUTHORIZATION postgres @@ -148,7 +148,7 @@ UPDATE pg_dist_partition SET partmethod='r' WHERE logicalrelid='non_mx_test_tabl SELECT unnest(master_metadata_snapshot()); unnest ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition + SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition TRUNCATE pg_dist_node CASCADE INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, isactive, noderole, nodecluster) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, TRUE, 'primary'::noderole, 'default'),(2, 2, 'localhost', 57638, 'default', FALSE, TRUE, 'primary'::noderole, 'default') CREATE SCHEMA IF NOT EXISTS mx_testing_schema AUTHORIZATION postgres diff --git a/src/test/regress/expected/multi_mx_partitioning.out b/src/test/regress/expected/multi_mx_partitioning.out index 3e91311fb..0cb0aa89b 100644 --- a/src/test/regress/expected/multi_mx_partitioning.out +++ b/src/test/regress/expected/multi_mx_partitioning.out @@ -272,3 +272,22 @@ DROP TABLE partitioning_test_2010; DROP TABLE partitioning_test; DROP TABLE IF EXISTS partitioning_test_2013; NOTICE: table "partitioning_test_2013" does not exist, skipping +-- test schema drop with partitioned tables +SET citus.replication_model TO 'streaming'; +SET citus.shard_replication_factor TO 1; +CREATE SCHEMA partition_test; +SET SEARCH_PATH TO partition_test; +CREATE TABLE partition_parent_table(a int, b int, c int) PARTITION BY RANGE (b); +SELECT create_distributed_table('partition_parent_table', 'a'); + create_distributed_table +-------------------------- + +(1 row) + +CREATE TABLE partition_0 PARTITION OF partition_parent_table FOR VALUES FROM (1) TO (10); +CREATE TABLE partition_1 PARTITION OF partition_parent_table FOR VALUES FROM (10) TO (20); +CREATE TABLE partition_2 PARTITION OF partition_parent_table FOR VALUES FROM (20) TO (30); +CREATE TABLE partition_3 PARTITION OF partition_parent_table FOR VALUES FROM (30) TO (40); +DROP SCHEMA partition_test CASCADE; +NOTICE: drop cascades to table partition_parent_table +RESET SEARCH_PATH; diff --git a/src/test/regress/expected/multi_mx_partitioning_0.out b/src/test/regress/expected/multi_mx_partitioning_0.out index f39462ead..efdc0f28b 100644 --- a/src/test/regress/expected/multi_mx_partitioning_0.out +++ b/src/test/regress/expected/multi_mx_partitioning_0.out @@ -237,3 +237,34 @@ ERROR: table "partitioning_test_2010" does not exist DROP TABLE partitioning_test; ERROR: table "partitioning_test" does not exist DROP TABLE IF EXISTS partitioning_test_2013; +-- test schema drop with partitioned tables +SET citus.replication_model TO 'streaming'; +SET citus.shard_replication_factor TO 1; +CREATE SCHEMA partition_test; +SET SEARCH_PATH TO partition_test; +CREATE TABLE partition_parent_table(a int, b int, c int) PARTITION BY RANGE (b); +ERROR: syntax error at or near "PARTITION" +LINE 1: ...TABLE partition_parent_table(a int, b int, c int) PARTITION ... + ^ +SELECT create_distributed_table('partition_parent_table', 'a'); +ERROR: relation "partition_parent_table" does not exist +LINE 1: SELECT create_distributed_table('partition_parent_table', 'a... + ^ +CREATE TABLE partition_0 PARTITION OF partition_parent_table FOR VALUES FROM (1) TO (10); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE partition_0 PARTITION OF partition_parent_table... + ^ +CREATE TABLE partition_1 PARTITION OF partition_parent_table FOR VALUES FROM (10) TO (20); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE partition_1 PARTITION OF partition_parent_table... + ^ +CREATE TABLE partition_2 PARTITION OF partition_parent_table FOR VALUES FROM (20) TO (30); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE partition_2 PARTITION OF partition_parent_table... + ^ +CREATE TABLE partition_3 PARTITION OF partition_parent_table FOR VALUES FROM (30) TO (40); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE TABLE partition_3 PARTITION OF partition_parent_table... + ^ +DROP SCHEMA partition_test CASCADE; +RESET SEARCH_PATH; diff --git a/src/test/regress/expected/multi_unsupported_worker_operations.out b/src/test/regress/expected/multi_unsupported_worker_operations.out index 2bce34f0b..573e35bf5 100644 --- a/src/test/regress/expected/multi_unsupported_worker_operations.out +++ b/src/test/regress/expected/multi_unsupported_worker_operations.out @@ -280,7 +280,7 @@ SELECT hasmetadata FROM pg_dist_node WHERE nodeport=:worker_2_port; (1 row) \c - - - :worker_2_port -SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition; +SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition; worker_drop_distributed_table ------------------------------- @@ -381,7 +381,7 @@ SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); \c - - - :worker_1_port DELETE FROM pg_dist_node; -SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition; +SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition; worker_drop_distributed_table ------------------------------- (0 rows) diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index c68db925d..284c435ad 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -150,6 +150,7 @@ ALTER EXTENSION citus UPDATE TO '8.0-4'; ALTER EXTENSION citus UPDATE TO '8.0-5'; ALTER EXTENSION citus UPDATE TO '8.0-6'; ALTER EXTENSION citus UPDATE TO '8.0-7'; +ALTER EXTENSION citus UPDATE TO '8.0-8'; -- show running version SHOW citus.version; diff --git a/src/test/regress/sql/multi_mx_partitioning.sql b/src/test/regress/sql/multi_mx_partitioning.sql index ba3dd3a32..6030980a0 100644 --- a/src/test/regress/sql/multi_mx_partitioning.sql +++ b/src/test/regress/sql/multi_mx_partitioning.sql @@ -173,3 +173,20 @@ DROP TABLE partitioning_test_2010; -- make sure we can drop partitioned table DROP TABLE partitioning_test; DROP TABLE IF EXISTS partitioning_test_2013; + +-- test schema drop with partitioned tables +SET citus.replication_model TO 'streaming'; +SET citus.shard_replication_factor TO 1; +CREATE SCHEMA partition_test; +SET SEARCH_PATH TO partition_test; + +CREATE TABLE partition_parent_table(a int, b int, c int) PARTITION BY RANGE (b); +SELECT create_distributed_table('partition_parent_table', 'a'); +CREATE TABLE partition_0 PARTITION OF partition_parent_table FOR VALUES FROM (1) TO (10); +CREATE TABLE partition_1 PARTITION OF partition_parent_table FOR VALUES FROM (10) TO (20); +CREATE TABLE partition_2 PARTITION OF partition_parent_table FOR VALUES FROM (20) TO (30); +CREATE TABLE partition_3 PARTITION OF partition_parent_table FOR VALUES FROM (30) TO (40); + +DROP SCHEMA partition_test CASCADE; +RESET SEARCH_PATH; + diff --git a/src/test/regress/sql/multi_unsupported_worker_operations.sql b/src/test/regress/sql/multi_unsupported_worker_operations.sql index b5a19c6fd..d67f4d9ab 100644 --- a/src/test/regress/sql/multi_unsupported_worker_operations.sql +++ b/src/test/regress/sql/multi_unsupported_worker_operations.sql @@ -156,7 +156,7 @@ SELECT hasmetadata FROM pg_dist_node WHERE nodeport=:worker_2_port; SELECT stop_metadata_sync_to_node('localhost', :worker_2_port); SELECT hasmetadata FROM pg_dist_node WHERE nodeport=:worker_2_port; \c - - - :worker_2_port -SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition; +SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition; DELETE FROM pg_dist_node; \c - - - :worker_1_port @@ -215,7 +215,7 @@ DROP TABLE mx_table_2; SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); \c - - - :worker_1_port DELETE FROM pg_dist_node; -SELECT worker_drop_distributed_table(logicalrelid) FROM pg_dist_partition; +SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition; \c - - - :master_port ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART :last_colocation_id;