From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng2.m.smailru.net (smtpng2.m.smailru.net [94.100.179.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 66A3E430D58 for ; Thu, 7 Nov 2019 13:36:22 +0300 (MSK) From: imeevma@tarantool.org Date: Thu, 7 Nov 2019 13:36:21 +0300 Message-Id: <3d55476778a0b8be41703ccdc4d25d558b861dfd.1573121685.git.imeevma@gmail.com> In-Reply-To: References: Subject: [Tarantool-patches] [PATCH v3 2/5] box: introdice _vsession_settings sysview List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: v.shpilevoy@tarantool.org Cc: tarantool-patches@dev.tarantool.org Hi! Thank you for review! My answers and new patch below. On 11/2/19 7:17 PM, Vladislav Shpilevoy wrote: > Hi! Thanks for the fixes! > > See 7 comments below. > >> diff --git a/src/box/session.cc b/src/box/session.cc >> index d69b657..6d301bf 100644 >> --- a/src/box/session.cc >> +++ b/src/box/session.cc >> @@ -361,3 +364,135 @@ generic_session_sync(struct session *session) >> (void) session; >> return 0; >> } >> + >> +int >> +session_options_get(struct index *index, const char *key, uint32_t part_count, >> + struct tuple **result) >> +{ >> + assert(part_count == 1); >> + (void)part_count; >> + struct space *space = space_cache_find(index->def->space_id); >> + uint32_t len; >> + const char *name = mp_decode_str(&key, &len); >> + name = tt_cstr(name, len); >> + /* >> + * Currently, the only session local options are SQL >> + * options. >> + */ >> + uint32_t id_max = sql_option_id_max(); >> + for (uint32_t id = 0; id < id_max; ++id) { >> + if (sql_option_compare(name, id) == 0) >> + return sql_option_tuple(space->format, id, result); >> + } >> + *result = NULL; >> + return 0; >> +} >> + >> +/** >> + * An iterator that iterates over current session options. >> + */ >> +struct session_options_iterator { >> + /** Base iterator. Must be the first member. */ >> + struct iterator base; >> + /** Format of the tuples this iterator returns. */ >> + struct tuple_format *format; >> + /** Counter, count number of options already shown. */ >> + uint32_t counter; >> + /** Decoded key. */ >> + char *key; >> + /** Type of iterator. */ >> + enum iterator_type iterator_type; >> +}; >> + >> +static int >> +session_options_iterator_next(struct iterator *itr, struct tuple **ret) >> +{ >> + struct session_options_iterator *it = >> + (struct session_options_iterator *)itr; >> + uint32_t id_max = sql_option_id_max(); >> + if (it->key == NULL) >> + return sql_option_tuple(it->format, it->counter++, ret); >> + for (; it->counter < id_max; ++it->counter) { >> + int compare = sql_option_compare(it->key, it->counter); >> + if (compare == 0 && (it->iterator_type == ITER_EQ || >> + it->iterator_type == ITER_GE || >> + it->iterator_type == ITER_ALL)) >> + break; >> + if (compare > 0 && (it->iterator_type == ITER_GT || >> + it->iterator_type == ITER_GE || >> + it->iterator_type == ITER_ALL)) >> + break; >> + } >> + if (it->counter < id_max) >> + return sql_option_tuple(it->format, it->counter++, ret); >> + *ret = NULL; >> + return 0; >> +} >> + >> +static int >> +session_options_iterator_prev(struct iterator *itr, struct tuple **ret) >> +{ >> + struct session_options_iterator *it = >> + (struct session_options_iterator *)itr; >> + uint32_t id_max = sql_option_id_max(); >> + if (it->key == NULL) >> + return sql_option_tuple(it->format, id_max - 1 - it->counter++, >> + ret); >> + for (; it->counter < id_max; ++it->counter) { >> + uint32_t id = id_max - it->counter - 1; > > 1. These 'id_max - 1 - counter' look really complex. Could you > simplify it? For example, rename it to 'current_id', and in > iterator_prev() decrement it instead of increment. > Fixed. >> + int compare = sql_option_compare(it->key, id); >> + if (compare == 0 && (it->iterator_type == ITER_REQ || >> + it->iterator_type == ITER_LE)) >> + break; >> + if (compare < 0 && (it->iterator_type == ITER_LT || >> + it->iterator_type == ITER_LE)) >> + break; >> + } >> + if (it->counter < id_max) >> + return sql_option_tuple(it->format, id_max - 1 - it->counter++, >> + ret); >> + *ret = NULL; >> + return 0; >> +} >> + >> +static void >> +session_options_iterator_free(struct iterator *itr) >> +{ >> + struct session_options_iterator *it = >> + (struct session_options_iterator *)itr; >> + free(it->key); >> + free(it); >> +} >> + >> +struct iterator * >> +session_options_create_iterator(struct index *index, enum iterator_type type, >> + const char *key, uint32_t part_count) >> +{ >> + char *decoded_key = NULL; >> + if (part_count > 0) { >> + assert(part_count == 1); >> + assert(mp_typeof(*key) == MP_STR); >> + uint32_t len; >> + const char *name = mp_decode_str(&key, &len); >> + name = tt_cstr(name, len); >> + decoded_key = (char *)malloc(len + 1); > > 2. I guess, you don't need tt_cstr() here, if you anyway > copy the key onto the heap memory. > Fixed. > 3. You don't check malloc() result. > Fixed. >> + memcpy(decoded_key, name, len + 1); >> + } >> + struct space *space = space_cache_find(index->def->space_id); >> + struct session_options_iterator *it = >> + (session_options_iterator *)malloc(sizeof(*it)); >> + if (it == NULL) { >> + diag_set(OutOfMemory, sizeof(*it), "malloc", "it"); >> + return NULL; >> + } >> + iterator_create(&it->base, index); >> + it->base.next = iterator_type_is_reverse(type) ? >> + session_options_iterator_prev : >> + session_options_iterator_next; >> + it->base.free = session_options_iterator_free; >> + it->key = decoded_key; >> + it->iterator_type = type; >> + it->format = space->format; >> + it->counter = 0; >> + return (struct iterator *)it; >> +} >> diff --git a/src/box/sql/build.c b/src/box/sql/build.c >> index 51cd7ce..dceb092 100644 >> --- a/src/box/sql/build.c >> +++ b/src/box/sql/build.c >> @@ -3242,3 +3243,151 @@ sql_fieldno_by_name(struct Parse *parse_context, struct Expr *field_name, >> *fieldno = i; >> return 0; >> } >> + >> +/** >> + * Identifiers of all SQL options that can be viewed. The >> + * identifier of the option is equal to its place in the sorted >> + * list of options, which starts at 0. >> + * >> + * It is IMPORTANT that SQL options are sorted by name. If this is >> + * not the case, the result returned by the _vsession_settings >> + * space iterator will not be sorted properly. >> + */ >> +enum { >> + SQL_OPTION_COMPOUND_SELECT_LIMIT = 0, >> + SQL_OPTION_DEFAULT_ENGINE, >> + SQL_OPTION_DEFER_FOREIGN_KEYS, >> + SQL_OPTION_FULL_COLUMN_NAMES, >> +#ifndef NDEBUG >> + SQL_OPTION_PARSER_TRACE, >> +#endif >> + SQL_OPTION_RECURSIVE_TRIGGERS, >> + SQL_OPTION_REVERSE_UNORDERED_SELECTS, >> +#ifndef NDEBUG >> + SQL_OPTION_SELECT_TRACE, >> + SQL_OPTION_TRACE, >> + SQL_OPTION_VDBE_ADDOPTRACE, >> + SQL_OPTION_VDBE_DEBUG, >> + SQL_OPTION_VDBE_EQP, >> + SQL_OPTION_VDBE_LISTING, >> + SQL_OPTION_VDBE_TRACE, >> + SQL_OPTION_WHERE_TRACE, >> +#endif >> + SQL_OPTION_max, >> +}; >> + >> +/** >> + * A local structure that allows to establish a connection between >> + * the name of the parameter, its field type and mask, if it have >> + * one. >> + */ >> +struct sql_option_metadata >> +{ >> + const char *name; >> + uint32_t field_type; >> + uint32_t mask; >> +}; >> + >> +/** >> + * Variable that contains names of the SQL options, their field >> + * types and mask if they have one or 0 if don't have. >> + * >> + * It is IMPORTANT that these options sorted by name. >> + */ >> +static struct sql_option_metadata sql_options[] = { >> + /** SQL_OPTION_COMPOUND_SELECT_LIMIT */ >> + {"sql_compound_select_limit", FIELD_TYPE_INTEGER, 0}, >> + /** SQL_OPTION_DEFAULT_ENGINE */ >> + {"sql_default_engine", FIELD_TYPE_STRING, 0}, >> + /** SQL_OPTION_DEFER_FOREIGN_KEYS */ >> + {"sql_defer_foreign_keys", FIELD_TYPE_BOOLEAN, SQL_DeferFKs}, >> + /** SQL_OPTION_FULL_COLUMN_NAMES */ >> + {"sql_full_column_names", FIELD_TYPE_BOOLEAN, SQL_FullColNames}, >> +#ifndef NDEBUG >> + /** SQL_OPTION_PARSER_TRACE */ >> + {"sql_parser_trace", FIELD_TYPE_BOOLEAN, PARSER_TRACE_FLAG}, >> +#endif >> + /** SQL_OPTION_RECURSIVE_TRIGGERS */ >> + {"sql_recursive_triggers", FIELD_TYPE_BOOLEAN, SQL_RecTriggers}, >> + /** SQL_OPTION_REVERSE_UNORDERED_SELECTS */ >> + {"sql_reverse_unordered_selects", FIELD_TYPE_BOOLEAN, SQL_ReverseOrder}, >> +#ifndef NDEBUG >> + /** SQL_OPTION_SELECT_TRACE */ >> + {"sql_select_trace", FIELD_TYPE_BOOLEAN, SQL_SelectTrace}, >> + /** SQL_OPTION_TRACE */ >> + {"sql_trace", FIELD_TYPE_BOOLEAN, SQL_SqlTrace}, >> + /** SQL_OPTION_VDBE_ADDOPTRACE */ >> + {"sql_vdbe_addoptrace", FIELD_TYPE_BOOLEAN, SQL_VdbeAddopTrace}, >> + /** SQL_OPTION_VDBE_DEBUG */ >> + {"sql_vdbe_debug", FIELD_TYPE_BOOLEAN, >> + SQL_SqlTrace | SQL_VdbeListing | SQL_VdbeTrace}, >> + /** SQL_OPTION_VDBE_EQP */ >> + {"sql_vdbe_eqp", FIELD_TYPE_BOOLEAN, SQL_VdbeEQP}, >> + /** SQL_OPTION_VDBE_LISTING */ >> + {"sql_vdbe_listing", FIELD_TYPE_BOOLEAN, SQL_VdbeListing}, >> + /** SQL_OPTION_VDBE_TRACE */ >> + {"sql_vdbe_trace", FIELD_TYPE_BOOLEAN, SQL_VdbeTrace}, >> + /** SQL_OPTION_WHERE_TRACE */ >> + {"sql_where_trace", FIELD_TYPE_BOOLEAN, SQL_WhereTrace}, >> +#endif >> +}; >> + >> +uint32_t >> +sql_option_id_max() >> +{ >> + return SQL_OPTION_max; >> +} >> + >> +int >> +sql_option_compare(const char *key, uint32_t id) >> +{ >> + assert(id < SQL_OPTION_max); >> + return strcmp(sql_options[id].name, key); >> +} >> + >> +int >> +sql_option_tuple(struct tuple_format *format, int option_id, >> + struct tuple **result) >> +{ >> + if (option_id < 0 || option_id >= SQL_OPTION_max) { >> + *result = NULL; >> + return 0; >> + } >> + int limit = 0; >> + const char *engine = NULL; >> + struct session *session = current_session(); >> + uint32_t flags = session->sql_flags; >> + uint32_t mask = sql_options[option_id].mask; >> + /* Tuple format contains two fields - name and value. */ >> + uint32_t column_count = format->min_field_count; >> + assert(column_count == 2); >> + size_t size = mp_sizeof_array(column_count) + >> + mp_sizeof_str(strlen(sql_options[option_id].name)); >> + if (sql_options[option_id].field_type == FIELD_TYPE_BOOLEAN) { >> + size += mp_sizeof_bool(true); >> + } else if (option_id == SQL_OPTION_DEFAULT_ENGINE) { >> + engine = sql_storage_engine_strs[session->sql_default_engine]; >> + size += mp_sizeof_str(strlen(engine)); >> + } else { >> + assert(option_id == SQL_OPTION_COMPOUND_SELECT_LIMIT); >> + limit = sql_get()->aLimit[SQL_LIMIT_COMPOUND_SELECT]; >> + size += mp_sizeof_uint(limit); > > 4. I propose you to remove compound select setting from there. Since > it is not session local. > Done. > Another question is where to show its value then? Certainly not > here. > >> + } >> + >> + char *pos_ret = static_alloc(size); >> + assert(pos_ret != NULL); >> + char *pos = mp_encode_array(pos_ret, column_count); >> + pos = mp_encode_str(pos, sql_options[option_id].name, >> + strlen(sql_options[option_id].name)); >> + if (sql_options[option_id].field_type == FIELD_TYPE_BOOLEAN) >> + pos = mp_encode_bool(pos, (flags & mask) == mask); >> + else if (option_id == SQL_OPTION_DEFAULT_ENGINE) >> + pos = mp_encode_str(pos, engine, strlen(engine)); >> + else >> + pos = mp_encode_uint(pos, limit); >> + struct tuple *tuple = tuple_new(format, pos_ret, pos_ret + size); >> + if (tuple == NULL) >> + return -1; >> + *result = tuple; >> + return 0; >> +} >> diff --git a/test/box/misc.result b/test/box/misc.result >> index b293051..1edd59e 100644 >> --- a/test/box/misc.result >> +++ b/test/box/misc.result >> @@ -1330,3 +1330,174 @@ test_run:grep_log('default', 'test log') >> --- >> - test log >> ... >> +-- >> +-- gh-4511: make sure that _vsession_settings sysview works as >> +-- intended. > > 5. I just realized that probably Kirill would want it to be in a > separate file. But if he doesn't, then probably access_sysview.test.lua > fits better than misc. > Fixed, moved to access_sysview.test.lua. >> +-- >> +v = box.space._vsession_settings >> +--- >> +... >> +option_count = v:count() >> +--- >> +... >> +v:format() >> +--- >> +- [{'name': 'name', 'type': 'string'}, {'name': 'value', 'type': 'any'}] >> +... >> +(#v:select()) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'ALL'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'REQ'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'EQ'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'GE'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'GT'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'LE'})) == option_count >> +--- >> +- true >> +... >> +(#v:select({}, {iterator = 'LT'})) == option_count > > 6. Why do you need braces around #v:select? > I thought that this looked easier to understand, but guess it is not so. >> +--- >> +- true >> +... >> +s = box.schema.space.create('settings', {format = v:format()}) >> +--- >> +... >> +_ = s:create_index('primary') >> +--- >> +... >> +for _,value in v:pairs() do s:insert(value) end >> +--- >> +... >> +(#v:select({'abcde'}, {iterator = 'ALL'})) == (#s:select({'abcde'}, {iterator = 'ALL'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'REQ'})) == (#s:select({'abcde'}, {iterator = 'REQ'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'EQ'})) == (#s:select({'abcde'}, {iterator = 'EQ'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'GE'})) == (#s:select({'abcde'}, {iterator = 'GE'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'GT'})) == (#s:select({'abcde'}, {iterator = 'GT'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'LE'})) == (#s:select({'abcde'}, {iterator = 'LE'})) >> +--- >> +- true >> +... >> +(#v:select({'abcde'}, {iterator = 'LT'})) == (#s:select({'abcde'}, {iterator = 'LT'})) >> +--- >> +- true > > 7. Sorry, but counts are rather useless here, they don't check order nor content. > Please, check them properly. Also there is still no a single test about partial > keys. You check for empty, full, not found, but not for partial matches like 'sql_v' > + GE/LE/GT/LT. Which already appeared to be buggy two times. > > I propose you to implement a function, which takes a key, and checks > it with different iterator types on both 's' and 'v', and compares their result > sets. Then you call it with different keys and without tests code duplication. > > Additionally, you could find useful gcov build to check code coverage locally. > Use make distclean && cmake . -DENABLE_GCOV=1 && make -j. Then run a needed > test. Then call > > gcov -o > > Then a file session.gcov is generated where you can check whether you have > covered your code with tests properly. File session.gcno should appear somewhere > after you run a test. > Done, gcov after access_sysview.test.lua: -: 367: -: 368:int 5: 369:session_options_get(struct index *index, const char *key, uint32_t part_count, -: 370: struct tuple **result) -: 371:{ 5: 372: assert(part_count == 1); -: 373: (void)part_count; 5: 374: struct space *space = space_cache_find(index->def->space_id); -: 375: uint32_t len; 5: 376: const char *name = mp_decode_str(&key, &len); 5: 377: name = tt_cstr(name, len); -: 378: /* -: 379: * Currently, the only session local options are SQL -: 380: * options. -: 381: */ 5: 382: uint32_t id_max = sql_session_opt_id_max(); 29: 383: for (uint32_t id = 0; id < id_max; ++id) { 28: 384: if (sql_session_opt_compare(name, id) == 0) 4: 385: return sql_session_opt_tuple(space->format, id, result); -: 386: } 1: 387: *result = NULL; 1: 388: return 0; -: 389:} -: 390: -: 391:/** -: 392: * An iterator that iterates over current session options. -: 393: */ -: 394:struct session_options_iterator { -: 395: /** Base iterator. Must be the first member. */ -: 396: struct iterator base; -: 397: /** Format of the tuples this iterator returns. */ -: 398: struct tuple_format *format; -: 399: /** ID of the option in global list of the options. */ -: 400: int current_id; -: 401: /** Decoded key. */ -: 402: char *key; -: 403: /** Type of iterator. */ -: 404: enum iterator_type iterator_type; -: 405:}; -: 406: -: 407:static int 264: 408:session_options_iterator_next(struct iterator *itr, struct tuple **ret) -: 409:{ -: 410: struct session_options_iterator *it = 264: 411: (struct session_options_iterator *)itr; 264: 412: if (it->key == NULL) 105: 413: return sql_session_opt_tuple(it->format, it->current_id++, ret); 159: 414: int id_max = sql_session_opt_id_max(); 351: 415: for (; it->current_id < id_max; ++it->current_id) { 238: 416: int compare = sql_session_opt_compare(it->key, it->current_id); 241: 417: if (compare == 0 && (it->iterator_type == ITER_EQ || 5: 418: it->iterator_type == ITER_GE || 2: 419: it->iterator_type == ITER_ALL)) -: 420: break; 385: 421: if (compare > 0 && (it->iterator_type == ITER_GT || 256: 422: it->iterator_type == ITER_GE || 105: 423: it->iterator_type == ITER_ALL)) -: 424: break; -: 425: } 159: 426: return sql_session_opt_tuple(it->format, it->current_id++, ret); -: 427:} -: 428: -: 429:static int 77: 430:session_options_iterator_prev(struct iterator *itr, struct tuple **ret) -: 431:{ -: 432: struct session_options_iterator *it = 77: 433: (struct session_options_iterator *)itr; 77: 434: if (it->key == NULL) 45: 435: return sql_session_opt_tuple(it->format, it->current_id--, ret); 328: 436: for (; it->current_id >= 0; --it->current_id) { 168: 437: int compare = sql_session_opt_compare(it->key, it->current_id); 170: 438: if (compare == 0 && (it->iterator_type == ITER_REQ || 2: 439: it->iterator_type == ITER_LE)) -: 440: break; 184: 441: if (compare < 0 && (it->iterator_type == ITER_LT || 18: 442: it->iterator_type == ITER_LE)) -: 443: break; -: 444: } 32: 445: return sql_session_opt_tuple(it->format, it->current_id--, ret); -: 446:} -: 447: -: 448:static void 37: 449:session_options_iterator_free(struct iterator *itr) -: 450:{ -: 451: struct session_options_iterator *it = 37: 452: (struct session_options_iterator *)itr; 37: 453: free(it->key); 37: 454: free(it); 37: 455:} -: 456: -: 457:struct iterator * 39: 458:session_options_create_iterator(struct index *index, enum iterator_type type, -: 459: const char *key, uint32_t part_count) -: 460:{ 39: 461: char *decoded_key = NULL; 39: 462: if (part_count > 0) { 29: 463: assert(part_count == 1); 58: 464: assert(mp_typeof(*key) == MP_STR); -: 465: uint32_t len; 29: 466: const char *name = mp_decode_str(&key, &len); 29: 467: decoded_key = (char *)malloc(len + 1); 29: 468: if (decoded_key == NULL) { #####: 469: diag_set(OutOfMemory, len + 1, "malloc", "decoded_key"); #####: 470: return NULL; -: 471: } 29: 472: memcpy(decoded_key, name, len); 29: 473: decoded_key[len] = '\0'; -: 474: } 39: 475: struct space *space = space_cache_find(index->def->space_id); -: 476: struct session_options_iterator *it = 39: 477: (session_options_iterator *)malloc(sizeof(*it)); 39: 478: if (it == NULL) { #####: 479: diag_set(OutOfMemory, sizeof(*it), "malloc", "it"); #####: 480: return NULL; -: 481: } 39: 482: int current_id = iterator_type_is_reverse(type) ? 39: 483: sql_session_opt_id_max() - 1 : 0; 39: 484: iterator_create(&it->base, index); 39: 485: it->base.next = iterator_type_is_reverse(type) ? -: 486: session_options_iterator_prev : 39: 487: session_options_iterator_next; 39: 488: it->base.free = session_options_iterator_free; 39: 489: it->key = decoded_key; 39: 490: it->iterator_type = type; 39: 491: it->format = space->format; 39: 492: it->current_id = current_id; 39: 493: return (struct iterator *)it; 12: 494:} New patch: >From 3d55476778a0b8be41703ccdc4d25d558b861dfd Mon Sep 17 00:00:00 2001 From: Mergen Imeev Date: Thu, 24 Oct 2019 15:18:09 +0300 Subject: [PATCH] box: introdice _vsession_settings sysview This patch creates the _vsession_settings sysview. This sysview contains names and values of the session-local options. This sysview creates tuples on the fly using its own get() and create_iterator() methods. This allows us to get a tuple without having to save it anywhere. Currently, only SQL options can be extracted from this sysview, since the only session-local options are SQL options. Part of #4511 diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap index 59f6cc1..6b4b96a 100644 Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc index f6e96f0..9b5df28 100644 --- a/src/box/lua/space.cc +++ b/src/box/lua/space.cc @@ -653,6 +653,8 @@ box_lua_space_init(struct lua_State *L) lua_setfield(L, -2, "SPACE_SEQUENCE_ID"); lua_pushnumber(L, BOX_FUNC_INDEX_ID); lua_setfield(L, -2, "FUNC_INDEX_ID"); + lua_pushnumber(L, BOX_VSESSION_SETTINGS_ID); + lua_setfield(L, -2, "VSESSION_SETTINGS_ID"); lua_pushnumber(L, BOX_SYSTEM_ID_MIN); lua_setfield(L, -2, "SYSTEM_ID_MIN"); lua_pushnumber(L, BOX_SYSTEM_ID_MAX); diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua index e71b7fb..94e4118 100644 --- a/src/box/lua/upgrade.lua +++ b/src/box/lua/upgrade.lua @@ -943,6 +943,28 @@ local function upgrade_to_2_3_0() end -------------------------------------------------------------------------------- +-- Tarantool 2.3.1 +-------------------------------------------------------------------------------- + +local function create_vsession_settings_sysview() + local _space = box.space[box.schema.SPACE_ID] + local _index = box.space[box.schema.INDEX_ID] + local format = {} + format[1] = {name='name', type='string'} + format[2] = {name='value', type='any'} + log.info("create space _vsession_settings") + _space:insert{box.schema.VSESSION_SETTINGS_ID, ADMIN, '_vsession_settings', + 'sysview', 0, setmap({}), format} + log.info("create index _vsession_settings:primary") + _index:insert{box.schema.VSESSION_SETTINGS_ID, 0, 'primary', 'tree', + {unique = true}, {{0, 'string'}}} +end + +local function upgrade_to_2_3_1() + create_vsession_settings_sysview() +end + +-------------------------------------------------------------------------------- local function get_version() local version = box.space._schema:get{'version'} @@ -977,6 +999,7 @@ local function upgrade(options) {version = mkversion(2, 1, 3), func = upgrade_to_2_1_3, auto = true}, {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true}, {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true}, + {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true}, } for _, handler in ipairs(handlers) do diff --git a/src/box/schema_def.h b/src/box/schema_def.h index ba870ff..b9824cb 100644 --- a/src/box/schema_def.h +++ b/src/box/schema_def.h @@ -114,6 +114,8 @@ enum { BOX_CK_CONSTRAINT_ID = 364, /** Space id of _func_index. */ BOX_FUNC_INDEX_ID = 372, + /** Space id of _vsession_settings. */ + BOX_VSESSION_SETTINGS_ID = 381, /** End of the reserved range of system spaces. */ BOX_SYSTEM_ID_MAX = 511, BOX_ID_NIL = 2147483647 diff --git a/src/box/session.cc b/src/box/session.cc index b171e23..d9e3b2d 100644 --- a/src/box/session.cc +++ b/src/box/session.cc @@ -36,6 +36,9 @@ #include "user.h" #include "error.h" #include "tt_static.h" +#include "index.h" +#include "schema.h" +#include "sql.h" const char *session_type_strs[] = { "background", @@ -361,3 +364,131 @@ generic_session_sync(struct session *session) (void) session; return 0; } + +int +session_options_get(struct index *index, const char *key, uint32_t part_count, + struct tuple **result) +{ + assert(part_count == 1); + (void)part_count; + struct space *space = space_cache_find(index->def->space_id); + uint32_t len; + const char *name = mp_decode_str(&key, &len); + name = tt_cstr(name, len); + /* + * Currently, the only session local options are SQL + * options. + */ + uint32_t id_max = sql_session_opt_id_max(); + for (uint32_t id = 0; id < id_max; ++id) { + if (sql_session_opt_compare(name, id) == 0) + return sql_session_opt_tuple(space->format, id, result); + } + *result = NULL; + return 0; +} + +/** + * An iterator that iterates over current session options. + */ +struct session_options_iterator { + /** Base iterator. Must be the first member. */ + struct iterator base; + /** Format of the tuples this iterator returns. */ + struct tuple_format *format; + /** ID of the option in global list of the options. */ + int current_id; + /** Decoded key. */ + char *key; + /** Type of iterator. */ + enum iterator_type iterator_type; +}; + +static int +session_options_iterator_next(struct iterator *itr, struct tuple **ret) +{ + struct session_options_iterator *it = + (struct session_options_iterator *)itr; + if (it->key == NULL) + return sql_session_opt_tuple(it->format, it->current_id++, ret); + int id_max = sql_session_opt_id_max(); + for (; it->current_id < id_max; ++it->current_id) { + int compare = sql_session_opt_compare(it->key, it->current_id); + if (compare == 0 && (it->iterator_type == ITER_EQ || + it->iterator_type == ITER_GE || + it->iterator_type == ITER_ALL)) + break; + if (compare > 0 && (it->iterator_type == ITER_GT || + it->iterator_type == ITER_GE || + it->iterator_type == ITER_ALL)) + break; + } + return sql_session_opt_tuple(it->format, it->current_id++, ret); +} + +static int +session_options_iterator_prev(struct iterator *itr, struct tuple **ret) +{ + struct session_options_iterator *it = + (struct session_options_iterator *)itr; + if (it->key == NULL) + return sql_session_opt_tuple(it->format, it->current_id--, ret); + for (; it->current_id >= 0; --it->current_id) { + int compare = sql_session_opt_compare(it->key, it->current_id); + if (compare == 0 && (it->iterator_type == ITER_REQ || + it->iterator_type == ITER_LE)) + break; + if (compare < 0 && (it->iterator_type == ITER_LT || + it->iterator_type == ITER_LE)) + break; + } + return sql_session_opt_tuple(it->format, it->current_id--, ret); +} + +static void +session_options_iterator_free(struct iterator *itr) +{ + struct session_options_iterator *it = + (struct session_options_iterator *)itr; + free(it->key); + free(it); +} + +struct iterator * +session_options_create_iterator(struct index *index, enum iterator_type type, + const char *key, uint32_t part_count) +{ + char *decoded_key = NULL; + if (part_count > 0) { + assert(part_count == 1); + assert(mp_typeof(*key) == MP_STR); + uint32_t len; + const char *name = mp_decode_str(&key, &len); + decoded_key = (char *)malloc(len + 1); + if (decoded_key == NULL) { + diag_set(OutOfMemory, len + 1, "malloc", "decoded_key"); + return NULL; + } + memcpy(decoded_key, name, len); + decoded_key[len] = '\0'; + } + struct space *space = space_cache_find(index->def->space_id); + struct session_options_iterator *it = + (session_options_iterator *)malloc(sizeof(*it)); + if (it == NULL) { + diag_set(OutOfMemory, sizeof(*it), "malloc", "it"); + return NULL; + } + int current_id = iterator_type_is_reverse(type) ? + sql_session_opt_id_max() - 1 : 0; + iterator_create(&it->base, index); + it->base.next = iterator_type_is_reverse(type) ? + session_options_iterator_prev : + session_options_iterator_next; + it->base.free = session_options_iterator_free; + it->key = decoded_key; + it->iterator_type = type; + it->format = space->format; + it->current_id = current_id; + return (struct iterator *)it; +} diff --git a/src/box/session.h b/src/box/session.h index eff3d7a..f5e3cf2 100644 --- a/src/box/session.h +++ b/src/box/session.h @@ -36,12 +36,15 @@ #include "fiber.h" #include "user.h" #include "authentication.h" +#include "iterator_type.h" #if defined(__cplusplus) extern "C" { #endif /* defined(__cplusplus) */ struct port; +struct index; +struct tuple; struct session_vtab; void @@ -347,6 +350,16 @@ generic_session_fd(struct session *session); int64_t generic_session_sync(struct session *session); +/** Method get() for _vsession_settings sysview. */ +int +session_options_get(struct index *index, const char *key, uint32_t part_count, + struct tuple **result); + +/** Method create_iterator() for _vsession_settings sysview. */ +struct iterator * +session_options_create_iterator(struct index *index, enum iterator_type type, + const char *key, uint32_t part_count); + #if defined(__cplusplus) } /* extern "C" */ diff --git a/src/box/sql.h b/src/box/sql.h index 0fa52fc..604c3d2 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -70,6 +70,7 @@ struct Select; struct Table; struct sql_trigger; struct space_def; +struct tuple_format; /** * Perform parsing of provided expression. This is done by @@ -420,6 +421,41 @@ void vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref, struct tuple *tuple); +/** + * Return the next number after the ID of the last SQL session + * option. + * + * @retval Next number after the last SQL option. + */ +uint32_t +sql_session_opt_id_max(); + +/** + * Return an integer less than, equal to, or greater than zero if + * name of session option with given ID is found, respectively, + * to be less than, to match, or be greater than key. + * + * @param key Key to compare with option name. + * @param id ID of the option. + * @retval result of comparison. + */ +int +sql_session_opt_compare(const char *key, uint32_t id); + +/** + * Create a tuple that contains the name and value of the SQL + * session option. + * + * @param format Format of the tuple. + * @param option_id ID of SQL option. + * @param[out] result New tuple or NULL. + * @retval 0 On success. + * @retval < 0 On error. + */ +int +sql_session_opt_tuple(struct tuple_format *format, int option_id, + struct tuple **result); + #if defined(__cplusplus) } /* extern "C" { */ #endif diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 51cd7ce..ce87b88 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -48,6 +48,7 @@ #include "vdbeInt.h" #include "tarantoolInt.h" #include "box/box.h" +#include "box/tuple.h" #include "box/ck_constraint.h" #include "box/fk_constraint.h" #include "box/sequence.h" @@ -3242,3 +3243,142 @@ sql_fieldno_by_name(struct Parse *parse_context, struct Expr *field_name, *fieldno = i; return 0; } + +/** + * Identifiers of all SQL session options that can be viewed. The + * identifier of the option is equal to its place in the sorted + * list of session options, which starts at 0. + * + * It is IMPORTANT that these options are sorted by name. If this + * is not the case, the result returned by the _vsession_settings + * space iterator will not be sorted properly. + */ +enum { + SQL_SESSION_OPTION_DEFAULT_ENGINE = 0, + SQL_SESSION_OPTION_DEFER_FOREIGN_KEYS, + SQL_SESSION_OPTION_FULL_COLUMN_NAMES, +#ifndef NDEBUG + SQL_SESSION_OPTION_PARSER_TRACE, +#endif + SQL_SESSION_OPTION_RECURSIVE_TRIGGERS, + SQL_SESSION_OPTION_REVERSE_UNORDERED_SELECTS, +#ifndef NDEBUG + SQL_SESSION_OPTION_SELECT_TRACE, + SQL_SESSION_OPTION_TRACE, + SQL_SESSION_OPTION_VDBE_ADDOPTRACE, + SQL_SESSION_OPTION_VDBE_DEBUG, + SQL_SESSION_OPTION_VDBE_EQP, + SQL_SESSION_OPTION_VDBE_LISTING, + SQL_SESSION_OPTION_VDBE_TRACE, + SQL_SESSION_OPTION_WHERE_TRACE, +#endif + SQL_SESSION_OPTION_max, +}; + +/** + * A local structure that allows to establish a connection between + * the name of the parameter, its field type and mask, if it have + * one. + */ +struct sql_option_metadata +{ + const char *name; + uint32_t field_type; + uint32_t mask; +}; + +/** + * Variable that contains names of the SQL session options, their + * field types and mask if they have one or 0 if don't have. + * + * It is IMPORTANT that these options sorted by name. + */ +static struct sql_option_metadata sql_session_opts[] = { + /** SQL_SESSION_OPTION_DEFAULT_ENGINE */ + {"sql_default_engine", FIELD_TYPE_STRING, 0}, + /** SQL_SESSION_OPTION_DEFER_FOREIGN_KEYS */ + {"sql_defer_foreign_keys", FIELD_TYPE_BOOLEAN, SQL_DeferFKs}, + /** SQL_SESSION_OPTION_FULL_COLUMN_NAMES */ + {"sql_full_column_names", FIELD_TYPE_BOOLEAN, SQL_FullColNames}, +#ifndef NDEBUG + /** SQL_SESSION_OPTION_PARSER_TRACE */ + {"sql_parser_trace", FIELD_TYPE_BOOLEAN, PARSER_TRACE_FLAG}, +#endif + /** SQL_SESSION_OPTION_RECURSIVE_TRIGGERS */ + {"sql_recursive_triggers", FIELD_TYPE_BOOLEAN, SQL_RecTriggers}, + /** SQL_SESSION_OPTION_REVERSE_UNORDERED_SELECTS */ + {"sql_reverse_unordered_selects", FIELD_TYPE_BOOLEAN, SQL_ReverseOrder}, +#ifndef NDEBUG + /** SQL_SESSION_OPTION_SELECT_TRACE */ + {"sql_select_trace", FIELD_TYPE_BOOLEAN, SQL_SelectTrace}, + /** SQL_SESSION_OPTION_TRACE */ + {"sql_trace", FIELD_TYPE_BOOLEAN, SQL_SqlTrace}, + /** SQL_SESSION_OPTION_VDBE_ADDOPTRACE */ + {"sql_vdbe_addoptrace", FIELD_TYPE_BOOLEAN, SQL_VdbeAddopTrace}, + /** SQL_SESSION_OPTION_VDBE_DEBUG */ + {"sql_vdbe_debug", FIELD_TYPE_BOOLEAN, + SQL_SqlTrace | SQL_VdbeListing | SQL_VdbeTrace}, + /** SQL_SESSION_OPTION_VDBE_EQP */ + {"sql_vdbe_eqp", FIELD_TYPE_BOOLEAN, SQL_VdbeEQP}, + /** SQL_SESSION_OPTION_VDBE_LISTING */ + {"sql_vdbe_listing", FIELD_TYPE_BOOLEAN, SQL_VdbeListing}, + /** SQL_SESSION_OPTION_VDBE_TRACE */ + {"sql_vdbe_trace", FIELD_TYPE_BOOLEAN, SQL_VdbeTrace}, + /** SQL_SESSION_OPTION_WHERE_TRACE */ + {"sql_where_trace", FIELD_TYPE_BOOLEAN, SQL_WhereTrace}, +#endif +}; + +uint32_t +sql_session_opt_id_max() +{ + return SQL_SESSION_OPTION_max; +} + +int +sql_session_opt_compare(const char *key, uint32_t id) +{ + assert(id < SQL_SESSION_OPTION_max); + return strcmp(sql_session_opts[id].name, key); +} + +int +sql_session_opt_tuple(struct tuple_format *format, int option_id, + struct tuple **result) +{ + if (option_id < 0 || option_id >= SQL_SESSION_OPTION_max) { + *result = NULL; + return 0; + } + struct session *session = current_session(); + uint32_t flags = session->sql_flags; + uint32_t mask = sql_session_opts[option_id].mask; + const char *engine = NULL; + /* Tuple format contains two fields - name and value. */ + uint32_t column_count = format->min_field_count; + assert(column_count == 2); + size_t size = mp_sizeof_array(column_count) + + mp_sizeof_str(strlen(sql_session_opts[option_id].name)); + if (sql_session_opts[option_id].field_type == FIELD_TYPE_BOOLEAN) { + size += mp_sizeof_bool(true); + } else { + assert(option_id == SQL_SESSION_OPTION_DEFAULT_ENGINE); + engine = sql_storage_engine_strs[session->sql_default_engine]; + size += mp_sizeof_str(strlen(engine)); + } + + char *pos_ret = static_alloc(size); + assert(pos_ret != NULL); + char *pos = mp_encode_array(pos_ret, column_count); + pos = mp_encode_str(pos, sql_session_opts[option_id].name, + strlen(sql_session_opts[option_id].name)); + if (sql_session_opts[option_id].field_type == FIELD_TYPE_BOOLEAN) + pos = mp_encode_bool(pos, (flags & mask) == mask); + else + pos = mp_encode_str(pos, engine, strlen(engine)); + struct tuple *tuple = tuple_new(format, pos_ret, pos_ret + size); + if (tuple == NULL) + return -1; + *result = tuple; + return 0; +} diff --git a/src/box/sysview.c b/src/box/sysview.c index 745cf09..83a8ccc 100644 --- a/src/box/sysview.c +++ b/src/box/sysview.c @@ -411,7 +411,7 @@ vsequence_filter(struct space *source, struct tuple *tuple) } static bool -vcollation_filter(struct space *source, struct tuple *tuple) +generic_filter(struct space *source, struct tuple *tuple) { (void) source; (void) tuple; @@ -481,10 +481,17 @@ sysview_space_create_index(struct space *space, struct index_def *def) case BOX_VCOLLATION_ID: source_space_id = BOX_COLLATION_ID; source_index_id = def->iid; - filter = vcollation_filter; + filter = generic_filter; get = index_get; create_iterator = index_create_iterator; break; + case BOX_VSESSION_SETTINGS_ID: + source_space_id = BOX_VSESSION_SETTINGS_ID; + source_index_id = def->iid; + filter = generic_filter; + get = session_options_get; + create_iterator = session_options_create_iterator; + break; default: diag_set(ClientError, ER_MODIFY_INDEX, def->name, space_name(space), @@ -569,9 +576,10 @@ sysview_engine_create_space(struct engine *engine, struct space_def *def, return NULL; } struct tuple_format *format = - tuple_format_new(NULL, NULL, keys, key_count, def->fields, - def->field_count, def->exact_field_count, - def->dict, def->opts.is_temporary, + tuple_format_new(&tuple_format_runtime->vtab, NULL, keys, + key_count, def->fields, def->field_count, + def->exact_field_count, def->dict, + def->opts.is_temporary, def->opts.is_ephemeral); if (format == NULL) { free(space); diff --git a/test/app-tap/tarantoolctl.test.lua b/test/app-tap/tarantoolctl.test.lua index f388208..a41fad3 100755 --- a/test/app-tap/tarantoolctl.test.lua +++ b/test/app-tap/tarantoolctl.test.lua @@ -410,8 +410,8 @@ do check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 1", "\n", 3) check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 1 --replica 2", "\n", 3) check_ctlcat_xlog(test_i, dir, "--from=3 --to=6 --format=json --show-system --replica 2", "\n", 0) - check_ctlcat_snap(test_i, dir, "--space=280", "---\n", 24) - check_ctlcat_snap(test_i, dir, "--space=288", "---\n", 52) + check_ctlcat_snap(test_i, dir, "--space=280", "---\n", 25) + check_ctlcat_snap(test_i, dir, "--space=288", "---\n", 53) end) end) diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result index 123aa2f..54b43ec 100644 --- a/test/box-py/bootstrap.result +++ b/test/box-py/bootstrap.result @@ -4,7 +4,7 @@ box.internal.bootstrap() box.space._schema:select{} --- - - ['max_id', 511] - - ['version', 2, 3, 0] + - ['version', 2, 3, 1] ... box.space._cluster:select{} --- @@ -96,6 +96,8 @@ box.space._space:select{} 'type': 'boolean'}]] - [372, 1, '_func_index', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'}, {'name': 'index_id', 'type': 'unsigned'}, {'name': 'func_id', 'type': 'unsigned'}]] + - [381, 1, '_vsession_settings', 'sysview', 0, {}, [{'name': 'name', 'type': 'string'}, + {'name': 'value', 'type': 'any'}]] ... box.space._index:select{} --- @@ -153,6 +155,7 @@ box.space._index:select{} - [364, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'string']]] - [372, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]] - [372, 1, 'fid', 'tree', {'unique': false}, [[2, 'unsigned']]] + - [381, 0, 'primary', 'tree', {'unique': true}, [[0, 'string']]] ... box.space._user:select{} --- diff --git a/test/box/access_sysview.result b/test/box/access_sysview.result index 1f33dec..c032248 100644 --- a/test/box/access_sysview.result +++ b/test/box/access_sysview.result @@ -246,11 +246,11 @@ box.session.su('guest') ... #box.space._vspace:select{} --- -- 25 +- 26 ... #box.space._vindex:select{} --- -- 53 +- 54 ... #box.space._vuser:select{} --- @@ -282,7 +282,7 @@ box.session.su('guest') ... #box.space._vindex:select{} --- -- 53 +- 54 ... #box.space._vuser:select{} --- @@ -743,3 +743,121 @@ s = box.space._space:create_index('owner', {parts = { 2, 'unsigned' }, id = 1, u session = nil --- ... +-- +-- gh-4511: make sure that _vsession_settings sysview works as +-- intended. +-- +v = box.space._vsession_settings +--- +... +v:format() +--- +- [{'name': 'name', 'type': 'string'}, {'name': 'value', 'type': 'any'}] +... +s = box.schema.space.create('settings', {format = v:format()}) +--- +... +_ = s:create_index('primary') +--- +... +for _,value in v:pairs() do s:insert(value) end +--- +... +env = require('test_run') +--- +... +test_run = env.new() +--- +... +test_run:cmd('setopt delimiter ";"') +--- +- true +... +function check_sorting(vs, ts, key) + local is_right = true + local iterators_list = {'ALL', 'REQ', 'EQ', 'GE', 'GT', 'LE', 'LT'} + for _, it in pairs(iterators_list) do + local view_space = vs:select({key}, {iterator = it}) + local test_space = ts:select({key}, {iterator = it}) + for key, value in pairs(view_space) do + is_right = is_right and (test_space[key].name == value.name) + end + end + return is_right +end; +--- +... +test_run:cmd('setopt delimiter ""'); +--- +- true +... +check_sorting(v, s) +--- +- true +... +check_sorting(v, s, 'abcde') +--- +- true +... +check_sorting(v, s, 'sql_d') +--- +- true +... +check_sorting(v, s, 'sql_v') +--- +- true +... +check_sorting(v, s, 'sql_defer_foreign_keys') +--- +- true +... +-- Check get() method. +v:get({'sql_defer_foreign_keys'}) +--- +- ['sql_defer_foreign_keys', false] +... +v:get({'sql_recursive_triggers'}) +--- +- ['sql_recursive_triggers', true] +... +v:get({'sql_reverse_unordered_selects'}) +--- +- ['sql_reverse_unordered_selects', false] +... +v:get({'sql_default_engine'}) +--- +- ['sql_default_engine', 'memtx'] +... +v:get({'abcd'}) +--- +... +-- Make sure that space is read-only. +new_record = v:frommap({name='abs', value=123}) +--- +... +v:insert(new_record) +--- +- error: View '_vsession_settings' is read-only +... +-- Check pairs() method. +t = {} +--- +... +for key, value in v:pairs() do table.insert(t, {key, value}) end +--- +... +#t == v:count() +--- +- true +... +-- Make sure that SQL SELECT works. +box.execute([[SELECT * from "_vsession_settings" WHERE "name" = 'sql_default_engine']]) +--- +- metadata: + - name: name + type: string + - name: value + type: any + rows: + - ['sql_default_engine', 'memtx'] +... diff --git a/test/box/access_sysview.test.lua b/test/box/access_sysview.test.lua index 17420ae..a35a79c 100644 --- a/test/box/access_sysview.test.lua +++ b/test/box/access_sysview.test.lua @@ -308,3 +308,57 @@ s = box.space._space:create_index('owner', {parts = { 2, 'unsigned' }, id = 1, u #box.space._vspace.index[1]:select(1) > 0 session = nil + +-- +-- gh-4511: make sure that _vsession_settings sysview works as +-- intended. +-- + +v = box.space._vsession_settings +v:format() + +s = box.schema.space.create('settings', {format = v:format()}) +_ = s:create_index('primary') +for _,value in v:pairs() do s:insert(value) end + +env = require('test_run') +test_run = env.new() +test_run:cmd('setopt delimiter ";"') +function check_sorting(vs, ts, key) + local is_right = true + local iterators_list = {'ALL', 'REQ', 'EQ', 'GE', 'GT', 'LE', 'LT'} + for _, it in pairs(iterators_list) do + local view_space = vs:select({key}, {iterator = it}) + local test_space = ts:select({key}, {iterator = it}) + for key, value in pairs(view_space) do + is_right = is_right and (test_space[key].name == value.name) + end + end + return is_right +end; +test_run:cmd('setopt delimiter ""'); + +check_sorting(v, s) +check_sorting(v, s, 'abcde') +check_sorting(v, s, 'sql_d') +check_sorting(v, s, 'sql_v') +check_sorting(v, s, 'sql_defer_foreign_keys') + +-- Check get() method. +v:get({'sql_defer_foreign_keys'}) +v:get({'sql_recursive_triggers'}) +v:get({'sql_reverse_unordered_selects'}) +v:get({'sql_default_engine'}) +v:get({'abcd'}) + +-- Make sure that space is read-only. +new_record = v:frommap({name='abs', value=123}) +v:insert(new_record) + +-- Check pairs() method. +t = {} +for key, value in v:pairs() do table.insert(t, {key, value}) end +#t == v:count() + +-- Make sure that SQL SELECT works. +box.execute([[SELECT * from "_vsession_settings" WHERE "name" = 'sql_default_engine']]) diff --git a/test/box/alter.result b/test/box/alter.result index 46ce868..2a0eaca 100644 --- a/test/box/alter.result +++ b/test/box/alter.result @@ -92,7 +92,7 @@ space = box.space[t[1]] ... space.id --- -- 373 +- 382 ... space.field_count --- @@ -137,7 +137,7 @@ space_deleted ... space:replace{0} --- -- error: Space '373' does not exist +- error: Space '382' does not exist ... _index:insert{_space.id, 0, 'primary', 'tree', {unique=true}, {{0, 'unsigned'}}} --- @@ -220,6 +220,7 @@ _index:select{} - [364, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'string']]] - [372, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned'], [1, 'unsigned']]] - [372, 1, 'fid', 'tree', {'unique': false}, [[2, 'unsigned']]] + - [381, 0, 'primary', 'tree', {'unique': true}, [[0, 'string']]] ... -- modify indexes of a system space _index:delete{_index.id, 0} diff --git a/test/wal_off/alter.result b/test/wal_off/alter.result index 62cb11d..97f7e6f 100644 --- a/test/wal_off/alter.result +++ b/test/wal_off/alter.result @@ -28,7 +28,7 @@ end; ... #spaces; --- -- 65502 +- 65501 ... -- cleanup for k, v in pairs(spaces) do -- 2.7.4