-
Kirill Yukhin authored
Persistency for SQL triggers supported by introducing new system space called `_trigger` which should store all SQL triggers registered. Add space itself, make visible to Lua, update bootstrap code and regenerate bootstrap.snap. Insertion into the space is done by introducing new VDBE opcode `OP_ParseSchema3` which provides a pair of trigger name and SQL which creates the trigger. During system restore `_trigger` is scanned and its contents parsed back into VDBE. Update tests and extend test for persistency w/ trigger case. Closes #2320
Kirill Yukhin authoredPersistency for SQL triggers supported by introducing new system space called `_trigger` which should store all SQL triggers registered. Add space itself, make visible to Lua, update bootstrap code and regenerate bootstrap.snap. Insertion into the space is done by introducing new VDBE opcode `OP_ParseSchema3` which provides a pair of trigger name and SQL which creates the trigger. During system restore `_trigger` is scanned and its contents parsed back into VDBE. Update tests and extend test for persistency w/ trigger case. Closes #2320
tarantoolctl.test.lua 14.52 KiB
#!/usr/bin/env tarantool
local ffi = require('ffi')
local fio = require('fio')
local tap = require('tap')
local uuid = require('uuid')
local yaml = require('yaml')
local errno = require('errno')
local fiber = require('fiber')
local test_run = require('test_run').new()
local function recursive_rmdir(path)
path = fio.abspath(path)
local path_content = fio.glob(fio.pathjoin(path, '*'))
for _, val in ipairs(fio.glob(fio.pathjoin(path, '.*'))) do
if fio.basename(val) ~= '.' and fio.basename(val) ~= '..' then
table.insert(path_content, val)
end
end
for _, file in ipairs(path_content) do
local stat = fio.stat(file)
if stat:is_dir() then
recursive_rmdir(file)
else
if fio.unlink(file) == false then
print(string.format('!!! failed to unlink file "%s"', file))
print(string.format('!!! [errno %s]: %s', errno(), errno.strerror()))
end
end
end
if fio.rmdir(path) == false then
print(string.format('!!! failed to rmdir path "%s"', file))
print(string.format('!!! [errno %s]: %s', errno(), errno.strerror()))
end
end
ffi.cdef[[
typedef int32_t pid_t;
int kill(pid_t pid, int sig);
/* For execution with background == false */
pid_t fork(void);
int open(const char *pathname, int flags, int mode);
int close(int fd);
int dup2(int oldfd, int newfd);
int execvp(const char *file, char *const argv[]);
]]
-- background checks
tctlcfg_code = [[default_cfg = {
pid_file = '.', wal_dir = '.', memtx_dir = '.' ,
vinyl_dir = '.', log = '.', background = true,
}
instance_dir = require('fio').abspath('.')]]
local function cleanup_instance(dir, name)
local pid = io.open(fio.pathjoin(dir, name .. ".pid"))
if pid ~= nil then
pid = tonumber(pid:read("*a"))
end
if pid ~= nil then
ffi.C.kill(pid, 9)
end
end
local function create_script(dir, name, code)
local path = fio.pathjoin(dir, name)
local script = fio.open(path, {'O_CREAT', 'O_WRONLY'},
tonumber('0777', 8))
assert(script ~= nil, ("assertion: Failed to open '%s' for writing"):format(path))
script:write(code)
script:close()
return path
end
local function run_command(dir, command)
local suffix = uuid.str():sub(1, 8)
local fstdout = fio.pathjoin(dir, 'stdout-' .. suffix)
local fstderr = fio.pathjoin(dir, 'stderr-' .. suffix)
local line = [[/bin/sh -c 'cd "%s" && %s >"%s" 2>"%s"']]
line = line:format(dir, command, fstdout, fstderr)
local res = os.execute(line)
local fstdout_e, fstderr_e = io.open(fstdout):read('*a'), io.open(fstderr):read('*a')
fio.unlink(fstdout); fio.unlink(fstderr);
return res/256, fstdout_e, fstderr_e
end
local function tctl_wait(dir, name)
if name then
local path = fio.pathjoin(dir, name .. '.control')
while not fio.stat(path) do
fiber.sleep(0.01)
end
::again::
while true do
local stat, nb = pcall(require('net.box').new, path, {
wait_connected = true, console = true
})
if stat == false then
fiber.sleep(0.01)
goto again
else
break
end
local stat, msg = pcall(nb.eval, nb, 'require("fiber").time()')
if stat == false then
fiber.sleep(0.01)
else
break
end
end
end
end
local function tctl_command(dir, cmd, args, name)
local pid = nil
if not fio.stat(fio.pathjoin(dir, '.tarantoolctl')) then
create_script(dir, '.tarantoolctl', tctlcfg_code)
end
local command = ('tarantoolctl %s %s'):format(cmd, args)
return run_command(dir, command)
end
local function check_ok(test, dir, cmd, args, e_res, e_stdout, e_stderr)
local res, stdout, stderr = tctl_command(dir, cmd, args)
stdout, stderr = stdout or '', stderr or ''
local ares = true
if (e_res ~= nil) then
local val = test:is(res, e_res, ("check '%s' command status for '%s'"):format(cmd,args))
ares = ares and val
end
if e_stdout ~= nil then
local val = test:is(res, e_res, ("check '%s' stdout for '%s'"):format(cmd,args))
ares = ares and val
if not val then
print(("Expected to find '%s' in '%s'"):format(e_stdout, stdout))
end
end
if e_stderr ~= nil then
local val = test:ok(stderr:find(e_stderr), ("check '%s' stderr for '%s'"):format(cmd,args))
ares = ares and val
if not val then
print(("Expected to find '%s' in '%s'"):format(e_stderr, stderr))
end
end
if not ares then
print(res, stdout, stderr)
end
end
local test = tap.test('tarantoolctl')
test:plan(6)
-- basic start/stop test
-- must be stopped afterwards
do
local dir = fio.tempdir()
local code = [[ box.cfg{memtx_memory = 104857600} ]]
create_script(dir, 'script.lua', code)
local status, err = pcall(function()
test:test("basic test", function(test_i)
test_i:plan(16)
check_ok(test_i, dir, 'start', 'script', 0, nil, "Starting instance")
tctl_wait(dir, 'script')
check_ok(test_i, dir, 'status', 'script', 0, nil, "is running")
check_ok(test_i, dir, 'start', 'script', 1, nil, "is already running")
check_ok(test_i, dir, 'status', 'script', 0, nil, "is running")
check_ok(test_i, dir, 'stop', 'script', 0, nil, "Stopping")
check_ok(test_i, dir, 'status', 'script', 1, nil, "is stopped")
check_ok(test_i, dir, 'stop', 'script', 0, nil, "is not running")
check_ok(test_i, dir, 'status', 'script', 1, nil, "is stopped" )
end)
end)
cleanup_instance(dir, 'script')
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
-- check sandboxes
do
local dir = fio.tempdir()
-- bad code
local code = [[ box.cfg{ ]]
create_script(dir, 'bad_script.lua', code)
local code = [[ box.cfg{memtx_memory = 104857600} ]]
create_script(dir, 'good_script.lua', code)
local status, err = pcall(function()
test:test("basic test for bad script", function(test_i)
test_i:plan(8)
check_ok(test_i, dir, 'start', 'script', 1, nil,
'Instance script is not found')
check_ok(test_i, dir, 'start', 'bad_script', 1, nil,
'unexpected symbol near')
check_ok(test_i, dir, 'start', 'good_script', 0)
tctl_wait(dir, 'good_script')
-- wait here
check_ok(test_i, dir, 'eval', 'good_script bad_script.lua', 3,
nil, 'Error while reloading config:')
check_ok(test_i, dir, 'stop', 'good_script', 0)
end)
end)
cleanup_instance(dir, 'good_script')
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
-- check answers in case of eval
do
local dir = fio.tempdir()
-- bad code
local code = [[ error('help'); return 1]]
create_script(dir, 'bad_script.lua', code)
local code = [[ return 1]]
create_script(dir, 'ok_script.lua', code)
local code = [[ box.cfg{memtx_memory = 104857600} box.once('help', function() end)]]
create_script(dir, 'good_script.lua', code)
local status, err = pcall(function()
test:test("check answers in case of call", function(test_i)
test_i:plan(6)
check_ok(test_i, dir, 'start', 'good_script', 0)
tctl_wait(dir, 'good_script')
check_ok(test_i, dir, 'eval', 'good_script bad_script.lua', 3, nil,
'Error while reloading config')
check_ok(test_i, dir, 'eval', 'good_script ok_script.lua', 0,
'---\n- 1\n...', nil)
check_ok(test_i, dir, 'stop', 'good_script', 0)
end)
end)
cleanup_instance(dir, 'good_script')
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
-- check basic help
do
local dir = fio.tempdir()
local function test_help(test, dir, cmd, e_stderr)
local desc = dir and 'with config' or 'without config'
dir = dir or './'
local res, stdout, stderr = run_command(dir, cmd)
if e_stderr ~= nil then
if not test:ok(stderr:find(e_stderr), ("check stderr of '%s' %s"):format(cmd, desc)) then
print(("Expected to find '%s' in '%s'"):format(e_stderr, stderr))
end
end
end
create_script(dir, '.tarantoolctl', tctlcfg_code)
local status, err = pcall(function()
test:test("check basic help", function(test_i)
test_i:plan(4)
test_help(test_i, nil, "tarantoolctl", "Usage:")
test_help(test_i, nil, "tarantoolctl help", "Usage:")
test_help(test_i, nil, "tarantoolctl --help", "Usage:")
test_help(test_i, dir, "tarantoolctl", "Usage:")
end)
end)
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
-- check cat
do
local dir = fio.tempdir()
local filler_code = [[
box.cfg{memtx_memory = 104857600, background=false}
local space = box.schema.create_space("test")
space:create_index("primary")
space:insert({[1] = 1, [2] = 2, [3] = 3, [4] = 4})
space:replace({[1] = 2, [2] = 2, [3] = 3, [4] = 4})
space:delete({[1] = 1})
space:update({[1] = 2}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
space:upsert({[1] = 3, [2] = 4, [3] = 5, [4] = 6}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
space:upsert({[1] = 3, [2] = 4, [3] = 5, [4] = 6}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
os.exit(0)
]]
create_script(dir, 'filler.lua', filler_code)
local function check_ctlcat_xlog(test, dir, args, delim, lc)
local command_base = 'tarantoolctl cat filler/00000000000000000000.xlog'
local desc = args and "cat + " .. args or "cat"
args = args and " " .. args or ""
local res, stdout, stderr = run_command(dir, command_base .. args)
test:is(res, 0, desc .. " result")
test:is(select(2, stdout:gsub(delim, delim)), lc, desc .. " line count")
end
local function check_ctlcat_snap(test, dir, args, delim, lc)
local command_base = 'tarantoolctl cat filler/00000000000000000000.snap'
local desc = args and "cat + " .. args or "cat"
args = args and " " .. args or ""
local res, stdout, stderr = run_command(dir, command_base .. args)
test:is(res, 0, desc .. " result")
test:is(select(2, stdout:gsub(delim, delim)), lc, desc .. " line count")
end
local status, err = pcall(function()
test:test("fill and test cat output", function(test_i)
test_i:plan(25)
check_ok(test_i, dir, 'start', 'filler', 0)
check_ctlcat_xlog(test_i, dir, nil, "---\n", 6)
check_ctlcat_xlog(test_i, dir, "--show-system", "---\n", 9)
check_ctlcat_xlog(test_i, dir, "--format=json", "\n", 6)
check_ctlcat_xlog(test_i, dir, "--format=lua", "\n", 6)
check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json", "\n", 2)
check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system", "\n", 3)
check_ctlcat_xlog(test_i, dir, "--from=6 --to=3 --format=json --show-system", "\n", 0)
check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 1", "\n", 3)
check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 1 --replica 2", "\n", 3)
check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 2", "\n", 0)
check_ctlcat_snap(test_i, dir, "--space=280", "---\n", 13)
check_ctlcat_snap(test_i, dir, "--space=288", "---\n", 32)
end)
end)
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
-- check play
do
local dir = fio.tempdir()
local filler_code = [[
box.cfg{memtx_memory = 104857600, background=false}
local space = box.schema.create_space("test")
space:create_index("primary")
space:insert({[1] = 1, [2] = 2, [3] = 3, [4] = 4})
space:replace({[1] = 2, [2] = 2, [3] = 3, [4] = 4})
space:delete({[1] = 1})
space:update({[1] = 2}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
space:upsert({[1] = 3, [2] = 4, [3] = 5, [4] = 6}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
space:upsert({[1] = 3, [2] = 4, [3] = 5, [4] = 6}, {[1] = {[1] = '\x3d', [2] = 3, [3] = 4}})
os.exit(0)
]]
create_script(dir, 'filler.lua', filler_code)
local remote_code = [[
box.cfg{
listen = os.getenv("LISTEN"),
memtx_memory = 104857600
}
local space = box.schema.create_space("test")
space:create_index("primary")
box.schema.user.grant("guest", "read,write", "space", "test")
require('console').listen(os.getenv("ADMIN"))
]]
local remote_path = create_script(dir, 'remote.lua', remote_code)
test_run:cmd(("create server remote with script='%s'"):format(remote_path))
test_run:cmd("start server remote")
local port = tonumber(
test_run:eval("remote",
"return require('uri').parse(box.cfg.listen).service")[1]
)
local command_base = ('tarantoolctl play localhost:%d filler/00000000000000000000.xlog'):format(port)
local status, err = pcall(function()
test:test("fill and test play output", function(test_i)
test_i:plan(6)
check_ok(test_i, dir, 'start', 'filler', 0)
local lsn_before = test_run:get_lsn("remote", 1)
test_i:is(lsn_before, 4, "check lsn before")
local res, stdout, stderr = run_command(dir, command_base)
test_i:is(res, 0, "execution result")
test_i:is(test_run:get_lsn("remote", 1), 10, "check lsn after")
local res, stdout, stderr = run_command(dir, command_base)
test_i:is(res, 0, "execution result")
test_i:is(test_run:get_lsn("remote", 1), 16, "check lsn after")
end)
end)
test_run:cmd("stop server remote")
test_run:cmd("cleanup server remote")
recursive_rmdir(dir)
if status == false then
print(("Error: %s"):format(err))
os.exit()
end
end
test:check()