From 97a6a4c5efbfb25246012941d661f3458c87ca0e Mon Sep 17 00:00:00 2001
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Date: Mon, 14 May 2018 23:03:49 +0300
Subject: [PATCH] collation: split collation into coll and id objects

In the issue #3290 the important problem appeared - Tarantool can
not create completely internal collations with no ID, name,
owner. Just for internal usage.

Original struct coll can not be used for this since
* it has fields that are not needed in internals;
* collation name is public thing, and the collation cache uses
  it, so it would be necessary to forbid to a user usage of some
  system names;
* when multiple collations has the same comparator and only their
  names/owners/IDs are different, the separate UCollator objects
  are created, but it would be good to be able to reference a
  single one.

This patch renames coll to coll_id, coll_def to call_id_def and
introduces coll - pure collation object with no any user defined
things.

Needed for #3290.
---
 src/CMakeLists.txt                        |   2 +
 src/box/CMakeLists.txt                    |   6 +-
 src/box/alter.cc                          | 105 ++++++++-------
 src/box/coll_id.c                         |  65 +++++++++
 src/box/coll_id.h                         |  77 +++++++++++
 src/box/{coll_cache.c => coll_id_cache.c} |  62 ++++-----
 src/box/{coll_cache.h => coll_id_cache.h} |  27 ++--
 src/box/{coll_def.c => coll_id_def.c}     |  34 +----
 src/box/coll_id_def.h                     |  54 ++++++++
 src/box/error.cc                          |   2 +
 src/box/key_def.c                         |  23 ++--
 src/box/key_def.h                         |   7 +-
 src/box/lua/space.cc                      |   8 +-
 src/box/schema.cc                         |   8 +-
 src/box/tuple.c                           |   6 +-
 src/box/tuple_compare.cc                  |   5 +-
 src/box/tuple_hash.cc                     |   1 -
 src/{box => }/coll.c                      | 153 ++++++++--------------
 src/{box => }/coll.h                      |  37 ++----
 src/coll_def.c                            |  63 +++++++++
 src/{box => }/coll_def.h                  |  35 +----
 src/diag.h                                |   2 +
 src/exception.cc                          |  24 ++++
 src/exception.h                           |   9 ++
 test/unit/coll.cpp                        |   8 +-
 25 files changed, 505 insertions(+), 318 deletions(-)
 create mode 100644 src/box/coll_id.c
 create mode 100644 src/box/coll_id.h
 rename src/box/{coll_cache.c => coll_id_cache.c} (56%)
 rename src/box/{coll_cache.h => coll_id_cache.h} (78%)
 rename src/box/{coll_def.c => coll_id_def.c} (86%)
 create mode 100644 src/box/coll_id_def.h
 rename src/{box => }/coll.c (63%)
 rename src/{box => }/coll.h (74%)
 create mode 100644 src/coll_def.c
 rename src/{box => }/coll_def.h (82%)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8ab09e968b..5bf17614be 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -94,6 +94,8 @@ set (core_sources
      random.c
      trigger.cc
      http_parser.c
+     coll.c
+     coll_def.c
  )
 
 if (TARGET_OS_NETBSD)
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 7b5de7372a..0bbc857a11 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -41,9 +41,9 @@ add_library(tuple STATIC
     tuple_bloom.c
     tuple_dictionary.c
     key_def.c
-    coll_def.c
-    coll.c
-    coll_cache.c
+    coll_id_def.c
+    coll_id.c
+    coll_id_cache.c
     field_def.c
     opt_def.c
 )
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 8766c8171d..7858af9890 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -34,7 +34,8 @@
 #include "space.h"
 #include "index.h"
 #include "func.h"
-#include "coll_cache.h"
+#include "coll_id_cache.h"
+#include "coll_id_def.h"
 #include "txn.h"
 #include "tuple.h"
 #include "fiber.h" /* for gc_pool */
@@ -2284,9 +2285,9 @@ on_replace_dd_func(struct trigger * /* trigger */, void *event)
 	}
 }
 
-/** Create a collation definition from tuple. */
+/** Create a collation identifier definition from tuple. */
 void
-coll_def_new_from_tuple(const struct tuple *tuple, struct coll_def *def)
+coll_id_def_new_from_tuple(const struct tuple *tuple, struct coll_id_def *def)
 {
 	memset(def, 0, sizeof(*def));
 	uint32_t name_len, locale_len, type_len;
@@ -2294,15 +2295,16 @@ coll_def_new_from_tuple(const struct tuple *tuple, struct coll_def *def)
 	def->name = tuple_field_str_xc(tuple, BOX_COLLATION_FIELD_NAME, &name_len);
 	def->name_len = name_len;
 	def->owner_id = tuple_field_u32_xc(tuple, BOX_COLLATION_FIELD_UID);
+	struct coll_def *base = &def->base;
 	const char *type = tuple_field_str_xc(tuple, BOX_COLLATION_FIELD_TYPE,
 					      &type_len);
-	def->type = STRN2ENUM(coll_type, type, type_len);
-	if (def->type == coll_type_MAX)
+	base->type = STRN2ENUM(coll_type, type, type_len);
+	if (base->type == coll_type_MAX)
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "unknown collation type");
-	def->locale = tuple_field_str_xc(tuple, BOX_COLLATION_FIELD_LOCALE,
-					 &locale_len);
-	def->locale_len = locale_len;
+	base->locale = tuple_field_str_xc(tuple, BOX_COLLATION_FIELD_LOCALE,
+					  &locale_len);
+	base->locale_len = locale_len;
 	const char *options =
 		tuple_field_with_type_xc(tuple, BOX_COLLATION_FIELD_OPTIONS,
 					 MP_MAP);
@@ -2315,53 +2317,53 @@ coll_def_new_from_tuple(const struct tuple *tuple, struct coll_def *def)
 			  "collation locale is too long");
 	/* Locale is an optional argument and can be NULL. */
 	if (locale_len > 0)
-		identifier_check_xc(def->locale, locale_len);
+		identifier_check_xc(base->locale, locale_len);
 	identifier_check_xc(def->name, name_len);
 
