[PATCH v1 1/1] box: introduce VARBINARY field type

Kirill Shcherbatov kshcherbatov at tarantool.org
Wed Jun 26 19:40:59 MSK 2019


Introduced a new field type VARBINARY to represent mp_bin values.
This field type would be useful for SQL type system.

Closes #4201
Needed for #4206
---
http://github.com/tarantool/tarantool/tree/kshch/gh-4201-varbinary-type
https://github.com/tarantool/tarantool/issues/4201


 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<FIELD_TYPE_STRING>(def);
 		break;
+	case FIELD_TYPE_VARBINARY:
+		key_def_set_hint_func<FIELD_TYPE_VARBINARY>(def);
+		break;
 	case FIELD_TYPE_SCALAR:
 		key_def_set_hint_func<FIELD_TYPE_SCALAR>(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




More information about the Tarantool-patches mailing list