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 A526143D678 for ; Sat, 19 Oct 2019 01:03:08 +0300 (MSK) References: <6a36d22fd2083d5ab415947ec46ee5708e89337f.1571322776.git.imeevma@gmail.com> From: Vladislav Shpilevoy Message-ID: Date: Sat, 19 Oct 2019 00:08:17 +0200 MIME-Version: 1.0 In-Reply-To: <6a36d22fd2083d5ab415947ec46ee5708e89337f.1571322776.git.imeevma@gmail.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Subject: Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 2/5] box: introduce _vsql_settings sysview List-Id: Tarantool development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@freelists.org, imeevma@tarantool.org Cc: tarantool-patches@dev.tarantool.org Thanks for the patch! See 14 comments below. On 17/10/2019 16:40, imeevma@tarantool.org wrote: > This patch creates the _vsql_settings sysview. This system view > has custom methods get() and create_iterator(). This allows us > to get SQL settings by creating a tuple on the fly without having > to save it anywhere. > > Part of #4511 > > @TarantoolBot document 1. I think, it is worth to create one doc request for the whole feature - both SET command and _vsql_settings space. They are useless without each other. > Title: The _vsql_settings sysview > Sysview _vsql_settings allows the user to get the current SQL > parameters. > > Currently available SQL settings: > 'defer_foreign_keys' > 'full_column_names' > 'recursive_triggers' > 'reverse_unordered_selects' > 'sql_compound_select_limit' > 'sql_default_engine' > > In addition, SQL debugging settings can also be obtained from this > system view in the debug build: > 'parser_trace' > 'select_trace' > 'sql_trace' > 'vdbe_addoptrace' > 'vdbe_debug' > 'vdbe_eqp' > 'vdbe_listing' > 'vdbe_trace' > 'where_trace' > > Example of usage: > box.space._vsql_settings:get{'full_column_names'} > box.space._vsql_settings:select{'defer_foreign_keys'} > box.space._vsql_settings:select{'full_column_names', {iterator = 'LE'}} 2. There is a problem with having generic iterators. Because if you allow to iterate over multiple settings, then you have to do it like normal indexes do, with a strict order. Currently you return settings without an order. There is 2 options what to do: - Ban all iterator types except =, and ban an empty key; - Keep the settings sorted and return them like a tree index does. That might be useful in case we will agree to create a generic space _vsession_storage. Then by a prefix a user could select all settings of a specific subsystem. Like 'sql_', 'vinyl_', etc. I am ok with both. Or maybe you will be able to find a third solution. Another problem is that: pairs() and select() somewhy return different results. tarantool> t = {} --- ... tarantool> for k, v in box.space._vsql_settings:pairs() do table.insert(t, {k, v}) end --- ... tarantool> t --- - - - - ['defer_foreign_keys', false] ... tarantool> box.space._vsql_settings:select() --- - - ['defer_foreign_keys', false] - ['full_column_names', false] - ['recursive_triggers', true] - ['reverse_unordered_selects', false] - ['sql_compound_select_limit', 30] - ['sql_default_engine', 'memtx'] - ['parser_trace', false] - ['select_trace', false] - ['sql_trace', false] - ['vdbe_addoptrace', false] - ['vdbe_debug', false] - ['vdbe_eqp', false] - ['vdbe_listing', false] - ['vdbe_trace', false] - ['where_trace', false] ... As you see, pairs() stops after a first tuple. For other spaces empty pairs and select work the same. > --- > src/box/bootstrap.snap | Bin 5934 -> 5973 bytes > src/box/lua/space.cc | 2 + > src/box/lua/upgrade.lua | 23 +++++ > src/box/schema_def.h | 2 + > src/box/sql.c | 203 +++++++++++++++++++++++++++++++++++++ > src/box/sql.h | 66 ++++++++++++ > src/box/sql/sqlInt.h | 14 +-- > src/box/sysview.c | 19 +++- > test/app-tap/tarantoolctl.test.lua | 4 +- > test/box-py/bootstrap.result | 5 +- > test/box/access_misc.result | 136 +++++++++++++------------ > test/box/access_sysview.result | 6 +- > test/box/alter.result | 5 +- > test/box/sql.result | 111 ++++++++++++++++++++ > test/box/sql.test.lua | 38 +++++++ > test/wal_off/alter.result | 2 +- > 16 files changed, 542 insertions(+), 94 deletions(-) > > diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua > index 2abd75d..f105db8 100644 > --- a/src/box/lua/upgrade.lua > +++ b/src/box/lua/upgrade.lua > @@ -930,6 +930,28 @@ local function upgrade_to_2_3_0() > end > > -------------------------------------------------------------------------------- > +-- Tarantool 2.3.1 > +-------------------------------------------------------------------------------- > + > +local function create_vsql_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 _vsql_settings") > + _space:insert{box.schema.VSQL_SETTINGS_ID, ADMIN, '_vsql_settings', > + 'sysview', 0, setmap({}), format} > + log.info("create index _sql_settings:primary") 3. _vsql, not _sql > + _index:insert{box.schema.VSQL_SETTINGS_ID, 0, 'primary', 'tree', 4. See? You said it is 'tree' and the key is string, but select() returns tuples in a mixed order: - ['sql_default_engine', 'memtx'] - ['parser_trace', false] - ['select_trace', false] 's', 'p', 's' - clearly not an alphabetical order. > + {unique = true}, {{0, 'string'}}} > +end > + > diff --git a/src/box/schema_def.h b/src/box/schema_def.h > index 85f652d..04e1e33 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 _vsql_settings. */ > + BOX_VSQL_SETTINGS_ID = 380, 5. Usually system views are the original space ID + 1. So here it would be 381. Sorry for the nit. > /** End of the reserved range of system spaces. */ > BOX_SYSTEM_ID_MAX = 511, > BOX_ID_NIL = 2147483647 > diff --git a/src/box/sql.c b/src/box/sql.c > index f1df555..0c73caf 100644 > --- a/src/box/sql.c > +++ b/src/box/sql.c > @@ -1265,3 +1265,206 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref, > vdbe_field_ref_create(field_ref, tuple, tuple_data(tuple), > tuple->bsize); > } > + > +struct sql_option_metadata sql_options[] = { > + /* SQL_OPTION_DEFER_FOREIGN_KEYS */ > + {"defer_foreign_keys", FIELD_TYPE_BOOLEAN, SQL_DeferFKs}, > + /* SQL_OPTION_DEFER_FOREIGN_KEYS */ 6. Double SQL_OPTION_DEFER_FOREIGN_KEYS. > + {"full_column_names", FIELD_TYPE_BOOLEAN, SQL_FullColNames}, > + /* SQL_OPTION_RECURSIVE_TRIGGERS */ > + {"recursive_triggers", FIELD_TYPE_BOOLEAN, SQL_RecTriggers}, > + /* SQL_OPTION_REVERSE_UNORDERED_SELECTS */ > + {"reverse_unordered_selects", FIELD_TYPE_BOOLEAN, SQL_ReverseOrder}, > + /* 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_PARSER_TRACE */ > + {"parser_trace", FIELD_TYPE_BOOLEAN, PARSER_TRACE_FLAG}, > + /* SQL_OPTION_SELECT_TRACE */ > + {"select_trace", FIELD_TYPE_BOOLEAN, SQL_SelectTrace}, > + /* SQL_OPTION_TRACE */ > + {"sql_trace", FIELD_TYPE_BOOLEAN, SQL_SqlTrace}, > + /* SQL_OPTION_VDBE_ADDOPTRACE */ > + {"vdbe_addoptrace", FIELD_TYPE_BOOLEAN, SQL_VdbeAddopTrace}, > + /* SQL_OPTION_VDBE_DEBUG */ > + {"vdbe_debug", FIELD_TYPE_BOOLEAN, > + SQL_SqlTrace | SQL_VdbeListing | SQL_VdbeTrace}, > + /* SQL_OPTION_VDBE_EQP */ > + {"vdbe_eqp", FIELD_TYPE_BOOLEAN, SQL_VdbeEQP}, > + /* SQL_OPTION_VDBE_LISTING */ > + {"vdbe_listing", FIELD_TYPE_BOOLEAN, SQL_VdbeListing}, > + /* SQL_OPTION_VDBE_TRACE */ > + {"vdbe_trace", FIELD_TYPE_BOOLEAN, SQL_VdbeTrace}, > + /* SQL_OPTION_WHERE_TRACE */ > + {"where_trace", FIELD_TYPE_BOOLEAN, SQL_WhereTrace}, > +}; > + > +static inline int > +sql_option_id_by_key(const char *key) 7. From the API it looks like the key is a normal string, but in fact it is a MessagePack encoded string. Please, say it somewhere in the function name/comment/parameter name. > +{ > + uint32_t len; > + struct region *region = &fiber()->gc; > + size_t svp = region_used(region); > + const char *tmp = mp_decode_str(&key, &len); > + char *str = region_alloc(region, len + 1); 8. Please, use tt_cstr. It is much faster and simpler. > + strncpy(str, tmp, len); > + str[len] = '\0'; > + int id; > + for (id = 0; id < SQL_OPTION_max; ++id) { > + if (strcmp(str, sql_options[id].name) == 0) > + break; > + } > + region_truncate(region, svp); > + return id; > +} > + > +/** > + * Create tuple that contains name and value of the option. > + * > + * @param format format for new tuple. > + * @param option_id id of option to return. > + * @param result[out] new tuple. 9. Please, use capital letters in the sentences beginning. 10. [out] modifier stands right after param: @param[out] > + */ > +static 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 region *region = &fiber()->gc; > + struct session *session = current_session(); > + uint32_t flags = session->sql_flags; > + uint32_t option_flag = sql_options[option_id].flag; > + /* Change to format->*_field_count */ 11. Sorry, I didn't understand the comment. Change what? Why not to change now? > + uint32_t 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); > + } > + > + size_t svp = region_used(region); > + char *pos_ret = region_alloc(region, size); > + if (pos_ret == NULL) { > + diag_set(OutOfMemory, size, "region_alloc", "pos_ret"); > + return -1; > + } > + 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 & option_flag) == option_flag); > + 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); > + region_truncate(region, svp); > + if (tuple == NULL) > + return -1; > + *result = tuple; > + return 0; > +} > + > +int> +sql_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 option_id = sql_option_id_by_key(key); > + if (option_id == SQL_OPTION_max) { > + *result = NULL; > + return 0; > + } > + return sql_option_tuple(space->format, option_id, result); > +} > + > +struct sql_options_iterator { > + struct iterator base; > + struct tuple_format *format; > + int option_id; > + bool is_eq_type; > + size_t svp; > +}; > + > +static int > +sql_options_iterator_next(struct iterator *itr, struct tuple **ret) > +{ > + struct sql_options_iterator *it = (struct sql_options_iterator *)itr; > + int rc = sql_option_tuple(it->format, it->option_id++, ret); > + if (it->is_eq_type) > + it->option_id = SQL_OPTION_max; > + return rc; > +} > + > +static int > +sql_options_iterator_prev(struct iterator *itr, struct tuple **ret) > +{ > + struct sql_options_iterator *it = (struct sql_options_iterator *)itr; > + int rc = sql_option_tuple(it->format, it->option_id--, ret); > + if (it->is_eq_type) > + it->option_id = -1; > + return rc; > +} > + > +static void > +sql_options_iterator_free(struct iterator *itr) > +{ > + struct sql_options_iterator *it = (struct sql_options_iterator *)itr; > + region_truncate(&fiber()->gc, it->svp); > +} > + > +struct iterator * > +sql_options_create_iterator(struct index *index, enum iterator_type type, > + const char *key, uint32_t part_count) > +{ > + bool is_eq_type = false; > + uint32_t option_id; > + if (part_count > 0) { > + assert(part_count == 1); > + option_id = sql_option_id_by_key(key); > + if (type == ITER_EQ || type == ITER_REQ) > + is_eq_type = true; > + else if (type == ITER_LT) > + --option_id; > + else if (type == ITER_GT) > + ++option_id; > + if (option_id == SQL_OPTION_max && type == ITER_LE) > + --option_id; > + } else { > + option_id = iterator_type_is_reverse(type) ? > + SQL_OPTION_max - 1 : 0; > + } > + struct space *space = space_cache_find(index->def->space_id); > + struct region *region = &fiber()->gc; > + size_t svp = region_used(region); > + struct sql_options_iterator *it = region_alloc(region, sizeof(*it)); 12. Unfortunately, you can't allocate iterators on a region - iterators should survive fiber garbage collection. > + if (it == NULL) { > + diag_set(OutOfMemory, sizeof(*it), "region_alloc", "it"); > + return NULL; > + } > + iterator_create(&it->base, index); > + it->base.next = iterator_type_is_reverse(type) ? > + sql_options_iterator_prev : > + sql_options_iterator_next; > + it->base.free = sql_options_iterator_free; > + it->option_id = option_id; > + it->is_eq_type = is_eq_type; > + it->format = space->format; > + it->svp = svp; > + return (struct iterator *)it; > +} > diff --git a/src/box/sql.h b/src/box/sql.h > index 0fa52fc..5c23748 100644 > --- a/src/box/sql.h > +++ b/src/box/sql.h > @@ -33,11 +33,68 @@ > > #include > #include > +#include "iterator_type.h" > > #if defined(__cplusplus) > extern "C" { > #endif > > +/** SQL options flags. */ > +/** True to trace VDBE execution */ > +#define SQL_VdbeTrace 0x00000001 > +/** Debug print info about SQL query as it parsed */ > +#define PARSER_TRACE_FLAG 0x00000002 > +/** Show full column names on SELECT */ > +#define SQL_FullColNames 0x00000004 > +/** Debug print SQL as it executes */ > +#define SQL_SqlTrace 0x00000200 > +/** Debug info about select statement */ > +#define SQL_SelectTrace 0x00000800 > +/** Debug info about optimizer's work */ > +#define SQL_WhereTrace 0x00008000 > +/** Debug listings of VDBE programs */ > +#define SQL_VdbeListing 0x00000400 > +/** Trace sqlVdbeAddOp() calls */ > +#define SQL_VdbeAddopTrace 0x00001000 > +/** Reverse unordered SELECTs */ > +#define SQL_ReverseOrder 0x00020000 > +/** Enable recursive triggers */ > +#define SQL_RecTriggers 0x00040000 > +/** Defer all FK constraints */ > +#define SQL_DeferFKs 0x02000000 > +/** Debug EXPLAIN QUERY PLAN */ > +#define SQL_VdbeEQP 0x08000000 13. Why have you moved these macros from their old place? > + > +enum { > + SQL_OPTION_DEFER_FOREIGN_KEYS = 0, > + SQL_OPTION_FULL_COLUMN_NAMES, > + SQL_OPTION_RECURSIVE_TRIGGERS, > + SQL_OPTION_REVERSE_UNORDERED_SELECTS, > + SQL_OPTION_COMPOUND_SELECT_LIMIT, > + SQL_OPTION_DEFAULT_ENGINE, > +#ifndef NDEBUG> diff --git a/test/box/access_misc.result b/test/box/access_misc.result > index a1b6435..66469e1 100644 > --- a/test/box/access_misc.result > +++ b/test/box/access_misc.result > @@ -835,144 +835,146 @@ box.space._space:select() > {'name': 'language', 'type': 'str'}, {'name': 'code', 'type': 'str'}]] > - [372, 1, '_func_index', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'}, > {'name': 'index_id', 'type': 'unsigned'}, {'name': 'func_id', 'type': 'unsigned'}]] > + - [380, 1, '_vsql_settings', 'sysview', 0, {}, [{'name': 'name', 'type': 'string'}, > + {'name': 'value', 'type': 'any'}]] > ... > box.space._func:select() > --- > - - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'function', [], 'any', 'none', 'none', > - false, false, true, ['LUA'], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, ['LUA'], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [2, 1, 'TRIM', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [3, 1, 'TYPEOF', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [4, 1, 'PRINTF', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [5, 1, 'UNICODE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [6, 1, 'CHAR', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [7, 1, 'HEX', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [8, 1, 'VERSION', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [9, 1, 'QUOTE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [10, 1, 'REPLACE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [11, 1, 'SUBSTR', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [12, 1, 'GROUP_CONCAT', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [13, 1, 'JULIANDAY', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [14, 1, 'DATE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [15, 1, 'TIME', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [16, 1, 'DATETIME', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [17, 1, 'STRFTIME', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [18, 1, 'CURRENT_TIME', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [19, 1, 'CURRENT_TIMESTAMP', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', > - 'none', false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + 'none', false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [20, 1, 'CURRENT_DATE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [21, 1, 'LENGTH', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [22, 1, 'POSITION', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [23, 1, 'ROUND', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [24, 1, 'UPPER', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [25, 1, 'LOWER', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [26, 1, 'IFNULL', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [27, 1, 'RANDOM', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [28, 1, 'CEIL', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [29, 1, 'CEILING', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [30, 1, 'CHARACTER_LENGTH', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', > - 'none', false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + 'none', false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [31, 1, 'CHAR_LENGTH', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [32, 1, 'FLOOR', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [33, 1, 'MOD', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [34, 1, 'OCTET_LENGTH', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [35, 1, 'ROW_COUNT', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [36, 1, 'COUNT', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [37, 1, 'LIKE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [38, 1, 'ABS', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [39, 1, 'EXP', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [40, 1, 'LN', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [41, 1, 'POWER', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [42, 1, 'SQRT', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [43, 1, 'SUM', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [44, 1, 'TOTAL', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [45, 1, 'AVG', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [46, 1, 'RANDOMBLOB', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [47, 1, 'NULLIF', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [48, 1, 'ZEROBLOB', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [49, 1, 'MIN', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [50, 1, 'MAX', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [51, 1, 'COALESCE', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [52, 1, 'EVERY', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [53, 1, 'EXISTS', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [54, 1, 'EXTRACT', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [55, 1, 'SOME', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', false, > - false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [56, 1, 'GREATER', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [57, 1, 'LESSER', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [58, 1, 'SOUNDEX', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [59, 1, 'LIKELIHOOD', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [60, 1, 'LIKELY', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [61, 1, 'UNLIKELY', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [62, 1, '_sql_stat_get', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', > - 'none', false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + 'none', false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [63, 1, '_sql_stat_push', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', > - 'none', false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + 'none', false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [64, 1, '_sql_stat_init', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', > - 'none', false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + 'none', false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [65, 1, 'LUA', 1, 'LUA', 'function(code) return assert(loadstring(code))() end', > 'function', ['string'], 'any', 'none', 'none', false, false, true, ['LUA', 'SQL'], > - {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [66, 1, 'GREATEST', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > - [67, 1, 'LEAST', 1, 'SQL_BUILTIN', '', 'function', [], 'any', 'none', 'none', > - false, false, true, [], {}, '', '2019-08-14 14:09:37', '2019-08-14 14:09:37'] > + false, false, true, [], {}, '', '2019-10-16 15:38:07', '2019-10-16 15:38:07'] > ... 14. Could we somehow refactor that test so as it would not change so much on each change of the schema? In a separate commit/branch.