-	assert(def->type == COLL_TYPE_ICU); /* no more defined now */
-	if (opts_decode(&def->icu, coll_icu_opts_reg, &options,
+	assert(base->type == COLL_TYPE_ICU);
+	if (opts_decode(&base->icu, coll_icu_opts_reg, &options,
 			ER_WRONG_COLLATION_OPTIONS,
 			BOX_COLLATION_FIELD_OPTIONS, NULL) != 0)
 		diag_raise();
 
-	if (def->icu.french_collation == coll_icu_on_off_MAX) {
+	if (base->icu.french_collation == coll_icu_on_off_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong french_collation option setting, "
 				  "expected ON | OFF");
 	}
 
-	if (def->icu.alternate_handling == coll_icu_alternate_handling_MAX) {
+	if (base->icu.alternate_handling == coll_icu_alternate_handling_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong alternate_handling option setting, "
 				  "expected NON_IGNORABLE | SHIFTED");
 	}
 
-	if (def->icu.case_first == coll_icu_case_first_MAX) {
+	if (base->icu.case_first == coll_icu_case_first_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong case_first option setting, "
 				  "expected OFF | UPPER_FIRST | LOWER_FIRST");
 	}
 
-	if (def->icu.case_level == coll_icu_on_off_MAX) {
+	if (base->icu.case_level == coll_icu_on_off_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong case_level option setting, "
 				  "expected ON | OFF");
 	}
 
-	if (def->icu.normalization_mode == coll_icu_on_off_MAX) {
+	if (base->icu.normalization_mode == coll_icu_on_off_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong normalization_mode option setting, "
 				  "expected ON | OFF");
 	}
 
-	if (def->icu.strength == coll_icu_strength_MAX) {
+	if (base->icu.strength == coll_icu_strength_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong strength option setting, "
 				  "expected PRIMARY | SECONDARY | "
 				  "TERTIARY | QUATERNARY | IDENTICAL");
 	}
 
-	if (def->icu.numeric_collation == coll_icu_on_off_MAX) {
+	if (base->icu.numeric_collation == coll_icu_on_off_MAX) {
 		tnt_raise(ClientError, ER_CANT_CREATE_COLLATION,
 			  "ICU wrong numeric_collation option setting, "
 				  "expected ON | OFF");
@@ -2373,36 +2375,36 @@ coll_def_new_from_tuple(const struct tuple *tuple, struct coll_def *def)
  * A change is only INSERT or DELETE, UPDATE is not supported.
  */
 static void
-coll_cache_rollback(struct trigger *trigger, void *event)
+coll_id_cache_rollback(struct trigger *trigger, void *event)
 {
-	struct coll *coll = (struct coll *) trigger->data;
+	struct coll_id *coll_id = (struct coll_id *) trigger->data;
 	struct txn_stmt *stmt = txn_last_stmt((struct txn*) event);
 
 	if (stmt->new_tuple == NULL) {
-		/*  Rollback DELETE: put the collation back. */
+		/* DELETE: put the collation identifier back. */
 		assert(stmt->old_tuple != NULL);
-		struct coll *replaced;
-		if (coll_cache_replace(coll, &replaced) != 0) {
+		struct coll_id *replaced_id;
+		if (coll_id_cache_replace(coll_id, &replaced_id) != 0) {
 			panic("Out of memory on insertion into collation "\
 			      "cache");
 		}
-		assert(replaced == NULL);
+		assert(replaced_id == NULL);
 	} else {
-		/* INSERT: remove and free the new collation */
+		/* INSERT: delete the new collation identifier. */
 		assert(stmt->old_tuple == NULL);
-		coll_cache_delete(coll);
-		coll_unref(coll);
+		coll_id_cache_delete(coll_id);
+		coll_id_delete(coll_id);
 	}
 }
 
 
-/** Dereference a deleted collation on commit. */
+/** Free a deleted collation identifier on commit. */
 static void
-coll_cache_commit(struct trigger *trigger, void *event)
+coll_id_cache_commit(struct trigger *trigger, void *event)
 {
 	(void) event;
-	struct coll *coll = (struct coll *) trigger->data;
-	coll_unref(coll);
+	struct coll_id *coll_id = (struct coll_id *) trigger->data;
+	coll_id_delete(coll_id);
 }
 
 /**
@@ -2418,44 +2420,47 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event)
 	struct tuple *new_tuple = stmt->new_tuple;
 	txn_check_singlestatement_xc(txn, "Space _collation");
 	struct trigger *on_rollback =
-		txn_alter_trigger_new(coll_cache_rollback, NULL);
+		txn_alter_trigger_new(coll_id_cache_rollback, NULL);
 	struct trigger *on_commit =
-		txn_alter_trigger_new(coll_cache_commit, NULL);
+		txn_alter_trigger_new(coll_id_cache_commit, NULL);
 	if (new_tuple == NULL && old_tuple != NULL) {
 		/* DELETE */
-		/* TODO: Check that no index uses the collation */
+		/*
+		 * TODO: Check that no index uses the collation
+		 * identifier.
+		 */
 		int32_t old_id = tuple_field_u32_xc(old_tuple,
 						    BOX_COLLATION_FIELD_ID);
-		struct coll *old_coll = coll_by_id(old_id);
-		assert(old_coll != NULL);
-		access_check_ddl(old_coll->name, old_coll->owner_id,
+		struct coll_id *old_coll_id = coll_by_id(old_id);
+		assert(old_coll_id != NULL);
+		access_check_ddl(old_coll_id->name, old_coll_id->owner_id,
 				 SC_COLLATION, PRIV_D, false);
 		/*
 		 * Set on_commit/on_rollback triggers after
 		 * deletion from the cache to make trigger logic
-		 * simple..
+		 * simple.
 		 */
-		coll_cache_delete(old_coll);
-		on_rollback->data = old_coll;
-		on_commit->data = old_coll;
+		coll_id_cache_delete(old_coll_id);
+		on_rollback->data = old_coll_id;
+		on_commit->data = old_coll_id;
 		txn_on_rollback(txn, on_rollback);
 		txn_on_commit(txn, on_commit);
 	} else if (new_tuple != NULL && old_tuple == NULL) {
 		/* INSERT */
-		struct coll_def new_def;
-		coll_def_new_from_tuple(new_tuple, &new_def);
+		struct coll_id_def new_def;
+		coll_id_def_new_from_tuple(new_tuple, &new_def);
 		access_check_ddl(new_def.name, new_def.owner_id, SC_COLLATION,
 				 PRIV_C, false);
-		struct coll *new_coll = coll_new(&new_def);
-		if (new_coll == NULL)
+		struct coll_id *new_coll_id = coll_id_new(&new_def);
+		if (new_coll_id == NULL)
 			diag_raise();
-		struct coll *replaced;
-		if (coll_cache_replace(new_coll, &replaced) != 0) {
-			coll_unref(new_coll);
+		struct coll_id *replaced_id;
+		if (coll_id_cache_replace(new_coll_id, &replaced_id) != 0) {
+			coll_id_delete(new_coll_id);
 			diag_raise();
 		}
-		assert(replaced == NULL);
-		on_rollback->data = new_coll;
+		assert(replaced_id == NULL);
+		on_rollback->data = new_coll_id;
 		txn_on_rollback(txn, on_rollback);
 	} else {
 		/* UPDATE */
diff --git a/src/box/coll_id.c b/src/box/coll_id.c
new file mode 100644
index 0000000000..2d5f8a09a7
--- /dev/null
+++ b/src/box/coll_id.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "coll_id.h"
+#include "coll_id_def.h"
+#include "coll.h"
+#include "error.h"
+#include "diag.h"
+
+struct coll_id *
+coll_id_new(const struct coll_id_def *def)
+{
+	assert(def->base.type == COLL_TYPE_ICU);
+	size_t total_len = sizeof(struct coll_id) + def->name_len + 1;
+	struct coll_id *coll_id = (struct coll_id *) malloc(total_len);
+	if (coll_id == NULL) {
+		diag_set(OutOfMemory, total_len, "malloc", "coll_id");
+		return NULL;
+	}
+	coll_id->coll = coll_new(&def->base);
+	if (coll_id->coll == NULL) {
+		free(coll_id);
+		return NULL;
+	}
+	coll_id->id = def->id;
+	coll_id->owner_id = def->owner_id;
+	coll_id->name_len = def->name_len;
+	memcpy(coll_id->name, def->name, def->name_len);
+	coll_id->name[coll_id->name_len] = 0;
+	return coll_id;
+}
+
+void
+coll_id_delete(struct coll_id *coll_id)
+{
+	coll_unref(coll_id->coll);
+	free(coll_id);
+}
diff --git a/src/box/coll_id.h b/src/box/coll_id.h
new file mode 100644
index 0000000000..1b67a3f861
--- /dev/null
+++ b/src/box/coll_id.h
@@ -0,0 +1,77 @@
+#ifndef TARANTOOL_BOX_COLL_ID_H_INCLUDED
+#define TARANTOOL_BOX_COLL_ID_H_INCLUDED
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct coll_id_def;
+struct coll;
+
+/**
+ * A collation identifier. It gives a name, owner and unique
+ * identifier to a base collation. Multiple coll_id can reference
+ * the same collation if their functional parts match.
+ */
+struct coll_id {
+	/** Personal ID */
+	uint32_t id;
+	/** Owner ID */
+	uint32_t owner_id;
+	/** Collation object. */
+	struct coll *coll;
+	/** Collation name. */
+	size_t name_len;
+	char name[0];
+};
+
+/**
+ * Create a collation identifier by definition.
+ * @param def Collation definition.
+ * @retval NULL Illegal parameters or memory error.
+ * @retval not NULL Collation.
+ */
+struct coll_id *
+coll_id_new(const struct coll_id_def *def);
+
+/** Delete collation identifier, unref the basic collation. */
+void
+coll_id_delete(struct coll_id *coll);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_COLL_ID_H_INCLUDED */
diff --git a/src/box/coll_cache.c b/src/box/coll_id_cache.c
similarity index 56%
rename from src/box/coll_cache.c
rename to src/box/coll_id_cache.c
index b7eb3edb96..122863937d 100644
--- a/src/box/coll_cache.c
+++ b/src/box/coll_id_cache.c
@@ -28,75 +28,63 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "coll_cache.h"
+#include "coll_id_cache.h"
+#include "coll_id.h"
 #include "diag.h"
 #include "assoc.h"
 
 /** mhash table (id -> collation) */
-static struct mh_i32ptr_t *coll_cache_id = NULL;
+static struct mh_i32ptr_t *coll_id_cache = NULL;
 
-/** Create global hash tables if necessary. */
 int
-coll_cache_init()
+coll_id_cache_init()
 {
-	coll_cache_id = mh_i32ptr_new();
-	if (coll_cache_id == NULL) {
-		diag_set(OutOfMemory, sizeof(*coll_cache_id), "malloc",
-			 "coll_cache_id");
+	coll_id_cache = mh_i32ptr_new();
+	if (coll_id_cache == NULL) {
+		diag_set(OutOfMemory, sizeof(*coll_id_cache), "malloc",
+			 "coll_id_cache");
 		return -1;
 	}
 	return 0;
 }
 
-/** Delete global hash tables. */
 void
-coll_cache_destroy()
+coll_id_cache_destroy()
 {
-	mh_i32ptr_delete(coll_cache_id);
+	mh_i32ptr_delete(coll_id_cache);
 }
 
-/**
- * Insert or replace a collation into collation cache.
- * @param coll - collation to insert/replace.
- * @return - NULL if inserted, replaced collation if replaced.
- */
 int
-coll_cache_replace(struct coll *coll, struct coll **replaced)
+coll_id_cache_replace(struct coll_id *coll_id, struct coll_id **replaced_id)
 {
-	const struct mh_i32ptr_node_t id_node = {coll->id, coll};
+	const struct mh_i32ptr_node_t id_node = {coll_id->id, coll_id};
 	struct mh_i32ptr_node_t repl_id_node = {0, NULL};
 	struct mh_i32ptr_node_t *prepl_id_node = &repl_id_node;
-	if (mh_i32ptr_put(coll_cache_id, &id_node, &prepl_id_node, NULL) ==
-	    mh_end(coll_cache_id)) {
-		diag_set(OutOfMemory, sizeof(id_node), "malloc", "coll_cache_id");
+	if (mh_i32ptr_put(coll_id_cache, &id_node, &prepl_id_node, NULL) ==
+	    mh_end(coll_id_cache)) {
+		diag_set(OutOfMemory, sizeof(id_node), "malloc",
+			 "coll_id_cache");
 		return -1;
 	}
 	assert(repl_id_node.val == NULL);
-	*replaced = repl_id_node.val;
+	*replaced_id = repl_id_node.val;
 	return 0;
 }
 
-/**
- * Delete a collation from collation cache.
- * @param coll - collation to delete.
- */
 void
-coll_cache_delete(const struct coll *coll)
+coll_id_cache_delete(const struct coll_id *coll_id)
 {
-	mh_int_t i = mh_i32ptr_find(coll_cache_id, coll->id, NULL);
-	if (i == mh_end(coll_cache_id))
+	mh_int_t i = mh_i32ptr_find(coll_id_cache, coll_id->id, NULL);
+	if (i == mh_end(coll_id_cache))
 		return;
-	mh_i32ptr_del(coll_cache_id, i, NULL);
+	mh_i32ptr_del(coll_id_cache, i, NULL);
 }
 
-/**
- * Find a collation object by its id.
- */
-struct coll *
+struct coll_id *
 coll_by_id(uint32_t id)
 {
-	mh_int_t pos = mh_i32ptr_find(coll_cache_id, id, NULL);
-	if (pos == mh_end(coll_cache_id))
+	mh_int_t pos = mh_i32ptr_find(coll_id_cache, id, NULL);
+	if (pos == mh_end(coll_id_cache))
 		return NULL;
-	return mh_i32ptr_node(coll_cache_id, pos)->val;
+	return mh_i32ptr_node(coll_id_cache, pos)->val;
 }
diff --git a/src/box/coll_cache.h b/src/box/coll_id_cache.h
similarity index 78%
rename from src/box/coll_cache.h
rename to src/box/coll_id_cache.h
index 418de4e35c..4bbbc85de5 100644
--- a/src/box/coll_cache.h
+++ b/src/box/coll_id_cache.h
@@ -1,5 +1,5 @@
-#ifndef TARANTOOL_BOX_COLL_CACHE_H_INCLUDED
-#define TARANTOOL_BOX_COLL_CACHE_H_INCLUDED
+#ifndef TARANTOOL_BOX_COLL_ID_CACHE_H_INCLUDED
+#define TARANTOOL_BOX_COLL_ID_CACHE_H_INCLUDED
 /*
  * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
  *
@@ -30,48 +30,49 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include "coll.h"
+#include <stdint.h>
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+struct coll_id;
+
 /**
  * Create global hash tables.
  * @return - 0 on success, -1 on memory error.
  */
 int
-coll_cache_init();
+coll_id_cache_init();
 
 /** Delete global hash tables. */
 void
-coll_cache_destroy();
+coll_id_cache_destroy();
 
 /**
  * Insert or replace a collation into collation cache.
- * @param coll - collation to insert/replace.
- * @param replaced - collation that was replaced.
+ * @param coll_id Collation to insert/replace.
+ * @param Replaced_id Collation that was replaced.
  * @return - 0 on success, -1 on memory error.
  */
 int
-coll_cache_replace(struct coll *coll, struct coll **replaced);
+coll_id_cache_replace(struct coll_id *coll_id, struct coll_id **replaced_id);
 
 /**
  * Delete a collation from collation cache.
- * @param coll - collation to delete.
+ * @param coll_id Collation to delete.
  */
 void
-coll_cache_delete(const struct coll *coll);
+coll_id_cache_delete(const struct coll_id *coll_id);
 
 /**
  * Find a collation object by its id.
  */
-struct coll *
+struct coll_id *
 coll_by_id(uint32_t id);
 
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
 
-#endif /* TARANTOOL_BOX_COLL_CACHE_H_INCLUDED */
+#endif /* TARANTOOL_BOX_COLL_ID_CACHE_H_INCLUDED */
diff --git a/src/box/coll_def.c b/src/box/coll_id_def.c
similarity index 86%
rename from src/box/coll_def.c
rename to src/box/coll_id_def.c
index f849845b3a..9fe0cda8cb 100644
--- a/src/box/coll_def.c
+++ b/src/box/coll_id_def.c
@@ -29,39 +29,7 @@
  * SUCH DAMAGE.
  */
 
-#include "coll_def.h"
-
-const char *coll_type_strs[] = {
-	"ICU"
-};
-
-const char *coll_icu_on_off_strs[] = {
-	"DEFAULT",
-	"ON",
-	"OFF"
-};
-
-const char *coll_icu_alternate_handling_strs[] = {
-	"DEFAULT",
-	"NON_IGNORABLE",
-	"SHIFTED"
-};
-
-const char *coll_icu_case_first_strs[] = {
-	"DEFAULT",
-	"OFF",
-	"UPPER_FIRST",
-	"LOWER_FIRST"
-};
-
-const char *coll_icu_strength_strs[] = {
-	"DEFAULT",
-	"PRIMARY",
-	"SECONDARY",
-	"TERTIARY",
-	"QUATERNARY",
-	"IDENTICAL"
-};
+#include "coll_id_def.h"
 
 static int64_t
 icu_on_off_from_str(const char *str, uint32_t len)
diff --git a/src/box/coll_id_def.h b/src/box/coll_id_def.h
new file mode 100644
index 0000000000..489280c00b
--- /dev/null
+++ b/src/box/coll_id_def.h
@@ -0,0 +1,54 @@
+#ifndef TARANTOOL_BOX_COLL_ID_DEF_H_INCLUDED
+#define TARANTOOL_BOX_COLL_ID_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <coll_def.h>
+#include "opt_def.h"
+
+/** Collation identifier definition. */
+struct coll_id_def {
+	/** Perconal ID */
+	uint32_t id;
+	/** Owner ID */
+	uint32_t owner_id;
+	/** Collation name. */
+	size_t name_len;
+	const char *name;
+	/** Core collation definition. */
+	struct coll_def base;
+};
+
+extern const struct opt_def coll_icu_opts_reg[];
+
+#endif /* TARANTOOL_BOX_COLL_ID_DEF_H_INCLUDED */
diff --git a/src/box/error.cc b/src/box/error.cc
index 99f5195375..6b14dff05d 100644
--- a/src/box/error.cc
+++ b/src/box/error.cc
@@ -155,6 +155,8 @@ ClientError::get_errcode(const struct error *e)
 		return ER_MEMORY_ISSUE;
 	if (type_cast(SystemError, e))
 		return ER_SYSTEM;
+	if (type_cast(CollationError, e))
+		return ER_CANT_CREATE_COLLATION;
 	return ER_PROC_LUA;
 }
 
diff --git a/src/box/key_def.c b/src/box/key_def.c
index 45997ae830..ee09dc99dd 100644
--- a/src/box/key_def.c
+++ b/src/box/key_def.c
@@ -34,7 +34,7 @@
 #include "tuple_hash.h"
 #include "column_mask.h"
 #include "schema_def.h"
-#include "coll_cache.h"
+#include "coll_id_cache.h"
 
 static const struct key_part_def key_part_def_default = {
 	0,
@@ -156,16 +156,17 @@ key_def_new_with_parts(struct key_part_def *parts, uint32_t part_count)
 		struct key_part_def *part = &parts[i];
 		struct coll *coll = NULL;
 		if (part->coll_id != COLL_NONE) {
-			coll = coll_by_id(part->coll_id);
-			if (coll == NULL) {
+			struct coll_id *coll_id = coll_by_id(part->coll_id);
+			if (coll_id == NULL) {
 				diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
 					 i + 1, "collation was not found by ID");
 				key_def_delete(def);
 				return NULL;
 			}
+			coll = coll_id->coll;
 		}
 		key_def_set_part(def, i, part->fieldno, part->type,
-				 part->is_nullable, coll);
+				 part->is_nullable, coll, part->coll_id);
 	}
 	return def;
 }
@@ -179,8 +180,7 @@ key_def_dump_parts(const struct key_def *def, struct key_part_def *parts)
 		part_def->fieldno = part->fieldno;
 		part_def->type = part->type;
 		part_def->is_nullable = part->is_nullable;
-		part_def->coll_id = (part->coll != NULL ?
-				     part->coll->id : COLL_NONE);
+		part_def->coll_id = part->coll_id;
 	}
 }
 
@@ -194,7 +194,8 @@ box_key_def_new(uint32_t *fields, uint32_t *types, uint32_t part_count)
 	for (uint32_t item = 0; item < part_count; ++item) {
 		key_def_set_part(key_def, item, fields[item],
 				 (enum field_type)types[item],
-				 key_part_def_default.is_nullable, NULL);
+				 key_part_def_default.is_nullable, NULL,
+				 COLL_NONE);
 	}
 	return key_def;
 }
@@ -246,7 +247,8 @@ key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 
 void
 key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno,
-		 enum field_type type, bool is_nullable, struct coll *coll)
+		 enum field_type type, bool is_nullable, struct coll *coll,
+		 uint32_t coll_id)
 {
 	assert(part_no < def->part_count);
 	assert(type < field_type_MAX);
@@ -255,6 +257,7 @@ key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno,
 	def->parts[part_no].fieldno = fieldno;
 	def->parts[part_no].type = type;
 	def->parts[part_no].coll = coll;
+	def->parts[part_no].coll_id = coll_id;
 	column_mask_set_fieldno(&def->column_mask, fieldno);
 	/**
 	 * When all parts are set, initialize the tuple
@@ -554,7 +557,7 @@ key_def_merge(const struct key_def *first, const struct key_def *second)
 	end = part + first->part_count;
 	for (; part != end; part++) {
 		key_def_set_part(new_def, pos++, part->fieldno, part->type,
-				 part->is_nullable, part->coll);
+				 part->is_nullable, part->coll, part->coll_id);
 	}
 
 	/* Set-append second key def's part to the new key def. */
@@ -564,7 +567,7 @@ key_def_merge(const struct key_def *first, const struct key_def *second)
 		if (key_def_find(first, part->fieldno))
 			continue;
 		key_def_set_part(new_def, pos++, part->fieldno, part->type,
-				 part->is_nullable, part->coll);
+				 part->is_nullable, part->coll, part->coll_id);
 	}
 	return new_def;
 }
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 12016a51a7..aecbe0345d 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -36,7 +36,7 @@
 #include <msgpuck.h>
 #include <limits.h>
 #include "field_def.h"
