diff --git a/changelogs/unreleased/gh-4544-segfault-if-i-delete-a-collation.md b/changelogs/unreleased/gh-4544-segfault-if-i-delete-a-collation.md new file mode 100644 index 0000000000000000000000000000000000000000..bd3d6c42079caa9ef8801b6f68967916f0e38bdb --- /dev/null +++ b/changelogs/unreleased/gh-4544-segfault-if-i-delete-a-collation.md @@ -0,0 +1,3 @@ +## bugfix/core + +* Fixed a crash when a collation used by a space was deleted (gh-4544). diff --git a/src/box/alter.cc b/src/box/alter.cc index 68a492356c6f24b801702137d092dec0bf9cbac0..e703b426b6adb9d59f2f347d0c1bcf254473dbec 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -868,6 +868,7 @@ alter_space_rollback(struct trigger *trigger, void * /* event */) space_swap_triggers(alter->new_space, alter->old_space); space_swap_constraint_ids(alter->new_space, alter->old_space); space_reattach_constraints(alter->old_space); + space_pin_collations(alter->old_space); space_cache_replace(alter->new_space, alter->old_space); alter_space_delete(alter); return 0; @@ -995,6 +996,7 @@ alter_space_do(struct txn_stmt *stmt, struct alter_space *alter) */ space_cache_replace(alter->old_space, alter->new_space); space_detach_constraints(alter->old_space); + space_unpin_collations(alter->old_space); /* * Install transaction commit/rollback triggers to either * finish or rollback the DDL depending on the results of @@ -1702,6 +1704,7 @@ on_drop_space_rollback(struct trigger *trigger, void *event) struct space *space = (struct space *)trigger->data; space_cache_replace(NULL, space); space_reattach_constraints(space); + space_pin_collations(space); return 0; } @@ -2197,6 +2200,7 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) * on commit or reattached on rollback. */ space_detach_constraints(old_space); + space_unpin_collations(old_space); /** * The space must be deleted from the space * cache right away to achieve linearisable @@ -3578,10 +3582,6 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event) txn_alter_trigger_new(on_drop_collation_rollback, NULL); if (on_commit == NULL || on_rollback == NULL) return -1; - /* - * TODO: Check that no index uses the collation - * identifier. - */ uint32_t out; if (tuple_field_u32(old_tuple, BOX_COLLATION_FIELD_ID, &out) != 0) return -1; @@ -3603,6 +3603,20 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event) old_coll_id->owner_id, SC_COLLATION, PRIV_D) != 0) return -1; + /* + * Don't allow user to drop a collation identifier that is + * currently used. + */ + enum coll_id_holder_type pinned_type; + if (coll_id_is_pinned(old_coll_id, &pinned_type)) { + const char *type_str = + coll_id_holder_type_strs[pinned_type]; + diag_set(ClientError, ER_DROP_COLLATION, + old_coll_id->name, + tt_sprintf("collation is referenced by %s", + type_str)); + return -1; + } /* * Set on_commit/on_rollback triggers after * deletion from the cache to make trigger logic diff --git a/src/box/coll_id.c b/src/box/coll_id.c index 5abeaed21f1a0c311e07bf94265be869412c7d7c..3ea61ddcc8dceb09853a135b62b5cd8d8c83388a 100644 --- a/src/box/coll_id.c +++ b/src/box/coll_id.c @@ -53,12 +53,14 @@ coll_id_new(const struct coll_id_def *def) coll_id->name_len = def->name_len; memcpy(coll_id->name, def->name, def->name_len); coll_id->name[coll_id->name_len] = 0; + rlist_create(&coll_id->cache_pin_list); return coll_id; } void coll_id_delete(struct coll_id *coll_id) { + assert(rlist_empty(&coll_id->cache_pin_list)); coll_unref(coll_id->coll); free(coll_id); } diff --git a/src/box/coll_id.h b/src/box/coll_id.h index 82e50d11beac563d5772c108b846bfba8303b685..e436150a1ac5a94fc35f3820595f084cd7f409e8 100644 --- a/src/box/coll_id.h +++ b/src/box/coll_id.h @@ -32,6 +32,7 @@ */ #include <stddef.h> #include <stdint.h> +#include "small/rlist.h" #if defined(__cplusplus) extern "C" { @@ -56,6 +57,11 @@ struct coll_id { uint32_t owner_id; /** Collation object. */ struct coll *coll; + /** + * Holders of the same collation identifier are linked into ring list by + * `coll_id_cache_holder::in_coll_id`. + */ + struct rlist cache_pin_list; /** Collation name. */ size_t name_len; char name[0]; diff --git a/src/box/coll_id_cache.c b/src/box/coll_id_cache.c index d02d3ea1656da42f8242cf2ff604975f472206d5..8aab57d5f5ffd28b430662dfa867f078c8e35782 100644 --- a/src/box/coll_id_cache.c +++ b/src/box/coll_id_cache.c @@ -38,6 +38,11 @@ static struct mh_strnptr_t *coll_cache_name = NULL; /** mhash table (id -> collation) */ static struct mh_i32ptr_t *coll_id_cache = NULL; +const char *coll_id_holder_type_strs[COLL_ID_HOLDER_MAX] = { + [COLL_ID_HOLDER_SPACE_FORMAT] = "space format", + [COLL_ID_HOLDER_INDEX] = "index", +}; + void coll_id_cache_init(void) { @@ -73,8 +78,9 @@ coll_id_cache_replace(struct coll_id *coll_id, struct coll_id **replaced_id) } void -coll_id_cache_delete(const struct coll_id *coll_id) +coll_id_cache_delete(struct coll_id *coll_id) { + assert(rlist_empty(&coll_id->cache_pin_list)); mh_int_t id_i = mh_i32ptr_find(coll_id_cache, coll_id->id, NULL); mh_i32ptr_del(coll_id_cache, id_i, NULL); mh_int_t name_i = mh_strnptr_find_str(coll_cache_name, coll_id->name, @@ -102,3 +108,43 @@ coll_by_name(const char *name, uint32_t len) return NULL; return mh_strnptr_node(coll_cache_name, pos)->val; } + +void +coll_id_pin(struct coll_id *coll_id, struct coll_id_cache_holder *holder, + enum coll_id_holder_type type) +{ + assert(coll_by_id(coll_id->id) != NULL); + holder->coll_id = coll_id; + holder->type = type; + rlist_add_tail(&coll_id->cache_pin_list, &holder->in_coll_id); +} + +void +coll_id_unpin(struct coll_id_cache_holder *holder) +{ + assert(coll_by_id(holder->coll_id->id) != NULL); +#ifndef NDEBUG + /* Paranoid check that the coll_id is pinned by holder. */ + bool is_in_list = false; + struct rlist *tmp; + rlist_foreach(tmp, &holder->coll_id->cache_pin_list) { + is_in_list = is_in_list || tmp == &holder->in_coll_id; + } + assert(is_in_list); +#endif + rlist_del(&holder->in_coll_id); + holder->coll_id = NULL; +} + +bool +coll_id_is_pinned(struct coll_id *coll_id, enum coll_id_holder_type *type) +{ + assert(coll_by_id(coll_id->id) != NULL); + if (rlist_empty(&coll_id->cache_pin_list)) + return false; + struct coll_id_cache_holder *h = + rlist_first_entry(&coll_id->cache_pin_list, + struct coll_id_cache_holder, in_coll_id); + *type = h->type; + return true; +} diff --git a/src/box/coll_id_cache.h b/src/box/coll_id_cache.h index d5325b6aee2279c333a8c1b3a37f760bcc02bcbd..60576cc2a53d350ad28c034c5c31c492c971bed9 100644 --- a/src/box/coll_id_cache.h +++ b/src/box/coll_id_cache.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. */ #include <stdint.h> +#include <stdbool.h> +#include "small/rlist.h" #if defined(__cplusplus) extern "C" { @@ -38,6 +40,39 @@ extern "C" { struct coll_id; +/** + * Type of a holder that can pin coll_id. See `struct coll_id_cache_holder`. + */ +enum coll_id_holder_type { + COLL_ID_HOLDER_SPACE_FORMAT, + COLL_ID_HOLDER_INDEX, + COLL_ID_HOLDER_MAX, +}; + +/** + * Lowercase name of each type. + */ +extern const char *coll_id_holder_type_strs[COLL_ID_HOLDER_MAX]; + +/** + * Definition of a holder that pinned some coll_id. Pinning of a coll_id is + * a mechanism that is designed for preventing of deletion of some coll_id from + * coll_id cache by storing links to holders that prevented that. + */ +struct coll_id_cache_holder { + /** Link in `space::coll_id_holders`. */ + struct rlist in_space; + /** Link in `coll_id::cache_pin_list`. */ + struct rlist in_coll_id; + /** Actual pointer to coll_id. */ + struct coll_id *coll_id; + /** + * Type of holder, mostly for better error generation, but also can be + * used for proper container_of application. + */ + enum coll_id_holder_type type; +}; + /** * Create global hash tables. */ @@ -62,7 +97,7 @@ coll_id_cache_replace(struct coll_id *coll_id, struct coll_id **replaced_id); * @param coll_id Collation to delete. */ void -coll_id_cache_delete(const struct coll_id *coll_id); +coll_id_cache_delete(struct coll_id *coll_id); /** * Find a collation object by its id. @@ -76,6 +111,32 @@ coll_by_id(uint32_t id); struct coll_id * coll_by_name(const char *name, uint32_t len); +/** + * Register that there is a `holder` of type `type` that is dependent on + * coll_id. coll_id must be in cache (asserted). + * If coll_id has holders, it must not be deleted (asserted). + */ +void +coll_id_pin(struct coll_id *coll_id, struct coll_id_cache_holder *holder, + enum coll_id_holder_type type); + +/** + * Notify that `holder` does not depend anymore on coll_id. + * coll_id must be in cache (asserted). + * If coll_id has no holders, it can be deleted. + */ +void +coll_id_unpin(struct coll_id_cache_holder *holder); + +/** + * Check whether coll_id has holders or not. + * If it has, `type` argument is set to the first holder's type. + * coll_id must be in cache (asserted). + * If coll_id has holders, it must not be deleted (asserted). + */ +bool +coll_id_is_pinned(struct coll_id *coll_id, enum coll_id_holder_type *type); + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/box/errcode.h b/src/box/errcode.h index bd909fae3ff854c2b69bb7eb4a5a90a87c1579ca..919353395afacb8b4f89be658a6f3debfd0aa6e4 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -225,7 +225,7 @@ struct errcode_record { /*170 */_(ER_CONSTRAINT_EXISTS, "%s constraint '%s' already exists in space '%s'") \ /*171 */_(ER_SQL_TYPE_MISMATCH, "Type mismatch: can not convert %s to %s") \ /*172 */_(ER_ROWID_OVERFLOW, "Rowid is overflowed: too many entries in ephemeral space") \ - /*173 */_(ER_DROP_COLLATION, "Can't drop collation %s : %s") \ + /*173 */_(ER_DROP_COLLATION, "Can't drop collation '%s': %s") \ /*174 */_(ER_ILLEGAL_COLLATION_MIX, "Illegal mix of collations") \ /*175 */_(ER_SQL_NO_SUCH_PRAGMA, "Pragma '%s' does not exist") \ /*176 */_(ER_SQL_CANT_RESOLVE_FIELD, "Can’t resolve field '%s'") \ diff --git a/src/box/index_def.h b/src/box/index_def.h index becd764867e8b6b62258d32aa2c66de137c0ed7a..0506f94990422054cfe63c5223ad90164f284b94 100644 --- a/src/box/index_def.h +++ b/src/box/index_def.h @@ -362,7 +362,7 @@ index_def_list_add(struct rlist *index_def_list, struct index_def *index_def) } /** - * Create a new index definition definition. + * Create a new index definition. * * @param key_def key definition, must be fully built * @param pk_def primary key definition, pass non-NULL diff --git a/src/box/space.c b/src/box/space.c index dd8f4003939cca5aa1169558d1828c230f26ae17..b7fbf6fdbc5bf10c0248031218e2e03e20ba9bc4 100644 --- a/src/box/space.c +++ b/src/box/space.c @@ -53,6 +53,7 @@ #include "tuple_constraint_func.h" #include "tuple_constraint_fkey.h" #include "wal_ext.h" +#include "coll_id_cache.h" int access_check_space(struct space *space, user_access_t access) @@ -237,6 +238,53 @@ space_cleanup_constraints(struct space *space) return 0; } +/** + * Pin collation identifier with `id` in the cache, so that it can't be deleted. + */ +static void +space_pin_collations_helper(struct space *space, uint32_t id, + enum coll_id_holder_type holder_type) +{ + if (id == COLL_NONE) + return; + struct coll_id *coll_id = coll_by_id(id); + assert(coll_id != NULL); + struct coll_id_cache_holder *h = xmalloc(sizeof(*h)); + rlist_add_tail_entry(&space->coll_id_holders, h, in_space); + coll_id_pin(coll_id, h, holder_type); +} + +void +space_pin_collations(struct space *space) +{ + struct tuple_format *format = space->format; + for (uint32_t i = 0; i < tuple_format_field_count(format); i++) { + struct tuple_field *field = tuple_format_field(format, i); + space_pin_collations_helper(space, field->coll_id, + COLL_ID_HOLDER_SPACE_FORMAT); + } + + for (uint32_t i = 0; i < space->index_count; i++) { + struct key_def *key_def = space->index[i]->def->key_def; + for (uint32_t i = 0; i < key_def->part_count; i++) { + struct key_part *part = &key_def->parts[i]; + space_pin_collations_helper(space, part->coll_id, + COLL_ID_HOLDER_INDEX); + } + } +} + +void +space_unpin_collations(struct space *space) +{ + struct coll_id_cache_holder *h, *tmp; + rlist_foreach_entry_safe(h, &space->coll_id_holders, in_space, tmp) { + coll_id_unpin(h); + free(h); + } + rlist_create(&space->coll_id_holders); +} + int space_create(struct space *space, struct engine *engine, const struct space_vtab *vtab, struct space_def *def, @@ -265,6 +313,7 @@ space_create(struct space *space, struct engine *engine, space->index_id_max = index_id_max; rlist_create(&space->before_replace); rlist_create(&space->on_replace); + rlist_create(&space->coll_id_holders); space->run_triggers = true; space->format = format; @@ -303,6 +352,7 @@ space_create(struct space *space, struct engine *engine, rlist_create(&space->space_cache_pin_list); if (space_init_constraints(space) != 0) goto fail_free_indexes; + space_pin_collations(space); /* * Check if there are unique indexes that are contained @@ -348,6 +398,7 @@ space_create(struct space *space, struct engine *engine, space_cleanup_constraints(space); tuple_format_unref(space->format); } + space_unpin_collations(space); return -1; } @@ -403,6 +454,7 @@ space_delete(struct space *space) space_cleanup_constraints(space); tuple_format_unref(space->format); } + space_unpin_collations(space); trigger_destroy(&space->before_replace); trigger_destroy(&space->on_replace); if (space->upgrade != NULL) diff --git a/src/box/space.h b/src/box/space.h index 79bca53aa7bec4d61ad09e13a08bc0bfcd761cf3..9a101caf0ec7937cb7f53f2653b4fe0d114d3db2 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -248,6 +248,11 @@ struct space { * (i.e. if not NULL WAL entries may contain extra fields). */ struct space_wal_ext *wal_ext; + /** + * List of collation identifier holders. + * Linked by `coll_id_cache_holder::in_space`. + */ + struct rlist coll_id_holders; }; /** Space alter statement. */ @@ -272,6 +277,19 @@ space_detach_constraints(struct space *space); void space_reattach_constraints(struct space *space); +/** + * Pin in cache the collation identifiers that are referenced by space format + * and/or indexes, so that they can't be deleted. + */ +void +space_pin_collations(struct space *space); + +/** + * Unpin collation identifiers. + */ +void +space_unpin_collations(struct space *space); + /** Initialize a base space instance. */ int space_create(struct space *space, struct engine *engine, diff --git a/test/box/net.box_is_nullable_gh-3256.result b/test/box/net.box_is_nullable_gh-3256.result index d8f9557afc719c1ad1661eedf2dc9e266c175a47..df95ad2c663d9579aca5a7a30f06c1fc160bcdc8 100644 --- a/test/box/net.box_is_nullable_gh-3256.result +++ b/test/box/net.box_is_nullable_gh-3256.result @@ -79,10 +79,10 @@ parts[1].collation == 'test' c:close() --- ... -box.internal.collation.drop('test') +space:drop() --- ... -space:drop() +box.internal.collation.drop('test') --- ... c.state diff --git a/test/box/net.box_is_nullable_gh-3256.test.lua b/test/box/net.box_is_nullable_gh-3256.test.lua index 695c30ae22478031b55d5c66bcb024259f4832cd..926681af6d216dfea4930dfd94bb0c29b3e57f1f 100644 --- a/test/box/net.box_is_nullable_gh-3256.test.lua +++ b/test/box/net.box_is_nullable_gh-3256.test.lua @@ -26,7 +26,7 @@ parts[1].type == 'string' parts[1].is_nullable == false parts[1].collation == 'test' c:close() -box.internal.collation.drop('test') space:drop() +box.internal.collation.drop('test') c.state c = nil diff --git a/test/engine-luatest/gh_4544_collation_drop_test.lua b/test/engine-luatest/gh_4544_collation_drop_test.lua index 4f4f5324055ea0abf30885dc88f37319fcc273aa..166630dbdd156c58a35af6da28fecad9630d6684 100644 --- a/test/engine-luatest/gh_4544_collation_drop_test.lua +++ b/test/engine-luatest/gh_4544_collation_drop_test.lua @@ -60,3 +60,294 @@ g1.test_keydef_replace_coll_different = function(cg) box.internal.collation.drop(new_name) end) end + +local g2 = t.group('gh-4544-2', {{engine = 'memtx'}, {engine = 'vinyl'}}) +g2.before_all(before_all) +g2.after_all(after_all) + +-- Pin/unpin collation by the space format, but not by the index. +g2.test_coll_pin_format = function(cg) + local coll_name = 'my coll 1' + + local function init() + cg.server:exec(function(engine, coll_name) + box.internal.collation.create(coll_name, 'ICU', 'ru-RU', {}) + local s = box.schema.space.create('test', {engine = engine}) + s:format({{name = 'p'}, + {name = 's', type = 'string', collation = coll_name}}) + s:create_index('pk') + end, {cg.params.engine, coll_name}) + end + + local function check_references() + cg.server:exec(function(coll_name) + t.assert_error_msg_equals( + "Can't drop collation '" .. coll_name .. "': collation is " .. + "referenced by space format", + box.internal.collation.drop, coll_name) + end, {coll_name}) + end + + -- Check that collation can not be dropped. + init() + check_references() + + cg.server:restart() + check_references() + + cg.server:eval('box.snapshot()') + cg.server:restart() + check_references() + + -- Check that collation is unpinned on space drop. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) + + -- Check that collation can be dropped in one transaction with space drop. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.commit() + end, {coll_name}) + + -- Check that collation is still pinned after rollback. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.rollback() + end, {coll_name}) + check_references() + + -- Check that collation is unpinned on space alter. + cg.server:exec(function(coll_name) + box.space.test:alter({format = {{name = 'p'}, {name = 's'}}}) + box.internal.collation.drop(coll_name) + box.space.test:drop() + end, {coll_name}) + + -- Check that collation can be dropped in one transaction with space alter. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:alter({format = {{name = 'p'}, {name = 's'}}}) + box.internal.collation.drop(coll_name) + box.commit() + box.space.test:drop() + end, {coll_name}) + + -- Check that collation is still pinned after rollback. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:alter({format = {{name = 'p'}, {name = 's'}}}) + box.internal.collation.drop(coll_name) + box.rollback() + end, {coll_name}) + check_references() + + -- Cleanup. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) +end + +-- Pin/unpin collation by the index, but not by the space format. +g2.test_coll_pin_index = function(cg) + local coll_name = 'my coll 2' + + local function init() + cg.server:exec(function(engine, coll_name) + box.internal.collation.create(coll_name, 'ICU', 'ru-RU', {}) + local s = box.schema.space.create('test', {engine = engine}) + s:create_index('pk') + s:create_index('sk', {parts = {2, 'string', + collation = coll_name}}) + end, {cg.params.engine, coll_name}) + end + + local function check_references() + cg.server:exec(function(coll_name) + t.assert_error_msg_equals( + "Can't drop collation '" .. coll_name .. "': collation is " .. + "referenced by index", + box.internal.collation.drop, coll_name) + end, {coll_name}) + end + + -- Check that collation can not be dropped. + init() + check_references() + + cg.server:restart() + check_references() + + cg.server:eval('box.snapshot()') + cg.server:restart() + check_references() + + -- Check that collation is unpinned on space drop. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) + + -- Check that collation can be dropped in one transaction with space drop. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.commit() + end, {coll_name}) + + -- Check that collation is still pinned after rollback. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.rollback() + end, {coll_name}) + check_references() + + -- Check that collation is unpinned on index drop. + cg.server:exec(function(coll_name) + box.space.test.index.sk:drop() + box.internal.collation.drop(coll_name) + box.space.test:drop() + end, {coll_name}) + + -- Check that collation can be dropped in one transaction with index drop. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test.index.sk:drop() + box.internal.collation.drop(coll_name) + box.commit() + box.space.test:drop() + end, {coll_name}) + + -- Check that collation is still pinned after rollback. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test.index.sk:drop() + box.internal.collation.drop(coll_name) + box.rollback() + end, {coll_name}) + check_references() + + -- Cleanup. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) +end + +-- Pin/unpin collation by both: space format and the index. +g2.test_coll_pin_format_index = function(cg) + local coll_name = 'my coll 3' + + local function init() + cg.server:exec(function(engine, coll_name) + box.internal.collation.create(coll_name, 'ICU', 'ru-RU', {}) + local s = box.schema.space.create('test', {engine = engine}) + s:format({{name = 'p'}, + {name = 's', type = 'string', collation = coll_name}}) + s:create_index('pk') + s:create_index('sk', {parts = {'s'}}) + end, {cg.params.engine, coll_name}) + end + + local function check_references() + cg.server:exec(function(coll_name) + t.assert_error_msg_equals( + "Can't drop collation '" .. coll_name .. "': collation is " .. + "referenced by space format", + box.internal.collation.drop, coll_name) + end, {coll_name}) + end + + -- Check that collation can not be dropped. + init() + check_references() + + cg.server:restart() + check_references() + + cg.server:eval('box.snapshot()') + cg.server:restart() + check_references() + + -- Check that collation is unpinned on space drop. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) + + -- Check that collation can be dropped in one transaction with space drop. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.commit() + end, {coll_name}) + + -- Check that collation is still pinned after rollback. + init() + check_references() + cg.server:exec(function(coll_name) + box.begin() + box.space.test:drop() + box.internal.collation.drop(coll_name) + box.rollback() + end, {coll_name}) + check_references() + + -- Check that collation is still pinned after index drop. + cg.server:exec(function() + box.space.test.index.sk:drop() + end) + check_references() + + -- Cleanup. + cg.server:exec(function(coll_name) + box.space.test:drop() + box.internal.collation.drop(coll_name) + end, {coll_name}) +end + +-- Check that collation is pinned from SQL. +g2.test_sql = function(cg) + cg.server:exec(function(engine) + local coll_name = 'unicode_af_s2' + local sql = [[CREATE TABLE test (id STRING COLLATE "%s" PRIMARY KEY) + WITH ENGINE = '%s']] + box.execute(string.format(sql, coll_name, engine)) + t.assert_error_msg_equals( + "Can't drop collation 'unicode_af_s2': collation is referenced " .. + "by space format", + box.internal.collation.drop, coll_name) + + -- Check that collation is unpinned on table drop. + box.execute("DROP TABLE test") + box.internal.collation.drop(coll_name) + end, {cg.params.engine}) +end diff --git a/test/sql/collation.result b/test/sql/collation.result index bd8b209695f209a3e9e3de5ed99fb5f8e2ab7557..af9a529bd4e5cecdc85f98b68277a093f5db7608 100644 --- a/test/sql/collation.result +++ b/test/sql/collation.result @@ -225,7 +225,7 @@ box.space._collation:select{0} ... box.space._collation:delete{0} --- -- error: 'Can''t drop collation none : system collation' +- error: 'Can''t drop collation ''none'': system collation' ... -- gh-3185: collations of LHS and RHS must be compatible. --