From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Subject: Re: [tarantool-patches] Re: [PATCH v1 1/1] box: introduce VARBINARY field type References: <20190626191130.GA3506@atlas> From: Kirill Shcherbatov Message-ID: Date: Thu, 27 Jun 2019 10:38:37 +0300 MIME-Version: 1.0 In-Reply-To: <20190626191130.GA3506@atlas> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit To: tarantool-patches@freelists.org, Konstantin Osipov Cc: Vladimir Davydov List-ID: > please add docbot request. In fact, I did not include TarantoolBot query consciously: this patch is preparatory for #4206, and only introduces a new field type, while preparing data corresponding to this field in Tarantool now is not easy (see the test case below). But in general, the documentation may be updated. So I've updated a commit message. ========================================== A new VARBINARY field type would be useful for SQL type system. Closes #4201 Needed for #4206 @TarantoolBot document Title: new varbinary field type Introduced a new field type varbinary to represent mp_bin values. The new type varbinary may be used in format or index definition. Example: s = box.schema.space.create('withdata') s:format({{"b", "varbinary"}}) pk = s:create_index('pk', {parts = {1, "varbinary"}}) --- src/box/field_def.c | 23 +++-- src/box/field_def.h | 1 + src/box/lua/key_def.c | 1 + src/box/tuple_compare.cc | 17 ++++ test/box/varbinary_type.result | 152 +++++++++++++++++++++++++++++++ test/box/varbinary_type.test.lua | 51 +++++++++++ 6 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 test/box/varbinary_type.result create mode 100644 test/box/varbinary_type.test.lua diff --git a/src/box/field_def.c b/src/box/field_def.c index 0ba3d3294..346042b98 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -55,6 +55,7 @@ const uint32_t field_mp_type[] = { (1U << MP_FLOAT) | (1U << MP_DOUBLE), /* [FIELD_TYPE_INTEGER] = */ (1U << MP_UINT) | (1U << MP_INT), /* [FIELD_TYPE_BOOLEAN] = */ 1U << MP_BOOL, + /* [FIELD_TYPE_VARBINARY] = */ 1U << MP_BIN, /* [FIELD_TYPE_SCALAR] = */ (1U << MP_UINT) | (1U << MP_INT) | (1U << MP_FLOAT) | (1U << MP_DOUBLE) | (1U << MP_STR) | (1U << MP_BIN) | (1U << MP_BOOL), @@ -69,6 +70,7 @@ const char *field_type_strs[] = { /* [FIELD_TYPE_NUMBER] = */ "number", /* [FIELD_TYPE_INTEGER] = */ "integer", /* [FIELD_TYPE_BOOLEAN] = */ "boolean", + /* [FIELD_TYPE_VARBINARY] = */"varbinary", /* [FIELD_TYPE_SCALAR] = */ "scalar", /* [FIELD_TYPE_ARRAY] = */ "array", /* [FIELD_TYPE_MAP] = */ "map", @@ -96,16 +98,17 @@ field_type_by_name_wrapper(const char *str, uint32_t len) * values can be stored in the j type. */ static const bool field_type_compatibility[] = { - /* ANY UNSIGNED STRING NUMBER INTEGER BOOLEAN SCALAR ARRAY MAP */ -/* ANY */ true, false, false, false, false, false, false, false, false, -/* UNSIGNED */ true, true, false, true, true, false, true, false, false, -/* STRING */ true, false, true, false, false, false, true, false, false, -/* NUMBER */ true, false, false, true, false, false, true, false, false, -/* INTEGER */ true, false, false, true, true, false, true, false, false, -/* BOOLEAN */ true, false, false, false, false, true, true, false, false, -/* SCALAR */ true, false, false, false, false, false, true, false, false, -/* ARRAY */ true, false, false, false, false, false, false, true, false, -/* MAP */ true, false, false, false, false, false, false, false, true, + /* ANY UNSIGNED STRING NUMBER INTEGER BOOLEAN VARBINARY SCALAR ARRAY MAP */ +/* ANY */ true, false, false, false, false, false, false, false, false, false, +/* UNSIGNED */ true, true, false, true, true, false, false, true, false, false, +/* STRING */ true, false, true, false, false, false, false, true, false, false, +/* NUMBER */ true, false, false, true, false, false, false, true, false, false, +/* INTEGER */ true, false, false, true, true, false, false, true, false, false, +/* BOOLEAN */ true, false, false, false, false, true, false, true, false, false, +/* VARBINARY*/ true, false, false, false, false, false, true, true, false, false, +/* SCALAR */ true, false, false, false, false, false, false, true, false, false, +/* ARRAY */ true, false, false, false, false, false, false, false, true, false, +/* MAP */ true, false, false, false, false, false, false, false, false, true, }; bool diff --git a/src/box/field_def.h b/src/box/field_def.h index f944de9d6..c1a7ec0a9 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -56,6 +56,7 @@ enum field_type { FIELD_TYPE_NUMBER, FIELD_TYPE_INTEGER, FIELD_TYPE_BOOLEAN, + FIELD_TYPE_VARBINARY, FIELD_TYPE_SCALAR, FIELD_TYPE_ARRAY, FIELD_TYPE_MAP, diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c index dfcc89442..052a1c85d 100644 --- a/src/box/lua/key_def.c +++ b/src/box/lua/key_def.c @@ -111,6 +111,7 @@ luaT_key_def_set_part(struct lua_State *L, struct key_part_def *part, part->type = field_type_by_name(type_name, type_len); switch (part->type) { case FIELD_TYPE_ANY: + case FIELD_TYPE_VARBINARY: case FIELD_TYPE_ARRAY: case FIELD_TYPE_MAP: /* Tuple comparators don't support these types. */ diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc index c1a70a087..95a0f58c9 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -404,6 +404,8 @@ tuple_compare_field(const char *field_a, const char *field_b, return mp_compare_number(field_a, field_b); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); + case FIELD_TYPE_VARBINARY: + return mp_compare_bin(field_a, field_b); case FIELD_TYPE_SCALAR: return coll != NULL ? mp_compare_scalar_coll(field_a, field_b, coll) : @@ -434,6 +436,8 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type, field_b, b_type); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); + case FIELD_TYPE_VARBINARY: + return mp_compare_bin(field_a, field_b); case FIELD_TYPE_SCALAR: return coll != NULL ? mp_compare_scalar_coll(field_a, field_b, coll) : @@ -1502,6 +1506,14 @@ field_hint_string(const char *field, struct coll *coll) hint_str_coll(field, len, coll); } +static inline hint_t +field_hint_varbinary(const char *field) +{ + assert(mp_typeof(*field) == MP_BIN); + uint32_t len = mp_decode_binl(&field); + return hint_bin(field, len); +} + static inline hint_t field_hint_scalar(const char *field, struct coll *coll) { @@ -1547,6 +1559,8 @@ field_hint(const char *field, struct coll *coll) return field_hint_number(field); case FIELD_TYPE_STRING: return field_hint_string(field, coll); + case FIELD_TYPE_VARBINARY: + return field_hint_varbinary(field); case FIELD_TYPE_SCALAR: return field_hint_scalar(field, coll); default: @@ -1648,6 +1662,9 @@ key_def_set_hint_func(struct key_def *def) case FIELD_TYPE_STRING: key_def_set_hint_func(def); break; + case FIELD_TYPE_VARBINARY: + key_def_set_hint_func(def); + break; case FIELD_TYPE_SCALAR: key_def_set_hint_func(def); break; diff --git a/test/box/varbinary_type.result b/test/box/varbinary_type.result new file mode 100644 index 000000000..c7cd5700c --- /dev/null +++ b/test/box/varbinary_type.result @@ -0,0 +1,152 @@ +env = require('test_run') +--- +... +test_run = env.new() +--- +... +-- +-- gh-4201: Introduce varbinary field type. +-- +s = box.schema.space.create('withdata') +--- +... +s:format({{"b", "integer"}}) +--- +... +_ = s:create_index('pk', {parts = {1, "varbinary"}}) +--- +- error: Field 1 has type 'integer' in space format, but type 'varbinary' in index + definition +... +s:format({{"b", "varbinary"}}) +--- +... +_ = s:create_index('pk', {parts = {1, "integer"}}) +--- +- error: Field 1 has type 'varbinary' in space format, but type 'integer' in index + definition +... +pk = s:create_index('pk', {parts = {1, "varbinary"}}) +--- +... +buffer = require('buffer') +--- +... +ffi = require('ffi') +--- +... +test_run:cmd("setopt delimiter ';'") +--- +- true +... +function bintuple_insert(space, bytes) + local tmpbuf = buffer.IBUF_SHARED + tmpbuf:reset() + local p = tmpbuf:alloc(3 + #bytes) + p[0] = 0x91 + p[1] = 0xC4 + p[2] = #bytes + for i, c in pairs(bytes) do p[i + 3 - 1] = c end + ffi.cdef[[int box_insert(uint32_t space_id, const char *tuple, const char *tuple_end, box_tuple_t **result);]] + ffi.C.box_insert(space.id, tmpbuf.rpos, tmpbuf.wpos, nil) +end +test_run:cmd("setopt delimiter ''"); +--- +... +bintuple_insert(s, {0xDE, 0xAD, 0xBE, 0xAF}) +--- +... +bintuple_insert(s, {0xFE, 0xED, 0xFA, 0xCE}) +--- +... +s:select() +--- +- - [!!binary 3q2+rw==] + - [!!binary /u36zg==] +... +box.execute("SELECT * FROM \"withdata\" WHERE \"b\" < x'FEEDFACE';") +--- +- metadata: + - name: b + type: varbinary + rows: + - [!!binary 3q2+rw==] +... +pk:alter({parts = {1, "scalar"}}) +--- +... +s:format({{"b", "scalar"}}) +--- +... +s:insert({11}) +--- +- [11] +... +s:insert({22}) +--- +- [22] +... +s:insert({"11"}) +--- +- ['11'] +... +s:insert({"22"}) +--- +- ['22'] +... +s:select() +--- +- - [11] + - [22] + - ['11'] + - ['22'] + - [!!binary 3q2+rw==] + - [!!binary /u36zg==] +... +box.execute("SELECT * FROM \"withdata\" WHERE \"b\" <= x'DEADBEAF';") +--- +- metadata: + - name: b + type: scalar + rows: + - [11] + - [22] + - ['11'] + - ['22'] + - [!!binary 3q2+rw==] +... +pk:alter({parts = {1, "varbinary"}}) +--- +- error: 'Tuple field 1 type does not match one required by operation: expected varbinary' +... +s:delete({11}) +--- +- [11] +... +s:delete({22}) +--- +- [22] +... +s:delete({"11"}) +--- +- ['11'] +... +s:delete({"22"}) +--- +- ['22'] +... +bintuple_insert(s, {0xFA, 0xDE, 0xDE, 0xAD}) +--- +... +pk:alter({parts = {1, "varbinary"}}) +--- +... +s:select() +--- +- - [!!binary 3q2+rw==] + - [!!binary +t7erQ==] + - [!!binary /u36zg==] +... +s:drop() +--- +... diff --git a/test/box/varbinary_type.test.lua b/test/box/varbinary_type.test.lua new file mode 100644 index 000000000..7895a1d22 --- /dev/null +++ b/test/box/varbinary_type.test.lua @@ -0,0 +1,51 @@ +env = require('test_run') +test_run = env.new() + +-- +-- gh-4201: Introduce varbinary field type. +-- +s = box.schema.space.create('withdata') +s:format({{"b", "integer"}}) +_ = s:create_index('pk', {parts = {1, "varbinary"}}) +s:format({{"b", "varbinary"}}) +_ = s:create_index('pk', {parts = {1, "integer"}}) +pk = s:create_index('pk', {parts = {1, "varbinary"}}) + +buffer = require('buffer') +ffi = require('ffi') + +test_run:cmd("setopt delimiter ';'") +function bintuple_insert(space, bytes) + local tmpbuf = buffer.IBUF_SHARED + tmpbuf:reset() + local p = tmpbuf:alloc(3 + #bytes) + p[0] = 0x91 + p[1] = 0xC4 + p[2] = #bytes + for i, c in pairs(bytes) do p[i + 3 - 1] = c end + ffi.cdef[[int box_insert(uint32_t space_id, const char *tuple, const char *tuple_end, box_tuple_t **result);]] + ffi.C.box_insert(space.id, tmpbuf.rpos, tmpbuf.wpos, nil) +end +test_run:cmd("setopt delimiter ''"); + +bintuple_insert(s, {0xDE, 0xAD, 0xBE, 0xAF}) +bintuple_insert(s, {0xFE, 0xED, 0xFA, 0xCE}) +s:select() +box.execute("SELECT * FROM \"withdata\" WHERE \"b\" < x'FEEDFACE';") +pk:alter({parts = {1, "scalar"}}) +s:format({{"b", "scalar"}}) +s:insert({11}) +s:insert({22}) +s:insert({"11"}) +s:insert({"22"}) +s:select() +box.execute("SELECT * FROM \"withdata\" WHERE \"b\" <= x'DEADBEAF';") +pk:alter({parts = {1, "varbinary"}}) +s:delete({11}) +s:delete({22}) +s:delete({"11"}) +s:delete({"22"}) +bintuple_insert(s, {0xFA, 0xDE, 0xDE, 0xAD}) +pk:alter({parts = {1, "varbinary"}}) +s:select() +s:drop() -- 2.21.0