-#include "coll.h"
+#include "coll_id.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -68,6 +68,8 @@ struct key_part {
 	uint32_t fieldno;
 	/** Type of the tuple field */
 	enum field_type type;
+	/** Collation ID for string comparison. */
+	uint32_t coll_id;
 	/** Collation definition for string comparison */
 	struct coll *coll;
 	/** True if a part can store NULLs. */
@@ -249,7 +251,8 @@ key_def_dump_parts(const struct key_def *def, struct key_part_def *parts);
  */
 void
 key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno,
-		 enum field_type type, bool is_nullable, struct coll *coll);
+		 enum field_type type, bool is_nullable, struct coll *coll,
+		 uint32_t coll_id);
 
 /**
  * Update 'has_optional_parts' of @a key_def with correspondence
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 333b6370f9..5243827501 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -46,6 +46,7 @@ extern "C" {
 #include "box/txn.h"
 #include "box/vclock.h" /* VCLOCK_MAX */
 #include "box/sequence.h"
+#include "box/coll_id_cache.h"
 
 /**
  * Trigger function for all spaces
@@ -291,8 +292,11 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 			lua_pushboolean(L, part->is_nullable);
 			lua_setfield(L, -2, "is_nullable");
 
-			if (part->coll != NULL) {
-				lua_pushstring(L, part->coll->name);
+			if (part->coll_id != COLL_NONE) {
+				struct coll_id *coll_id =
+					coll_by_id(part->coll_id);
+				assert(coll_id != NULL);
+				lua_pushstring(L, coll_id->name);
 				lua_setfield(L, -2, "collation");
 			}
 
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 1b96f978cc..8df4aa73bb 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -281,13 +281,13 @@ schema_init()
 	auto key_def_guard = make_scoped_guard([&] { key_def_delete(key_def); });
 
 	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-			 FIELD_TYPE_STRING, false, NULL);
+			 FIELD_TYPE_STRING, false, NULL, COLL_NONE);
 	sc_space_new(BOX_SCHEMA_ID, "_schema", key_def, &on_replace_schema,
 		     NULL);
 
 	/* _space - home for all spaces. */
 	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-			 FIELD_TYPE_UNSIGNED, false, NULL);
