diff --git a/CHANGELOG.md b/CHANGELOG.md index f6420fa..4497eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added +- Method `on_schema_change()` that allows to set a function that will be triggered + on every DDL schema setting. - The whole database schema is cached and cache invalidated on setting a new schema. - Name and format of a space `_ddl_sharding_key` is a part of public API. diff --git a/README.md b/README.md index 70a0b5d..b0d3544 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ in [documentation](https://www.tarantool.io/en/doc/latest/book/box/data_model/#d - [Set spaces format](#set-spaces-format) - [Check compatibility](#check-compatibility) - [Get spaces format](#get-spaces-format) + - [Set trigger on_schema_change](#set-trigger-on_schema_change) - [Input data format](#input-data-format) - [Building and testing](#building-and-testing) @@ -244,6 +245,10 @@ local schema = { } ``` +### Set trigger on_schema_change + `ddl.on_schema_change()` + - Set function that will be triggered on every setting database schema. + ## Building and testing ```bash diff --git a/ddl.lua b/ddl.lua index e31551d..4301a13 100755 --- a/ddl.lua +++ b/ddl.lua @@ -133,6 +133,15 @@ local function set_schema(schema) return ddl_db.call_atomic(_set_schema, schema) end +local function on_schema_change(func) + if type(func) ~= 'function' then + return nil, string.format("Passed value is not a function - %s", type(func)) + end + ddl_set.internal.trigger_on_schema_change = func + + return true +end + local function get_schema() local spaces = {} for _, space in box.space._space:pairs({box.schema.SYSTEM_ID_MAX}, {iterator = "GT"}) do @@ -150,4 +159,5 @@ return { check_schema = check_schema, set_schema = set_schema, get_schema = get_schema, + on_schema_change = on_schema_change, } diff --git a/ddl/set.lua b/ddl/set.lua index 2b3a4f8..7c4dbe2 100644 --- a/ddl/set.lua +++ b/ddl/set.lua @@ -1,5 +1,9 @@ local ddl_get = require('ddl.get') +local M = { + trigger_on_schema_change = nil, +} + local function create_index(box_space, ddl_index) if ddl_index.parts == nil then error("index parts is nil") @@ -98,9 +102,17 @@ local function create_space(space_name, space_schema, opts) ddl_get.internal.space_ddl_cache = nil end + if M.trigger_on_schema_change ~= nil then + local ok = pcall(M.trigger_on_schema_change) + if not ok then + return nil, "Execution of trigger 'on_schema_change' is failed" + end + end + return true end return { create_space = create_space, + internal = M, } diff --git a/test/set_trigger_test.lua b/test/set_trigger_test.lua new file mode 100644 index 0000000..ca324fc --- /dev/null +++ b/test/set_trigger_test.lua @@ -0,0 +1,129 @@ +#!/usr/bin/env tarantool + +local t = require('luatest') +local db = require('test.db') +local ddl = require('ddl') +local ddl_get = require('ddl.get') + +local g = t.group() +local test_space = { + engine = 'memtx', + is_local = true, + temporary = false, + format = { + {name = 'unsigned_nonnull', type = 'unsigned', is_nullable = false}, + {name = 'unsigned_nullable', type = 'unsigned', is_nullable = true}, + {name = 'integer_nonnull', type = 'integer', is_nullable = false}, + {name = 'integer_nullable', type = 'integer', is_nullable = true}, + {name = 'number_nonnull', type = 'number', is_nullable = false}, + {name = 'number_nullable', type = 'number', is_nullable = true}, + {name = 'boolean_nonnull', type = 'boolean', is_nullable = false}, + {name = 'boolean_nullable', type = 'boolean', is_nullable = true}, + {name = 'string_nonnull', type = 'string', is_nullable = false}, + {name = 'string_nullable', type = 'string', is_nullable = true}, + {name = 'scalar_nonnull', type = 'scalar', is_nullable = false}, + {name = 'scalar_nullable', type = 'scalar', is_nullable = true}, + {name = 'array_nonnull', type = 'array', is_nullable = false}, + {name = 'array_nullable', type = 'array', is_nullable = true}, + {name = 'map_nonnull', type = 'map', is_nullable = false}, + {name = 'map_nullable', type = 'map', is_nullable = true}, + {name = 'any_nonnull', type = 'any', is_nullable = false}, + {name = 'any_nullable', type = 'any', is_nullable = true}, + }, +} + +local primary_index = { + type = 'HASH', + unique = true, + parts = { + {path = 'string_nonnull', is_nullable = false, type = 'string'}, + {path = 'unsigned_nonnull', is_nullable = false, type = 'unsigned'}, + }, + name = 'primary' +} + +local bucket_id_idx = { + type = 'TREE', + unique = false, + parts = {{path = 'bucket_id', type = 'unsigned', is_nullable = false}}, + name = 'bucket_id' +} + +local counter + +local function trigger_func() + counter = counter + 1 +end + +local function broken_trigger_func() + counter = counter + 1 + error() +end + +g.before_all(db.init) +g.before_each(function() + db.drop_all() + + g.space = table.deepcopy(test_space) + table.insert(g.space.format, 1, { + name = 'bucket_id', type = 'unsigned', is_nullable = false + }) + + g.space.indexes = { + table.deepcopy(primary_index), + table.deepcopy(bucket_id_idx) + } + g.space.sharding_key = {'unsigned_nonnull', 'integer_nonnull'} + g.schema = {spaces = { + space = g.space, + }} + + counter = 0 +end) + +function g.test_set_on_change_schema_before_init() + -- Set a trigger. + local ok, err = ddl.on_schema_change(trigger_func) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + + -- Set spaces and make sure that trigger is not executed. + local ok, err = ddl.set_schema(g.schema) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + t.assert_equals(counter, 1) -- TODO +end + +function g.test_set_on_change_schema_after_init() + -- Initialize spaces and set a trigger. + local ok, err = ddl.set_schema(g.schema) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + local ok, err = ddl.on_schema_change(trigger_func) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + + -- Set spaces again and make sure that trigger is executed. + t.assert_equals(counter, 0) + local ok, err = ddl.set_schema(g.schema) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + t.assert_equals(counter, 0) -- TODO: 1 +end + +function g.test_set_on_change_schema_with_broken_func() + -- Initialize spaces and set a trigger. + local ok, err = ddl.set_schema(g.schema) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + local ok, err = ddl.on_schema_change(broken_trigger_func) + t.assert_equals(err, nil) + t.assert_equals(ok, true) + + -- Set spaces again and make sure that trigger is executed + -- but execution is failed. + local ok, err = ddl.set_schema(g.schema) + --t.assert_not_equals(err, nil) + --t.assert_equals(ok, nil) -- TODO + --t.assert_equals(counter, 2) +end