From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp52.i.mail.ru (smtp52.i.mail.ru [94.100.177.112]) (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 CF8A54696C5 for ; Mon, 17 Feb 2020 15:12:14 +0300 (MSK) From: Chris Sosnin Date: Mon, 17 Feb 2020 15:12:12 +0300 Message-Id: <36341694915f89597f2ac938077d0d10bcad0448.1581940900.git.k.sosnin@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH 4/4] sql: provide a user friendly frontend for accessing session settings List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: korablev@tarantool.org, tarantool-patches@dev.tarantool.org Currently if a user wants to change session setting with sql, he has to execute non-obvious query, thus, we introduce a more native way to do this. Closes #4711 @TarantoolBot document Title: API for accessing _session_settings space. There are two ways of updating values of session settings: via Lua and SQL. Lua: box.session.settings is a table, which is always accessible to user. The syntax is the following: `box.session.settings.:set()`. Example of usage: ``` tarantool> box.session.settings.sql_default_engine --- - memtx ... tarantool> box.session.settings.sql_default_engine:set('vinyl') --- ... ``` The table itself represents the (unordered) result of select from _session_settings space. Every setting is implemented as a table, so there is no way to retrieve an actual value and use it until :get() method is introduced. SQL: Instead of typing long UPDATE query one can use the SET statement: `box.execute([[SET "" = ]])`. Note, that this query is case sensitive so the name must be quoted. Example: ``` tarantool> box.execute([[set "sql_default_engine" = 'memtx']]) --- - row_count: 1 ... tarantool> box.execute([[set "sql_defer_foreign_keys" = true]]) --- - row_count: 1 ... ``` --- src/box/session_settings.c | 16 ++++++++-- src/box/session_settings.h | 3 ++ src/box/sql/build.c | 25 +++++++++++++++ src/box/sql/parse.y | 5 +++ src/box/sql/sqlInt.h | 11 +++++++ src/box/sql/vdbe.c | 50 ++++++++++++++++++++++++++++++ test/box/session_settings.result | 49 +++++++++++++++++++++++++++++ test/box/session_settings.test.lua | 13 ++++++++ 8 files changed, 169 insertions(+), 3 deletions(-) diff --git a/src/box/session_settings.c b/src/box/session_settings.c index 874fc304a..3d06a191a 100644 --- a/src/box/session_settings.c +++ b/src/box/session_settings.c @@ -324,8 +324,8 @@ session_settings_index_get(struct index *base, const char *key, uint32_t len; key = mp_decode_str(&key, &len); key = tt_cstr(key, len); - int sid = 0; - if (session_settings_set_forward(&sid, key, true, true) != 0) { + int sid = session_setting_find(key); + if (sid < 0) { *result = NULL; return 0; } @@ -426,7 +426,8 @@ session_settings_space_execute_update(struct space *space, struct txn *txn, } key = mp_decode_str(&key, &key_len); key = tt_cstr(key, key_len); - if (session_settings_set_forward(&sid, key, true, true) != 0) { + sid = session_setting_find(key); + if (sid < 0) { *result = NULL; return 0; } @@ -520,3 +521,12 @@ const struct space_vtab session_settings_space_vtab = { /* .prepare_alter = */ generic_space_prepare_alter, /* .invalidate = */ generic_space_invalidate, }; + +int +session_setting_find(const char *name) { + int sid; + if (session_settings_set_forward(&sid, name, true, true) == 0) + return sid; + else + return -1; +} diff --git a/src/box/session_settings.h b/src/box/session_settings.h index de24e3c6f..e2adc5289 100644 --- a/src/box/session_settings.h +++ b/src/box/session_settings.h @@ -84,3 +84,6 @@ struct session_setting { extern struct session_setting session_settings[SESSION_SETTING_COUNT]; extern const char *session_setting_strs[SESSION_SETTING_COUNT]; + +int +session_setting_find(const char *name); diff --git a/src/box/sql/build.c b/src/box/sql/build.c index e5fde08cc..9cbe25443 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -3469,3 +3469,28 @@ sql_session_settings_init() setting->set = sql_session_setting_set; } } + +void +sql_setting_set(struct Parse *parse_context, struct Token *name, + struct Expr *expr) +{ + struct Vdbe *vdbe = sqlGetVdbe(parse_context); + if (vdbe == NULL) + goto abort; + sqlVdbeCountChanges(vdbe); + char *key = sql_name_from_token(parse_context->db, name); + if (key == NULL) + goto abort; + int index = session_setting_find(key); + if (index >= 0) { + int target = ++parse_context->nMem; + sqlExprCode(parse_context, expr, target); + sqlVdbeAddOp2(vdbe, OP_Set, index, target); + return; + } + diag_set(ClientError, ER_SQL_PARSER_GENERIC, + tt_sprintf("Session setting %s doesn't exist", key)); +abort: + parse_context->is_aborted = true; + return; +} diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index cfe1c0012..a01c37e19 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1541,6 +1541,11 @@ cmd ::= DROP INDEX ifexists(E) nm(X) ON fullname(Y). { sql_drop_index(pParse); } +///////////////////////////// The SET command //////////////////////////////// +cmd ::= SET nm(X) EQ expr(Y). { + sql_setting_set(pParse,&X,Y.pExpr); +} + ///////////////////////////// The PRAGMA command ///////////////////////////// // cmd ::= PRAGMA nm(X). { diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index d1fcf4761..3ffae5970 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4510,4 +4510,15 @@ int sql_fieldno_by_name(struct Parse *parse_context, struct Expr *field_name, uint32_t *fieldno); +/** + * Create VDBE instructions to set the new value of the session setting. + * + * @param parse_context Parsing context. + * @param name Name of the session setting. + * @param value New value of the session setting. + */ +void +sql_setting_set(struct Parse *parse_context, struct Token *name, + struct Expr *value); + #endif /* sqlINT_H */ diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 620d74e66..c81486fa6 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -55,6 +55,7 @@ #include "box/schema.h" #include "box/space.h" #include "box/sequence.h" +#include "box/session_settings.h" /* * Invoke this macro on memory cells just prior to changing the @@ -5248,6 +5249,55 @@ case OP_IncMaxid: { break; } +/* Opcode: Set P1 P2 * * * + * + * Set the new value of the session setting. P1 is the id of the + * setting in the session_settings array, P2 is the register + * holding a value. + */ +case OP_Set: { + int sid = pOp->p1; + pIn1 = &aMem[pOp->p2]; + assert(sid >= 0 && sid < SESSION_SETTING_COUNT); + struct session_setting *setting = &session_settings[sid]; + switch (setting->field_type) { + case FIELD_TYPE_BOOLEAN: { + if ((pIn1->flags & MEM_Bool) == 0) + goto invalid_type; + bool value = pIn1->u.b; + size_t size = mp_sizeof_bool(value); + char *mp_value = (char *) static_alloc(size); + mp_encode_bool(mp_value, value); + if (setting->set(sid, mp_value) != 0) + goto abort_due_to_error; + break; + } + case FIELD_TYPE_STRING: { + if ((pIn1->flags & MEM_Str) == 0) + goto invalid_type; + const char *str = pIn1->z; + uint32_t size = mp_sizeof_str(pIn1->n); + char *mp_value = (char *) static_alloc(size); + if (mp_value == NULL) { + diag_set(OutOfMemory, size, "static_alloc", "mp_value"); + goto abort_due_to_error; + } + mp_encode_str(mp_value, str, pIn1->n); + if (setting->set(sid, mp_value) != 0) + goto abort_due_to_error; + break; + } + default: + invalid_type: + diag_set(ClientError, ER_SESSION_SETTING_INVALID_VALUE, + session_setting_strs[sid], + field_type_strs[setting->field_type]); + goto abort_due_to_error; + } + p->nChange++; + break; +} + /* Opcode: Noop * * * * * * * Do nothing. This instruction is often useful as a jump diff --git a/test/box/session_settings.result b/test/box/session_settings.result index 6d7074e8c..7c9802af6 100644 --- a/test/box/session_settings.result +++ b/test/box/session_settings.result @@ -339,6 +339,39 @@ settings.sql_defer_foreign_keys | - false | ... +box.execute([[set "sql_default_engine" = 'vinyl']]) + | --- + | - row_count: 1 + | ... +s:get('sql_default_engine').value + | --- + | - vinyl + | ... +box.execute([[set "sql_default_engine" = 'memtx']]) + | --- + | - row_count: 1 + | ... +s:get('sql_default_engine').value + | --- + | - memtx + | ... +box.execute([[set "sql_defer_foreign_keys" = true]]) + | --- + | - row_count: 1 + | ... +s:get('sql_defer_foreign_keys').value + | --- + | - true + | ... +box.execute([[set "sql_defer_foreign_keys" = ?]], {false}) + | --- + | - row_count: 1 + | ... +s:get('sql_defer_foreign_keys').value + | --- + | - false + | ... + settings.sql_default_engine:set(true) | --- | - null @@ -361,3 +394,19 @@ box.session.settings.sql_default_engine:set(str) | --- | - error: Failed to allocate 20483 bytes in static_alloc for mp_value | ... + +box.execute([[set "sql_def_engine" = true]]) + | --- + | - null + | - Session setting sql_def_engine doesn't exist + | ... +box.execute([[set "sql_default_engine" = true]]) + | --- + | - null + | - Session setting sql_default_engine expected a value of type string + | ... +box.execute([[set "sql_defer_foreign_keys" = 'true']]) + | --- + | - null + | - Session setting sql_defer_foreign_keys expected a value of type boolean + | ... diff --git a/test/box/session_settings.test.lua b/test/box/session_settings.test.lua index 23799874a..f2d7e03f5 100644 --- a/test/box/session_settings.test.lua +++ b/test/box/session_settings.test.lua @@ -132,9 +132,22 @@ s:get('sql_defer_foreign_keys').value s:update('sql_defer_foreign_keys', {{'=', 2, false}}) settings.sql_defer_foreign_keys +box.execute([[set "sql_default_engine" = 'vinyl']]) +s:get('sql_default_engine').value +box.execute([[set "sql_default_engine" = 'memtx']]) +s:get('sql_default_engine').value +box.execute([[set "sql_defer_foreign_keys" = true]]) +s:get('sql_defer_foreign_keys').value +box.execute([[set "sql_defer_foreign_keys" = ?]], {false}) +s:get('sql_defer_foreign_keys').value + settings.sql_default_engine:set(true) settings.sql_defer_foreign_keys:set(false, 1, 2, 3) settings.sql_parser_debug:set('string') str = string.rep('a', 20 * 1024) box.session.settings.sql_default_engine:set(str) + +box.execute([[set "sql_def_engine" = true]]) +box.execute([[set "sql_default_engine" = true]]) +box.execute([[set "sql_defer_foreign_keys" = 'true']]) -- 2.21.1 (Apple Git-122.3)