+			 FIELD_TYPE_UNSIGNED, false, NULL, COLL_NONE);
 
 	/* _collation - collation description. */
 	sc_space_new(BOX_COLLATION_ID, "_collation", key_def,
@@ -335,10 +335,10 @@ schema_init()
 		diag_raise();
 	/* space no */
 	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */,
-			 FIELD_TYPE_UNSIGNED, false, NULL);
+			 FIELD_TYPE_UNSIGNED, false, NULL, COLL_NONE);
 	/* index no */
 	key_def_set_part(key_def, 1 /* part no */, 1 /* field no */,
-			 FIELD_TYPE_UNSIGNED, false, NULL);
+			 FIELD_TYPE_UNSIGNED, false, NULL, COLL_NONE);
 	sc_space_new(BOX_INDEX_ID, "_index", key_def,
 		     &alter_space_on_replace_index, &on_stmt_begin_index);
 }
diff --git a/src/box/tuple.c b/src/box/tuple.c
index 6f89fe53d8..014f374062 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -38,7 +38,7 @@
 #include "small/small.h"
 
 #include "tuple_update.h"
-#include "coll_cache.h"
+#include "coll_id_cache.h"
 
 static struct mempool tuple_iterator_pool;
 static struct small_alloc runtime_alloc;
@@ -211,7 +211,7 @@ tuple_init(field_name_hash_f hash)
 
 	box_tuple_last = NULL;
 
