diff --git a/cfg/core_cfg.cfg_tmpl b/cfg/core_cfg.cfg_tmpl index 9c695ebef3606f2f2159cbbc4acb0dbf8554b02a..fca66315d3388760c59f32705b4159dfa8fcb804 100644 --- a/cfg/core_cfg.cfg_tmpl +++ b/cfg/core_cfg.cfg_tmpl @@ -71,10 +71,6 @@ snap_io_rate_limit=0.0 # Write no more rows in WAL rows_per_wal=500000, ro -# OBSOLETE -# Starting from 1.4.5, this variable has no effect. -wal_writer_inbox_size=16384, ro - # Defines fiber/data synchronization fsync(2) policy: # "none": does not write to WAL # "write": fibers wait for their data to be written to the log. diff --git a/cfg/tarantool_box_cfg.c b/cfg/tarantool_box_cfg.c index 7ceaca74cd242c3698f8ff107cc37b0ac4d92700..045aac656263fbf4aa0a386079cebdbef6fa8951 100644 --- a/cfg/tarantool_box_cfg.c +++ b/cfg/tarantool_box_cfg.c @@ -50,7 +50,6 @@ init_tarantool_cfg(tarantool_cfg *c) { c->readahead = 0; c->snap_io_rate_limit = 0; c->rows_per_wal = 0; - c->wal_writer_inbox_size = 0; c->wal_mode = NULL; c->wal_fsync_delay = 0; c->wal_dir_rescan_delay = 0; @@ -66,7 +65,6 @@ init_tarantool_cfg(tarantool_cfg *c) { c->memcached_expire_per_loop = 0; c->memcached_expire_full_sweep = 0; c->replication_source = NULL; - c->space = NULL; } int @@ -100,7 +98,6 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->readahead = 16320; c->snap_io_rate_limit = 0; c->rows_per_wal = 500000; - c->wal_writer_inbox_size = 16384; c->wal_mode = strdup("fsync_delay"); if (c->wal_mode == NULL) return CNF_NOMEMORY; c->wal_fsync_delay = 0; @@ -117,7 +114,6 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->memcached_expire_per_loop = 1024; c->memcached_expire_full_sweep = 3600; c->replication_source = NULL; - c->space = NULL; return 0; } @@ -128,32 +124,6 @@ swap_tarantool_cfg(struct tarantool_cfg *c1, struct tarantool_cfg *c2) { *c2 = tmpcfg; } -static int -acceptDefault_name__space(tarantool_cfg_space *c) { - c->enabled = -1; - c->arity = -1; - c->estimated_rows = 0; - c->index = NULL; - return 0; -} - -static int -acceptDefault_name__space__index(tarantool_cfg_space_index *c) { - c->type = strdup(""); - if (c->type == NULL) return CNF_NOMEMORY; - c->unique = -1; - c->key_field = NULL; - return 0; -} - -static int -acceptDefault_name__space__index__key_field(tarantool_cfg_space_index_key_field *c) { - c->fieldno = -1; - c->type = strdup(""); - if (c->type == NULL) return CNF_NOMEMORY; - return 0; -} - static NameAtom _name__username[] = { { "username", -1, NULL } }; @@ -220,9 +190,6 @@ static NameAtom _name__snap_io_rate_limit[] = { static NameAtom _name__rows_per_wal[] = { { "rows_per_wal", -1, NULL } }; -static NameAtom _name__wal_writer_inbox_size[] = { - { "wal_writer_inbox_size", -1, NULL } -}; static NameAtom _name__wal_mode[] = { { "wal_mode", -1, NULL } }; @@ -268,52 +235,6 @@ static NameAtom _name__memcached_expire_full_sweep[] = { static NameAtom _name__replication_source[] = { { "replication_source", -1, NULL } }; -static NameAtom _name__space[] = { - { "space", -1, NULL } -}; -static NameAtom _name__space__enabled[] = { - { "space", -1, _name__space__enabled + 1 }, - { "enabled", -1, NULL } -}; -static NameAtom _name__space__arity[] = { - { "space", -1, _name__space__arity + 1 }, - { "arity", -1, NULL } -}; -static NameAtom _name__space__estimated_rows[] = { - { "space", -1, _name__space__estimated_rows + 1 }, - { "estimated_rows", -1, NULL } -}; -static NameAtom _name__space__index[] = { - { "space", -1, _name__space__index + 1 }, - { "index", -1, NULL } -}; -static NameAtom _name__space__index__type[] = { - { "space", -1, _name__space__index__type + 1 }, - { "index", -1, _name__space__index__type + 2 }, - { "type", -1, NULL } -}; -static NameAtom _name__space__index__unique[] = { - { "space", -1, _name__space__index__unique + 1 }, - { "index", -1, _name__space__index__unique + 2 }, - { "unique", -1, NULL } -}; -static NameAtom _name__space__index__key_field[] = { - { "space", -1, _name__space__index__key_field + 1 }, - { "index", -1, _name__space__index__key_field + 2 }, - { "key_field", -1, NULL } -}; -static NameAtom _name__space__index__key_field__fieldno[] = { - { "space", -1, _name__space__index__key_field__fieldno + 1 }, - { "index", -1, _name__space__index__key_field__fieldno + 2 }, - { "key_field", -1, _name__space__index__key_field__fieldno + 3 }, - { "fieldno", -1, NULL } -}; -static NameAtom _name__space__index__key_field__type[] = { - { "space", -1, _name__space__index__key_field__type + 1 }, - { "index", -1, _name__space__index__key_field__type + 2 }, - { "key_field", -1, _name__space__index__key_field__type + 3 }, - { "type", -1, NULL } -}; #define ARRAYALLOC(x,n,t,_chk_ro, __flags) do { \ int l = 0, ar; \ @@ -655,20 +576,6 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { return CNF_RDONLY; c->rows_per_wal = i32; } - else if ( cmpNameAtoms( opt->name, _name__wal_writer_inbox_size) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - c->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - long int i32 = strtol(opt->paramValue.scalarval, NULL, 10); - if (i32 == 0 && errno == EINVAL) - return CNF_WRONGINT; - if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) - return CNF_WRONGRANGE; - if (check_rdonly && c->wal_writer_inbox_size != i32) - return CNF_RDONLY; - c->wal_writer_inbox_size = i32; - } else if ( cmpNameAtoms( opt->name, _name__wal_mode) ) { if (opt->paramType != scalarType ) return CNF_WRONGTYPE; @@ -886,201 +793,6 @@ acceptValue(tarantool_cfg* c, OptDef* opt, int check_rdonly) { if (opt->paramValue.scalarval && c->replication_source == NULL) return CNF_NOMEMORY; } - else if ( cmpNameAtoms( opt->name, _name__space) ) { - if (opt->paramType != arrayType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - } - else if ( cmpNameAtoms( opt->name, _name__space__enabled) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - bool bln; - - if (strcasecmp(opt->paramValue.scalarval, "true") == 0 || - strcasecmp(opt->paramValue.scalarval, "yes") == 0 || - strcasecmp(opt->paramValue.scalarval, "enable") == 0 || - strcasecmp(opt->paramValue.scalarval, "on") == 0 || - strcasecmp(opt->paramValue.scalarval, "1") == 0 ) - bln = true; - else if (strcasecmp(opt->paramValue.scalarval, "false") == 0 || - strcasecmp(opt->paramValue.scalarval, "no") == 0 || - strcasecmp(opt->paramValue.scalarval, "disable") == 0 || - strcasecmp(opt->paramValue.scalarval, "off") == 0 || - strcasecmp(opt->paramValue.scalarval, "0") == 0 ) - bln = false; - else - return CNF_WRONGRANGE; - if (check_rdonly && c->space[opt->name->index]->enabled != bln) - return CNF_RDONLY; - c->space[opt->name->index]->enabled = bln; - } - else if ( cmpNameAtoms( opt->name, _name__space__arity) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - long int i32 = strtol(opt->paramValue.scalarval, NULL, 10); - if (i32 == 0 && errno == EINVAL) - return CNF_WRONGINT; - if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) - return CNF_WRONGRANGE; - if (check_rdonly && c->space[opt->name->index]->arity != i32) - return CNF_RDONLY; - c->space[opt->name->index]->arity = i32; - } - else if ( cmpNameAtoms( opt->name, _name__space__estimated_rows) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - long int i32 = strtol(opt->paramValue.scalarval, NULL, 10); - if (i32 == 0 && errno == EINVAL) - return CNF_WRONGINT; - if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) - return CNF_WRONGRANGE; - if (check_rdonly && c->space[opt->name->index]->estimated_rows != i32) - return CNF_RDONLY; - c->space[opt->name->index]->estimated_rows = i32; - } - else if ( cmpNameAtoms( opt->name, _name__space__index) ) { - if (opt->paramType != arrayType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - } - else if ( cmpNameAtoms( opt->name, _name__space__index__type) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, opt->name->next->index + 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - if (check_rdonly && ( (opt->paramValue.scalarval == NULL && c->space[opt->name->index]->index[opt->name->next->index]->type == NULL) || strcmp(opt->paramValue.scalarval, c->space[opt->name->index]->index[opt->name->next->index]->type) != 0)) - return CNF_RDONLY; - if (c->space[opt->name->index]->index[opt->name->next->index]->type) free(c->space[opt->name->index]->index[opt->name->next->index]->type); - c->space[opt->name->index]->index[opt->name->next->index]->type = (opt->paramValue.scalarval) ? strdup(opt->paramValue.scalarval) : NULL; - if (opt->paramValue.scalarval && c->space[opt->name->index]->index[opt->name->next->index]->type == NULL) - return CNF_NOMEMORY; - } - else if ( cmpNameAtoms( opt->name, _name__space__index__unique) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, opt->name->next->index + 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - bool bln; - - if (strcasecmp(opt->paramValue.scalarval, "true") == 0 || - strcasecmp(opt->paramValue.scalarval, "yes") == 0 || - strcasecmp(opt->paramValue.scalarval, "enable") == 0 || - strcasecmp(opt->paramValue.scalarval, "on") == 0 || - strcasecmp(opt->paramValue.scalarval, "1") == 0 ) - bln = true; - else if (strcasecmp(opt->paramValue.scalarval, "false") == 0 || - strcasecmp(opt->paramValue.scalarval, "no") == 0 || - strcasecmp(opt->paramValue.scalarval, "disable") == 0 || - strcasecmp(opt->paramValue.scalarval, "off") == 0 || - strcasecmp(opt->paramValue.scalarval, "0") == 0 ) - bln = false; - else - return CNF_WRONGRANGE; - if (check_rdonly && c->space[opt->name->index]->index[opt->name->next->index]->unique != bln) - return CNF_RDONLY; - c->space[opt->name->index]->index[opt->name->next->index]->unique = bln; - } - else if ( cmpNameAtoms( opt->name, _name__space__index__key_field) ) { - if (opt->paramType != arrayType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, opt->name->next->index + 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index[opt->name->next->index]->key_field, 1, _name__space__index__key_field, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - } - else if ( cmpNameAtoms( opt->name, _name__space__index__key_field__fieldno) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, opt->name->next->index + 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index[opt->name->next->index]->key_field, opt->name->next->next->index + 1, _name__space__index__key_field, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - long int i32 = strtol(opt->paramValue.scalarval, NULL, 10); - if (i32 == 0 && errno == EINVAL) - return CNF_WRONGINT; - if ( (i32 == LONG_MIN || i32 == LONG_MAX) && errno == ERANGE) - return CNF_WRONGRANGE; - if (check_rdonly && c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->fieldno != i32) - return CNF_RDONLY; - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->fieldno = i32; - } - else if ( cmpNameAtoms( opt->name, _name__space__index__key_field__type) ) { - if (opt->paramType != scalarType ) - return CNF_WRONGTYPE; - ARRAYALLOC(c->space, opt->name->index + 1, _name__space, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index, opt->name->next->index + 1, _name__space__index, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - ARRAYALLOC(c->space[opt->name->index]->index[opt->name->next->index]->key_field, opt->name->next->next->index + 1, _name__space__index__key_field, check_rdonly, CNF_FLAG_STRUCT_NEW | CNF_FLAG_STRUCT_NOTSET); - if (c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags & CNF_FLAG_STRUCT_NEW) - check_rdonly = 0; - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NOTSET; - errno = 0; - if (check_rdonly && ( (opt->paramValue.scalarval == NULL && c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type == NULL) || strcmp(opt->paramValue.scalarval, c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type) != 0)) - return CNF_RDONLY; - if (c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type) free(c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type); - c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type = (opt->paramValue.scalarval) ? strdup(opt->paramValue.scalarval) : NULL; - if (opt->paramValue.scalarval && c->space[opt->name->index]->index[opt->name->next->index]->key_field[opt->name->next->next->index]->type == NULL) - return CNF_NOMEMORY; - } else { return opt->optional ? CNF_OPTIONAL : CNF_MISSED; } @@ -1219,7 +931,6 @@ typedef enum IteratorState { S_name__readahead, S_name__snap_io_rate_limit, S_name__rows_per_wal, - S_name__wal_writer_inbox_size, S_name__wal_mode, S_name__wal_fsync_delay, S_name__wal_dir_rescan_delay, @@ -1235,24 +946,11 @@ typedef enum IteratorState { S_name__memcached_expire_per_loop, S_name__memcached_expire_full_sweep, S_name__replication_source, - S_name__space, - S_name__space__enabled, - S_name__space__arity, - S_name__space__estimated_rows, - S_name__space__index, - S_name__space__index__type, - S_name__space__index__unique, - S_name__space__index__key_field, - S_name__space__index__key_field__fieldno, - S_name__space__index__key_field__type, _S_Finished } IteratorState; struct tarantool_cfg_iterator_t { IteratorState state; - int idx_name__space; - int idx_name__space__index; - int idx_name__space__index__key_field; }; tarantool_cfg_iterator_t* @@ -1504,17 +1202,6 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char } sprintf(*v, "%"PRId32, c->rows_per_wal); snprintf(buf, PRINTBUFLEN-1, "rows_per_wal"); - i->state = S_name__wal_writer_inbox_size; - return buf; - case S_name__wal_writer_inbox_size: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%"PRId32, c->wal_writer_inbox_size); - snprintf(buf, PRINTBUFLEN-1, "wal_writer_inbox_size"); i->state = S_name__wal_mode; return buf; case S_name__wal_mode: @@ -1677,140 +1364,8 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char return NULL; } snprintf(buf, PRINTBUFLEN-1, "replication_source"); - i->state = S_name__space; + i->state = _S_Finished; return buf; - case S_name__space: - i->state = S_name__space; - case S_name__space__enabled: - case S_name__space__arity: - case S_name__space__estimated_rows: - case S_name__space__index: - case S_name__space__index__type: - case S_name__space__index__unique: - case S_name__space__index__key_field: - case S_name__space__index__key_field__fieldno: - case S_name__space__index__key_field__type: - if (c->space && c->space[i->idx_name__space]) { - switch(i->state) { - case S_name__space: - case S_name__space__enabled: - *v = malloc(8); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%s", c->space[i->idx_name__space]->enabled == -1 ? "false" : c->space[i->idx_name__space]->enabled ? "true" : "false"); - snprintf(buf, PRINTBUFLEN-1, "space[%d].enabled", i->idx_name__space); - i->state = S_name__space__arity; - return buf; - case S_name__space__arity: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%"PRId32, c->space[i->idx_name__space]->arity); - snprintf(buf, PRINTBUFLEN-1, "space[%d].arity", i->idx_name__space); - i->state = S_name__space__estimated_rows; - return buf; - case S_name__space__estimated_rows: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%"PRId32, c->space[i->idx_name__space]->estimated_rows); - snprintf(buf, PRINTBUFLEN-1, "space[%d].estimated_rows", i->idx_name__space); - i->state = S_name__space__index; - return buf; - case S_name__space__index: - i->state = S_name__space__index; - case S_name__space__index__type: - case S_name__space__index__unique: - case S_name__space__index__key_field: - case S_name__space__index__key_field__fieldno: - case S_name__space__index__key_field__type: - if (c->space[i->idx_name__space]->index && c->space[i->idx_name__space]->index[i->idx_name__space__index]) { - switch(i->state) { - case S_name__space__index: - case S_name__space__index__type: - *v = (c->space[i->idx_name__space]->index[i->idx_name__space__index]->type) ? strdup(c->space[i->idx_name__space]->index[i->idx_name__space__index]->type) : NULL; - if (*v == NULL && c->space[i->idx_name__space]->index[i->idx_name__space__index]->type) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - snprintf(buf, PRINTBUFLEN-1, "space[%d].index[%d].type", i->idx_name__space, i->idx_name__space__index); - i->state = S_name__space__index__unique; - return buf; - case S_name__space__index__unique: - *v = malloc(8); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%s", c->space[i->idx_name__space]->index[i->idx_name__space__index]->unique == -1 ? "false" : c->space[i->idx_name__space]->index[i->idx_name__space__index]->unique ? "true" : "false"); - snprintf(buf, PRINTBUFLEN-1, "space[%d].index[%d].unique", i->idx_name__space, i->idx_name__space__index); - i->state = S_name__space__index__key_field; - return buf; - case S_name__space__index__key_field: - i->state = S_name__space__index__key_field; - case S_name__space__index__key_field__fieldno: - case S_name__space__index__key_field__type: - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field && c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]) { - switch(i->state) { - case S_name__space__index__key_field: - case S_name__space__index__key_field__fieldno: - *v = malloc(32); - if (*v == NULL) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - sprintf(*v, "%"PRId32, c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->fieldno); - snprintf(buf, PRINTBUFLEN-1, "space[%d].index[%d].key_field[%d].fieldno", i->idx_name__space, i->idx_name__space__index, i->idx_name__space__index__key_field); - i->state = S_name__space__index__key_field__type; - return buf; - case S_name__space__index__key_field__type: - *v = (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type) ? strdup(c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type) : NULL; - if (*v == NULL && c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type) { - free(i); - out_warning(CNF_NOMEMORY, "No memory to output value"); - return NULL; - } - snprintf(buf, PRINTBUFLEN-1, "space[%d].index[%d].key_field[%d].type", i->idx_name__space, i->idx_name__space__index, i->idx_name__space__index__key_field); - i->state = S_name__space__index__key_field; - i->idx_name__space__index__key_field++; - return buf; - default: - break; - } - } - else { - i->state = S_name__space__index; - i->idx_name__space__index++; - i->idx_name__space__index__key_field = 0; - goto again; - } - default: - break; - } - } - else { - i->state = S_name__space; - i->idx_name__space++; - i->idx_name__space__index = 0; - i->idx_name__space__index__key_field = 0; - goto again; - } - default: - break; - } - } case _S_Finished: free(i); break; @@ -1824,136 +1379,24 @@ tarantool_cfg_iterator_next(tarantool_cfg_iterator_t* i, tarantool_cfg *c, char /************** Checking of required fields **************/ int check_cfg_tarantool_cfg(tarantool_cfg *c) { - tarantool_cfg_iterator_t iterator, *i = &iterator; int res = 0; if (c->primary_port == 0) { res++; out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__primary_port)); } - i->idx_name__space = 0; - while (c->space && c->space[i->idx_name__space]) { - if (c->space[i->idx_name__space]->__confetti_flags & CNF_FLAG_STRUCT_NOTSET) { - (void)0; - } else { - if (c->space[i->idx_name__space]->enabled == -1) { - res++; - _name__space__enabled->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__enabled)); - } - if (c->space[i->idx_name__space]->index == NULL) { - res++; - _name__space__index->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index)); - } - i->idx_name__space__index = 0; - while (c->space[i->idx_name__space]->index && c->space[i->idx_name__space]->index[i->idx_name__space__index]) { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->__confetti_flags & CNF_FLAG_STRUCT_NOTSET) { - res++; - _name__space__index->next->index = i->idx_name__space__index; - _name__space__index->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set", dumpOptDef(_name__space__index)); - } else { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->type != NULL && strcmp(c->space[i->idx_name__space]->index[i->idx_name__space__index]->type, "") == 0) { - res++; - _name__space__index__type->next->index = i->idx_name__space__index; - _name__space__index__type->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index__type)); - } - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->unique == -1) { - res++; - _name__space__index__unique->next->index = i->idx_name__space__index; - _name__space__index__unique->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index__unique)); - } - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field == NULL) { - res++; - _name__space__index__key_field->next->index = i->idx_name__space__index; - _name__space__index__key_field->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index__key_field)); - } - i->idx_name__space__index__key_field = 0; - while (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field && c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]) { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->__confetti_flags & CNF_FLAG_STRUCT_NOTSET) { - res++; - _name__space__index__key_field->next->next->index = i->idx_name__space__index__key_field; - _name__space__index__key_field->next->index = i->idx_name__space__index; - _name__space__index__key_field->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set", dumpOptDef(_name__space__index__key_field)); - } else { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->fieldno == -1) { - res++; - _name__space__index__key_field__fieldno->next->next->index = i->idx_name__space__index__key_field; - _name__space__index__key_field__fieldno->next->index = i->idx_name__space__index; - _name__space__index__key_field__fieldno->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index__key_field__fieldno)); - } - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type != NULL && strcmp(c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type, "") == 0) { - res++; - _name__space__index__key_field__type->next->next->index = i->idx_name__space__index__key_field; - _name__space__index__key_field__type->next->index = i->idx_name__space__index; - _name__space__index__key_field__type->index = i->idx_name__space; - out_warning(CNF_NOTSET, "Option '%s' is not set (or has a default value)", dumpOptDef(_name__space__index__key_field__type)); - } - } - - i->idx_name__space__index__key_field++; - } - - } - - i->idx_name__space__index++; - } - - } - - i->idx_name__space++; - } - return res; } static void -cleanFlags(tarantool_cfg* c, OptDef* opt) { - tarantool_cfg_iterator_t iterator, *i = &iterator; - - - if (c->space != NULL) { - i->idx_name__space = 0; - while (c->space[i->idx_name__space] != NULL) { - c->space[i->idx_name__space]->__confetti_flags &= ~CNF_FLAG_STRUCT_NEW; - - - if (c->space[i->idx_name__space]->index != NULL) { - i->idx_name__space__index = 0; - while (c->space[i->idx_name__space]->index[i->idx_name__space__index] != NULL) { - c->space[i->idx_name__space]->index[i->idx_name__space__index]->__confetti_flags &= ~CNF_FLAG_STRUCT_NEW; +cleanFlags(tarantool_cfg* c __attribute__((unused)), OptDef* opt __attribute__((unused))) { - - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field != NULL) { - i->idx_name__space__index__key_field = 0; - while (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field] != NULL) { - c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->__confetti_flags &= ~CNF_FLAG_STRUCT_NEW; - - - i->idx_name__space__index__key_field++; - } - } - - i->idx_name__space__index++; - } - } - - i->idx_name__space++; - } - } } /************** Duplicate config **************/ int dup_tarantool_cfg(tarantool_cfg* dst, tarantool_cfg* src) { - tarantool_cfg_iterator_t iterator, *i = &iterator; if (dst->username) free(dst->username);dst->username = src->username == NULL ? NULL : strdup(src->username); if (src->username != NULL && dst->username == NULL) @@ -1993,7 +1436,6 @@ dup_tarantool_cfg(tarantool_cfg* dst, tarantool_cfg* src) { dst->readahead = src->readahead; dst->snap_io_rate_limit = src->snap_io_rate_limit; dst->rows_per_wal = src->rows_per_wal; - dst->wal_writer_inbox_size = src->wal_writer_inbox_size; if (dst->wal_mode) free(dst->wal_mode);dst->wal_mode = src->wal_mode == NULL ? NULL : strdup(src->wal_mode); if (src->wal_mode != NULL && dst->wal_mode == NULL) return CNF_NOMEMORY; @@ -2016,59 +1458,6 @@ dup_tarantool_cfg(tarantool_cfg* dst, tarantool_cfg* src) { if (src->replication_source != NULL && dst->replication_source == NULL) return CNF_NOMEMORY; - dst->space = NULL; - if (src->space != NULL) { - i->idx_name__space = 0; - ARRAYALLOC(dst->space, 1, _name__space, 0, 0); - - while (src->space[i->idx_name__space] != NULL) { - ARRAYALLOC(dst->space, i->idx_name__space + 1, _name__space, 0, 0); - - dst->space[i->idx_name__space]->__confetti_flags = src->space[i->idx_name__space]->__confetti_flags; - dst->space[i->idx_name__space]->enabled = src->space[i->idx_name__space]->enabled; - dst->space[i->idx_name__space]->arity = src->space[i->idx_name__space]->arity; - dst->space[i->idx_name__space]->estimated_rows = src->space[i->idx_name__space]->estimated_rows; - - dst->space[i->idx_name__space]->index = NULL; - if (src->space[i->idx_name__space]->index != NULL) { - i->idx_name__space__index = 0; - ARRAYALLOC(dst->space[i->idx_name__space]->index, 1, _name__space__index, 0, 0); - - while (src->space[i->idx_name__space]->index[i->idx_name__space__index] != NULL) { - ARRAYALLOC(dst->space[i->idx_name__space]->index, i->idx_name__space__index + 1, _name__space__index, 0, 0); - - dst->space[i->idx_name__space]->index[i->idx_name__space__index]->__confetti_flags = src->space[i->idx_name__space]->index[i->idx_name__space__index]->__confetti_flags; - if (dst->space[i->idx_name__space]->index[i->idx_name__space__index]->type) free(dst->space[i->idx_name__space]->index[i->idx_name__space__index]->type);dst->space[i->idx_name__space]->index[i->idx_name__space__index]->type = src->space[i->idx_name__space]->index[i->idx_name__space__index]->type == NULL ? NULL : strdup(src->space[i->idx_name__space]->index[i->idx_name__space__index]->type); - if (src->space[i->idx_name__space]->index[i->idx_name__space__index]->type != NULL && dst->space[i->idx_name__space]->index[i->idx_name__space__index]->type == NULL) - return CNF_NOMEMORY; - dst->space[i->idx_name__space]->index[i->idx_name__space__index]->unique = src->space[i->idx_name__space]->index[i->idx_name__space__index]->unique; - - dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field = NULL; - if (src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field != NULL) { - i->idx_name__space__index__key_field = 0; - ARRAYALLOC(dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field, 1, _name__space__index__key_field, 0, 0); - - while (src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field] != NULL) { - ARRAYALLOC(dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field, i->idx_name__space__index__key_field + 1, _name__space__index__key_field, 0, 0); - - dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->__confetti_flags = src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->__confetti_flags; - dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->fieldno = src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->fieldno; - if (dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type) free(dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type);dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type = src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type == NULL ? NULL : strdup(src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type); - if (src->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type != NULL && dst->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type == NULL) - return CNF_NOMEMORY; - - i->idx_name__space__index__key_field++; - } - } - - i->idx_name__space__index++; - } - } - - i->idx_name__space++; - } - } - return CNF_OK; } @@ -2076,7 +1465,6 @@ dup_tarantool_cfg(tarantool_cfg* dst, tarantool_cfg* src) { void destroy_tarantool_cfg(tarantool_cfg* c) { - tarantool_cfg_iterator_t iterator, *i = &iterator; if (c->username != NULL) free(c->username); @@ -2100,46 +1488,6 @@ destroy_tarantool_cfg(tarantool_cfg* c) { free(c->custom_proc_title); if (c->replication_source != NULL) free(c->replication_source); - - if (c->space != NULL) { - i->idx_name__space = 0; - while (c->space[i->idx_name__space] != NULL) { - - if (c->space[i->idx_name__space]->index != NULL) { - i->idx_name__space__index = 0; - while (c->space[i->idx_name__space]->index[i->idx_name__space__index] != NULL) { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->type != NULL) - free(c->space[i->idx_name__space]->index[i->idx_name__space__index]->type); - - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field != NULL) { - i->idx_name__space__index__key_field = 0; - while (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field] != NULL) { - if (c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type != NULL) - free(c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]->type); - - free(c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field[i->idx_name__space__index__key_field]); - - i->idx_name__space__index__key_field++; - } - - free(c->space[i->idx_name__space]->index[i->idx_name__space__index]->key_field); - } - - free(c->space[i->idx_name__space]->index[i->idx_name__space__index]); - - i->idx_name__space__index++; - } - - free(c->space[i->idx_name__space]->index); - } - - free(c->space[i->idx_name__space]); - - i->idx_name__space++; - } - - free(c->space); - } } /************** Compare config **************/ @@ -2158,7 +1506,6 @@ confetti_strcmp(char *s1, char *s2) { char * cmp_tarantool_cfg(tarantool_cfg* c1, tarantool_cfg* c2, int only_check_rdonly) { - tarantool_cfg_iterator_t iterator1, iterator2, *i1 = &iterator1, *i2 = &iterator2; static char diff[PRINTBUFLEN]; if (confetti_strcmp(c1->username, c2->username) != 0) { @@ -2279,11 +1626,6 @@ cmp_tarantool_cfg(tarantool_cfg* c1, tarantool_cfg* c2, int only_check_rdonly) { return diff; } - if (c1->wal_writer_inbox_size != c2->wal_writer_inbox_size) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->wal_writer_inbox_size"); - - return diff; - } if (!only_check_rdonly) { if (confetti_strcmp(c1->wal_mode, c2->wal_mode) != 0) { snprintf(diff, PRINTBUFLEN - 1, "%s", "c->wal_mode"); @@ -2372,80 +1714,6 @@ cmp_tarantool_cfg(tarantool_cfg* c1, tarantool_cfg* c2, int only_check_rdonly) { } } - i1->idx_name__space = 0; - i2->idx_name__space = 0; - while (c1->space != NULL && c1->space[i1->idx_name__space] != NULL && c2->space != NULL && c2->space[i2->idx_name__space] != NULL) { - if (c1->space[i1->idx_name__space]->enabled != c2->space[i2->idx_name__space]->enabled) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->enabled"); - - return diff; - } - if (c1->space[i1->idx_name__space]->arity != c2->space[i2->idx_name__space]->arity) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->arity"); - - return diff; - } - if (c1->space[i1->idx_name__space]->estimated_rows != c2->space[i2->idx_name__space]->estimated_rows) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->estimated_rows"); - - return diff; - } - - i1->idx_name__space__index = 0; - i2->idx_name__space__index = 0; - while (c1->space[i1->idx_name__space]->index != NULL && c1->space[i1->idx_name__space]->index[i1->idx_name__space__index] != NULL && c2->space[i2->idx_name__space]->index != NULL && c2->space[i2->idx_name__space]->index[i2->idx_name__space__index] != NULL) { - if (confetti_strcmp(c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->type, c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->type) != 0) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]->type"); - - return diff; -} - if (c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->unique != c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->unique) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]->unique"); - - return diff; - } - - i1->idx_name__space__index__key_field = 0; - i2->idx_name__space__index__key_field = 0; - while (c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field != NULL && c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field[i1->idx_name__space__index__key_field] != NULL && c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field != NULL && c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field[i2->idx_name__space__index__key_field] != NULL) { - if (c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field[i1->idx_name__space__index__key_field]->fieldno != c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field[i2->idx_name__space__index__key_field]->fieldno) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]->key_field[]->fieldno"); - - return diff; - } - if (confetti_strcmp(c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field[i1->idx_name__space__index__key_field]->type, c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field[i2->idx_name__space__index__key_field]->type) != 0) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]->key_field[]->type"); - - return diff; -} - - i1->idx_name__space__index__key_field++; - i2->idx_name__space__index__key_field++; - } - if (!(c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field == c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field && c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field == NULL) && (c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field == NULL || c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field == NULL || c1->space[i1->idx_name__space]->index[i1->idx_name__space__index]->key_field[i1->idx_name__space__index__key_field] != NULL || c2->space[i2->idx_name__space]->index[i2->idx_name__space__index]->key_field[i2->idx_name__space__index__key_field] != NULL)) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]->key_field[]"); - - return diff; - } - - i1->idx_name__space__index++; - i2->idx_name__space__index++; - } - if (!(c1->space[i1->idx_name__space]->index == c2->space[i2->idx_name__space]->index && c1->space[i1->idx_name__space]->index == NULL) && (c1->space[i1->idx_name__space]->index == NULL || c2->space[i2->idx_name__space]->index == NULL || c1->space[i1->idx_name__space]->index[i1->idx_name__space__index] != NULL || c2->space[i2->idx_name__space]->index[i2->idx_name__space__index] != NULL)) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]->index[]"); - - return diff; - } - - i1->idx_name__space++; - i2->idx_name__space++; - } - if (!(c1->space == c2->space && c1->space == NULL) && (c1->space == NULL || c2->space == NULL || c1->space[i1->idx_name__space] != NULL || c2->space[i2->idx_name__space] != NULL)) { - snprintf(diff, PRINTBUFLEN - 1, "%s", "c->space[]"); - - return diff; - } - return 0; } diff --git a/cfg/tarantool_box_cfg.h b/cfg/tarantool_box_cfg.h index 2f698156450b4882a88b805498a0e07f77f2d8cb..39cbd4111db93ddc5a40cae4d18c9e8a02aa615e 100644 --- a/cfg/tarantool_box_cfg.h +++ b/cfg/tarantool_box_cfg.h @@ -13,30 +13,6 @@ * Autogenerated file, do not edit it! */ -typedef struct tarantool_cfg_space_index_key_field { - unsigned char __confetti_flags; - - int32_t fieldno; - char* type; -} tarantool_cfg_space_index_key_field; - -typedef struct tarantool_cfg_space_index { - unsigned char __confetti_flags; - - char* type; - confetti_bool_t unique; - tarantool_cfg_space_index_key_field** key_field; -} tarantool_cfg_space_index; - -typedef struct tarantool_cfg_space { - unsigned char __confetti_flags; - - confetti_bool_t enabled; - int32_t arity; - int32_t estimated_rows; - tarantool_cfg_space_index** index; -} tarantool_cfg_space; - typedef struct tarantool_cfg { unsigned char __confetti_flags; @@ -126,12 +102,6 @@ typedef struct tarantool_cfg { /* Write no more rows in WAL */ int32_t rows_per_wal; - /* - * OBSOLETE - * Starting from 1.4.5, this variable has no effect. - */ - int32_t wal_writer_inbox_size; - /* * Defines fiber/data synchronization fsync(2) policy: * "none": does not write to WAL @@ -204,7 +174,6 @@ typedef struct tarantool_cfg { * only accepts reads. */ char* replication_source; - tarantool_cfg_space** space; } tarantool_cfg; #ifndef CNF_FLAG_STRUCT_NEW diff --git a/client/tarancheck/tc_generate.c b/client/tarancheck/tc_generate.c index 214585db749f0f678ac8d434c6f3c2da97433c31..b478c4623fba552a5ad7bb2c40ebd91d78588d5c 100644 --- a/client/tarancheck/tc_generate.c +++ b/client/tarancheck/tc_generate.c @@ -439,11 +439,13 @@ int tc_generate(struct tc_options *opts) int rc = tc_space_init(&s); if (rc == -1) return -1; +#if 0 rc = tc_space_fill(&s, opts); if (rc == -1) { tc_space_free(&s); return -1; } +#endif printf("configured spaces: %d\n", mh_size(s.t)); printf("snap_dir: %s\n", opts->cfg.snap_dir); printf("wal_dir: %s\n", opts->cfg.wal_dir); diff --git a/client/tarancheck/tc_space.c b/client/tarancheck/tc_space.c index 37a4e8ceb94708e86dce8d29865b32023df74a15..9635d6742b1f6c9b07704f65cfb68b60951c4241 100644 --- a/client/tarancheck/tc_space.c +++ b/client/tarancheck/tc_space.c @@ -122,6 +122,7 @@ tc_space_key_typeof(char *name) return TC_SPACE_KEY_UNKNOWN; } +#if 0 static int tc_space_key_init(struct tc_space *s, tarantool_cfg_space *cs) { @@ -196,3 +197,4 @@ int tc_space_fill(struct tc_spaces *s, struct tc_options *opts) } return 0; } +#endif diff --git a/client/tarancheck/tc_space.h b/client/tarancheck/tc_space.h index 7c08701e04f249e1b31cecebcde997525908ec8b..6ec7ddea4a90f941645f480cf4b9f223356b84ac 100644 --- a/client/tarancheck/tc_space.h +++ b/client/tarancheck/tc_space.h @@ -35,6 +35,7 @@ void tc_space_free(struct tc_spaces *s); struct tc_space *tc_space_create(struct tc_spaces *s, uint32_t id); struct tc_space *tc_space_match(struct tc_spaces *s, uint32_t id); +#if 0 int tc_space_fill(struct tc_spaces *s, struct tc_options *opts); - +#endif #endif diff --git a/client/tarancheck/tc_verify.c b/client/tarancheck/tc_verify.c index 5ff94c6ded4cbe584d76ed50e4e3362c003522bc..cfa8c7101154fc8748efb28002261585a60a54b3 100644 --- a/client/tarancheck/tc_verify.c +++ b/client/tarancheck/tc_verify.c @@ -186,10 +186,12 @@ int tc_verify(struct tc_options *opts) int rc = tc_space_init(&ss); if (rc == -1) return -1; +#if 0 rc = tc_space_fill(&ss, opts); if (rc == -1) goto error; +#endif /* 2. load signature file */ uint64_t last_xlog_lsn = 0; uint64_t last_snap_lsn = 0; diff --git a/client/tarantar/main.c b/client/tarantar/main.c index c2a5c5bb83a009262bd305c9050ac285f97fab83..76bb6fd317dc242237d20e250ec26c7810d7a32d 100644 --- a/client/tarantar/main.c +++ b/client/tarantar/main.c @@ -147,12 +147,14 @@ int main(int argc, char *argv[]) ts_options_free(&tss.opts); return 1; } +#if 0 rc = ts_space_fill(&tss.s, &tss.opts); if (rc == -1) { ts_space_free(&tss.s); ts_options_free(&tss.opts); return 1; } +#endif printf("snap_dir: %s\n", tss.opts.cfg.snap_dir); printf("wal_dir: %s\n", tss.opts.cfg.wal_dir); diff --git a/client/tarantar/space.c b/client/tarantar/space.c index 82ecbc9270b3cda3e68c63e17102d41888d99d75..7391dbfcbe38592b1be051545e4313888fd9a414 100644 --- a/client/tarantar/space.c +++ b/client/tarantar/space.c @@ -140,6 +140,7 @@ ts_space_key_typeof(char *name) return TS_SPACE_KEY_UNKNOWN; } +#if 0 static int ts_space_key_init(struct ts_space *s, tarantool_cfg_space *cs) { @@ -249,6 +250,7 @@ int ts_space_fill(struct ts_spaces *s, struct ts_options *opts) } return 0; } +#endif static inline struct ts_key* ts_space_keyalloc_sha(struct ts_space *s, struct tnt_tuple *t, int fileid, diff --git a/client/tarantar/space.h b/client/tarantar/space.h index a338b9101c1af38195670ed3bb28b0bc6652792b..7ead69e40bc55863e279d1aa43f6e63558dca1a0 100644 --- a/client/tarantar/space.h +++ b/client/tarantar/space.h @@ -44,7 +44,9 @@ void ts_space_recycle(struct ts_spaces *s); struct ts_space *ts_space_create(struct ts_spaces *s, uint32_t id); struct ts_space *ts_space_match(struct ts_spaces *s, uint32_t id); +#if 0 int ts_space_fill(struct ts_spaces *s, struct ts_options *opts); +#endif struct ts_key* ts_space_keyalloc(struct ts_space *s, struct tnt_tuple *t, int fileid, diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index e1ca97b2791da6afb57cb9ea82b83edafb95296c..ccd9db5470c37f849f15363c642eeec676172d73 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -100,9 +100,6 @@ macro(enable_tnt_compile_flags) add_compile_flags("CXX" "-std=gnu++0x") endif() - # Disable Run-time type information - add_compile_flags("CXX" "-fno-rtti") - add_compile_flags("C;CXX" "-Wall" "-Wextra" diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index 541b9a3d35af0fb8afc875be50c6c6d6b7f2c251..608d242c19bfc25830c5f65b75a32ec8cd8b66b6 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -1331,7 +1331,7 @@ lua box.cjson.decode('{"hello": "world"}').hello <emphasis role="lua" xml:id="box.space.select_reverse_range" xreflabel="box.space.select_reverse_range"> - space:select_reverse_range(limit, key)</emphasis> + space:select_reverse_range(index_no, limit, key)</emphasis> </term> <listitem> <simpara> diff --git a/extra/schema_erase.lua b/extra/schema_erase.lua new file mode 100644 index 0000000000000000000000000000000000000000..eb8a1bf2d0ae08a5600e42b74b451da0f20eeca0 --- /dev/null +++ b/extra/schema_erase.lua @@ -0,0 +1,11 @@ +lua _schema = box.space[box.schema.SCHEMA_ID] +lua _space = box.space[box.schema.SPACE_ID] +lua _index = box.space[box.schema.INDEX_ID] +-- destroy everything - save snapshot produces an empty snapshot now +lua _schema:run_triggers(false) +lua _schema:truncate() +lua _space:run_triggers(false) +lua _space:truncate() +lua _index:run_triggers(false) +lua _index:truncate() + diff --git a/extra/schema_fill.lua b/extra/schema_fill.lua new file mode 100644 index 0000000000000000000000000000000000000000..8e357085b8fcf78b842a7d0f53a8f5f04ad06675 --- /dev/null +++ b/extra/schema_fill.lua @@ -0,0 +1,20 @@ +lua _schema = box.space[box.schema.SCHEMA_ID] +lua _space = box.space[box.schema.SPACE_ID] +lua _index = box.space[box.schema.INDEX_ID] +-- define schema version +lua _schema:insert('version', 1, 6) +-- define system spaces +lua _space:insert(_schema.n, 0, '_schema') +lua _space:insert(_space.n, 0, '_space') +lua _space:insert(_index.n, 0, '_index') +-- define indexes +lua _index:insert(_schema.n, 0, 'primary', 'tree', 1, 1, 0, 'str') + +-- space name is unique +lua _index:insert(_space.n, 0, 'primary', 'tree', 1, 1, 0, 'num') +lua _index:insert(_space.n, 1, 'name', 'tree', 1, 1, 2, 'str') + +-- index name is unique within a space +lua _index:insert(_index.n, 0, 'primary', 'tree', 1, 2, 0, 'num', 1, 'num') +lua _index:insert(_index.n, 1, 'name', 'tree', 1, 2, 0, 'num', 2, 'str') +-- diff --git a/include/errcode.h b/include/errcode.h index c18e414a91852c5dc6ef11976744a71c75561e70..1af5bca5719130b24efa637be64092cd65b9cc67 100644 --- a/include/errcode.h +++ b/include/errcode.h @@ -55,10 +55,10 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 2 */_(ER_ILLEGAL_PARAMS, 2, "Illegal parameters, %s") \ /* 3 */_(ER_SECONDARY, 2, "Can't modify data upon a request on the secondary port.") \ /* 4 */_(ER_TUPLE_IS_RO, 1, "Tuple is marked as read-only") \ - /* 5 */_(ER_INDEX_TYPE, 2, "Unsupported index type: %s") \ + /* 5 */_(ER_INDEX_TYPE, 2, "Unsupported index type supplied for index %u in space %u") \ /* 6 */_(ER_SPACE_EXISTS, 2, "Space %u already exists") \ /* 7 */_(ER_MEMORY_ISSUE, 1, "Failed to allocate %u bytes in %s for %s") \ - /* 8 */_(ER_UNUSED8, 2, "Unused8") \ + /* 8 */_(ER_CREATE_SPACE, 2, "Failed to create space %u: %s") \ /* 9 */_(ER_INJECTION, 2, "Error injection '%s'") \ /* 10 */_(ER_UNSUPPORTED, 2, "%s does not support %s") \ /* silverproxy error codes */ \ @@ -76,13 +76,13 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 22 */_(ER_RESERVED22, 0, "Reserved22") \ /* 23 */_(ER_RESERVED23, 0, "Reserved23") \ /* end of silverproxy error codes */ \ - /* 24 */_(ER_UNUSED24, 2, "Unused24") \ - /* 25 */_(ER_UNUSED25, 2, "Unused25") \ + /* 24 */_(ER_DROP_SPACE, 2, "Can't drop space %u: %s") \ + /* 25 */_(ER_ALTER_SPACE, 2, "Can't modify space %u: %s") \ /* 26 */_(ER_FIBER_STACK, 2, "Can not create a new fiber: recursion limit reached") \ - /* 27 */_(ER_UNUSED27, 2, "Unused27") \ + /* 27 */_(ER_MODIFY_INDEX, 2, "Can't modify index %u in space %u: %s") \ /* 28 */_(ER_TUPLE_FORMAT_LIMIT, 2, "Tuple format limit reached: %u") \ - /* 29 */_(ER_UNUSED29, 2, "Unused29") \ - /* 30 */_(ER_UNUSED30, 2, "Unused30") \ + /* 29 */_(ER_LAST_DROP, 2, "Can't drop the primary key in a system space, space id %u") \ + /* 30 */_(ER_DROP_PRIMARY_KEY, 2, "Can't drop primary key in space %u while secondary keys exist") \ /* 31 */_(ER_UNUSED31, 2, "Unused31") \ /* 32 */_(ER_UNUSED32, 2, "Unused32") \ /* 33 */_(ER_UNUSED33, 2, "Unused33") \ diff --git a/include/memcached.h b/include/memcached.h index f9e536eefdc8b535bed3ce9f1d26182c812754cb..cd3a2b7399a3a5140c549c4eb7774c646046f06f 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -35,13 +35,11 @@ struct tarantool_cfg; void memcached_init(const char *bind_ipaddr, int memcached_port); -void -memcached_space_init(); - int memcached_check_config(struct tarantool_cfg *conf); -void memcached_start_expire(); -void memcached_stop_expire(); +int +memcached_reload_config(struct tarantool_cfg *oldcfg, + struct tarantool_cfg *newcfg); #endif /* TARANTOOL_MEMCACHED_H_INCLUDED */ diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 14603d831bff5ec114610b07dbab76cc8192fb54..1b39de179f5c28481264b10022b35d650a8235d0 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -5,6 +5,7 @@ endif() file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/box/lua) set(lua_sources) +lua_source(lua_sources lua/schema.lua) lua_source(lua_sources lua/box.lua) lua_source(lua_sources lua/box_net.lua) lua_source(lua_sources lua/misc.lua) @@ -25,6 +26,7 @@ tarantool_module("box" tree_index.cc bitset_index.cc space.cc + alter.cc schema.cc port.cc request.cc diff --git a/src/box/alter.cc b/src/box/alter.cc new file mode 100644 index 0000000000000000000000000000000000000000..9d1c39f764217aa12c2be8f05b95bd60a7a4f56f --- /dev/null +++ b/src/box/alter.cc @@ -0,0 +1,1024 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "alter.h" +#include "schema.h" +#include "space.h" +#include "txn.h" +#include "tuple.h" +#include "fiber.h" /* for gc_pool */ +#include <new> /* for placement new */ + +/** _space columns */ +#define ID 0 +#define ARITY 1 +#define NAME 2 +/** _index columns */ +#define INDEX_ID 1 +#define INDEX_TYPE 3 +#define INDEX_IS_UNIQUE 4 +#define INDEX_PART_COUNT 5 + +/* {{{ Auxiliary functions and methods. */ + +/** + * Create a key_def object from a record in _index + * system space. + * + * Check that: + * - index id is within range + * - index type is supported + * - part count > 0 + * - there are parts for the specified part count + * - types of parts in the parts array are known to the system + * - fieldno of each part in the parts array is within limits + */ +struct key_def * +key_def_new_from_tuple(struct tuple *tuple) +{ + uint32_t id = tuple_field_u32(tuple, ID); + uint32_t index_id = tuple_field_u32(tuple, INDEX_ID); + const char *type_str = tuple_field_cstr(tuple, INDEX_TYPE); + enum index_type type = STR2ENUM(index_type, type_str); + uint32_t is_unique = tuple_field_u32(tuple, INDEX_IS_UNIQUE); + uint32_t part_count = tuple_field_u32(tuple, INDEX_PART_COUNT); + + struct key_def *key_def = key_def_new(index_id, type, + is_unique > 0, part_count); + try { + struct tuple_iterator it; + tuple_rewind(&it, tuple); + uint32_t len; /* unused */ + /* Parts follow part count. */ + (void) tuple_seek(&it, INDEX_PART_COUNT, &len); + + for (uint32_t i = 0; i < part_count; i++) { + uint32_t fieldno = tuple_next_u32(&it); + const char *field_type_str = tuple_next_cstr(&it); + enum field_type field_type = + STR2ENUM(field_type, field_type_str); + key_def_set_part(key_def, i, fieldno, field_type); + } + key_def_check(id, key_def); + } catch (...) { + key_def_delete(key_def); + throw; + } + return key_def; +} + +/* }}} */ + +/* {{{ struct alter_space - the body of a full blown alter */ +struct alter_space; + +class AlterSpaceOp { +public: + struct rlist link; + virtual void prepare(struct alter_space * /* alter */) {} + virtual void alter_def(struct alter_space * /* alter */) {} + virtual void alter(struct alter_space * /* alter */) {} + virtual void commit(struct alter_space * /* alter */) {} + virtual void rollback(struct alter_space * /* alter */) {} + virtual ~AlterSpaceOp() {} + template <typename T> static T *create(); + static void destroy(AlterSpaceOp *op); +}; + +template <typename T> T * +AlterSpaceOp::create() +{ + return new (p0alloc(fiber->gc_pool, sizeof(T))) T; +} + +void +AlterSpaceOp::destroy(AlterSpaceOp *op) +{ + op->~AlterSpaceOp(); +} + +/** + * A trigger installed on transaction commit/rollback events of + * the transaction which initiated the alter. + */ +struct txn_alter_trigger { + struct trigger trigger; + struct alter_space *alter; +}; + +struct txn_alter_trigger * +txn_alter_trigger(struct trigger *trigger) +{ + return (struct txn_alter_trigger *) trigger; +} + +struct txn_alter_trigger * +txn_alter_trigger_new(trigger_f run, struct alter_space *alter) +{ + struct txn_alter_trigger *trigger = (struct txn_alter_trigger *) + p0alloc(fiber->gc_pool, sizeof(*trigger)); + trigger->trigger.run = run; + trigger->alter = alter; + return trigger; +} + +struct alter_space { + /** List of alter operations */ + struct rlist ops; + /** Definition of the new space - space_def. */ + struct space_def space_def; + /** Definition of the new space - keys. */ + struct rlist key_list; + /** Old space. */ + struct space *old_space; + /** New space. */ + struct space *new_space; +}; + +struct alter_space * +alter_space_new() +{ + struct alter_space *alter = (struct alter_space *) + p0alloc(fiber->gc_pool, sizeof(*alter)); + rlist_create(&alter->ops); + return alter; +} + +/** Destroy alter. */ +static void +alter_space_delete(struct alter_space *alter) +{ + /* Destroy the ops. */ + while (! rlist_empty(&alter->ops)) { + AlterSpaceOp *op = rlist_shift_entry(&alter->ops, + AlterSpaceOp, link); + AlterSpaceOp::destroy(op); + } + /* Delete the new space, if any. */ + if (alter->new_space) + space_delete(alter->new_space); +} + +/** Add a single operation to the list of alter operations. */ +static void +alter_space_add_op(struct alter_space *alter, AlterSpaceOp *op) +{ + /* Add to the tail: operations must be processed in order. */ + rlist_add_tail_entry(&alter->ops, op, link); +} + +/** + * Commit the alter. + * + * Move all unchanged indexes from the old space to the new space. + * Set the newly built indexes in the new space, or free memory + * of the dropped indexes. + * Replace the old space with a new one in the space cache. + */ +static void +alter_space_commit(struct trigger *trigger, void * /* event */) +{ + struct alter_space *alter = txn_alter_trigger(trigger)->alter; +#if 0 + /* + * Clear the lock first - should there be an + * exception/bug, the lock must not be left around. + */ + space_unset_on_replace(space, alter); +#endif + /* + * If an index is unchanged, all its properties, including + * ID are intact. Move this index here. If an index is + * changed, even if this is a minor change, there is a + * ModifyIndex instance which will move the index from an + * old position to the new one. + */ + for (uint32_t i = 0; i < alter->new_space->index_count; i++) { + Index *new_index = alter->new_space->index[i]; + Index *old_index = space_index(alter->old_space, + index_id(new_index)); + /* + * Move unchanged index from the old space to the + * new one. + */ + if (old_index != NULL && + key_def_cmp(new_index->key_def, + old_index->key_def) == 0) { + space_swap_index(alter->old_space, + alter->new_space, + index_id(old_index), + index_id(new_index)); + } + } + /* + * Commit alter ops, this will move the changed + * indexes into their new places. + */ + struct AlterSpaceOp *op; + rlist_foreach_entry(op, &alter->ops, link) { + op->commit(alter); + } + /* Rebuild index maps once for all indexes. */ + space_fill_index_map(alter->old_space); + space_fill_index_map(alter->new_space); + /* + * Don't forget about space triggers. + */ + rlist_swap(&alter->new_space->on_replace, + &alter->old_space->on_replace); + /* + * The new space is ready. Time to update the space + * cache with it. + */ + struct space *old_space = space_cache_replace(alter->new_space); + assert(old_space == alter->old_space); + space_delete(old_space); + alter->new_space = NULL; /* for alter_space_delete(). */ + alter_space_delete(alter); +} + +/** + * Rollback all effects of space alter. This is + * a transaction trigger, and it fires most likely + * upon a failed write to the WAL. + * + * Keep in mind that we may end up here in case of + * alter_space_commit() failure (unlikely) + */ +static void +alter_space_rollback(struct trigger *trigger, void * /* event */) +{ + struct alter_space *alter = txn_alter_trigger(trigger)->alter; +#if 0 + /* Clear the lock, first thing. */ + op->rollback(alter); + space_remove_trigger(alter); +#endif + struct AlterSpaceOp *op; + rlist_foreach_entry(op, &alter->ops, link) + op->rollback(alter); + alter_space_delete(alter); +} + +/** + * alter_space_do() - do all the work necessary to + * create a new space. + * + * If something may fail during alter, it must be done here, + * before a record is written to the Write Ahead Log. Only + * trivial and infallible actions are left to the commit phase + * of the alter. + * + * The implementation of this function follows "Template Method" + * pattern, providing a skeleton of the alter, while all the + * details are encapsulated in AlterSpaceOp methods. + * + * These are the major steps of alter defining the structure of + * the algorithm and performed regardless of what is altered: + * + * - the input is checked for validity; each check is + * encapsulated in AlterSpaceOp::prepare() method. + * - a copy of the definition of the old space is created + * - the definition of the old space is altered, to get + * definition of a new space + * - an instance of the new space is created, according to the new + * definition; the space is so far empty + * - data structures of the new space are built; sometimes, it + * doesn't need to happen, e.g. when alter only changes the name + * of a space or an index, or other accidental property. + * If any data structure needs to be built, e.g. a new index, + * only this index is built, not the entire space with all its + * indexes. + * - at commit, the new space is coalesced with the old one. + * On rollback, the new space is deleted. + */ +static void +alter_space_do(struct txn *txn, struct alter_space *alter, + struct space *old_space) +{ +#if 0 + /* + * Mark the space as being altered, to abort + * concurrent alter operations from while this + * alter is being written to the write ahead log. + * Must be the last, to make sure we reach commit and + * remove it. It's removed only in comit/rollback. + * + * @todo This is, essentially, an implicit pessimistic + * metadata lock on the space (ugly!), and it should be + * replaced with an explicit lock, since there is nothing + * worse than having to retry your alter -- usually alter + * is done in a script without error-checking. + * Plus, implicit locks are evil. + */ + if (space->on_replace == space_alter_on_replace) + tnt_raise(ER_ALTER, space_id(space)); +#endif + alter->old_space = old_space; + alter->space_def = old_space->def; + /* Create a definition of the new space. */ + space_dump_def(old_space, &alter->key_list); + /* + * Allow for a separate prepare step so that some ops + * can be optimized. + */ + struct AlterSpaceOp *op, *tmp; + rlist_foreach_entry_safe(op, &alter->ops, link, tmp) + op->prepare(alter); + /* + * Alter the definition of the old space, so that + * a new space can be created with a new definition. + */ + rlist_foreach_entry(op, &alter->ops, link) + op->alter_def(alter); + /* + * Create a new (empty) space for the new definition. + * Sic: the space engine is not the same yet, the + * triggers are not set. + */ + alter->new_space = space_new(&alter->space_def, &alter->key_list); + /* + * Copy the engine, the new space is at the same recovery + * phase as the old one. Do it before performing the alter, + * since engine.recover does different things depending on + * the recovery phase. + */ + alter->new_space->engine = alter->old_space->engine; + /* + * Change the new space: build the new index, rename, + * change arity. + */ + rlist_foreach_entry(op, &alter->ops, link) + op->alter(alter); + /* + * Install transaction commit/rollback triggers to either + * finish or rollback the DDL depending on the results of + * writing to WAL. + */ + struct txn_alter_trigger *on_commit = + txn_alter_trigger_new(alter_space_commit, alter); + trigger_set(&txn->on_commit, &on_commit->trigger); + struct txn_alter_trigger *on_rollback = + txn_alter_trigger_new(alter_space_rollback, alter); + trigger_set(&txn->on_rollback, &on_rollback->trigger); +} + +/* }}} */ + +/* {{{ AlterSpaceOp descendants - alter operations, such as Add/Drop index */ + +/** Change non-essential properties of a space. */ +class ModifySpace: public AlterSpaceOp +{ +public: + /* New space definition. */ + struct space_def def; + virtual void prepare(struct alter_space *alter); + virtual void alter_def(struct alter_space *alter); +}; + +/** Check that space properties are OK to change. */ +void +ModifySpace::prepare(struct alter_space *alter) +{ + if (def.id != space_id(alter->old_space)) + tnt_raise(ClientError, ER_ALTER_SPACE, + (unsigned) space_id(alter->old_space), + "space id is immutable"); + + if (def.arity != 0 && + def.arity > alter->old_space->def.arity && + alter->old_space->engine.state != READY_NO_KEYS && + space_size(alter->old_space) > 0) { + + tnt_raise(ClientError, ER_ALTER_SPACE, + (unsigned) def.id, + "can not enlarge arity on a non-empty space"); + } +} + +/** Amend the definition of the new space. */ +void +ModifySpace::alter_def(struct alter_space *alter) +{ + alter->space_def = def; +} + +/** DropIndex - remove an index from space. */ + +class AddIndex; + +class DropIndex: public AlterSpaceOp { +public: + /** A reference to Index key def of the dropped index. */ + struct key_def *old_key_def; + virtual void alter_def(struct alter_space *alter); + virtual void alter(struct alter_space *alter); + virtual void commit(struct alter_space *alter); +}; + +/* + * Alter the definition of the new space and remove + * the new index from it. + */ +void +DropIndex::alter_def(struct alter_space * /* alter */) +{ + rlist_del_entry(old_key_def, link); +} + +/* Do the drop. */ +void +DropIndex::alter(struct alter_space *alter) +{ + /* + * If it's not the primary key, nothing to do -- + * the dropped index didn't exist in the new space + * definition, so does not exist in the created space. + */ + if (space_index(alter->new_space, 0) != NULL) + return; + /* + * Deal with various cases of dropping of the primary key. + */ + /* + * Dropping the primary key in a system space: off limits. + */ + if (space_is_system(alter->new_space)) + tnt_raise(ClientError, ER_LAST_DROP, + space_id(alter->new_space)); + /* + * Can't drop primary key before secondary keys. + */ + if (alter->new_space->index_count) { + tnt_raise(ClientError, ER_DROP_PRIMARY_KEY, + (unsigned) alter->new_space->def.id); + } + /* + * OK to drop the primary key. Put the space back to + * 'READY_NO_KEYS' state, so that: + * - DML returns proper errors rather than crashes the + * server (thanks to engine_no_keys.replace), + * - When a new primary key is finally added, the space + * can be put back online properly with + * engine_no_keys.recover. + */ + alter->new_space->engine = engine_no_keys; +} + +void +DropIndex::commit(struct alter_space *alter) +{ + /* + * Delete all tuples in the old space if dropping the + * primary key. + */ + if (space_index(alter->new_space, 0) != NULL) + return; + Index *pk = index_find(alter->old_space, 0); + if (pk == NULL) + return; + struct iterator *it = pk->position(); + pk->initIterator(it, ITER_ALL, NULL, 0); + struct tuple *tuple; + while ((tuple = it->next(it))) + tuple_ref(tuple, -1); +} + +/** Change non-essential (no data change) properties of an index. */ +class ModifyIndex: public AlterSpaceOp +{ +public: + struct key_def *new_key_def; + struct key_def *old_key_def; + virtual void alter_def(struct alter_space *alter); + virtual void commit(struct alter_space *alter); + virtual ~ModifyIndex(); +}; + +/** Update the definition of the new space */ +void +ModifyIndex::alter_def(struct alter_space *alter) +{ + rlist_del_entry(old_key_def, link); + rlist_add_entry(&alter->key_list, new_key_def, link); +} + +/** Move the index from the old space to the new one. */ +void +ModifyIndex::commit(struct alter_space *alter) +{ + /* Move the old index to the new place but preserve */ + space_swap_index(alter->old_space, alter->new_space, + old_key_def->id, new_key_def->id); +} + +ModifyIndex::~ModifyIndex() +{ + /* new_key_def is NULL if exception is raised before it's set. */ + if (new_key_def) + key_def_delete(new_key_def); +} + +/** + * Add to index trigger -- invoked on any change in the old space, + * while the AddIndex tuple is being written to the WAL. The job + * of this trigger is to keep the added index up to date with the + * state of the primary key in the old space. + * + * Initially it's installed as old_space->on_replace trigger, and + * for each successfully replaced tuple in the new index, + * a trigger is added to txn->on_rollback list to remove the tuple + * from the new index if the transaction rolls back. + * + * The trigger is removed when alter operation commits/rolls back. + */ +struct add2index_trigger { + struct trigger trigger; + Index *new_index; +}; + +struct add2index_trigger * +add2index_trigger(struct trigger *trigger) +{ + return (struct add2index_trigger *) trigger; +} + +struct add2index_trigger * +add2index_trigger_new(trigger_f run, Index *new_index) +{ + struct add2index_trigger *trigger = (struct add2index_trigger *) + p0alloc(fiber->gc_pool, sizeof(*trigger)); + trigger->trigger.run = run; + trigger->new_index = new_index; + return trigger; +} + +/** AddIndex - add a new index to the space. */ +class AddIndex: public AlterSpaceOp { +public: + /** New index key_def. */ + struct key_def *new_key_def; + struct add2index_trigger *on_replace; + virtual void prepare(struct alter_space *alter); + virtual void alter_def(struct alter_space *alter); + virtual void alter(struct alter_space *alter); + virtual ~AddIndex(); +}; + +/** + * Optimize addition of a new index: try to either completely + * remove it or at least avoid building from scratch. + */ +void +AddIndex::prepare(struct alter_space *alter) +{ + AlterSpaceOp *prev_op = rlist_prev_entry_safe(this, &alter->ops, + link); + DropIndex *drop = dynamic_cast<DropIndex *>(prev_op); + + if (drop == NULL || + drop->old_key_def->type != new_key_def->type || + key_part_cmp(drop->old_key_def->parts, + drop->old_key_def->part_count, + new_key_def->parts, + new_key_def->part_count) != 0) { + /* + * The new index is too distinct from the old one, + * have to rebuild. + */ + return; + } + /* Only index meta has changed, no data change. */ + rlist_del_entry(drop, link); + rlist_del_entry(this, link); + /* Add ModifyIndex only if the there is a change. */ + if (key_def_cmp(drop->old_key_def, new_key_def) != 0) { + ModifyIndex *modify = AlterSpaceOp::create<ModifyIndex>(); + alter_space_add_op(alter, modify); + modify->new_key_def = new_key_def; + new_key_def = NULL; + modify->old_key_def = drop->old_key_def; + } + AlterSpaceOp::destroy(drop); + AlterSpaceOp::destroy(this); +} + +/** Add definition of the new key to the new space def. */ +void +AddIndex::alter_def(struct alter_space *alter) +{ + rlist_add_entry(&alter->key_list, new_key_def, link); +} + +/** + * A trigger invoked on rollback in old space while the record + * about alter is being written to the WAL. + */ +static void +on_rollback_in_old_space(struct trigger *trigger, void *event) +{ + struct txn *txn = (struct txn *) event; + Index *new_index = add2index_trigger(trigger)->new_index; + /* Remove the failed tuple from the new index. */ + new_index->replace(txn->new_tuple, txn->old_tuple, DUP_INSERT); +} + +/** + * A trigger invoked on replace in old space while + * the record about alter is being written to the WAL. + */ +static void +on_replace_in_old_space(struct trigger *trigger, void *event) +{ + struct txn *txn = (struct txn *) event; + Index *new_index = add2index_trigger(trigger)->new_index; + /* + * First set rollback trigger, then do replace, since + * creating the trigger may fail. + */ + struct add2index_trigger *on_rollback = + add2index_trigger_new(on_rollback_in_old_space, new_index); + trigger_set(&txn->on_rollback, &on_rollback->trigger); + /* Put the tuple into thew new index. */ + (void) new_index->replace(txn->old_tuple, txn->new_tuple, + DUP_INSERT); +} + +/** + * Optionally build the new index. + * + * During recovery the space is often not fully constructed yet + * anyway, so there is no need to fully populate index with data, + * it is done at the end of recovery. + * + * Note, that system spaces are exception to this, since + * they are fully enabled at all times. + */ +void +AddIndex::alter(struct alter_space *alter) +{ + /* + * READY_NO_KEYS is when a space has no functional keys. + * Possible both during and after recovery. + */ + if (alter->new_space->engine.state == READY_NO_KEYS) { + if (new_key_def->id == 0) { + /* + * Adding a primary key: bring the space + * up to speed with the current recovery + * state. During snapshot recovery it + * means preparing the primary key for + * build (beginBuild()). During xlog + * recovery, it means building the primary + * key. After recovery, it means building + * all keys. + */ + alter->new_space->engine.recover(alter->new_space); + } else { + /* + * Adding a secondary key: nothing to do. + * Before the end of recovery, nothing to do + * because secondary keys are built in bulk later. + * During normal operation, nothing to do + * because without a primary key there is + * no data in the space, and secondary + * keys are built once the primary is + * added. + * TODO Consider prohibiting this branch + * altogether. + */ + } + return; + } + Index *pk = index_find(alter->old_space, 0); + Index *new_index = index_find(alter->new_space, new_key_def->id); + /* READY_PRIMARY_KEY is a state that only occurs during WAL recovery. */ + if (alter->new_space->engine.state == READY_PRIMARY_KEY) { + if (new_key_def->id == 0) { + /* + * Bulk rebuild of the new primary key + * from old primary key - it is safe to do + * in bulk and without tuple-by-tuple + * verification, since all tuples have + * been verified when inserted, before + * shutdown. + */ + index_build(new_index, pk); + } else { + /* + * No need to build a secondary key during + * WAL recovery. + */ + } + return; + } + /* Now deal with any kind of add index during normal operation. */ + struct iterator *it = pk->position(); + pk->initIterator(it, ITER_ALL, NULL, 0); + /* + * The index has to be built tuple by tuple, since + * there is no guarantee that all tuples satisfy + * new index' constraints. If any tuple can not be + * added to the index (insufficient number of fields, + * etc., the build is aborted. + */ + new_index->beginBuild(); + new_index->endBuild(); + /* Build the new index. */ + struct tuple *tuple; + while ((tuple = it->next(it))) { + /* + * @todo: + * tuple_format_validate(alter->new_space->format, + * tuple) + * @todo: better message if there is a duplicate. + */ + struct tuple *old_tuple = + new_index->replace(NULL, tuple, DUP_INSERT); + assert(old_tuple == NULL); /* Guaranteed by DUP_INSERT. */ + (void) old_tuple; + } + on_replace = add2index_trigger_new(on_replace_in_old_space, + new_index); + trigger_set(&alter->old_space->on_replace, &on_replace->trigger); +} + +AddIndex::~AddIndex() +{ + /* + * The trigger by now may reside in the new space (on + * commit) or in the old space (rollback). Remove it + * from the list, wherever it is. + */ + if (on_replace) + trigger_clear(&on_replace->trigger); + if (new_key_def) + key_def_delete(new_key_def); +} + +/* }}} */ + +/** + * A trigger invoked on commit/rollback of DROP/ADD space. + * The trigger removed the space from the space cache. + */ +static void +on_drop_space(struct trigger * /* trigger */, void *event) +{ + struct txn *txn = (struct txn *) event; + uint32_t id = tuple_field_u32(txn->old_tuple ? + txn->old_tuple : txn->new_tuple, ID); + struct space *space = space_cache_delete(id); + space_delete(space); +} + +static struct trigger drop_space_trigger = { rlist_nil, on_drop_space }; + +/** + * A trigger which is invoked on replace in a data dictionary + * space _space. + * + * Generally, whenever a data dictionary change occurs + * 2 things should be done: + * + * - space cache should be updated, and changes in the space + * cache should be reflected in Lua bindings + * (this is done in space_cache_replace() and + * space_cache_delete()) + * + * - the space which is changed should be rebuilt according + * to the nature of the modification, i.e. indexes added/dropped, + * tuple format changed, etc. + * + * When dealing with an update of _space space, we have 3 major + * cases: + * + * 1) insert a new tuple: creates a new space + * The trigger prepares a space structure to insert + * into the space cache and registers an on commit + * hook to perform the registration. Should the statement + * itself fail, transaction is rolled back, the transaction + * rollback hook must be there to delete the created space + * object, avoiding a memory leak. The hooks are written + * in a way that excludes the possibility of a failure. + * + * 2) delete a tuple: drops an existing space. + * + * A space can be dropped only if it has no indexes. + * The only reason for this restriction is that there + * must be no tuples in _index without a corresponding tuple + * in _space. It's not possible to delete such tuples + * automatically (this would require multi-statement + * transactions), so instead the trigger verifies that the + * records have been deleted by the user. + * + * Then the trigger registers transaction commit hook to + * perform the deletion from the space cache. No rollback hook + * is required: if the transaction is rolled back, nothing is + * done. + * + * 3) modify an existing tuple: some space + * properties are immutable, but it's OK to change + * space name or arity. This is done in WAL-error- + * safe mode. + * + * A note about memcached_space: Tarantool 1.4 had a check + * which prevented re-definition of memcached_space. With + * dynamic space configuration such a check would be particularly + * clumsy, so it is simply not done. + */ +static void +on_replace_dd_space(struct trigger * /* trigger */, void *event) +{ + struct txn *txn = (struct txn *) event; + struct tuple *old_tuple = txn->old_tuple; + struct tuple *new_tuple = txn->new_tuple; + /* + * Things to keep in mind: + * - old_tuple is set only in case of UPDATE. For INSERT + * or REPLACE it is NULL. + * - the trigger may be called inside recovery from a snapshot, + * when index look up is not possible + * - _space, _index and other metaspaces initially don't + * have a tuple which represents it, this tuple is only + * created during recovery from + * a snapshot. + * + * Let's establish whether an old space exists. Use + * old_tuple ID field, if old_tuple is set, since UPDATE + * may have changed space id. + */ + uint32_t old_id = tuple_field_u32(old_tuple ? + old_tuple : new_tuple, ID); + struct space *old_space = space_by_id(old_id); + if (new_tuple != NULL && old_space == NULL) { /* INSERT */ + struct space_def def; + def.id = old_id; + def.arity = tuple_field_u32(new_tuple, ARITY); + if (def.id > BOX_SPACE_MAX) { + tnt_raise(ClientError, ER_CREATE_SPACE, + (unsigned) def.id, + "space id is too big"); + } + if (def.id >= SC_SYSTEM_ID_MIN && def.id < SC_SYSTEM_ID_MAX) { + say_warn("\n" +"*******************************************************\n" +"* Creating a space with a reserved id %3u. *\n" +"* Ids in range %3u-%3u may be used for a system space *\n" +"* the future. Assuming you know what you're doing. *\n" +"*******************************************************", + (unsigned) def.id, + (unsigned) SC_SYSTEM_ID_MIN, + (unsigned) SC_SYSTEM_ID_MAX); + } + struct space *space = space_new(&def, &rlist_nil); + (void) space_cache_replace(space); + /* + * So may happen that until the DDL change record + * is written to the WAL, the space is used for + * insert/update/delete. All these updates are + * rolled back by the pipelined rollback mechanism, + * so it's safe to simply drop the space on + * rollback. + */ + trigger_set(&txn->on_rollback, &drop_space_trigger); + } else if (new_tuple == NULL) { /* DELETE */ + /* Verify that the space is empty (has no indexes) */ + if (old_space->index_count) { + tnt_raise(ClientError, ER_DROP_SPACE, + (unsigned) space_id(old_space), + "the space has indexes"); + } + /* @todo lock space metadata until commit. */ + /* + * dd_space_delete() can't fail, any such + * failure would have to abort the server. + */ + trigger_set(&txn->on_commit, &drop_space_trigger); + } else { /* UPDATE, REPLACE */ + assert(old_space != NULL && new_tuple != NULL); + /* + * Allow change of space properties, but do it + * in WAL-error-safe mode. + */ + struct alter_space *alter = alter_space_new(); + try { + ModifySpace *modify = + AlterSpaceOp::create<ModifySpace>(); + alter_space_add_op(alter, modify); + modify->def.id = tuple_field_u32(new_tuple, ID); + modify->def.arity = tuple_field_u32(new_tuple, ARITY); + alter_space_do(txn, alter, old_space); + } catch (...) { + alter_space_delete(alter); + throw; + } + } +} + +/** + * Just like with _space, 3 major cases: + * + * - insert a tuple = addition of a new index. The + * space should exist. + * + * - delete a tuple - drop index. + * + * - update a tuple - change of index type or key parts. + * Change of index type is the same as deletion of the old + * index and addition of the new one. + * + * A new index needs to be built before we attempt to commit + * a record to the write ahead log, since: + * + * 1) if it fails, it's not good to end up with a corrupt index + * which is already committed to WAL + * + * 2) Tarantool indexes also work as constraints (min number of + * fields in the space, field uniqueness), and it's not good to + * commit to WAL a constraint which is not enforced in the + * current data set. + * + * When adding a new index, ideally we'd also need to rebuild + * all tuple formats in all tuples, since the old format may not + * be ideal for the new index. We, however, do not do that, + * since that would entail rebuilding all indexes at once. + * Instead, the default tuple format of the space is changed, + * and as tuples get updated/replaced, all tuples acquire a new + * format. + * + * The same is the case with dropping an index: nothing is + * rebuilt right away, but gradually the extra space reserved + * for offsets is relinquished to the slab allocator as tuples + * are modified. + */ +static void +on_replace_dd_index(struct trigger * /* trigger */, void *event) +{ + struct txn *txn = (struct txn *) event; + struct tuple *old_tuple = txn->old_tuple; + struct tuple *new_tuple = txn->new_tuple; + uint32_t id = tuple_field_u32(old_tuple ? old_tuple : new_tuple, ID); + uint32_t index_id = tuple_field_u32(old_tuple ? old_tuple : new_tuple, + INDEX_ID); + struct space *old_space = space_find(id); + Index *old_index = space_index(old_space, index_id); + struct alter_space *alter = alter_space_new(); + try { + /* + * The order of checks is important, DropIndex most be added + * first, so that AddIndex::prepare() can change + * Drop + Add to a Modify. + */ + if (old_index != NULL) { + DropIndex *drop_index = AlterSpaceOp::create<DropIndex>(); + alter_space_add_op(alter, drop_index); + drop_index->old_key_def = old_index->key_def; + } + if (new_tuple != NULL) { + AddIndex *add_index = AlterSpaceOp::create<AddIndex>(); + alter_space_add_op(alter, add_index); + add_index->new_key_def = key_def_new_from_tuple(new_tuple); + } + alter_space_do(txn, alter, old_space); + } catch (...) { + alter_space_delete(alter); + throw; + } +} + +struct trigger alter_space_on_replace_space = { + rlist_nil, on_replace_dd_space +}; + +struct trigger alter_space_on_replace_index = { + rlist_nil, on_replace_dd_index +}; + +/* vim: set foldmethod=marker */ diff --git a/src/box/alter.h b/src/box/alter.h new file mode 100644 index 0000000000000000000000000000000000000000..089412c59a3946c812294e805d03f24a45f97453 --- /dev/null +++ b/src/box/alter.h @@ -0,0 +1,36 @@ +#ifndef INCLUDES_TARANTOOL_BOX_ALTER_H +#define INCLUDES_TARANTOOL_BOX_ALTER_H +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "trigger.h" + +extern struct trigger alter_space_on_replace_space; +extern struct trigger alter_space_on_replace_index; + +#endif /* INCLUDES_TARANTOOL_BOX_ALTER_H */ diff --git a/src/box/box.cc b/src/box/box.cc index 107e5730f481f94bfa0ddc3bc5987caed8e6b973..ab20ae872615770e4baeba26a241006215dd9ae9 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -43,7 +43,6 @@ extern "C" { #include <stat.h> #include <tarantool.h> #include "tuple.h" -#include "memcached.h" #include "box_lua.h" #include "schema.h" #include "space.h" @@ -157,8 +156,6 @@ box_enter_master_or_replica_mode(struct tarantool_cfg *conf) } else { box_process = process_rw; - memcached_start_expire(); - snprintf(status, sizeof(status), "primary%s", custom_proc_title); title("primary%s", custom_proc_title); @@ -219,22 +216,6 @@ box_check_config(struct tarantool_cfg *conf) return -1; } - /* check if at least one space is defined */ - if (conf->space == NULL && conf->memcached_port == 0) { - out_warning(CNF_OK, "at least one space or memcached port must be defined"); - return -1; - } - - /* check configured spaces */ - if (check_spaces(conf) != 0) { - return -1; - } - - /* check memcached configuration */ - if (memcached_check_config(conf) != 0) { - return -1; - } - return 0; } @@ -255,10 +236,6 @@ box_reload_config(struct tarantool_cfg *old_conf, struct tarantool_cfg *new_conf return -1; } - - if (!old_is_replica && new_is_replica) - memcached_stop_expire(); - if (recovery_state->remote) recovery_stop_remote(recovery_state); @@ -283,8 +260,6 @@ box_init() tuple_init(); schema_init(); - /* configure memcached space */ - memcached_space_init(); /* recovery initialization */ recovery_init(cfg.snap_dir, cfg.wal_dir, @@ -337,6 +312,8 @@ snapshot_space(struct space *sp, void *udata) struct tuple *tuple; struct snapshot_space_param *ud = (struct snapshot_space_param *) udata; Index *pk = space_index(sp, 0); + if (pk == NULL) + return; struct iterator *it = pk->position(); pk->initIterator(it, ITER_ALL, NULL, 0); diff --git a/src/box/box_cfg.cfg_tmpl b/src/box/box_cfg.cfg_tmpl index 5b1e3864c7d7e77d396c50a72f9a23d4e21fac54..f135f6eb9e28e5a3ba108b0ee42b8b2f37cbf768 100644 --- a/src/box/box_cfg.cfg_tmpl +++ b/src/box/box_cfg.cfg_tmpl @@ -31,23 +31,3 @@ memcached_expire_full_sweep=3600.0 # fetching records from it.. In replication mode the server # only accepts reads. replication_source=NULL - -space = [ - { - enabled = false, required - arity = -1 - estimated_rows = 0 - index = [ - { - type = "", required - unique = false, required - key_field = [ - { - fieldno = -1, required - type = "", required - }, ro, required - ], required - }, ro, required - ], required - }, ro -], ro diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc index ea79e7b8b79bb300bdc131a4ad88820db288554b..fef590d0dd93fee04156b9436ca9d97e0752ec6a 100644 --- a/src/box/box_lua.cc +++ b/src/box/box_lua.cc @@ -55,8 +55,8 @@ extern "C" { #include "scoped_guard.h" /* contents of box.lua, misc.lua, box.net.lua respectively */ -extern char box_lua[], box_net_lua[], misc_lua[], sql_lua[]; -static const char *lua_sources[] = { box_lua, box_net_lua, misc_lua, sql_lua, NULL }; +extern char schema_lua[], box_lua[], box_net_lua[], misc_lua[], sql_lua[]; +static const char *lua_sources[] = { schema_lua, box_lua, box_net_lua, misc_lua, sql_lua, NULL }; /** * All box connections share the same Lua state. We use @@ -1094,10 +1094,7 @@ static void port_add_lua_ret(struct port *port, struct lua_State *L, int index) { struct tuple *tuple = lua_totuple(L, index); - auto scoped_guard = make_scoped_guard([=] { - if (tuple->refs == 0) - tuple_delete(tuple); - }); + TupleGuard guard(tuple); port_add_tuple(port, tuple, BOX_RETURN_TUPLE); } @@ -1815,6 +1812,32 @@ static const struct luaL_reg boxlib[] = { {NULL, NULL} }; +void +schema_lua_init(struct lua_State *L) +{ + lua_getfield(L, LUA_GLOBALSINDEX, "box"); + lua_newtable(L); + lua_setfield(L, -2, "schema"); + lua_getfield(L, -1, "schema"); + lua_pushnumber(L, SC_SCHEMA_ID); + lua_setfield(L, -2, "SCHEMA_ID"); + lua_pushnumber(L, SC_SPACE_ID); + lua_setfield(L, -2, "SPACE_ID"); + lua_pushnumber(L, SC_INDEX_ID); + lua_setfield(L, -2, "INDEX_ID"); + lua_pushnumber(L, SC_SYSTEM_ID_MIN); + lua_setfield(L, -2, "SYSTEM_ID_MIN"); + lua_pushnumber(L, SC_SYSTEM_ID_MAX); + lua_setfield(L, -2, "SYSTEM_ID_MAX"); + lua_pushnumber(L, BOX_INDEX_MAX); + lua_setfield(L, -2, "INDEX_MAX"); + lua_pushnumber(L, BOX_SPACE_MAX); + lua_setfield(L, -2, "SPACE_MAX"); + lua_pushnumber(L, BOX_FIELD_MAX); + lua_setfield(L, -2, "FIELD_MAX"); + lua_pop(L, 2); /* box, schema */ +} + void mod_lua_init(struct lua_State *L) { @@ -1822,6 +1845,7 @@ mod_lua_init(struct lua_State *L) tarantool_lua_register_type(L, tuplelib_name, lbox_tuple_meta); luaL_register(L, tuplelib_name, lbox_tuplelib); lua_pop(L, 1); + schema_lua_init(L); tarantool_lua_register_type(L, tuple_iteratorlib_name, lbox_tuple_iterator_meta); luaL_register(L, "box", boxlib); diff --git a/src/box/box_lua_space.cc b/src/box/box_lua_space.cc index 443fe1d5effc397d5f587872061cea6e45924802..182866b6fbc4f9c401feb8a308abd26c2daf77e1 100644 --- a/src/box/box_lua_space.cc +++ b/src/box/box_lua_space.cc @@ -59,9 +59,8 @@ lbox_pushspace(struct lua_State *L, struct space *space) lua_pushnumber(L, space_id(space)); lua_settable(L, -3); - /* all exists spaces are enabled */ lua_pushstring(L, "enabled"); - lua_pushboolean(L, 1); + lua_pushboolean(L, space->engine.state != READY_NO_KEYS); lua_settable(L, -3); /* space.index */ @@ -115,12 +114,16 @@ lbox_pushspace(struct lua_State *L, struct space *space) lua_settable(L, -3); /* push space.index */ lua_getfield(L, LUA_GLOBALSINDEX, "box"); - lua_pushstring(L, "bless_space"); + lua_pushstring(L, "schema"); + lua_gettable(L, -2); + lua_pushstring(L, "space"); + lua_gettable(L, -2); + lua_pushstring(L, "bless"); lua_gettable(L, -2); - lua_pushvalue(L, -3); /* box, bless, space */ + lua_pushvalue(L, -5); /* box, schema, space, bless, space */ lua_call(L, 1, 0); - lua_pop(L, 1); /* cleanup stack */ + lua_pop(L, 3); /* cleanup stack - box, schema, space */ return 1; } diff --git a/src/box/key_def.cc b/src/box/key_def.cc index 28d09b84b476d076e24d94813c657d42bca46374..c0b7f76b76db891967cd07a52c66e124b31a1bfd 100644 --- a/src/box/key_def.cc +++ b/src/box/key_def.cc @@ -28,6 +28,7 @@ */ #include "key_def.h" #include <stdlib.h> +#include "exception.h" const char *field_type_strs[] = {"UNKNOWN", "NUM", "NUM64", "STR", "\0"}; STRS(index_type, ENUM_INDEX_TYPE); @@ -54,3 +55,102 @@ key_def_delete(struct key_def *key_def) { free(key_def); } + +int +key_part_cmp(const struct key_part *parts1, uint32_t part_count1, + const struct key_part *parts2, uint32_t part_count2) +{ + const struct key_part *part1 = parts1; + const struct key_part *part2 = parts2; + uint32_t part_count = MIN(part_count1, part_count2); + const struct key_part *end = parts1 + part_count; + for (; part1 != end; part1++, part2++) { + if (part1->fieldno != part2->fieldno) + return part1->fieldno < part2->fieldno ? -1 : 1; + if ((int) part1->type != (int) part2->type) + return (int) part1->type < (int) part2->type ? -1 : 1; + } + return part_count1 < part_count2 ? -1 : part_count1 > part_count2; +} + +int +key_def_cmp(const struct key_def *key1, const struct key_def *key2) +{ + if (key1->id != key2->id) + return key1->id < key2->id ? -1 : 1; + if (key1->type != key2->type) + return (int) key1->type < (int) key2->type ? -1 : 1; + if (key1->is_unique != key2->is_unique) + return (int) key1->is_unique < (int) key2->is_unique ? -1 : 1; + + return key_part_cmp(key1->parts, key1->part_count, + key2->parts, key2->part_count); +} + +void +key_list_del_key(struct rlist *key_list, uint32_t id) +{ + struct key_def *key; + rlist_foreach_entry(key, key_list, link) { + if (key->id == id) { + rlist_del_entry(key, link); + return; + } + } + assert(false); +} + +void +key_def_check(uint32_t id, struct key_def *key_def) +{ + if (key_def->id > BOX_INDEX_MAX) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "index id too big"); + } + if (key_def->part_count == 0) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "part count must be positive"); + } + for (uint32_t i = 0; i < key_def->part_count; i++) { + if (key_def->parts[i].type == field_type_MAX) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "unknown field type"); + } + if (key_def->parts[i].fieldno > BOX_FIELD_MAX) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "field no is too big"); + } + } + switch (key_def->type) { + case HASH: + if (! key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "HASH index must be unique"); + } + break; + case TREE: + /* TREE index has no limitations. */ + break; + case BITSET: + if (key_def->part_count != 1) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "BITSET index key can not be multipart"); + } + if (key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) id, (unsigned) key_def->id, + "BITSET can not be unique"); + } + break; + default: + tnt_raise(ClientError, ER_INDEX_TYPE, + (unsigned) id, (unsigned) key_def->id); + break; + } +} diff --git a/src/box/key_def.h b/src/box/key_def.h index 8dd62168078192f3583598a065525ed8c5328442..40aa20c8f1d987d2fd33e134a809104373883316 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -113,6 +113,35 @@ key_def_set_part(struct key_def *def, uint32_t part_no, def->parts[part_no].type = type; } +/** Compare two key part arrays. + * + * This function is used to find out whether alteration + * of an index has changed it substantially enough to warrant + * a rebuild or not. For example, change of index id is + * not a substantial change, whereas change of index type + * or key parts requires a rebuild. + * + * One key part is considered to be greater than the other if: + * - its fieldno is greater + * - given the same fieldno, NUM < NUM64 < STRING + * (coarsely speaking, based on field_type_maxlen()). + * + * A key part array is considered greater than the other if all + * its key parts are greater, or, all common key parts are equal + * but there are additional parts in the bigger array. + */ +int +key_part_cmp(const struct key_part *parts1, uint32_t part_count1, + const struct key_part *parts2, uint32_t part_count2); + +/** + * One key definition is greater than the other if it's id is + * greater, it's index type is greater (HASH < TREE < BITSET) + * or its key part array is greater. + */ +int +key_def_cmp(const struct key_def *key1, const struct key_def *key2); + /* Destroy and free a key_def. */ void key_def_delete(struct key_def *def); @@ -124,6 +153,19 @@ key_list_add_key(struct rlist *key_list, struct key_def *key) rlist_add_entry(key_list, key, link); } +/** Remove a key from the list of keys. */ +void +key_list_del_key(struct rlist *key_list, uint32_t id); + +/** + * Check a key definition for violation of various limits. + * + * @param id space id + * @param key_def key_def + * @param type_str type name (to produce a nice error) + */ +void +key_def_check(uint32_t id, struct key_def *key_def); /** Space metadata. */ struct space_def { diff --git a/src/box/lua/box.lua b/src/box/lua/box.lua index bf67a9ca563ab52f4134fd852a2614866de1576e..e43677b670661bec2b3a54907538d6da0cf674b3 100644 --- a/src/box/lua/box.lua +++ b/src/box/lua/box.lua @@ -96,117 +96,6 @@ function box.dostring(s, ...) return chunk(...) end -function box.bless_space(space) - local index_mt = {} - -- __len and __index - index_mt.len = function(index) return #index.idx end - index_mt.__newindex = function(table, index) - return error('Attempt to modify a read-only table') end - index_mt.__index = index_mt - -- min and max - index_mt.min = function(index) return index.idx:min() end - index_mt.max = function(index) return index.idx:max() end - index_mt.random = function(index, rnd) return index.idx:random(rnd) end - -- iteration - index_mt.iterator = function(index, ...) - return index.idx:iterator(...) - end - -- - -- pairs/next/prev methods are provided for backward compatibility purposes only - index_mt.pairs = function(index) - return index.idx.next, index.idx, nil - end - -- - local next_compat = function(idx, iterator_type, ...) - local arg = {...} - if #arg == 1 and type(arg[1]) == "userdata" then - return idx:next(...) - else - return idx:next(iterator_type, ...) - end - end - index_mt.next = function(index, ...) - return next_compat(index.idx, box.index.GE, ...); - end - index_mt.prev = function(index, ...) - return next_compat(index.idx, box.index.LE, ...); - end - index_mt.next_equal = function(index, ...) - return next_compat(index.idx, box.index.EQ, ...); - end - index_mt.prev_equal = function(index, ...) - return next_compat(index.idx, box.index.REQ, ...); - end - -- index subtree size - index_mt.count = function(index, ...) - return index.idx:count(...) - end - -- - index_mt.select_range = function(index, limit, ...) - local range = {} - for v in index:iterator(box.index.GE, ...) do - if #range >= limit then - break - end - table.insert(range, v) - iterator_state, v = index:next(iterator_state) - end - return unpack(range) - end - index_mt.select_reverse_range = function(index, limit, ...) - local range = {} - for v in index:iterator(box.index.LE, ...) do - if #range >= limit then - break - end - table.insert(range, v) - iterator_state, v = index:prev(iterator_state) - end - return unpack(range) - end - -- - local space_mt = {} - space_mt.len = function(space) return space.index[0]:len() end - space_mt.__newindex = index_mt.__newindex - space_mt.select = function(space, ...) return box.select(space.n, ...) end - space_mt.select_range = function(space, ino, limit, ...) - return space.index[ino]:select_range(limit, ...) - end - space_mt.select_reverse_range = function(space, ino, limit, ...) - return space.index[ino]:select_reverse_range(limit, ...) - end - space_mt.select_limit = function(space, ino, offset, limit, ...) - return box.select_limit(space.n, ino, offset, limit, ...) - end - space_mt.insert = function(space, ...) return box.insert(space.n, ...) end - space_mt.update = function(space, ...) return box.update(space.n, ...) end - space_mt.replace = function(space, ...) return box.replace(space.n, ...) end - space_mt.delete = function(space, ...) return box.delete(space.n, ...) end - space_mt.truncate = function(space) - local pk = space.index[0] - while #pk.idx > 0 do - for t in pk:iterator() do - local key = {}; - -- ipairs does not work because pk.key_field is zero-indexed - for _k2, key_field in pairs(pk.key_field) do - table.insert(key, t[key_field.fieldno]) - end - space:delete(unpack(key)) - end - end - end - space_mt.pairs = function(space) return space.index[0]:pairs() end - space_mt.__index = space_mt - - setmetatable(space, space_mt) - if type(space.index) == 'table' and space.enabled then - for j, index in pairs(space.index) do - rawset(index, 'idx', box.index.new(space.n, j)) - setmetatable(index, index_mt) - end - end -end - -- User can redefine the hook function box.on_reload_configuration() end diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua new file mode 100644 index 0000000000000000000000000000000000000000..0a699bd9c0ef0dc1bc2f84d3bac37d3afef2d655 --- /dev/null +++ b/src/box/lua/schema.lua @@ -0,0 +1,183 @@ +-- schema.lua (internal file) +-- +local ffi = require('ffi') +ffi.cdef[[ + struct space *space_by_id(uint32_t id); + void space_run_triggers(struct space *space, bool yesno); +]] + +box.schema.space = {} +box.schema.space.create = function(name, options) + local _space = box.space[box.schema.SPACE_ID] + if options == nil then + options = {} + end + local if_not_exists = options.if_not_exists + if if_not_exists and box.space[name] then + return box.space[name], "not created" + end + local id = box.unpack('i', _space.index[0]:max()[0]) + if id < box.schema.SYSTEM_ID_MAX then + id = box.schema.SYSTEM_ID_MAX + 1 + else + id = id + 1 + end + if options.arity == nil then + options.arity = 0 + end + _space:insert(id, options.arity, name) + box.space[name] = box.space[id] + return box.space[id], "created" +end +box.schema.create_space = box.schema.space.create +box.schema.space.drop = function(space_id) + local _space = box.space[box.schema.SPACE_ID] + local _index = box.space[box.schema.INDEX_ID] + local keys = { _index:select(0, space_id) } + for i = #keys, 1, -1 do + local v = keys[i] + _index:delete(v[0], v[1]) + end + _space:delete(space_id) +end + +box.schema.index = {} + +box.schema.index.create = function(space_id, index_id, index_type, ...) + local _index = box.space[box.schema.INDEX_ID] + _index:insert(space_id, index_id, index_type, ...) +end +box.schema.index.drop = function(space_id, index_id) + local _index = box.space[box.schema.INDEX_ID] + _index:delete(space_id, index_id) +end + +function box.schema.space.bless(space) + local index_mt = {} + -- __len and __index + index_mt.len = function(index) return #index.idx end + index_mt.__newindex = function(table, index) + return error('Attempt to modify a read-only table') end + index_mt.__index = index_mt + -- min and max + index_mt.min = function(index) return index.idx:min() end + index_mt.max = function(index) return index.idx:max() end + index_mt.random = function(index, rnd) return index.idx:random(rnd) end + -- iteration + index_mt.iterator = function(index, ...) + return index.idx:iterator(...) + end + -- + -- pairs/next/prev methods are provided for backward compatibility purposes only + index_mt.pairs = function(index) + return index.idx.next, index.idx, nil + end + -- + local next_compat = function(idx, iterator_type, ...) + local arg = {...} + if #arg == 1 and type(arg[1]) == "userdata" then + return idx:next(...) + else + return idx:next(iterator_type, ...) + end + end + index_mt.next = function(index, ...) + return next_compat(index.idx, box.index.GE, ...); + end + index_mt.prev = function(index, ...) + return next_compat(index.idx, box.index.LE, ...); + end + index_mt.next_equal = function(index, ...) + return next_compat(index.idx, box.index.EQ, ...); + end + index_mt.prev_equal = function(index, ...) + return next_compat(index.idx, box.index.REQ, ...); + end + -- index subtree size + index_mt.count = function(index, ...) + return index.idx:count(...) + end + -- + index_mt.select_range = function(index, limit, ...) + local range = {} + for v in index:iterator(box.index.GE, ...) do + if #range >= limit then + break + end + table.insert(range, v) + iterator_state, v = index:next(iterator_state) + end + return unpack(range) + end + index_mt.select_reverse_range = function(index, limit, ...) + local range = {} + for v in index:iterator(box.index.LE, ...) do + if #range >= limit then + break + end + table.insert(range, v) + iterator_state, v = index:prev(iterator_state) + end + return unpack(range) + end + index_mt.drop = function(index) + return box.schema.index.drop(index.n, index.id) + end + -- + local space_mt = {} + space_mt.len = function(space) return space.index[0]:len() end + space_mt.__newindex = index_mt.__newindex + space_mt.select = function(space, ...) return box.select(space.n, ...) end + space_mt.select_range = function(space, ino, limit, ...) + return space.index[ino]:select_range(limit, ...) + end + space_mt.select_reverse_range = function(space, ino, limit, ...) + return space.index[ino]:select_reverse_range(limit, ...) + end + space_mt.select_limit = function(space, ino, offset, limit, ...) + return box.select_limit(space.n, ino, offset, limit, ...) + end + space_mt.insert = function(space, ...) return box.insert(space.n, ...) end + space_mt.update = function(space, ...) return box.update(space.n, ...) end + space_mt.replace = function(space, ...) return box.replace(space.n, ...) end + space_mt.delete = function(space, ...) return box.delete(space.n, ...) end + space_mt.truncate = function(space) + local pk = space.index[0] + while #pk.idx > 0 do + for t in pk:iterator() do + local key = {}; + -- ipairs does not work because pk.key_field is zero-indexed + for _k2, key_field in pairs(pk.key_field) do + table.insert(key, t[key_field.fieldno]) + end + space:delete(unpack(key)) + end + end + end + space_mt.pairs = function(space) return space.index[0]:pairs() end + space_mt.drop = function(space) + return box.schema.space.drop(space.n) + end + space_mt.create_index = function(space, ...) + return box.schema.index.create(space.n, ...) + end + space_mt.run_triggers = function(space, yesno) + local space = ffi.C.space_by_id(space.n) + if space == nil then + box.raise(box.error.ER_NO_SUCH_SPACE, "Space not found") + end + ffi.C.space_run_triggers(space, yesno) + end + space_mt.__index = space_mt + + setmetatable(space, space_mt) + if type(space.index) == 'table' and space.enabled then + for j, index in pairs(space.index) do + rawset(index, 'idx', box.index.new(space.n, j)) + rawset(index, 'id', j) + rawset(index, 'n', space.n) + setmetatable(index, index_mt) + end + end +end + diff --git a/src/box/request.cc b/src/box/request.cc index 9e72464d7ee9cd4590412923d20953e93260a6f9..a23c872c05691d0c88b8cc3fec192fbad0f1bb9d 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -73,15 +73,10 @@ execute_replace(const struct request *request, struct txn *txn, struct tuple *new_tuple = tuple_new(space->format, field_count, &tuple, request->r.tuple_end); - try { - space_validate_tuple(space, new_tuple); - enum dup_replace_mode mode = dup_replace_mode(request->flags); - txn_replace(txn, space, NULL, new_tuple, mode); - - } catch (const Exception &e) { - tuple_delete(new_tuple); - throw; - } + TupleGuard guard(new_tuple); + space_validate_tuple(space, new_tuple); + enum dup_replace_mode mode = dup_replace_mode(request->flags); + txn_replace(txn, space, NULL, new_tuple, mode); } static void @@ -110,13 +105,9 @@ execute_update(const struct request *request, struct txn *txn, fiber->gc_pool, old_tuple, request->u.expr, request->u.expr_end); - try { - space_validate_tuple(space, new_tuple); - txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT); - } catch (const Exception &e) { - tuple_delete(new_tuple); - throw; - } + TupleGuard guard(new_tuple); + space_validate_tuple(space, new_tuple); + txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT); } /** }}} */ diff --git a/src/box/schema.cc b/src/box/schema.cc index 2ef8f3cc1b380e808570ca627a7dfd98853896ee..5cc8b72ae373710a07b3182f7ca5a99db6f7aff4 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -28,20 +28,22 @@ */ #include "schema.h" #include "space.h" +#include "tuple.h" #include "assoc.h" #include "lua/init.h" #include "box_lua_space.h" #include "key_def.h" -extern "C" { -#include <cfg/warning.h> -#include <cfg/tarantool_box_cfg.h> -} /* extern "C" */ +#include "alter.h" +#include "scoped_guard.h" /** * @module Data Dictionary * * The data dictionary is responsible for storage and caching * of system metadata, such as information about existing - * spaces, indexes, tuple formats. + * spaces, indexes, tuple formats. Space and index metadata + * is called in dedicated spaces, _space and _index respectively. + * The contents of these spaces is fully cached in a cache of + * struct space objects. * * struct space is an in-memory instance representing a single * space with its metadata, space data, and methods to manage @@ -51,11 +53,15 @@ extern "C" { /** All existing spaces. */ static struct mh_i32ptr_t *spaces; -static void -space_config(); +bool +space_is_system(struct space *space) +{ + return space->def.id > SC_SYSTEM_ID_MIN && + space->def.id < SC_SYSTEM_ID_MAX; +} /** Return space by its number */ -struct space * +extern "C" struct space * space_by_id(uint32_t id) { mh_int_t space = mh_i32ptr_find(spaces, id, NULL); @@ -71,9 +77,36 @@ void space_foreach(void (*func)(struct space *sp, void *udata), void *udata) { mh_int_t i; + struct space *space; + struct { char len; uint32_t id; } __attribute__((packed)) + key = { sizeof(uint32_t), SC_SYSTEM_ID_MIN }; + /* + * Make sure we always visit system spaces first, + * in order from lowest space id to the highest.. + * This is essential for correctly recovery from the + * snapshot, and harmless otherwise. + */ + space = space_by_id(SC_SPACE_ID); + Index *pk = space ? space_index(space, 0) : NULL; + if (pk) { + struct iterator *it = pk->allocIterator(); + auto scoped_guard = make_scoped_guard([=] { it->free(it); }); + pk->initIterator(it, ITER_GE, (char *) &key, 1); + struct tuple *tuple; + while ((tuple = it->next(it))) { + /* Get space id, primary key, field 0. */ + uint32_t id = tuple_field_u32(tuple, 0); + space = space_find(id); + if (! space_is_system(space)) + break; + func(space, udata); + } + } + mh_foreach(spaces, i) { - struct space *space = (struct space *) - mh_i32ptr_node(spaces, i)->val; + space = (struct space *) mh_i32ptr_node(spaces, i)->val; + if (space_is_system(space)) + continue; func(space, udata); } } @@ -123,6 +156,35 @@ do_one_recover_step(struct space *space, void * /* param */) space->engine = engine_no_keys; } +/** A wrapper around space_new() for data dictionary spaces. */ +struct space * +sc_space_new(struct space_def *space_def, + struct key_def *key_def, + struct trigger *trigger) +{ + struct rlist key_list; + rlist_create(&key_list); + rlist_add_entry(&key_list, key_def, link); + struct space *space = space_new(space_def, &key_list); + (void) space_cache_replace(space); + if (trigger) + trigger_set(&space->on_replace, trigger); + /* + * Data dictionary spaces are fully built since: + * - they contain data right from the start + * - they are fully operable already during recovery + * - if there is a record in the snapshot which mandates + * addition of a new index to a system space, this + * index is built tuple-by-tuple, not in bulk, which + * ensures validation of tuples when starting from + * a snapshot of older version. + */ + space->engine.recover(space); /* load snapshot - begin */ + space->engine.recover(space); /* load snapshot - end */ + space->engine.recover(space); /* build secondary keys */ + return space; +} + /** * Initialize a prototype for the two mandatory data * dictionary spaces and create a cache entry for them. @@ -134,8 +196,46 @@ schema_init() { /* Initialize the space cache. */ spaces = mh_i32ptr_new(); - space_config(); - space_foreach(do_one_recover_step, NULL); + /* + * Create surrogate space objects for the mandatory system + * spaces (the primal eggs from which we get all the + * chicken). Their definitions will be overwritten by the + * data in the snapshot, and they will thus be + * *re-created* during recovery. Note, the index type + * must be TREE and space identifiers must be the smallest + * one to ensure that these spaces are always recovered + * (and re-created) first. + */ + /* _schema - key/value space with schema description */ + struct space_def space_def = { SC_SCHEMA_ID, 0 }; + struct key_def *key_def = key_def_new(0 /* id */, + TREE /* index type */, + true /* unique */, + 1 /* part count */); + key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, STRING); + (void) sc_space_new(&space_def, key_def, NULL); + + /* _space - home for all spaces. */ + space_def.id = SC_SPACE_ID; + key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM); + + (void) sc_space_new(&space_def, key_def, + &alter_space_on_replace_space); + key_def_delete(key_def); + + /* _index - definition of indexes in all spaces */ + key_def = key_def_new(0 /* id */, + TREE /* index type */, + true /* unique */, + 2 /* part count */); + /* space no */ + key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM); + /* index no */ + key_def_set_part(key_def, 1 /* part no */, 1 /* field no */, NUM); + space_def.id = SC_INDEX_ID; + (void) sc_space_new(&space_def, key_def, + &alter_space_on_replace_index); + key_def_delete(key_def); } void @@ -173,263 +273,3 @@ schema_free(void) } mh_i32ptr_delete(spaces); } - -struct key_def * -key_def_new_from_cfg(uint32_t id, - struct tarantool_cfg_space_index *cfg_index) -{ - uint32_t part_count = 0; - enum index_type type = STR2ENUM(index_type, cfg_index->type); - - if (type == index_type_MAX) - tnt_raise(LoggedError, ER_INDEX_TYPE, cfg_index->type); - - /* Find out key part count. */ - for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) { - auto cfg_key = cfg_index->key_field[k]; - - if (cfg_key->fieldno == -1) { - /* last filled key reached */ - break; - } - part_count++; - } - - struct key_def *key= key_def_new(id, type, cfg_index->unique, - part_count); - - for (uint32_t k = 0; k < part_count; k++) { - auto cfg_key = cfg_index->key_field[k]; - - key_def_set_part(key, k, cfg_key->fieldno, - STR2ENUM(field_type, cfg_key->type)); - } - return key; -} - -static void -space_config() -{ - extern tarantool_cfg cfg; - /* exit if no spaces are configured */ - if (cfg.space == NULL) { - return; - } - - /* fill box spaces */ - for (uint32_t i = 0; cfg.space[i] != NULL; ++i) { - struct space_def space_def; - space_def.id = i; - tarantool_cfg_space *cfg_space = cfg.space[i]; - - if (!CNF_STRUCT_DEFINED(cfg_space) || !cfg_space->enabled) - continue; - - assert(cfg.memcached_port == 0 || i != cfg.memcached_space); - - space_def.arity = (cfg_space->arity != -1 ? - cfg_space->arity : 0); - - struct rlist key_defs; - rlist_create(&key_defs); - struct key_def *key; - - for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) { - auto cfg_index = cfg_space->index[j]; - key = key_def_new_from_cfg(j, cfg_index); - key_list_add_key(&key_defs, key); - } - space_cache_replace(space_new(&space_def, &key_defs)); - - say_info("space %i successfully configured", i); - } -} - -int -check_spaces(struct tarantool_cfg *conf) -{ - /* exit if no spaces are configured */ - if (conf->space == NULL) { - return 0; - } - - for (size_t i = 0; conf->space[i] != NULL; ++i) { - auto space = conf->space[i]; - - if (i >= BOX_SPACE_MAX) { - out_warning(CNF_OK, "(space = %zu) invalid id, (maximum=%u)", - i, BOX_SPACE_MAX); - return -1; - } - - if (!CNF_STRUCT_DEFINED(space)) { - /* space undefined, skip it */ - continue; - } - - if (!space->enabled) { - /* space disabled, skip it */ - continue; - } - - if (conf->memcached_port && i == conf->memcached_space) { - out_warning(CNF_OK, "Space %zu is already used as " - "memcached_space.", i); - return -1; - } - - /* at least one index in space must be defined - * */ - if (space->index == NULL) { - out_warning(CNF_OK, "(space = %zu) " - "at least one index must be defined", i); - return -1; - } - - uint32_t max_key_fieldno = 0; - - /* check spaces indexes */ - for (size_t j = 0; space->index[j] != NULL; ++j) { - auto index = space->index[j]; - uint32_t key_part_count = 0; - enum index_type index_type; - - /* check index bound */ - if (j >= BOX_INDEX_MAX) { - /* maximum index in space reached */ - out_warning(CNF_OK, "(space = %zu index = %zu) " - "too many indexed (%u maximum)", i, j, BOX_INDEX_MAX); - return -1; - } - - /* at least one key in index must be defined */ - if (index->key_field == NULL) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "at least one field must be defined", i, j); - return -1; - } - - /* check unique property */ - if (index->unique == -1) { - /* unique property undefined */ - out_warning(CNF_OK, "(space = %zu index = %zu) " - "unique property is undefined", i, j); - } - - for (size_t k = 0; index->key_field[k] != NULL; ++k) { - auto key = index->key_field[k]; - - if (key->fieldno == -1) { - /* last key reached */ - break; - } - - if (key->fieldno >= BOX_FIELD_MAX) { - /* maximum index in space reached */ - out_warning(CNF_OK, "(space = %zu index = %zu) " - "invalid field number (%u maximum)", - i, j, BOX_FIELD_MAX); - return -1; - } - - /* key must has valid type */ - if (STR2ENUM(field_type, key->type) == field_type_MAX) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "unknown field data type: `%s'", i, j, key->type); - return -1; - } - - if (max_key_fieldno < key->fieldno + 1) { - max_key_fieldno = key->fieldno + 1; - } - - ++key_part_count; - } - - /* Check key part count. */ - if (key_part_count == 0) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "at least one field must be defined", i, j); - return -1; - } - - index_type = STR2ENUM(index_type, index->type); - - /* check index type */ - if (index_type == index_type_MAX) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "unknown index type '%s'", i, j, index->type); - return -1; - } - - /* First index must be unique. */ - if (j == 0 && index->unique == false) { - out_warning(CNF_OK, "(space = %zu) space first index must be unique", i); - return -1; - } - - switch (index_type) { - case HASH: - /* check hash index */ - /* hash index must be unique */ - if (!index->unique) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "hash index must be unique", i, j); - return -1; - } - break; - case TREE: - /* extra check for tree index not needed */ - break; - case BITSET: - /* check bitset index */ - /* bitset index must has single-field key */ - if (key_part_count != 1) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "bitset index must has a single-field key", i, j); - return -1; - } - /* bitset index must not be unique */ - if (index->unique) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "bitset index must be non-unique", i, j); - return -1; - } - break; - default: - assert(false); - } - } - - /* Check for index field type conflicts */ - if (max_key_fieldno > 0) { - char *types = (char *) alloca(max_key_fieldno); - memset(types, 0, max_key_fieldno); - for (size_t j = 0; space->index[j] != NULL; ++j) { - auto index = space->index[j]; - for (size_t k = 0; index->key_field[k] != NULL; ++k) { - auto key = index->key_field[k]; - if (key->fieldno == -1) - break; - - uint32_t f = key->fieldno; - enum field_type t = STR2ENUM(field_type, key->type); - assert(t != field_type_MAX); - if (types[f] != t) { - if (types[f] == UNKNOWN) { - types[f] = t; - } else { - out_warning(CNF_OK, "(space = %zu fieldno = %zu) " - "index field type mismatch", i, f); - return -1; - } - } - } - - } - } - } - - return 0; -} - diff --git a/src/box/schema.h b/src/box/schema.h index 89a9fbda24d213e5590737c11828d627b4c91465..15eb1653b84dc7156e3b4537ec52322415e3e523 100644 --- a/src/box/schema.h +++ b/src/box/schema.h @@ -30,6 +30,22 @@ */ #include "exception.h" +enum schema_id { + /** Start of the reserved range of system spaces. */ + SC_SYSTEM_ID_MIN = 256, + /** Space id of _schema. */ + SC_SCHEMA_ID = 272, + /** Space id of _space. */ + SC_SPACE_ID = 280, + /** Space id of _index. */ + SC_INDEX_ID = 288, + /** End of the reserved range of system spaces. */ + SC_SYSTEM_ID_MAX = 511 +}; + +enum schema_field_id { +}; + struct space; /** Call a visitor function on every space in the space cache. */ @@ -38,10 +54,11 @@ space_foreach(void (*func)(struct space *sp, void *udata), void *udata); /** * Try to look up a space by space number in the space cache. + * FFI-friendly no-exception-thrown space lookup function. * * @return NULL if space not found, otherwise space object. */ -struct space * +extern "C" struct space * space_by_id(uint32_t id); static inline struct space * @@ -66,6 +83,9 @@ space_cache_replace(struct space *space); struct space * space_cache_delete(uint32_t id); +bool +space_is_system(struct space *space); + void schema_init(); @@ -86,9 +106,7 @@ space_end_recover_snapshot(); void space_end_recover(); -struct tarantool_cfg; +struct space *schema_space(uint32_t id); -int -check_spaces(struct tarantool_cfg *conf); #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */ diff --git a/src/box/space.cc b/src/box/space.cc index 186a701358b7bcf87d2913e391fde8b04f868a04..9aca827cc003581734ea154ecc5bcc2022e694bc 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -80,6 +80,8 @@ space_new(struct space_def *space_def, struct rlist *key_list) } space_fill_index_map(space); space->engine = engine_no_keys; + rlist_create(&space->on_replace); + space->run_triggers = true; return space; error: space_delete(space); @@ -186,6 +188,12 @@ space_replace_all_keys(struct space *space, struct tuple *old_tuple, return NULL; } +uint32_t +space_size(struct space *space) +{ + return space_index(space, 0)->size(); +} + /** * Secondary indexes are built in bulk after all data is * recovered. This function enables secondary keys on a space. @@ -289,4 +297,29 @@ space_validate_tuple(struct space *sp, struct tuple *new_tuple) } +void +space_dump_def(const struct space *space, struct rlist *key_list) +{ + rlist_create(key_list); + + for (int j = 0; j < space->index_count; j++) + rlist_add_tail_entry(key_list, space->index[j]->key_def, + link); +} + +void +space_swap_index(struct space *lhs, struct space *rhs, uint32_t lhs_id, + uint32_t rhs_id) +{ + Index *tmp = lhs->index_map[lhs_id]; + lhs->index_map[lhs_id] = rhs->index_map[rhs_id]; + rhs->index_map[rhs_id] = tmp; +} + +extern "C" void +space_run_triggers(struct space *space, bool yesno) +{ + space->run_triggers = yesno; +} + /* vim: set fm=marker */ diff --git a/src/box/space.h b/src/box/space.h index 91aa007054bb96fc203864afdf08604bc8f81a63..fa494e1bdb92b2cea8d5d1df2164e4d8768f137a 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -30,6 +30,7 @@ */ #include "index.h" #include "key_def.h" +#include "rlist.h" #include <exception.h> typedef void (*space_f)(struct space *space); @@ -83,6 +84,9 @@ struct space { * deletion and addition of indexes. */ struct engine engine; + + /** Triggers fired after space_replace() -- see txn_replace(). */ + struct rlist on_replace; /** * The number of *enabled* indexes in the space. * @@ -99,6 +103,8 @@ struct space { uint32_t index_id_max; /** Space meta. */ struct space_def def; + /** Enable/disable triggers. */ + bool run_triggers; /** Default tuple format used by this space */ struct tuple_format *format; @@ -213,6 +219,9 @@ space_replace(struct space *space, struct tuple *old_tuple, mode); } +uint32_t +space_size(struct space *space); + /** * Check that the tuple has correct arity and correct field * types (a pre-requisite for an INSERT). @@ -232,6 +241,26 @@ space_new(struct space_def *space_def, struct rlist *key_list); void space_delete(struct space *space); +/** + * Dump space definition (key definitions, key count) + * for ALTER. + */ +void +space_dump_def(const struct space *space, struct rlist *key_list); + +/** + * Exchange two index objects in two spaces. Used + * to update a space with a newly built index, while + * making sure the old index doesn't leak. + */ +void +space_swap_index(struct space *lhs, struct space *rhs, uint32_t lhs_id, + uint32_t rhs_id); + +/** Rebuild index map in a space after a series of swap index. */ +void +space_fill_index_map(struct space *space); + /** * Get index by index id. * @return NULL if the index is not found. @@ -258,4 +287,7 @@ index_find(struct space *space, uint32_t index_id) return index; } +extern "C" void +space_run_triggers(struct space *space, bool yesno); + #endif /* TARANTOOL_BOX_SPACE_H_INCLUDED */ diff --git a/src/box/tuple.cc b/src/box/tuple.cc index ed370b1aee59791f3b960852ddcd84c3937a338d..d9b458b0deba9e8a040903e7a27c35c8fdf20976 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -341,6 +341,8 @@ const char * tuple_seek(struct tuple_iterator *it, uint32_t i, uint32_t *len) { it->pos = tuple_field_old(tuple_format(it->tuple), it->tuple, i); + it->fieldno = it->pos == it->tuple->data + it->tuple->bsize ? + it->tuple->field_count : i; return tuple_next(it, len); } @@ -353,11 +355,69 @@ tuple_next(struct tuple_iterator *it, uint32_t *len) const char *field = it->pos; it->pos += *len; assert(it->pos <= tuple_end); + it->fieldno++; return field; } return NULL; } +static const char * +tuple_field_to_cstr(const char *field, uint32_t len, uint32_t field_index) +{ + if (field == NULL) + tnt_raise(ClientError, ER_NO_SUCH_FIELD, field_index); + static __thread char buf[256]; + len = MIN(len, sizeof(buf) - 1); + memcpy(buf, field, len); + buf[len] = '\0'; + return buf; +} + +static uint32_t +tuple_field_to_u32(const char *field, uint32_t len, uint32_t field_index) +{ + if (field == NULL) + tnt_raise(ClientError, ER_NO_SUCH_FIELD, field_index); + if (len != sizeof(uint32_t)) + tnt_raise(ClientError, ER_FIELD_TYPE, field_index, + field_type_strs[NUM]); + return pick_u32(&field, field + len); +} + +const char * +tuple_next_cstr(struct tuple_iterator *it) +{ + uint32_t len; + int fieldno = it->fieldno; + const char *field = tuple_next(it, &len); + return tuple_field_to_cstr(field, len, fieldno); +} + +uint32_t +tuple_next_u32(struct tuple_iterator *it) +{ + uint32_t len; + int fieldno = it->fieldno; + const char *field = tuple_next(it, &len); + return tuple_field_to_u32(field, len, fieldno); +} + +uint32_t +tuple_field_u32(struct tuple *tuple, uint32_t i) +{ + uint32_t len; + const char *field = tuple_field(tuple, i, &len); + return tuple_field_to_u32(field, len, i); +} + +const char * +tuple_field_cstr(struct tuple *tuple, uint32_t i) +{ + uint32_t len; + const char *field = tuple_field(tuple, i, &len); + return tuple_field_to_cstr(field, len, i); +} + /** print field to tbuf */ static void print_field(struct tbuf *buf, const char *field, uint32_t len) diff --git a/src/box/tuple.h b/src/box/tuple.h index 2ea4442ab48b6438fb89d0f073e87f481e3cc8ed..8e55eecc4a8bf818cd8da30120b378114586e280 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -175,6 +175,16 @@ tuple_new(struct tuple_format *format, uint32_t field_count, void tuple_ref(struct tuple *tuple, int count); +void +tuple_delete(struct tuple *tuple); + +/** Make tuple references exception-friendly in absence of @finally. */ +struct TupleGuard { + struct tuple *tuple; + TupleGuard(struct tuple *arg) :tuple(arg) {} + ~TupleGuard() { if (tuple->refs == 0) tuple_delete(tuple); } +}; + /** * @brief Return a tuple format instance * @param tuple tuple @@ -217,6 +227,20 @@ tuple_field(const struct tuple *tuple, uint32_t i, uint32_t *len) return NULL; } +/** + * A convenience shortcut for data dictionary - get a tuple field + * as uint32_t. + */ +uint32_t +tuple_field_u32(struct tuple *tuple, uint32_t i); + +/** + * A convenience shortcut for data dictionary - get a tuple field + * as a NUL-terminated string - returns a string of up to 256 bytes. + */ +const char * +tuple_field_cstr(struct tuple *tuple, uint32_t i); + /** * @brief Tuple Interator */ @@ -227,6 +251,8 @@ struct tuple_iterator { /** Always points to the beginning of the next field. */ const char *pos; /** @endcond **/ + /** field no of the next field. */ + int fieldno; }; /** @@ -251,6 +277,7 @@ tuple_rewind(struct tuple_iterator *it, const struct tuple *tuple) { it->tuple = tuple; it->pos = tuple->data; + it->fieldno = 0; } /** @@ -270,6 +297,22 @@ tuple_seek(struct tuple_iterator *it, uint32_t field_no, uint32_t *len); const char * tuple_next(struct tuple_iterator *it, uint32_t *len); +/** + * A convenience shortcut for the data dictionary - get next field + * from iterator as uint32_t or raise an error if there is + * no next field. + */ +uint32_t +tuple_next_u32(struct tuple_iterator *it); + +/** + * A convenience shortcut for the data dictionary - get next field + * from iterator as a C string or raise an error if there is no + * next field. + */ +const char * +tuple_next_cstr(struct tuple_iterator *it); + /** * @brief Print a tuple in yaml-compatible mode to tbuf: * key: { value, value, value } @@ -304,7 +347,6 @@ tuple_range_size(const char **begin, const char *end, uint32_t count) return *begin - start; } -void tuple_delete(struct tuple *tuple); /** * @brief Compare two tuples using field by field using key definition diff --git a/src/box/txn.cc b/src/box/txn.cc index 0214a4e5150499a05364c155d20c08385a63dd40..96f4f2316c911b13a682a5e2a80fbd972f466fa7 100644 --- a/src/box/txn.cc +++ b/src/box/txn.cc @@ -62,12 +62,20 @@ txn_replace(struct txn *txn, struct space *space, tuple_ref(txn->new_tuple, 1); } txn->space = space; + /* + * Run on_replace triggers. For now, disallow mutation + * of tuples in the trigger. + */ + if (! rlist_empty(&space->on_replace) && space->run_triggers) + trigger_run(&space->on_replace, txn); } struct txn * txn_begin() { struct txn *txn = (struct txn *) p0alloc(fiber->gc_pool, sizeof(*txn)); + rlist_create(&txn->on_commit); + rlist_create(&txn->on_rollback); return txn; } @@ -93,8 +101,14 @@ txn_commit(struct txn *txn) tnt_raise(LoggedError, ER_WAL_IO); } + trigger_run(&txn->on_commit, txn); /* must not throw. */ } +/** + * txn_finish() follows txn_commit() on success. + * It's moved to a separate call to be able to send + * old tuple to the user before it's deleted. + */ void txn_finish(struct txn *txn) { @@ -107,7 +121,9 @@ void txn_rollback(struct txn *txn) { if (txn->old_tuple || txn->new_tuple) { - space_replace(txn->space, txn->new_tuple, txn->old_tuple, DUP_INSERT); + space_replace(txn->space, txn->new_tuple, + txn->old_tuple, DUP_INSERT); + trigger_run(&txn->on_rollback, txn); /* must not throw. */ if (txn->new_tuple) tuple_ref(txn->new_tuple, -1); } diff --git a/src/box/txn.h b/src/box/txn.h index 32520ca68d5336158e9576aacbba59466a6b0aff..3d6526d37fdb8eb819c81dd328e8b0fdd8fc0296 100644 --- a/src/box/txn.h +++ b/src/box/txn.h @@ -29,6 +29,7 @@ * SUCH DAMAGE. */ #include "index.h" +#include "trigger.h" struct tuple; struct space; @@ -39,6 +40,9 @@ struct txn { struct tuple *old_tuple; struct tuple *new_tuple; + struct rlist on_commit; + struct rlist on_rollback; + /* Redo info: binary packet */ const char *data; uint32_t len; diff --git a/src/lua/init.cc b/src/lua/init.cc index 9462fc01939dc12daeb7176807409edfd1b77adf..aef9371b39e39a406f2e21e290dc63452b75e05d 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -528,7 +528,7 @@ tarantool_lua_init() tarantool_lua_setpath(L, "cpath", LUA_LIBCPATH, LUA_SYSCPATH, NULL); - /* Loadi 'ffi' extension and make it inaccessible */ + /* Load 'ffi' extension and make it inaccessible */ lua_getglobal(L, "require"); lua_pushstring(L, "ffi"); if (lua_pcall(L, 1, 0, 0) != 0) diff --git a/src/memcached.cc b/src/memcached.cc index 3aa6836ef8eec77521612be1f70a0c42f3fdddc0..fc28c516c65bbc70013cdbc90d18a2404772b551 100644 --- a/src/memcached.cc +++ b/src/memcached.cc @@ -33,10 +33,10 @@ #include "box/box.h" #include "box/request.h" +#include "box/schema.h" #include "box/space.h" #include "box/port.h" #include "box/tuple.h" -#include "box/schema.h" #include "fiber.h" extern "C" { #include <cfg/warning.h> @@ -59,7 +59,6 @@ ENUM(memcached_stat, STAT); STRS(memcached_stat, STAT); static int stat_base; -static struct fiber *memcached_expire = NULL; static Index *memcached_index; static struct iterator *memcached_it; @@ -477,6 +476,8 @@ memcached_free(void) memcached_it->free(memcached_it); } +void +memcached_start_expire(); void memcached_init(const char *bind_ipaddr, int memcached_port) @@ -488,15 +489,13 @@ memcached_init(const char *bind_ipaddr, int memcached_port) stat_base = stat_register(memcached_stat_strs, memcached_stat_MAX); - struct space *sp = space_by_id(cfg.memcached_space); - memcached_index = space_index(sp, 0); - /* run memcached server */ static struct coio_service memcached; coio_service_init(&memcached, "memcached", bind_ipaddr, memcached_port, memcached_handler, NULL); evio_service_start(&memcached.evio_service); + memcached_start_expire(); } void @@ -555,6 +554,7 @@ memcached_delete_expired_keys(struct tbuf *keys_to_delete) void memcached_expire_loop(va_list ap __attribute__((unused))) { + memcached_space_init(); struct tuple *tuple = NULL; say_info("memcached expire fiber started"); @@ -593,10 +593,10 @@ memcached_expire_loop(va_list ap __attribute__((unused))) void memcached_start_expire() { + struct fiber *memcached_expire; if (cfg.memcached_port == 0 || cfg.memcached_expire == 0) return; - assert(memcached_expire == NULL); try { memcached_expire = fiber_new("memcached_expire", memcached_expire_loop); @@ -607,11 +607,13 @@ void memcached_start_expire() fiber_call(memcached_expire); } -void memcached_stop_expire() +int +memcached_reload_config(struct tarantool_cfg *oldcfg, + struct tarantool_cfg *newcfg) { - if (cfg.memcached_port == 0 || cfg.memcached_expire == 0) - return; - assert(memcached_expire != NULL); - fiber_cancel(memcached_expire); - memcached_expire = NULL; + (void) oldcfg; + (void) newcfg; + if (newcfg->memcached_port == 0) + return 0; + return -1; } diff --git a/src/tarantool.cc b/src/tarantool.cc index 12ff10034021df8bc089ff11e5843906d1312802..f966168b80c70ceb480b41804c73d17d2c016b57 100644 --- a/src/tarantool.cc +++ b/src/tarantool.cc @@ -169,6 +169,10 @@ load_cfg(struct tarantool_cfg *conf, int32_t check_rdonly) if (replication_check_config(conf) != 0) return -1; + /* check memcached configuration */ + if (memcached_check_config(conf) != 0) + return -1; + return box_check_config(conf); } @@ -279,6 +283,9 @@ reload_cfg(struct tbuf *out) /* Now pass the config to the module, to take action. */ if (box_reload_config(&cfg, &new_cfg) != 0) return -1; + + if (memcached_reload_config(&cfg, &new_cfg) != 0) + return -1; /* All OK, activate the config. */ swap_tarantool_cfg(&cfg, &new_cfg); tarantool_lua_load_cfg(tarantool_L, &cfg); @@ -857,16 +864,12 @@ main(int argc, char **argv) tarantool_L = tarantool_lua_init(); box_init(); atexit(tarantool_lua_free); - memcached_init(cfg.bind_ipaddr, cfg.memcached_port); tarantool_lua_load_cfg(tarantool_L, &cfg); /* - * init iproto before admin and after memcached: + * init iproto before admin: * recovery is finished on bind to the primary port, * and it has to happen before requests on the * administrative port start to arrive. - * And when recovery is finalized, memcached - * expire loop is started, so binding can happen - * only after memcached is initialized. */ iproto_init(cfg.bind_ipaddr, cfg.primary_port, cfg.secondary_port); @@ -880,6 +883,12 @@ main(int argc, char **argv) * initialized. */ tarantool_lua_load_init_script(tarantool_L); + /* + * And when recovery is finalized, memcached + * expire loop is started, so binding can happen + * only after memcached is initialized. + */ + memcached_init(cfg.bind_ipaddr, cfg.memcached_port); prelease(fiber->gc_pool); say_crit("log level %i", cfg.log_level); say_crit("entering the event loop"); diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index 34cbe57003bb885a83535c4b9ec96221c818e2b0..194668fc5b498beaeddd3c537b2787e869c06516 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -33,7 +33,7 @@ ER = { 5: "ER_INDEX_TYPE" , 6: "ER_SPACE_EXISTS" , 7: "ER_MEMORY_ISSUE" , - 8: "ER_UNUSED8" , + 8: "ER_CREATE_SPACE" , 9: "ER_INJECTION" , 10: "ER_UNSUPPORTED" , 11: "ER_RESERVED11" , @@ -49,13 +49,13 @@ ER = { 21: "ER_RESERVED21" , 22: "ER_RESERVED22" , 23: "ER_RESERVED23" , - 24: "ER_UNUSED24" , - 25: "ER_UNUSED25" , + 24: "ER_DROP_SPACE" , + 25: "ER_ALTER_SPACE" , 26: "ER_FIBER_STACK" , - 27: "ER_UNUSED27" , + 27: "ER_MODIFY_INDEX" , 28: "ER_TUPLE_FORMAT_LIMIT" , - 29: "ER_UNUSED29" , - 30: "ER_UNUSED30" , + 29: "ER_LAST_DROP" , + 30: "ER_DROP_PRIMARY_KEY" , 31: "ER_UNUSED31" , 32: "ER_UNUSED32" , 33: "ER_UNUSED33" ,