[patches] [PATCH v2 1/2] format: add collation to filed_def and tuple_field

Nikita Pettik korablev at tarantool.org
Tue Mar 6 21:11:03 MSK 2018


Originally, collation can be assigned only to index parts. However, SQL
standard says that each column is capable of having such property. Thus,
in order to support standard, add collation id to field_def and pointer
to collation to tuple_field.

Closes #2937
---
 src/box/alter.cc            |  8 ++++++++
 src/box/field_def.c         |  5 ++++-
 src/box/field_def.h         |  2 ++
 src/box/lua/schema.lua      |  7 +++++++
 src/box/sql.c               | 11 ++++++++++-
 src/box/tuple_format.c      | 14 +++++++++++++-
 src/box/tuple_format.h      |  3 +++
 test/box/ddl.result         | 46 +++++++++++++++++++++++++++++++++++++++++++++
 test/box/ddl.test.lua       | 18 ++++++++++++++++++
 test/engine/iterator.result |  2 +-
 10 files changed, 112 insertions(+), 4 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 20f98550b..9bc966a4c 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -394,6 +394,14 @@ field_def_decode(struct field_def *field, const char **data,
 				     "nullable action properties", fieldno +
 				     TUPLE_INDEX_BASE));
 	}
+	if (field->coll_id != COLL_NONE &&
+	    field->type != FIELD_TYPE_STRING &&
+	    field->type != FIELD_TYPE_SCALAR &&
+	    field->type != FIELD_TYPE_ANY) {
+		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("collation is reasonable only for "
+				     "string, scalar and any fields"));
+	}
 }
 
 /**
diff --git a/src/box/field_def.c b/src/box/field_def.c
index 2c9aa9974..151050998 100644
--- a/src/box/field_def.c
+++ b/src/box/field_def.c
@@ -31,6 +31,7 @@
 
 #include "field_def.h"
 #include "trivia/util.h"
+#include "key_def.h"
 
 const char *field_type_strs[] = {
 	/* [FIELD_TYPE_ANY]      = */ "any",
@@ -92,6 +93,7 @@ const struct opt_def field_def_reg[] = {
 	OPT_DEF("is_nullable", OPT_BOOL, struct field_def, is_nullable),
 	OPT_DEF_ENUM("nullable_action", on_conflict_action, struct field_def,
 		     nullable_action, NULL),
+	OPT_DEF("collation", OPT_UINT32, struct field_def, coll_id),
 	OPT_END,
 };
 
@@ -99,7 +101,8 @@ const struct field_def field_def_default = {
 	.type = FIELD_TYPE_ANY,
 	.name = NULL,
 	.is_nullable = false,
-	.nullable_action = ON_CONFLICT_ACTION_DEFAULT
+	.nullable_action = ON_CONFLICT_ACTION_DEFAULT,
+	.coll_id = COLL_NONE
 };
 
 enum field_type
diff --git a/src/box/field_def.h b/src/box/field_def.h
index 25f7708b4..e7be06a15 100644
--- a/src/box/field_def.h
+++ b/src/box/field_def.h
@@ -108,6 +108,8 @@ struct field_def {
 	bool is_nullable;
 	/** Action to perform if NULL constraint failed. */
 	enum on_conflict_action nullable_action;
+	/** Collation ID for string comparison. */
+	uint32_t coll_id;
 };
 
 #if defined(__cplusplus)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 6b9c76e6d..5ed08cc52 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -346,6 +346,13 @@ function update_format(format)
                     end
                 elseif k == 2 and not given.type and not given.name then
                     field.type = v
+                elseif k == 'collation' then
+                    local coll = box.space._collation.index.name:get{v}
+                    if not coll then
+                        box.error(box.error.ILLEGAL_PARAMS,
+                            "format[" .. i .. "]: collation was not found by name '" .. v .. "'")
+                    end
+                    field[k] = coll[1]
                 else
                     field[k] = v
                 end
diff --git a/src/box/sql.c b/src/box/sql.c
index 9ce270da9..eca80f14a 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1561,7 +1561,12 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 
 	for (i = 0; i < n; i++) {
 		const char *t;
-		p = enc->encode_map(p, 4);
+		struct coll *coll = NULL;
+		if (aCol[i].zColl != NULL &&
+		    strcasecmp(aCol[i].zColl, "binary") != 0) {
+			coll = sqlite3FindCollSeq(NULL, aCol[i].zColl, 0);
+		}
+		p = enc->encode_map(p, coll ? 5 : 4);
 		p = enc->encode_str(p, "name", 4);
 		p = enc->encode_str(p, aCol[i].zName, strlen(aCol[i].zName));
 		p = enc->encode_str(p, "type", 4);
@@ -1579,6 +1584,10 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 		assert(aCol[i].notNull < on_conflict_action_MAX);
 		const char *action = on_conflict_action_strs[aCol[i].notNull];
 		p = enc->encode_str(p, action, strlen(action));
+		if (coll != NULL) {
+			p = enc->encode_str(p, "collation", strlen("collation"));
+			p = enc->encode_uint(p, coll->id);
+		}
 	}
 	return (int)(p - base);
 }
diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 6f342219b..b2b3b2675 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -37,7 +37,8 @@ static intptr_t recycled_format_ids = FORMAT_ID_NIL;
 static uint32_t formats_size = 0, formats_capacity = 0;
 
 static const struct tuple_field tuple_field_default = {
-	FIELD_TYPE_ANY, TUPLE_OFFSET_SLOT_NIL, false, ON_CONFLICT_ACTION_DEFAULT
+	FIELD_TYPE_ANY, TUPLE_OFFSET_SLOT_NIL, false,
+	ON_CONFLICT_ACTION_DEFAULT, NULL
 };
 
 /**
@@ -62,6 +63,17 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
 		format->fields[i].type = fields[i].type;
 		format->fields[i].offset_slot = TUPLE_OFFSET_SLOT_NIL;
 		format->fields[i].nullable_action = fields[i].nullable_action;
+		struct coll *coll = NULL;
+		uint32_t coll_id = fields[i].coll_id;
+		if (coll_id != COLL_NONE) {
+			coll = coll_by_id(coll_id);
+			if (coll == NULL) {
+				diag_set(ClientError,ER_WRONG_COLLATION_OPTIONS,
+					 i + 1, "collation was not found by ID");
+				return -1;
+			}
+		}
+		format->fields[i].coll = coll;
 	}
 	/* Initialize remaining fields */
 	for (uint32_t i = field_count; i < format->field_count; i++)
diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h
index a9cf0a68d..341039d73 100644
--- a/src/box/tuple_format.h
+++ b/src/box/tuple_format.h
@@ -35,6 +35,7 @@
 #include "field_def.h"
 #include "errinj.h"
 #include "tuple_dictionary.h"
+#include "coll_cache.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -98,6 +99,8 @@ struct tuple_field {
 	bool is_key_part;
 	/** Action to perform if NULL constraint failed. */
 	enum on_conflict_action nullable_action;
+	/** Collation definition for string comparison */
+	struct coll *coll;
 };
 
 /**
diff --git a/test/box/ddl.result b/test/box/ddl.result
index 0eef37992..335f55500 100644
--- a/test/box/ddl.result
+++ b/test/box/ddl.result
@@ -514,3 +514,49 @@ s:format()[3].custom_field
 s:drop()
 ---
 ...
+--
+-- gh-2937: allow to specify collation in field definition.
+--
+format = {}
+---
+...
+format[1] = {name = 'field1', type = 'string', collation = 'unicode'}
+---
+...
+format[2] = {'field2', 'any', collation = 'unicode_ci'}
+---
+...
+format[3] = {type = 'scalar', name = 'field3', collation = 'unicode'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+...
+s:format()
+---
+- [{'type': 'string', 'name': 'field1', 'collation': 1}, {'type': 'any', 'name': 'field2',
+    'collation': 2}, {'type': 'scalar', 'name': 'field3', 'collation': 1}]
+...
+s:drop()
+---
+...
+-- Check that collation is allowed only for stings, scalar and any types.
+format = {}
+---
+...
+format[1] = {'field1', 'unsigned', collation = 'unicode'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+- error: 'Failed to create space ''test'': collation is reasonable only for string,
+    scalar and any fields'
+...
+format[1] = {'field2', 'array', collation = 'unicode_ci'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+- error: 'Failed to create space ''test'': collation is reasonable only for string,
+    scalar and any fields'
+...
diff --git a/test/box/ddl.test.lua b/test/box/ddl.test.lua
index 820fe7d4d..a6cab53f0 100644
--- a/test/box/ddl.test.lua
+++ b/test/box/ddl.test.lua
@@ -198,3 +198,21 @@ format[3] = {'field3', 'unsigned', custom_field = 'custom_value'}
 s = box.schema.create_space('test', {format = format})
 s:format()[3].custom_field
 s:drop()
+
+--
+-- gh-2937: allow to specify collation in field definition.
+--
+format = {}
+format[1] = {name = 'field1', type = 'string', collation = 'unicode'}
+format[2] = {'field2', 'any', collation = 'unicode_ci'}
+format[3] = {type = 'scalar', name = 'field3', collation = 'unicode'}
+s = box.schema.create_space('test', {format = format})
+s:format()
+s:drop()
+
+-- Check that collation is allowed only for stings, scalar and any types.
+format = {}
+format[1] = {'field1', 'unsigned', collation = 'unicode'}
+s = box.schema.create_space('test', {format = format})
+format[1] = {'field2', 'array', collation = 'unicode_ci'}
+s = box.schema.create_space('test', {format = format})
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 98d939bf6..dcf4de74d 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4215,7 +4215,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:989: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:996: usage: next(param, state)'
 ...
 value
 ---
-- 
2.15.1




More information about the Tarantool-patches mailing list