-	if (coll_cache_init() != 0)
+	if (coll_id_cache_init() != 0)
 		return -1;
 
 	return 0;
@@ -264,7 +264,7 @@ tuple_free(void)
 
 	tuple_format_free();
 
-	coll_cache_destroy();
+	coll_id_cache_destroy();
 }
 
 box_tuple_format_t *
diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc
index cfee004967..e53afba428 100644
--- a/src/box/tuple_compare.cc
+++ b/src/box/tuple_compare.cc
@@ -30,9 +30,9 @@
  */
 #include "tuple_compare.h"
 #include "tuple.h"
+#include "coll.h"
 #include "trivia/util.h" /* NOINLINE */
 #include <math.h>
-#include "coll_def.h"
 
 /* {{{ tuple_compare */
 
@@ -295,8 +295,7 @@ mp_compare_str(const char *field_a, const char *field_b)
 }
 
 static inline int
-mp_compare_str_coll(const char *field_a, const char *field_b,
-		    struct coll *coll)
+mp_compare_str_coll(const char *field_a, const char *field_b, struct coll *coll)
 {
 	uint32_t size_a = mp_decode_strl(&field_a);
 	uint32_t size_b = mp_decode_strl(&field_b);
diff --git a/src/box/tuple_hash.cc b/src/box/tuple_hash.cc
index 0fa8ea561e..dee9be3283 100644
--- a/src/box/tuple_hash.cc
+++ b/src/box/tuple_hash.cc
@@ -30,7 +30,6 @@
  */
 
 #include "tuple_hash.h"
-
 #include "third_party/PMurHash.h"
 #include "coll.h"
 
diff --git a/src/box/coll.c b/src/coll.c
similarity index 63%
rename from src/box/coll.c
rename to src/coll.c
index 436d8d1276..66afa6c4fa 100644
--- a/src/box/coll.c
+++ b/src/coll.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
  *
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -31,19 +31,18 @@
 
 #include "coll.h"
 #include "third_party/PMurHash.h"
-#include "error.h"
 #include "diag.h"
 #include <unicode/ucol.h>
 #include <trivia/config.h>
 
 enum {
-	MAX_HASH_BUFFER = 1024,
 	MAX_LOCALE = 1024,
 };
 
-/**
- * Compare two string using ICU collation.
- */
+static_assert(MAX_LOCALE <= TT_STATIC_BUF_LEN,
+	      "static buf is used to 0-terminate locale name");
+
+/** Compare two string using ICU collation. */
 static int
 coll_icu_cmp(const char *s, size_t slen, const char *t, size_t tlen,
 	     const struct coll *coll)
@@ -66,9 +65,7 @@ coll_icu_cmp(const char *s, size_t slen, const char *t, size_t tlen,
 	return (int)result;
 }
 
-/**
- * Get a hash of a string using ICU collation.
- */
+/** Get a hash of a string using ICU collation. */
 static uint32_t
 coll_icu_hash(const char *s, size_t s_len, uint32_t *ph, uint32_t *pcarry,
 	      struct coll *coll)
@@ -76,115 +73,103 @@ coll_icu_hash(const char *s, size_t s_len, uint32_t *ph, uint32_t *pcarry,
 	uint32_t total_size = 0;
 	UCharIterator itr;
 	uiter_setUTF8(&itr, s, s_len);
-	uint8_t buf[MAX_HASH_BUFFER];
+	uint8_t *buf = (uint8_t *) tt_static_buf();
 	uint32_t state[2] = {0, 0};
 	UErrorCode status = U_ZERO_ERROR;
-	while (true) {
-		int32_t got = ucol_nextSortKeyPart(coll->icu.collator,
-						   &itr, state, buf,
-						   MAX_HASH_BUFFER, &status);
+	int32_t got;
+	do {
+		got = ucol_nextSortKeyPart(coll->icu.collator, &itr, state, buf,
+					   TT_STATIC_BUF_LEN, &status);
 		PMurHash32_Process(ph, pcarry, buf, got);
 		total_size += got;
-		if (got < MAX_HASH_BUFFER)
-			break;
-	}
+	} while (got == TT_STATIC_BUF_LEN);
 	return total_size;
 }
 
 /**
  * Set up ICU collator and init cmp and hash members of collation.
- * @param coll - collation to set up.
- * @param def - collation definition.
- * @return 0 on success, -1 on error.
+ * @param coll Collation to set up.
+ * @param def Collation definition.
+ * @retval  0 Success.
+ * @retval -1 Collation error.
  */
 static int
 coll_icu_init_cmp(struct coll *coll, const struct coll_def *def)
 {
-	if (coll->icu.collator != NULL) {
-		ucol_close(coll->icu.collator);
-		coll->icu.collator = NULL;
-	}
-
 	if (def->locale_len >= MAX_LOCALE) {
-		diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-			 "too long locale");
+		diag_set(CollationError, "too long locale");
 		return -1;
 	}
-	char locale[MAX_LOCALE];
+	char *locale = tt_static_buf();
 	memcpy(locale, def->locale, def->locale_len);
 	locale[def->locale_len] = '\0';
 	UErrorCode status = U_ZERO_ERROR;
 	struct UCollator *collator = ucol_open(locale, &status);
 	if (U_FAILURE(status)) {
-		diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-			 u_errorName(status));
+		diag_set(CollationError, u_errorName(status));
 		return -1;
 	}
 	coll->icu.collator = collator;
 
 	if (def->icu.french_collation != COLL_ICU_DEFAULT) {
 		enum coll_icu_on_off w = def->icu.french_collation;
-		UColAttributeValue v =
-			w == COLL_ICU_ON ? UCOL_ON :
-			w == COLL_ICU_OFF ? UCOL_OFF :
-			UCOL_DEFAULT;
+		UColAttributeValue v = w == COLL_ICU_ON ? UCOL_ON :
+				       w == COLL_ICU_OFF ? UCOL_OFF :
+				       UCOL_DEFAULT;
 		ucol_setAttribute(collator, UCOL_FRENCH_COLLATION, v, &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set french_collation");
+			diag_set(CollationError, "failed to set "\
+				 "french_collation: %s", u_errorName(status));
 			return -1;
 		}
 	}
 	if (def->icu.alternate_handling != COLL_ICU_AH_DEFAULT) {
-		enum coll_icu_alternate_handling w = def->icu.alternate_handling;
+		enum coll_icu_alternate_handling w =
+			def->icu.alternate_handling;
 		UColAttributeValue v =
 			w == COLL_ICU_AH_NON_IGNORABLE ? UCOL_NON_IGNORABLE :
-			w == COLL_ICU_AH_SHIFTED ? UCOL_SHIFTED :
-			UCOL_DEFAULT;
-		ucol_setAttribute(collator, UCOL_ALTERNATE_HANDLING, v, &status);
+			w == COLL_ICU_AH_SHIFTED ? UCOL_SHIFTED : UCOL_DEFAULT;
+		ucol_setAttribute(collator, UCOL_ALTERNATE_HANDLING, v,
+				  &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set alternate_handling");
+			diag_set(CollationError, "failed to set "\
+				 "alternate_handling: %s", u_errorName(status));
 			return -1;
 		}
 	}
 	if (def->icu.case_first != COLL_ICU_CF_DEFAULT) {
 		enum coll_icu_case_first w = def->icu.case_first;
-		UColAttributeValue v =
-			w == COLL_ICU_CF_OFF ? UCOL_OFF :
+		UColAttributeValue v = w == COLL_ICU_CF_OFF ? UCOL_OFF :
 			w == COLL_ICU_CF_UPPER_FIRST ? UCOL_UPPER_FIRST :
 			w == COLL_ICU_CF_LOWER_FIRST ? UCOL_LOWER_FIRST :
 			UCOL_DEFAULT;
 		ucol_setAttribute(collator, UCOL_CASE_FIRST, v, &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set case_first");
+			diag_set(CollationError, "failed to set case_first: "\
+				 "%s", u_errorName(status));
 			return -1;
 		}
 	}
 	if (def->icu.case_level != COLL_ICU_DEFAULT) {
 		enum coll_icu_on_off w = def->icu.case_level;
-		UColAttributeValue v =
-			w == COLL_ICU_ON ? UCOL_ON :
-			w == COLL_ICU_OFF ? UCOL_OFF :
-			UCOL_DEFAULT;
+		UColAttributeValue v = w == COLL_ICU_ON ? UCOL_ON :
+			w == COLL_ICU_OFF ? UCOL_OFF : UCOL_DEFAULT;
 		ucol_setAttribute(collator, UCOL_CASE_LEVEL , v, &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set case_level");
+			diag_set(CollationError, "failed to set case_level: "\
+				 "%s", u_errorName(status));
 			return -1;
 		}
 	}
 	if (def->icu.normalization_mode != COLL_ICU_DEFAULT) {
 		enum coll_icu_on_off w = def->icu.normalization_mode;
-		UColAttributeValue v =
-			w == COLL_ICU_ON ? UCOL_ON :
-			w == COLL_ICU_OFF ? UCOL_OFF :
-			UCOL_DEFAULT;
-		ucol_setAttribute(collator, UCOL_NORMALIZATION_MODE, v, &status);
+		UColAttributeValue v = w == COLL_ICU_ON ? UCOL_ON :
+			w == COLL_ICU_OFF ? UCOL_OFF : UCOL_DEFAULT;
+		ucol_setAttribute(collator, UCOL_NORMALIZATION_MODE, v,
+				  &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set normalization_mode");
+			diag_set(CollationError, "failed to set "\
+				 "normalization_mode: %s", u_errorName(status));
 			return -1;
 		}
 	}
