From b6a08c5c00512bb1dff0c410afaf4fa710143926 Mon Sep 17 00:00:00 2001 From: Mergen Imeev <imeevma@gmail.com> Date: Fri, 26 Nov 2021 10:44:14 +0300 Subject: [PATCH] sql: introduce binding for MAP After this patch, MAP values can be used as bind variables. However, due to the current syntax for binding in Lua, the only possible way is to use MAP values as the named bind variable. Part of #4763 --- src/box/bind.c | 7 +++---- src/box/lua/execute.c | 5 +---- src/box/sql/sqlInt.h | 3 +++ src/box/sql/vdbeapi.c | 8 ++++++++ test/sql-tap/map.test.lua | 25 ++++++++++++++++++++++++- test/sql/bind.result | 6 +++++- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/box/bind.c b/src/box/bind.c index af9f9eac54..441c9f46ff 100644 --- a/src/box/bind.c +++ b/src/box/bind.c @@ -100,15 +100,12 @@ sql_bind_decode(struct sql_bind *bind, int i, const char **packet) bind->s = mp_decode_bin(packet, &bind->bytes); break; case MP_ARRAY: + case MP_MAP: case MP_EXT: bind->s = *packet; mp_next(packet); bind->bytes = *packet - bind->s; break; - case MP_MAP: - diag_set(ClientError, ER_SQL_BIND_TYPE, "MAP", - sql_bind_name(bind)); - return -1; default: unreachable(); } @@ -189,6 +186,8 @@ sql_bind_column(struct sql_stmt *stmt, const struct sql_bind *p, return sql_bind_bin_static(stmt, pos, p->s, p->bytes); case MP_ARRAY: return sql_bind_array_static(stmt, pos, p->s, p->bytes); + case MP_MAP: + return sql_bind_map_static(stmt, pos, p->s, p->bytes); case MP_EXT: assert(p->ext_type == MP_UUID || p->ext_type == MP_DECIMAL); if (p->ext_type == MP_UUID) diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c index 71d4d7faed..b3f81d709f 100644 --- a/src/box/lua/execute.c +++ b/src/box/lua/execute.c @@ -386,6 +386,7 @@ lua_sql_bind_decode(struct lua_State *L, struct sql_bind *bind, int idx, int i) diag_set(ClientError, ER_SQL_BIND_TYPE, "USERDATA", sql_bind_name(bind)); return -1; + case MP_MAP: case MP_ARRAY: { size_t used = region_used(region); struct mpstream stream; @@ -410,10 +411,6 @@ lua_sql_bind_decode(struct lua_State *L, struct sql_bind *bind, int idx, int i) diag_set(OutOfMemory, bind->bytes, "region_join", "bind->s"); return -1; } - case MP_MAP: - diag_set(ClientError, ER_SQL_BIND_TYPE, "MAP", - sql_bind_name(bind)); - return -1; default: unreachable(); } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 716110edc6..0db16b293c 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -559,6 +559,9 @@ sql_bind_bin_static(sql_stmt *stmt, int i, const char *str, uint32_t size); int sql_bind_array_static(sql_stmt *stmt, int i, const char *str, uint32_t size); +int +sql_bind_map_static(sql_stmt *stmt, int i, const char *str, uint32_t size); + int sql_bind_uuid(struct sql_stmt *stmt, int i, const struct tt_uuid *uuid); diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c index 3ea155d17b..060846572f 100644 --- a/src/box/sql/vdbeapi.c +++ b/src/box/sql/vdbeapi.c @@ -532,6 +532,14 @@ sql_bind_array_static(sql_stmt *stmt, int i, const char *str, uint32_t size) return sql_bind_type(vdbe, i, "array"); } +int +sql_bind_map_static(sql_stmt *stmt, int i, const char *str, uint32_t size) +{ + struct Vdbe *vdbe = (struct Vdbe *)stmt; + mem_set_map_static(&vdbe->aVar[i - 1], (char *)str, size); + return sql_bind_type(vdbe, i, "map"); +} + int sql_bind_uuid(struct sql_stmt *stmt, int i, const struct tt_uuid *uuid) { diff --git a/test/sql-tap/map.test.lua b/test/sql-tap/map.test.lua index 1afbb2b1de..8cdbb16629 100755 --- a/test/sql-tap/map.test.lua +++ b/test/sql-tap/map.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool local test = require("sqltester") -test:plan(110) +test:plan(112) box.schema.func.create('M1', { language = 'Lua', @@ -982,6 +982,29 @@ test:do_catchsql_test( 1, "Failed to execute SQL statement: wrong arguments for function ZEROBLOB()" }) +-- Make sure that MAP values can be used as a bound variable. +test:do_test( + "builtins-13.1", + function() + local res = box.execute([[SELECT #a;]], {{['#a'] = {abc = 2, [1] = 3}}}) + return {res.rows[1][1]} + end, { + {abc = 2, [1] = 3} + }) + +local remote = require('net.box') +box.cfg{listen = os.getenv('LISTEN')} +local cn = remote.connect(box.cfg.listen) +test:do_test( + "builtins-13.2", + function() + local res = cn:execute([[SELECT #a;]], {{['#a'] = {abc = 2, [1] = 3}}}) + return {res.rows[1][1]} + end, { + {abc = 2, [1] = 3} + }) +cn:close() + box.execute([[DROP TABLE t1;]]) box.execute([[DROP TABLE t;]]) diff --git a/test/sql/bind.result b/test/sql/bind.result index f269056e22..e738a91cda 100644 --- a/test/sql/bind.result +++ b/test/sql/bind.result @@ -276,7 +276,11 @@ parameters[1][':value'] = {kek = 300} ... execute('SELECT :value', parameters) --- -- error: Bind value type MAP for parameter ':value' is not supported +- metadata: + - name: COLUMN_1 + type: map + rows: + - [{'kek': 300}] ... -- gh-3810: bind values of integer in range up to 2^64 - 1. -- -- GitLab