@@ -199,81 +184,51 @@ coll_icu_init_cmp(struct coll *coll, const struct coll_def *def)
 			UCOL_DEFAULT;
 		ucol_setAttribute(collator, UCOL_STRENGTH, v, &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set strength");
+			diag_set(CollationError, "failed to set strength: %s",
+				 u_errorName(status));
 			return -1;
 		}
 	}
 	if (def->icu.numeric_collation != COLL_ICU_DEFAULT) {
 		enum coll_icu_on_off w = def->icu.numeric_collation;
-		UColAttributeValue v =
-			w == COLL_ICU_ON ? UCOL_ON :
-			w == COLL_ICU_OFF ? UCOL_OFF :
-			UCOL_DEFAULT;
+		UColAttributeValue v = w == COLL_ICU_ON ? UCOL_ON :
+			w == COLL_ICU_OFF ? UCOL_OFF : UCOL_DEFAULT;
 		ucol_setAttribute(collator, UCOL_NUMERIC_COLLATION, v, &status);
 		if (U_FAILURE(status)) {
-			diag_set(ClientError, ER_CANT_CREATE_COLLATION,
-				 "failed to set numeric_collation");
+			diag_set(CollationError, "failed to set "\
+				 "numeric_collation: %s", u_errorName(status));
 			return -1;
 		}
 	}
-
 	coll->cmp = coll_icu_cmp;
 	coll->hash = coll_icu_hash;
 	return 0;
 }
 
-/**
- * Destroy ICU collation.
- */
-static void
-coll_icu_destroy(struct coll *coll)
-{
-	if (coll->icu.collator != NULL)
-		ucol_close(coll->icu.collator);
-}
-
-/**
- * Create a collation by definition.
- * @param def - collation definition.
- * @return - the collation OR NULL on memory error (diag is set).
- */
 struct coll *
 coll_new(const struct coll_def *def)
 {
-	assert(def->type == COLL_TYPE_ICU); /* no more types are implemented yet */
-
-	size_t total_len = sizeof(struct coll) + def->name_len + 1;
-	struct coll *coll = (struct coll *)calloc(1, total_len);
+	assert(def->type == COLL_TYPE_ICU);
+	struct coll *coll = (struct coll *) malloc(sizeof(*coll));
 	if (coll == NULL) {
-		diag_set(OutOfMemory, total_len, "malloc", "struct coll");
+		diag_set(OutOfMemory, sizeof(*coll), "malloc", "coll");
 		return NULL;
 	}
-
 	coll->refs = 1;
-	coll->id = def->id;
-	coll->owner_id = def->owner_id;
 	coll->type = def->type;
-	coll->name_len = def->name_len;
-	memcpy(coll->name, def->name, def->name_len);
-	coll->name[coll->name_len] = 0;
-
 	if (coll_icu_init_cmp(coll, def) != 0) {
 		free(coll);
 		return NULL;
 	}
-
 	return coll;
 }
 
 void
 coll_unref(struct coll *coll)
 {
-	/* No more types are implemented yet. */
-	assert(coll->type == COLL_TYPE_ICU);
 	assert(coll->refs > 0);
 	if (--coll->refs == 0) {
-		coll_icu_destroy(coll);
+		ucol_close(coll->icu.collator);
 		free(coll);
 	}
 }
diff --git a/src/box/coll.h b/src/coll.h
similarity index 74%
rename from src/box/coll.h
rename to src/coll.h
index 248500ab4c..cc834f446c 100644
--- a/src/box/coll.h
+++ b/src/coll.h
@@ -1,7 +1,7 @@
-#ifndef TARANTOOL_BOX_COLL_H_INCLUDED
-#define TARANTOOL_BOX_COLL_H_INCLUDED
+#ifndef TARANTOOL_COLL_H_INCLUDED
+#define TARANTOOL_COLL_H_INCLUDED
 /*
- * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
  *
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -41,17 +41,13 @@ extern "C" {
 
 struct coll;
 
-typedef int (*coll_cmp_f)(const char *s, size_t s_len,
-			  const char *t, size_t t_len,
-			  const struct coll *coll);
+typedef int (*coll_cmp_f)(const char *s, size_t s_len, const char *t,
+			  size_t t_len, const struct coll *coll);
 
-typedef uint32_t (*coll_hash_f)(const char *s, size_t s_len,
-				uint32_t *ph, uint32_t *pcarry,
-				struct coll *coll);
+typedef uint32_t (*coll_hash_f)(const char *s, size_t s_len, uint32_t *ph,
+				uint32_t *pcarry, struct coll *coll);
 
-/**
- * ICU collation specific data.
- */
+/** ICU collation specific data. */
 struct UCollator;
 
 struct coll_icu {
@@ -59,13 +55,10 @@ struct coll_icu {
 };
 
 /**
- * A collation.
+ * Collation. It has no unique features like name, id or owner.
+ * Only functional part - comparator, locale, ICU settings.
  */
 struct coll {
-	/** Personal ID */
-	uint32_t id;
-	/** Owner ID */
-	uint32_t owner_id;
 	/** Collation type. */
 	enum coll_type type;
 	/** Type specific data. */
@@ -75,15 +68,13 @@ struct coll {
 	coll_hash_f hash;
 	/** Reference counter. */
 	int refs;
-	/** Collation name. */
-	size_t name_len;
-	char name[0];
 };
 
 /**
  * Create a collation by definition.
- * @param def - collation definition.
- * @return - the collation OR NULL on memory error (diag is set).
+ * @param def Collation definition.
+ * @retval NULL Collation or memory error.
+ * @retval not NULL Collation.
  */
 struct coll *
 coll_new(const struct coll_def *def);
@@ -103,4 +94,4 @@ coll_unref(struct coll *coll);
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
 
-#endif /* TARANTOOL_BOX_COLL_H_INCLUDED */
+#endif /* TARANTOOL_COLL_H_INCLUDED */
diff --git a/src/coll_def.c b/src/coll_def.c
new file mode 100644
index 0000000000..df58caca8d
--- /dev/null
+++ b/src/coll_def.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "coll_def.h"
+
+const char *coll_type_strs[] = {
+	"ICU"
+};
+
+const char *coll_icu_on_off_strs[] = {
+	"DEFAULT",
+	"ON",
+	"OFF"
+};
+
+const char *coll_icu_alternate_handling_strs[] = {
+	"DEFAULT",
+	"NON_IGNORABLE",
+	"SHIFTED"
+};
+
+const char *coll_icu_case_first_strs[] = {
+	"DEFAULT",
+	"OFF",
+	"UPPER_FIRST",
+	"LOWER_FIRST"
+};
+
+const char *coll_icu_strength_strs[] = {
+	"DEFAULT",
+	"PRIMARY",
+	"SECONDARY",
+	"TERTIARY",
+	"QUATERNARY",
+	"IDENTICAL"
+};
diff --git a/src/box/coll_def.h b/src/coll_def.h
similarity index 82%
rename from src/box/coll_def.h
rename to src/coll_def.h
index 7a1027a1e1..10dbc860e0 100644
--- a/src/box/coll_def.h
+++ b/src/coll_def.h
@@ -1,7 +1,7 @@
-#ifndef TARANTOOL_BOX_COLL_DEF_H_INCLUDED
-#define TARANTOOL_BOX_COLL_DEF_H_INCLUDED
+#ifndef TARANTOOL_COLL_DEF_H_INCLUDED
+#define TARANTOOL_COLL_DEF_H_INCLUDED
 /*
- * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
  *
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -30,18 +30,10 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
 #include <stddef.h>
 #include <stdint.h>
-#include "opt_def.h"
-
-#if defined(__cplusplus)
-extern "C" {
-#endif /* defined(__cplusplus) */
 
-/**
- * The supported collation types
- */
+/** The supported collation types */
 enum coll_type {
 	COLL_TYPE_ICU = 0,
 	coll_type_MAX,
@@ -109,17 +101,8 @@ struct coll_icu_def {
 	enum coll_icu_on_off numeric_collation;
 };
 
-/**
- * Definition of a collation.
- */
+/** Collation definition. */
 struct coll_def {
-	/** Perconal ID */
-	uint32_t id;
-	/** Owner ID */
-	uint32_t owner_id;
-	/** Collation name. */
-	size_t name_len;
-	const char *name;
 	/** Locale. */
 	size_t locale_len;
 	const char *locale;
@@ -129,10 +112,4 @@ struct coll_def {
 	struct coll_icu_def icu;
 };
 
-extern const struct opt_def coll_icu_opts_reg[];
-
-#if defined(__cplusplus)
-} /* extern "C" */
-#endif /* defined(__cplusplus) */
-
-#endif /* TARANTOOL_BOX_COLL_DEF_H_INCLUDED */
+#endif /* TARANTOOL_COLL_DEF_H_INCLUDED */
diff --git a/src/diag.h b/src/diag.h
index dc6c132d5a..bd5a539b01 100644
--- a/src/diag.h
+++ b/src/diag.h
@@ -249,6 +249,8 @@ struct error *
 BuildSystemError(const char *file, unsigned line, const char *format, ...);
 struct error *
 BuildXlogError(const char *file, unsigned line, const char *format, ...);
+struct error *
+BuildCollationError(const char *file, unsigned line, const char *format, ...);
 
 struct index_def;
 
diff --git a/src/exception.cc b/src/exception.cc
index 56077f76d1..1cbf8852f2 100644
--- a/src/exception.cc
+++ b/src/exception.cc
@@ -235,6 +235,18 @@ IllegalParams::IllegalParams(const char *file, unsigned line,
 	va_end(ap);
 }
 
+const struct type_info type_CollationError =
+	make_type("CollationError", &type_Exception);
+
+CollationError::CollationError(const char *file, unsigned line,
+			       const char *format, ...)
+	: Exception(&type_CollationError, file, line)
+{
+	va_list ap;
+	va_start(ap, format);
+	error_vformat_msg(this, format, ap);
+	va_end(ap);
+}
 
 #define BuildAlloc(type)				\
 	void *p = malloc(sizeof(type));			\
@@ -303,6 +315,18 @@ BuildSystemError(const char *file, unsigned line, const char *format, ...)
 	return e;
 }
 
+struct error *
+BuildCollationError(const char *file, unsigned line, const char *format, ...)
+{
+	BuildAlloc(CollationError);
+	CollationError *e =  new (p) CollationError(file, line, "");
+	va_list ap;
+	va_start(ap, format);
+	error_vformat_msg(e, format, ap);
+	va_end(ap);
+	return e;
+}
+
 void
 exception_init()
 {
diff --git a/src/exception.h b/src/exception.h
index fe7ab84f0c..f56616b682 100644
--- a/src/exception.h
+++ b/src/exception.h
@@ -49,6 +49,7 @@ extern const struct type_info type_ChannelIsClosed;
 extern const struct type_info type_LuajitError;
 extern const struct type_info type_IllegalParams;
 extern const struct type_info type_SystemError;
+extern const struct type_info type_CollationError;
 
 const char *
 exception_get_string(struct error *e, const struct method_info *method);
@@ -139,6 +140,14 @@ class IllegalParams: public Exception {
 	IllegalParams(const char *file, unsigned line, const char *format, ...);
 	virtual void raise() { throw this; }
 };
+
+class CollationError: public Exception {
+public:
+	CollationError(const char *file, unsigned line, const char *format,
+		       ...);
+	virtual void raise() { throw this; }
+};
+
 /**
  * Initialize the exception subsystem.
  */
diff --git a/test/unit/coll.cpp b/test/unit/coll.cpp
index d77959606a..53e06f2cea 100644
--- a/test/unit/coll.cpp
+++ b/test/unit/coll.cpp
@@ -1,14 +1,14 @@
-#include "box/coll.h"
 #include <iostream>
 #include <vector>
 #include <algorithm>
 #include <string.h>
-#include <box/coll_def.h>
 #include <assert.h>
 #include <msgpuck.h>
 #include <diag.h>
 #include <fiber.h>
 #include <memory.h>
+#include "coll_def.h"
+#include "coll.h"
 #include "third_party/PMurHash.h"
 
 using namespace std;
@@ -51,8 +51,6 @@ manual_test()
 	def.locale = "ru_RU";
 	def.locale_len = strlen(def.locale);
 	def.type = COLL_TYPE_ICU;
-	def.name = "test";
-	def.name_len = strlen(def.name);
 	struct coll *coll;
 
 	cout << " -- default ru_RU -- " << endl;
@@ -136,8 +134,6 @@ hash_test()
 	def.locale = "ru_RU";
 	def.locale_len = strlen(def.locale);
 	def.type = COLL_TYPE_ICU;
-	def.name = "test";
-	def.name_len = strlen(def.name);
 	struct coll *coll;
 
 	/* Case sensitive */
-- 
GitLab