diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 689a0674061bdfa1e90b0b5e1522cd4bb3285b19..58f44d71bf75de4a48040021c563863109029d29 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -178,10 +178,11 @@ set(api_headers
     ${CMAKE_SOURCE_DIR}/src/lua/utils.h
     ${CMAKE_SOURCE_DIR}/src/box/txn.h
     ${CMAKE_SOURCE_DIR}/src/box/key_def.h
+    ${CMAKE_SOURCE_DIR}/src/box/field_def.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple_format.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple_compare.h
-    ${CMAKE_SOURCE_DIR}/src/box/schema.h
+    ${CMAKE_SOURCE_DIR}/src/box/schema_def.h
     ${CMAKE_SOURCE_DIR}/src/box/box.h
     ${CMAKE_SOURCE_DIR}/src/box/index.h
     ${CMAKE_SOURCE_DIR}/src/box/iterator_type.h
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index e0433994093c1d665552cce48095a8b23fad0fcc..60e3628a7208a002c969ece2cb22a59d5bd248ba 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -36,6 +36,8 @@ add_library(tuple STATIC
     tuple_compare.cc
     tuple_hash.cc
     key_def.cc
+    field_def.c
+    opt_def.c
 )
 target_link_libraries(tuple box_error core ${MSGPUCK_LIBRARIES} misc)
 
@@ -48,6 +50,7 @@ add_library(box STATIC
     xrow_io.cc
     tuple_convert.c
     index.cc
+    index_def.c
     iterator_type.c
     memtx_index.cc
     memtx_hash.cc
@@ -77,9 +80,12 @@ add_library(box STATIC
     vy_log.c
     vy_upsert.c
     space.cc
+    space_def.c
     func.c
+    func_def.c
     alter.cc
     schema.cc
+    schema_def.c
     session.cc
     port.c
     txn.cc
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 9a369db2a05cfee7e95f3478ff75fc2727d1c612..57b5ed606c2a565bbf3efa6bc98b6f20324adc41 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -390,7 +390,7 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *old_space)
 	if (index_def == NULL)
 		diag_raise();
 	auto index_def_guard = make_scoped_guard([=] { index_def_delete(index_def); });
-	index_def_check(index_def, space_name(old_space));
+	index_def_check_xc(index_def, space_name(old_space));
 	old_space->handler->checkIndexDef(old_space, index_def);
 	index_def_guard.is_active = false;
 	return index_def;
@@ -452,7 +452,7 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode)
 	auto def_guard = make_scoped_guard([=] { space_def_delete(def); });
 	memcpy(def->name, name, name_len);
 	def->name[name_len] = 0;
-	identifier_check(def->name);
+	identifier_check_xc(def->name);
 	def->id = tuple_field_u32_xc(tuple, BOX_SPACE_FIELD_ID);
 	if (def->id > BOX_SPACE_MAX) {
 		tnt_raise(ClientError, errcode,
@@ -476,7 +476,7 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode)
 	}
 	memcpy(def->engine_name, engine_name, name_len);
 	def->engine_name[name_len] = 0;
-	identifier_check(def->engine_name);
+	identifier_check_xc(def->engine_name);
 	space_opts_create(&def->opts, tuple);
 	Engine *engine = engine_find(def->engine_name);
 	engine->checkSpaceDef(def);
@@ -550,7 +550,7 @@ alter_space_new(struct space *old_space)
 		region_calloc_object_xc(&fiber()->gc, struct alter_space);
 	rlist_create(&alter->ops);
 	alter->old_space = old_space;
-	alter->space_def = space_def_dup(alter->old_space->def);
+	alter->space_def = space_def_dup_xc(alter->old_space->def);
 	return alter;
 }
 
@@ -1751,7 +1751,7 @@ user_def_new_from_tuple(struct tuple *tuple)
 		tnt_raise(ClientError, ER_CREATE_USER,
 			  user->name, "unknown user type");
 	}
-	identifier_check(user->name);
+	identifier_check_xc(user->name);
 	access_check_ddl(user->owner, SC_USER);
 	/*
 	 * AUTH_DATA field in _user space should contain
diff --git a/src/box/authentication.cc b/src/box/authentication.cc
index 802d1a64cb637b43249870713ce4b12a57dae00d..d2c96c254d80aabc15078c658401ad421d2d6aa9 100644
--- a/src/box/authentication.cc
+++ b/src/box/authentication.cc
@@ -31,6 +31,7 @@
 #include "authentication.h"
 #include "user.h"
 #include "session.h"
+#include "msgpuck.h"
 
 static char zero_hash[SCRAMBLE_SIZE];
 
diff --git a/src/box/engine.cc b/src/box/engine.cc
index 8ec6deb800a670fb52126e8eafcee1e7114ba44b..4375685a8a9da255dd0c35d1dcf824d3245e26c5 100644
--- a/src/box/engine.cc
+++ b/src/box/engine.cc
@@ -35,9 +35,7 @@
 #include "port.h"
 #include "space.h"
 #include "exception.h"
-#include "schema.h"
 #include "small/rlist.h"
-#include "scoped_guard.h"
 #include "vclock.h"
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/box/field_def.c b/src/box/field_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..53c2b60beaa6de080f8800b296bc0ce32b6f6bd0
--- /dev/null
+++ b/src/box/field_def.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "field_def.h"
+#include "trivia/util.h"
+
+const char *field_type_strs[] = {
+	/* [FIELD_TYPE_ANY]      = */ "any",
+	/* [FIELD_TYPE_UNSIGNED] = */ "unsigned",
+	/* [FIELD_TYPE_STRING]   = */ "string",
+	/* [FIELD_TYPE_ARRAY]    = */ "array",
+	/* [FIELD_TYPE_NUMBER]   = */ "number",
+	/* [FIELD_TYPE_INTEGER]  = */ "integer",
+	/* [FIELD_TYPE_SCALAR]   = */ "scalar",
+	/* [FIELD_TYPE_MAP]      = */ "map",
+};
+
+enum field_type
+field_type_by_name(const char *name)
+{
+	enum field_type field_type = STR2ENUM(field_type, name);
+	/*
+	 * FIELD_TYPE_ANY can't be used as type of indexed field,
+	 * because it is internal type used only for filling
+	 * struct tuple_format.fields array.
+	 */
+	if (field_type != field_type_MAX && field_type != FIELD_TYPE_ANY)
+		return field_type;
+	/* 'num' and 'str' in _index are deprecated since Tarantool 1.7 */
+	if (strcasecmp(name, "num") == 0)
+		return FIELD_TYPE_UNSIGNED;
+	else if (strcasecmp(name, "str") == 0)
+		return FIELD_TYPE_STRING;
+	return field_type_MAX;
+}
diff --git a/src/box/field_def.h b/src/box/field_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2cd57e12584102d714beb6aa495741cfd8b2105
--- /dev/null
+++ b/src/box/field_def.h
@@ -0,0 +1,68 @@
+#ifndef TARANTOOL_BOX_FIELD_DEF_H_INCLUDED
+#define TARANTOOL_BOX_FIELD_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/** \cond public */
+
+/*
+ * Possible field data types. Can't use STRS/ENUM macros for them,
+ * since there is a mismatch between enum name (STRING) and type
+ * name literal ("STR"). STR is already used as Objective C type.
+ */
+enum field_type {
+	FIELD_TYPE_ANY = 0,
+	FIELD_TYPE_UNSIGNED,
+	FIELD_TYPE_STRING,
+	FIELD_TYPE_ARRAY,
+	FIELD_TYPE_NUMBER,
+	FIELD_TYPE_INTEGER,
+	FIELD_TYPE_SCALAR,
+	FIELD_TYPE_MAP,
+	field_type_MAX
+};
+
+/** \endcond public */
+
+extern const char *field_type_strs[];
+
+enum field_type
+field_type_by_name(const char *name);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_FIELD_DEF_H_INCLUDED */
diff --git a/src/box/func.c b/src/box/func.c
index 1a4283802604e43995390aaa79b4a0e41a3c232a..e33783e4d92779b19ef653c820cd3ae1c9a90e90 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -29,14 +29,12 @@
  * SUCH DAMAGE.
  */
 #include "func.h"
-
 #include "trivia/config.h"
-
-#include <dlfcn.h>
-
 #include "assoc.h"
-
 #include "lua/utils.h"
+#include "error.h"
+#include "diag.h"
+#include <dlfcn.h>
 
 /**
  * Parsed symbol and package names.
diff --git a/src/box/func.h b/src/box/func.h
index b152a2285b15ef7b419de60ccc59b54a45ba2716..0957546c62e4d2639ca08542365d12a74d1bffbf 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -34,10 +34,9 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdbool.h>
-
-#include <small/rlist.h>
-
-#include "key_def.h"
+#include "small/rlist.h"
+#include "func_def.h"
+#include "user_def.h"
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/src/box/func_def.c b/src/box/func_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..76ed77b24b7c30ae5e1bdd0e48dde4de79194a57
--- /dev/null
+++ b/src/box/func_def.c
@@ -0,0 +1,3 @@
+#include "func_def.h"
+
+const char *func_language_strs[] = {"LUA", "C"};
diff --git a/src/box/func_def.h b/src/box/func_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..5b52ab498f7fb933a482b67219a9fe9935a154a9
--- /dev/null
+++ b/src/box/func_def.h
@@ -0,0 +1,89 @@
+#ifndef TARANTOOL_BOX_FUNC_DEF_H_INCLUDED
+#define TARANTOOL_BOX_FUNC_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "trivia/util.h"
+#include <stdbool.h>
+
+/**
+ * The supported language of the stored function.
+ */
+enum func_language {
+	FUNC_LANGUAGE_LUA,
+	FUNC_LANGUAGE_C,
+	func_language_MAX,
+};
+
+extern const char *func_language_strs[];
+
+/**
+ * Definition of a function. Function body is not stored
+ * or replicated (yet).
+ */
+struct func_def {
+	/** Function id. */
+	uint32_t fid;
+	/** Owner of the function. */
+	uint32_t uid;
+	/**
+	 * True if the function requires change of user id before
+	 * invocation.
+	 */
+	bool setuid;
+	/**
+	 * The language of the stored function.
+	 */
+	enum func_language language;
+	/** Function name. */
+	char name[0];
+};
+
+/**
+ * @param name_len length of func_def->name
+ * @returns size in bytes needed to allocate for struct func_def
+ * for a function of length @a a name_len.
+ */
+static inline size_t
+func_def_sizeof(uint32_t name_len)
+{
+	/* +1 for '\0' name terminating. */
+	return sizeof(struct func_def) + name_len + 1;
+}
+
+/**
+ * API of C stored function.
+ */
+typedef struct box_function_ctx box_function_ctx_t;
+typedef int (*box_function_f)(box_function_ctx_t *ctx,
+	     const char *args, const char *args_end);
+
+#endif /* TARANTOOL_BOX_FUNC_DEF_H_INCLUDED */
diff --git a/src/box/index.h b/src/box/index.h
index 35af19ae79262d46709d6a5ceac3d02d9e5204fc..d41db48d01989e52f96d20b6b9d952aa4d96e312 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -212,7 +212,7 @@ box_index_info(uint32_t space_id, uint32_t index_id,
 
 #if defined(__cplusplus)
 } /* extern "C" */
-#include "key_def.h"
+#include "index_def.h"
 
 struct iterator {
 	struct tuple *(*next)(struct iterator *);
diff --git a/src/box/index_def.c b/src/box/index_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..f4159e3041edde92da97cdd03716b50cc0e67404
--- /dev/null
+++ b/src/box/index_def.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "index_def.h"
+#include "schema_def.h"
+
+const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
+
+const char *rtree_index_distance_type_strs[] = { "EUCLID", "MANHATTAN" };
+
+const struct index_opts index_opts_default = {
+	/* .unique              = */ true,
+	/* .dimension           = */ 2,
+	/* .distancebuf         = */ { '\0' },
+	/* .distance            = */ RTREE_INDEX_DISTANCE_TYPE_EUCLID,
+	/* .range_size          = */ 0,
+	/* .page_size           = */ 0,
+	/* .run_count_per_level = */ 2,
+	/* .run_size_ratio      = */ 3.5,
+	/* .bloom_fpr           = */ 0.05,
+	/* .lsn                 = */ 0,
+};
+
+const struct opt_def index_opts_reg[] = {
+	OPT_DEF("unique", OPT_BOOL, struct index_opts, is_unique),
+	OPT_DEF("dimension", OPT_INT, struct index_opts, dimension),
+	OPT_DEF("distance", OPT_STR, struct index_opts, distancebuf),
+	OPT_DEF("range_size", OPT_INT, struct index_opts, range_size),
+	OPT_DEF("page_size", OPT_INT, struct index_opts, page_size),
+	OPT_DEF("run_count_per_level", OPT_INT, struct index_opts, run_count_per_level),
+	OPT_DEF("run_size_ratio", OPT_FLOAT, struct index_opts, run_size_ratio),
+	OPT_DEF("bloom_fpr", OPT_FLOAT, struct index_opts, bloom_fpr),
+	OPT_DEF("lsn", OPT_INT, struct index_opts, lsn),
+	{ NULL, opt_type_MAX, 0, 0 },
+};
+
+struct index_def *
+index_def_new(uint32_t space_id, uint32_t iid, const char *name,
+	      uint32_t name_len, enum index_type type,
+	      const struct index_opts *opts,
+	      struct key_def *key_def, struct key_def *pk_def)
+{
+	assert(name_len <= BOX_NAME_MAX);
+	/* Use calloc to make index_def_delete() safe at all times. */
+	struct index_def *def = (struct index_def *) calloc(1, sizeof(*def));
+	if (def == NULL) {
+		diag_set(OutOfMemory, sizeof(*def), "malloc", "struct index_def");
+		return NULL;
+	}
+	def->name = strndup(name, name_len);
+	if (def->name == NULL) {
+		index_def_delete(def);
+		diag_set(OutOfMemory, name_len + 1, "malloc", "index_def name");
+		return NULL;
+	}
+	if (!identifier_is_valid(def->name)) {
+		diag_set(ClientError, ER_IDENTIFIER, def->name);
+		index_def_delete(def);
+		return NULL;
+	}
+	def->key_def = key_def_dup(key_def);
+	if (pk_def)
+		def->cmp_def = key_def_merge(key_def, pk_def);
+	else
+		def->cmp_def = key_def_dup(key_def);
+	if (def->key_def == NULL || def->cmp_def == NULL) {
+		index_def_delete(def);
+		return NULL;
+	}
+	def->type = type;
+	def->space_id = space_id;
+	def->iid = iid;
+	def->opts = *opts;
+	return def;
+}
+
+struct index_def *
+index_def_dup(const struct index_def *def)
+{
+	struct index_def *dup = (struct index_def *) malloc(sizeof(*dup));
+	if (dup == NULL) {
+		diag_set(OutOfMemory, sizeof(*dup), "malloc",
+			 "struct index_def");
+		return NULL;
+	}
+	*dup = *def;
+	dup->name = strdup(def->name);
+	if (dup->name == NULL) {
+		free(dup);
+		diag_set(OutOfMemory, strlen(def->name) + 1, "malloc",
+			 "index_def name");
+		return NULL;
+	}
+	dup->key_def = key_def_dup(def->key_def);
+	dup->cmp_def = key_def_dup(def->cmp_def);
+	if (dup->key_def == NULL || dup->cmp_def == NULL) {
+		index_def_delete(dup);
+		return NULL;
+	}
+	rlist_create(&dup->link);
+	return dup;
+}
+
+void
+index_def_swap(struct index_def *def1, struct index_def *def2)
+{
+	/*
+	 * Swap const-size items and name. Keep the original key
+	 * definitions, they are used in the engines.
+	 */
+	struct index_def tmp_def = *def1;
+	memcpy(def1, def2, offsetof(struct index_def, key_def));
+	memcpy(def2, &tmp_def, offsetof(struct index_def, key_def));
+	/*
+	 * index_def_swap() is used only during alter to modify
+	 * index metadata.
+	 */
+}
+
+/** Free a key definition. */
+void
+index_def_delete(struct index_def *index_def)
+{
+	free(index_def->name);
+
+	if (index_def->key_def) {
+		TRASH(index_def->key_def);
+		free(index_def->key_def);
+	}
+
+	if (index_def->cmp_def) {
+		TRASH(index_def->cmp_def);
+		free(index_def->cmp_def);
+	}
+
+	TRASH(index_def);
+	free(index_def);
+}
+
+bool
+index_def_change_requires_rebuild(struct index_def *old_index_def,
+				  struct index_def *new_index_def)
+{
+	if (old_index_def->iid != new_index_def->iid ||
+	    old_index_def->type != new_index_def->type ||
+	    old_index_def->opts.is_unique != new_index_def->opts.is_unique ||
+	    key_part_cmp(old_index_def->key_def->parts,
+			 old_index_def->key_def->part_count,
+			 new_index_def->key_def->parts,
+			 new_index_def->key_def->part_count) != 0) {
+		return true;
+	}
+	if (old_index_def->type == RTREE) {
+		if (old_index_def->opts.dimension != new_index_def->opts.dimension
+		    || old_index_def->opts.distance != new_index_def->opts.distance)
+			return true;
+	}
+	return false;
+}
+
+int
+index_def_cmp(const struct index_def *key1, const struct index_def *key2)
+{
+	assert(key1->space_id == key2->space_id);
+	if (key1->iid != key2->iid)
+		return key1->iid < key2->iid ? -1 : 1;
+	if (strcmp(key1->name, key2->name))
+		return strcmp(key1->name, key2->name);
+	if (key1->type != key2->type)
+		return (int) key1->type < (int) key2->type ? -1 : 1;
+	if (index_opts_cmp(&key1->opts, &key2->opts))
+		return index_opts_cmp(&key1->opts, &key2->opts);
+
+	return key_part_cmp(key1->key_def->parts, key1->key_def->part_count,
+			    key2->key_def->parts, key2->key_def->part_count);
+}
+
+bool
+index_def_is_valid(struct index_def *index_def, const char *space_name)
+
+{
+	if (index_def->iid >= BOX_INDEX_MAX) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "index id too big");
+		return false;
+	}
+	if (index_def->iid == 0 && index_def->opts.is_unique == false) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "primary key must be unique");
+		return false;
+	}
+	if (index_def->key_def->part_count == 0) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "part count must be positive");
+		return false;
+	}
+	if (index_def->key_def->part_count > BOX_INDEX_PART_MAX) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "too many key parts");
+		return false;
+	}
+	for (uint32_t i = 0; i < index_def->key_def->part_count; i++) {
+		assert(index_def->key_def->parts[i].type > FIELD_TYPE_ANY &&
+		       index_def->key_def->parts[i].type < field_type_MAX);
+		if (index_def->key_def->parts[i].fieldno > BOX_INDEX_FIELD_MAX) {
+			diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+				 space_name, "field no is too big");
+			return false;
+		}
+		for (uint32_t j = 0; j < i; j++) {
+			/*
+			 * Courtesy to a user who could have made
+			 * a typo.
+			 */
+			if (index_def->key_def->parts[i].fieldno ==
+			    index_def->key_def->parts[j].fieldno) {
+				diag_set(ClientError, ER_MODIFY_INDEX,
+					 index_def->name, space_name,
+					 "same key part is indexed twice");
+				return false;
+			}
+		}
+	}
+	return true;
+}
diff --git a/src/box/index_def.h b/src/box/index_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d56c12ab87697a1a19e42565614c94d4fd04c85
--- /dev/null
+++ b/src/box/index_def.h
@@ -0,0 +1,253 @@
+#ifndef TARANTOOL_BOX_INDEX_DEF_H_INCLUDED
+#define TARANTOOL_BOX_INDEX_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "key_def.h"
+#include "opt_def.h"
+#include "small/rlist.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+enum index_type {
+	HASH = 0, /* HASH Index */
+	TREE,     /* TREE Index */
+	BITSET,   /* BITSET Index */
+	RTREE,    /* R-Tree Index */
+	index_type_MAX,
+};
+
+extern const char *index_type_strs[];
+
+enum rtree_index_distance_type {
+	 /* Euclid distance, sqrt(dx*dx + dy*dy) */
+	RTREE_INDEX_DISTANCE_TYPE_EUCLID,
+	/* Manhattan distance, fabs(dx) + fabs(dy) */
+	RTREE_INDEX_DISTANCE_TYPE_MANHATTAN,
+	rtree_index_distance_type_MAX
+};
+extern const char *rtree_index_distance_type_strs[];
+
+/** Index options */
+struct index_opts {
+	/**
+	 * Is this index unique or not - relevant to HASH/TREE
+	 * index
+	 */
+	bool is_unique;
+	/**
+	 * RTREE index dimension.
+	 */
+	int64_t dimension;
+	/**
+	 * RTREE distance type.
+	 */
+	char distancebuf[16];
+	enum rtree_index_distance_type distance;
+	/**
+	 * Vinyl index options.
+	 */
+	int64_t range_size;
+	int64_t page_size;
+	/**
+	 * Maximal number of runs that can be created in a level
+	 * of the LSM tree before triggering compaction.
+	 */
+	int64_t run_count_per_level;
+	/**
+	 * The LSM tree multiplier. Each subsequent level of
+	 * the LSM tree is run_size_ratio times larger than
+	 * previous one.
+	 */
+	double run_size_ratio;
+	/* Bloom filter false positive rate. */
+	double bloom_fpr;
+	/**
+	 * LSN from the time of index creation.
+	 */
+	int64_t lsn;
+};
+
+extern const struct index_opts index_opts_default;
+extern const struct opt_def index_opts_reg[];
+
+static inline int
+index_opts_cmp(const struct index_opts *o1, const struct index_opts *o2)
+{
+	if (o1->is_unique != o2->is_unique)
+		return o1->is_unique < o2->is_unique ? -1 : 1;
+	if (o1->dimension != o2->dimension)
+		return o1->dimension < o2->dimension ? -1 : 1;
+	if (o1->distance != o2->distance)
+		return o1->distance < o2->distance ? -1 : 1;
+	if (o1->range_size != o2->range_size)
+		return o1->range_size < o2->range_size ? -1 : 1;
+	if (o1->page_size != o2->page_size)
+		return o1->page_size < o2->page_size ? -1 : 1;
+	if (o1->run_count_per_level != o2->run_count_per_level)
+		return o1->run_count_per_level < o2->run_count_per_level ?
+		       -1 : 1;
+	if (o1->run_size_ratio != o2->run_size_ratio)
+		return o1->run_size_ratio < o2->run_size_ratio ? -1 : 1;
+	if (o1->bloom_fpr != o2->bloom_fpr)
+		return o1->bloom_fpr < o2->bloom_fpr ? -1 : 1;
+	return 0;
+}
+
+/* Definition of an index. */
+struct index_def {
+	/* A link in key list. */
+	struct rlist link;
+	/** Ordinal index number in the index array. */
+	uint32_t iid;
+	/* Space id. */
+	uint32_t space_id;
+	/** Index name. */
+	char *name;
+	/** Index type. */
+	enum index_type type;
+	struct index_opts opts;
+
+	/* Index key definition. */
+	struct key_def *key_def;
+	/**
+	 * User-defined key definition, merged with the primary
+	 * key parts. Used by non-unique keys to uniquely identify
+	 * iterator position.
+	 */
+	struct key_def *cmp_def;
+};
+
+struct index_def *
+index_def_dup(const struct index_def *def);
+
+void
+index_def_swap(struct index_def *def1, struct index_def *def2);
+
+/* Destroy and free an index_def. */
+void
+index_def_delete(struct index_def *def);
+
+/**
+ * Add an index definition to a list, preserving the
+ * first position of the primary key.
+ *
+ * In non-unique indexes, secondary keys must contain key parts
+ * of the primary key. This is necessary to make ordered
+ * retrieval from a secondary key useful to SQL
+ * optimizer and make iterators over secondary keys stable
+ * in presence of concurrent updates.
+ * Thus we always create the primary key first, and put
+ * the primary key key_def first in the index_def list.
+ */
+static inline void
+index_def_list_add(struct rlist *index_def_list, struct index_def *index_def)
+{
+	/** Preserve the position of the primary key */
+	if (index_def->iid == 0)
+		rlist_add_entry(index_def_list, index_def, link);
+	else
+		rlist_add_tail_entry(index_def_list, index_def, link);
+}
+
+/**
+ * True, if the index change by alter requires an index rebuild.
+ *
+ * Some changes, such as a new page size or bloom_fpr do not
+ * take effect immediately, so do not require a rebuild.
+ *
+ * Others, such as index name change, do not change the data, only
+ * metadata, so do not require a rebuild either.
+ *
+ * Finally, changing index type or number of parts always requires
+ * a rebuild.
+ */
+bool
+index_def_change_requires_rebuild(struct index_def *old_index_def,
+				  struct index_def *new_index_def);
+
+/**
+ * Create a new index definition definition.
+ *
+ * @param key_def  key definition, must be fully built
+ * @param pk_def   primary key definition, pass non-NULL
+ *                 for secondary keys to construct
+ *                 index_def::cmp_def
+ * @retval not NULL Success.
+ * @retval NULL     Memory error.
+ */
+struct index_def *
+index_def_new(uint32_t space_id, uint32_t iid, const char *name,
+	      uint32_t name_len, enum index_type type,
+	      const struct index_opts *opts,
+	      struct key_def *key_def, struct key_def *pk_def);
+
+/**
+ * One key definition is greater than the other if it's id is
+ * greater, it's name is greater,  it's index type is greater
+ * (HASH < TREE < BITSET) or its key part array is greater.
+ */
+int
+index_def_cmp(const struct index_def *key1, const struct index_def *key2);
+
+/**
+ * Check a key definition for violation of various limits.
+ *
+ * @param index_def   index definition
+ * @param old_space   space definition
+ */
+bool
+index_def_is_valid(struct index_def *index_def, const char *space_name);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+static inline struct index_def *
+index_def_dup_xc(const struct index_def *def)
+{
+	struct index_def *ret = index_def_dup(def);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+static inline void
+index_def_check_xc(struct index_def *index_def, const char *space_name)
+{
+	if (! index_def_is_valid(index_def, space_name))
+		diag_raise();
+}
+
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_INDEX_DEF_H_INCLUDED */
diff --git a/src/box/key_def.cc b/src/box/key_def.cc
index c989bb4cc9b0ae9fb1affc74baa88cfedb12bf93..7db87ee228af5f4b925dbd2845daeab903923445 100644
--- a/src/box/key_def.cc
+++ b/src/box/key_def.cc
@@ -29,48 +29,10 @@
  * SUCH DAMAGE.
  */
 #include "key_def.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <msgpuck/msgpuck.h>
-
-#include "trivia/util.h"
-#include "scoped_guard.h"
-
 #include "tuple_compare.h"
 #include "tuple_hash.h"
 #include "column_mask.h"
-
-const char *field_type_strs[] = {
-	/* [FIELD_TYPE_ANY]      = */ "any",
-	/* [FIELD_TYPE_UNSIGNED] = */ "unsigned",
-	/* [FIELD_TYPE_STRING]   = */ "string",
-	/* [FIELD_TYPE_ARRAY]    = */ "array",
-	/* [FIELD_TYPE_NUMBER]   = */ "number",
-	/* [FIELD_TYPE_INTEGER]  = */ "integer",
-	/* [FIELD_TYPE_SCALAR]   = */ "scalar",
-	/* [FIELD_TYPE_MAP]      = */ "map",
-};
-
-enum field_type
-field_type_by_name(const char *name)
-{
-	enum field_type field_type = STR2ENUM(field_type, name);
-	/*
-	 * FIELD_TYPE_ANY can't be used as type of indexed field,
-	 * because it is internal type used only for filling
-	 * struct tuple_format.fields array.
-	 */
-	if (field_type != field_type_MAX && field_type != FIELD_TYPE_ANY)
-		return field_type;
-	/* 'num' and 'str' in _index are deprecated since Tarantool 1.7 */
-	if (strcasecmp(name, "num") == 0)
-		return FIELD_TYPE_UNSIGNED;
-	else if (strcasecmp(name, "str") == 0)
-		return FIELD_TYPE_STRING;
-	return field_type_MAX;
-}
+#include "schema_def.h"
 
 const char *mp_type_strs[] = {
 	/* .MP_NIL    = */ "nil",
@@ -86,12 +48,6 @@ const char *mp_type_strs[] = {
 	/* .MP_EXT    = */ "extension",
 };
 
-const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
-
-const char *rtree_index_distance_type_strs[] = { "EUCLID", "MANHATTAN" };
-
-const char *func_language_strs[] = {"LUA", "C"};
-
 const uint32_t key_mp_type[] = {
 	/* [FIELD_TYPE_ANY]      =  */ UINT32_MAX,
 	/* [FIELD_TYPE_UNSIGNED] =  */ 1U << MP_UINT,
@@ -105,61 +61,6 @@ const uint32_t key_mp_type[] = {
 		(1U << MP_BIN) | (1U << MP_BOOL),
 };
 
-const char *opt_type_strs[] = {
-	/* [OPT_BOOL]	= */ "boolean",
-	/* [OPT_INT]	= */ "integer",
-	/* [OPT_FLOAT]	= */ "float",
-	/* [OPT_STR]	= */ "string",
-};
-
-const struct index_opts index_opts_default = {
-	/* .unique              = */ true,
-	/* .dimension           = */ 2,
-	/* .distancebuf         = */ { '\0' },
-	/* .distance            = */ RTREE_INDEX_DISTANCE_TYPE_EUCLID,
-	/* .range_size          = */ 0,
-	/* .page_size           = */ 0,
-	/* .run_count_per_level = */ 2,
-	/* .run_size_ratio      = */ 3.5,
-	/* .bloom_fpr           = */ 0.05,
-	/* .lsn                 = */ 0,
-};
-
-const struct opt_def index_opts_reg[] = {
-	OPT_DEF("unique", OPT_BOOL, struct index_opts, is_unique),
-	OPT_DEF("dimension", OPT_INT, struct index_opts, dimension),
-	OPT_DEF("distance", OPT_STR, struct index_opts, distancebuf),
-	OPT_DEF("range_size", OPT_INT, struct index_opts, range_size),
-	OPT_DEF("page_size", OPT_INT, struct index_opts, page_size),
-	OPT_DEF("run_count_per_level", OPT_INT, struct index_opts, run_count_per_level),
-	OPT_DEF("run_size_ratio", OPT_FLOAT, struct index_opts, run_size_ratio),
-	OPT_DEF("bloom_fpr", OPT_FLOAT, struct index_opts, bloom_fpr),
-	OPT_DEF("lsn", OPT_INT, struct index_opts, lsn),
-	{ NULL, opt_type_MAX, 0, 0 },
-};
-
-static const char *object_type_strs[] = {
-	"unknown", "universe", "space", "function", "user", "role" };
-
-enum schema_object_type
-schema_object_type(const char *name)
-{
-	/**
-	 * There may be other places in which we look object type by
-	 * name, and they are case-sensitive, so be case-sensitive
-	 * here too.
-	 */
-	int n_strs = sizeof(object_type_strs)/sizeof(*object_type_strs);
-	int index = strindex(object_type_strs, name, n_strs);
-	return (enum schema_object_type) (index == n_strs ? 0 : index);
-}
-
-const char *
-schema_object_name(enum schema_object_type type)
-{
-	return object_type_strs[type];
-}
-
 struct key_def *
 key_def_dup(const struct key_def *src)
 {
@@ -215,164 +116,6 @@ box_key_def_delete(box_key_def_t *key_def)
 	free(key_def);
 }
 
-struct space_def *
-space_def_dup(const struct space_def *src)
-{
-	size_t size = space_def_sizeof(strlen(src->name));
-	struct space_def *ret = (struct space_def *) malloc(size);
-	if (ret == NULL)
-		tnt_raise(OutOfMemory, size, "malloc", "ret");
-	memcpy(ret, src, size);
-	return ret;
-}
-
-struct space_def *
-space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
-	      const char *name, uint32_t name_len,
-	      const char *engine_name, uint32_t engine_len,
-	      const struct space_opts *opts)
-{
-	size_t size = space_def_sizeof(name_len);
-	struct space_def *def = (struct space_def *) malloc(size);
-	if (def == NULL)
-		tnt_raise(OutOfMemory, size, "malloc", "def");
-	assert(name_len <= BOX_NAME_MAX);
-	assert(engine_len <= ENGINE_NAME_MAX);
-	def->id = id;
-	def->uid = uid;
-	def->exact_field_count = exact_field_count;
-	memcpy(def->name, name, name_len);
-	def->name[name_len] = 0;
-	memcpy(def->engine_name, engine_name, engine_len);
-	def->engine_name[engine_len] = 0;
-	def->opts = *opts;
-	return def;
-}
-
-struct index_def *
-index_def_new(uint32_t space_id, uint32_t iid, const char *name,
-	      uint32_t name_len, enum index_type type,
-	      const struct index_opts *opts,
-	      struct key_def *key_def, struct key_def *pk_def)
-{
-	assert(name_len <= BOX_NAME_MAX);
-	/* Use calloc to make index_def_delete() safe at all times. */
-	struct index_def *def = (struct index_def *) calloc(1, sizeof(*def));
-	if (def == NULL) {
-		diag_set(OutOfMemory, sizeof(*def), "malloc", "struct index_def");
-		return NULL;
-	}
-	def->name = strndup(name, name_len);
-	if (def->name == NULL) {
-		index_def_delete(def);
-		diag_set(OutOfMemory, name_len + 1, "malloc", "index_def name");
-		return NULL;
-	}
-	if (!identifier_is_valid(def->name)) {
-		diag_set(ClientError, ER_IDENTIFIER, def->name);
-		index_def_delete(def);
-		return NULL;
-	}
-	def->key_def = key_def_dup(key_def);
-	if (pk_def)
-		def->cmp_def = key_def_merge(key_def, pk_def);
-	else
-		def->cmp_def = key_def_dup(key_def);
-	if (def->key_def == NULL || def->cmp_def == NULL) {
-		index_def_delete(def);
-		return NULL;
-	}
-	def->type = type;
-	def->space_id = space_id;
-	def->iid = iid;
-	def->opts = *opts;
-	return def;
-}
-
-struct index_def *
-index_def_dup(const struct index_def *def)
-{
-	struct index_def *dup = (struct index_def *) malloc(sizeof(*dup));
-	if (dup == NULL) {
-		diag_set(OutOfMemory, sizeof(*dup), "malloc",
-			 "struct index_def");
-		return NULL;
-	}
-	*dup = *def;
-	dup->name = strdup(def->name);
-	if (dup->name == NULL) {
-		free(dup);
-		diag_set(OutOfMemory, strlen(def->name) + 1, "malloc",
-			 "index_def name");
-		return NULL;
-	}
-	dup->key_def = key_def_dup(def->key_def);
-	dup->cmp_def = key_def_dup(def->cmp_def);
-	if (dup->key_def == NULL || dup->cmp_def == NULL) {
-		index_def_delete(dup);
-		return NULL;
-	}
-	rlist_create(&dup->link);
-	return dup;
-}
-
-void
-index_def_swap(struct index_def *def1, struct index_def *def2)
-{
-	/*
-	 * Swap const-size items and name. Keep the original key
-	 * definitions, they are used in the engines.
-	 */
-	struct index_def tmp_def = *def1;
-	memcpy(def1, def2, offsetof(struct index_def, key_def));
-	memcpy(def2, &tmp_def, offsetof(struct index_def, key_def));
-	/*
-	 * index_def_swap() is used only during alter to modify
-	 * index metadata.
-	 */
-}
-
-/** Free a key definition. */
-void
-index_def_delete(struct index_def *index_def)
-{
-	free(index_def->name);
-
-	if (index_def->key_def) {
-		TRASH(index_def->key_def);
-		free(index_def->key_def);
-	}
-
-	if (index_def->cmp_def) {
-		TRASH(index_def->cmp_def);
-		free(index_def->cmp_def);
-	}
-
-	TRASH(index_def);
-	free(index_def);
-}
-
-bool
-index_def_change_requires_rebuild(struct index_def *old_index_def,
-				  struct index_def *new_index_def)
-{
-	if (old_index_def->iid != new_index_def->iid ||
-	    old_index_def->type != new_index_def->type ||
-	    old_index_def->opts.is_unique != new_index_def->opts.is_unique ||
-	    key_part_cmp(old_index_def->key_def->parts,
-			 old_index_def->key_def->part_count,
-			 new_index_def->key_def->parts,
-			 new_index_def->key_def->part_count) != 0) {
-		return true;
-	}
-	if (old_index_def->type == RTREE) {
-		if (old_index_def->opts.dimension != new_index_def->opts.dimension
-		    || old_index_def->opts.distance != new_index_def->opts.distance)
-			return true;
-	}
-	return false;
-}
-
 int
 key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	     const struct key_part *parts2, uint32_t part_count2)
@@ -390,75 +133,6 @@ key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	return part_count1 < part_count2 ? -1 : part_count1 > part_count2;
 }
 
-int
-index_def_cmp(const struct index_def *key1, const struct index_def *key2)
-{
-	assert(key1->space_id == key2->space_id);
-	if (key1->iid != key2->iid)
-		return key1->iid < key2->iid ? -1 : 1;
-	if (strcmp(key1->name, key2->name))
-		return strcmp(key1->name, key2->name);
-	if (key1->type != key2->type)
-		return (int) key1->type < (int) key2->type ? -1 : 1;
-	if (index_opts_cmp(&key1->opts, &key2->opts))
-		return index_opts_cmp(&key1->opts, &key2->opts);
-
-	return key_part_cmp(key1->key_def->parts, key1->key_def->part_count,
-			    key2->key_def->parts, key2->key_def->part_count);
-}
-
-void
-index_def_check(struct index_def *index_def, const char *space_name)
-
-{
-	if (index_def->iid >= BOX_INDEX_MAX) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name, space_name,
-			  "index id too big");
-	}
-	if (index_def->iid == 0 && index_def->opts.is_unique == false) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "primary key must be unique");
-	}
-	if (index_def->key_def->part_count == 0) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "part count must be positive");
-	}
-	if (index_def->key_def->part_count > BOX_INDEX_PART_MAX) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "too many key parts");
-	}
-	for (uint32_t i = 0; i < index_def->key_def->part_count; i++) {
-		assert(index_def->key_def->parts[i].type > FIELD_TYPE_ANY &&
-		       index_def->key_def->parts[i].type < field_type_MAX);
-		if (index_def->key_def->parts[i].fieldno > BOX_INDEX_FIELD_MAX) {
-			tnt_raise(ClientError, ER_MODIFY_INDEX,
-				  index_def->name,
-				  space_name,
-				  "field no is too big");
-		}
-		for (uint32_t j = 0; j < i; j++) {
-			/*
-			 * Courtesy to a user who could have made
-			 * a typo.
-			 */
-			if (index_def->key_def->parts[i].fieldno ==
-			    index_def->key_def->parts[j].fieldno) {
-				tnt_raise(ClientError, ER_MODIFY_INDEX,
-					  index_def->name,
-					  space_name,
-					  "same key part is indexed twice");
-			}
-		}
-	}
-}
-
 void
 key_def_set_part(struct key_def *def, uint32_t part_no,
 		 uint32_t fieldno, enum field_type type)
@@ -663,44 +337,3 @@ key_validate_parts(struct key_def *key_def, const char *key,
 	}
 	return 0;
 }
-
-const struct space_opts space_opts_default = {
-	/* .temporary = */ false,
-};
-
-const struct opt_def space_opts_reg[] = {
-	OPT_DEF("temporary", OPT_BOOL, struct space_opts, temporary),
-	{ NULL, opt_type_MAX, 0, 0 }
-};
-
-bool
-identifier_is_valid(const char *str)
-{
-	mbstate_t state;
-	memset(&state, 0, sizeof(state));
-	wchar_t w;
-	ssize_t len = mbrtowc(&w, str, MB_CUR_MAX, &state);
-	if (len <= 0)
-		return false; /* invalid character or zero-length string */
-	if (!iswalpha(w) && w != L'_')
-		return false; /* fail to match [a-zA-Z_] */
-
-	while ((len = mbrtowc(&w, str, MB_CUR_MAX, &state)) > 0) {
-		if (!iswalnum(w) && w != L'_')
-			return false; /* fail to match [a-zA-Z0-9_]* */
-		str += len;
-	}
-
-	if (len < 0)
-		return false; /* invalid character  */
-
-	return true;
-}
-
-void
-identifier_check(const char *str)
-{
-	if (! identifier_is_valid(str))
-		tnt_raise(ClientError, ER_IDENTIFIER, str);
-}
-
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 478d9bf366bf8caf89954dbe33dc5ef5c0e72637..3147b6f9e03d25a4a3c22aba4c8d98a43c24d201 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -31,221 +31,25 @@
  * SUCH DAMAGE.
  */
 #include "trivia/util.h"
-#include "small/rlist.h"
 #include "error.h"
 #include "diag.h"
 #include <msgpuck.h>
-#define RB_COMPACT 1
-#include "small/rb.h"
 #include <limits.h>
-#include <wchar.h>
-#include <wctype.h>
+#include "field_def.h"
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
-enum {
-	BOX_ENGINE_MAX = 3, /* + 1 to the actual number of engines */
-	BOX_SPACE_MAX = INT32_MAX,
-	BOX_FUNCTION_MAX = 32000,
-	BOX_INDEX_MAX = 128,
-	BOX_NAME_MAX = 65000,
-	BOX_INVALID_NAME_MAX = 64,
-	ENGINE_NAME_MAX = 16,
-	FIELD_TYPE_NAME_MAX = 16,
-	GRANT_NAME_MAX = 16,
-	BOX_FIELD_MAX = INT32_MAX,
-	BOX_USER_MAX = 32,
-	/**
-	 * A fairly arbitrary limit which is still necessary
-	 * to keep tuple_format object small.
-	 */
-	BOX_INDEX_FIELD_MAX = INT16_MAX,
-	/** Yet another arbitrary limit which simply needs to
-	 * exist.
-	 */
-	BOX_INDEX_PART_MAX = UINT8_MAX
-};
-static_assert(BOX_INVALID_NAME_MAX <= BOX_NAME_MAX,
-	      "invalid name max is less than name max");
-
-/*
- * Different objects which can be subject to access
- * control.
- *
- * Use 0 for unknown to use the same index consistently
- * even when there are more object types in the future.
- */
-enum schema_object_type {
-	SC_UNKNOWN = 0, SC_UNIVERSE = 1, SC_SPACE = 2, SC_FUNCTION = 3,
-	SC_USER = 4, SC_ROLE = 5
-};
-
-enum schema_object_type
-schema_object_type(const char *name);
-
-const char *
-schema_object_name(enum schema_object_type type);
-
-/** \cond public */
-
-/*
- * Possible field data types. Can't use STRS/ENUM macros for them,
- * since there is a mismatch between enum name (STRING) and type
- * name literal ("STR"). STR is already used as Objective C type.
- */
-enum field_type {
-	FIELD_TYPE_ANY = 0,
-	FIELD_TYPE_UNSIGNED,
-	FIELD_TYPE_STRING,
-	FIELD_TYPE_ARRAY,
-	FIELD_TYPE_NUMBER,
-	FIELD_TYPE_INTEGER,
-	FIELD_TYPE_SCALAR,
-	FIELD_TYPE_MAP,
-	field_type_MAX
-};
-
-/** \endcond public */
-
-extern const char *field_type_strs[];
-
 /* MsgPack type names */
 extern const char *mp_type_strs[];
 
-/**
- * The supported language of the stored function.
- */
-enum func_language {
-	FUNC_LANGUAGE_LUA,
-	FUNC_LANGUAGE_C,
-	func_language_MAX,
-};
-extern const char *func_language_strs[];
-
-static inline uint32_t
-field_type_maxlen(enum field_type type)
-{
-	static const uint32_t maxlen[] =
-		{ UINT32_MAX, 8, UINT32_MAX, UINT32_MAX, UINT32_MAX };
-	return maxlen[type];
-}
-
-enum field_type
-field_type_by_name(const char *name);
-
-enum index_type {
-	HASH = 0, /* HASH Index */
-	TREE,     /* TREE Index */
-	BITSET,   /* BITSET Index */
-	RTREE,    /* R-Tree Index */
-	index_type_MAX,
-};
-
-extern const char *index_type_strs[];
-
-enum opt_type {
-	OPT_BOOL,	/* bool */
-	OPT_INT,	/* int64_t */
-	OPT_FLOAT,	/* double */
-	OPT_STR,	/* char[] */
-	opt_type_MAX,
-};
-
-extern const char *opt_type_strs[];
-
-struct opt_def {
-	const char *name;
-	enum opt_type type;
-	ptrdiff_t offset;
-	uint32_t len;
-};
-
-#define OPT_DEF(key, type, opts, field) \
-	{ key, type, offsetof(opts, field), sizeof(((opts *)0)->field) }
-
-enum rtree_index_distance_type {
-	 /* Euclid distance, sqrt(dx*dx + dy*dy) */
-	RTREE_INDEX_DISTANCE_TYPE_EUCLID,
-	/* Manhattan distance, fabs(dx) + fabs(dy) */
-	RTREE_INDEX_DISTANCE_TYPE_MANHATTAN,
-	rtree_index_distance_type_MAX
-};
-extern const char *rtree_index_distance_type_strs[];
-
 /** Descriptor of a single part in a multipart key. */
 struct key_part {
 	uint32_t fieldno;
 	enum field_type type;
 };
 
-/** Index options */
-struct index_opts {
-	/**
-	 * Is this index unique or not - relevant to HASH/TREE
-	 * index
-	 */
-	bool is_unique;
-	/**
-	 * RTREE index dimension.
-	 */
-	int64_t dimension;
-	/**
-	 * RTREE distance type.
-	 */
-	char distancebuf[16];
-	enum rtree_index_distance_type distance;
-	/**
-	 * Vinyl index options.
-	 */
-	int64_t range_size;
-	int64_t page_size;
-	/**
-	 * Maximal number of runs that can be created in a level
-	 * of the LSM tree before triggering compaction.
-	 */
-	int64_t run_count_per_level;
-	/**
-	 * The LSM tree multiplier. Each subsequent level of
-	 * the LSM tree is run_size_ratio times larger than
-	 * previous one.
-	 */
-	double run_size_ratio;
-	/* Bloom filter false positive rate. */
-	double bloom_fpr;
-	/**
-	 * LSN from the time of index creation.
-	 */
-	int64_t lsn;
-};
-
-extern const struct index_opts index_opts_default;
-extern const struct opt_def index_opts_reg[];
-
-static inline int
-index_opts_cmp(const struct index_opts *o1, const struct index_opts *o2)
-{
-	if (o1->is_unique != o2->is_unique)
-		return o1->is_unique < o2->is_unique ? -1 : 1;
-	if (o1->dimension != o2->dimension)
-		return o1->dimension < o2->dimension ? -1 : 1;
-	if (o1->distance != o2->distance)
-		return o1->distance < o2->distance ? -1 : 1;
-	if (o1->range_size != o2->range_size)
-		return o1->range_size < o2->range_size ? -1 : 1;
-	if (o1->page_size != o2->page_size)
-		return o1->page_size < o2->page_size ? -1 : 1;
-	if (o1->run_count_per_level != o2->run_count_per_level)
-		return o1->run_count_per_level < o2->run_count_per_level ?
-		       -1 : 1;
-	if (o1->run_size_ratio != o2->run_size_ratio)
-		return o1->run_size_ratio < o2->run_size_ratio ? -1 : 1;
-	if (o1->bloom_fpr != o2->bloom_fpr)
-		return o1->bloom_fpr < o2->bloom_fpr ? -1 : 1;
-	return 0;
-}
-
 struct key_def;
 struct tuple;
 
@@ -332,231 +136,6 @@ box_key_def_delete(box_key_def_t *key_def);
 
 /** \endcond public */
 
-/* Definition of an index. */
-struct index_def {
-	/* A link in key list. */
-	struct rlist link;
-	/** Ordinal index number in the index array. */
-	uint32_t iid;
-	/* Space id. */
-	uint32_t space_id;
-	/** Index name. */
-	char *name;
-	/** Index type. */
-	enum index_type type;
-	struct index_opts opts;
-
-	/* Index key definition. */
-	struct key_def *key_def;
-	/**
-	 * User-defined key definition, merged with the primary
-	 * key parts. Used by non-unique keys to uniquely identify
-	 * iterator position.
-	 */
-	struct key_def *cmp_def;
-};
-
-struct index_def *
-index_def_dup(const struct index_def *def);
-
-void
-index_def_swap(struct index_def *def1, struct index_def *def2);
-
-/* Destroy and free an index_def. */
-void
-index_def_delete(struct index_def *def);
-
-/**
- * Add an index definition to a list, preserving the
- * first position of the primary key.
- *
- * In non-unique indexes, secondary keys must contain key parts
- * of the primary key. This is necessary to make ordered
- * retrieval from a secondary key useful to SQL
- * optimizer and make iterators over secondary keys stable
- * in presence of concurrent updates.
- * Thus we always create the primary key first, and put
- * the primary key key_def first in the index_def list.
- */
-static inline void
-index_def_list_add(struct rlist *index_def_list, struct index_def *index_def)
-{
-	/** Preserve the position of the primary key */
-	if (index_def->iid == 0)
-		rlist_add_entry(index_def_list, index_def, link);
-	else
-		rlist_add_tail_entry(index_def_list, index_def, link);
-}
-
-/**
- * True, if the index change by alter requires an index rebuild.
- *
- * Some changes, such as a new page size or bloom_fpr do not
- * take effect immediately, so do not require a rebuild.
- *
- * Others, such as index name change, do not change the data, only
- * metadata, so do not require a rebuild either.
- *
- * Finally, changing index type or number of parts always requires
- * a rebuild.
- */
-bool
-index_def_change_requires_rebuild(struct index_def *old_index_def,
-				  struct index_def *new_index_def);
-
-/**
- * Encapsulates privileges of a user on an object.
- * I.e. "space" object has an instance of this
- * structure for each user.
- */
-struct access {
-	/**
-	 * Granted access has been given to a user explicitly
-	 * via some form of a grant.
-	 */
-	uint8_t granted;
-	/**
-	 * Effective access is a sum of granted access and
-	 * all privileges inherited by a user on this object
-	 * via some role. Since roles may be granted to other
-	 * roles, this may include indirect grants.
-	 */
-	uint8_t effective;
-};
-
-/**
- * Effective session user. A cache of user data
- * and access stored in session and fiber local storage.
- * Differs from the authenticated user when executing
- * setuid functions.
- */
-struct credentials {
-	/** A look up key to quickly find session user. */
-	uint8_t auth_token;
-	/**
-	 * Cached global grants, to avoid an extra look up
-	 * when checking global grants.
-	 */
-	uint8_t universal_access;
-	/** User id of the authenticated user. */
-	uint32_t uid;
-};
-
-/**
- * Definition of a function. Function body is not stored
- * or replicated (yet).
- */
-struct func_def {
-	/** Function id. */
-	uint32_t fid;
-	/** Owner of the function. */
-	uint32_t uid;
-	/**
-	 * True if the function requires change of user id before
-	 * invocation.
-	 */
-	bool setuid;
-	/**
-	 * The language of the stored function.
-	 */
-	enum func_language language;
-	/** Function name. */
-	char name[0];
-};
-
-/**
- * @param name_len length of func_def->name
- * @returns size in bytes needed to allocate for struct func_def
- * for a function of length @a a name_len.
- */
-static inline size_t
-func_def_sizeof(uint32_t name_len)
-{
-	/* +1 for '\0' name terminating. */
-	return sizeof(struct func_def) + name_len + 1;
-}
-
-/**
- * Definition of a privilege
- */
-struct priv_def {
-	/** Who grants the privilege. */
-	uint32_t grantor_id;
-	/** Whom the privilege is granted. */
-	uint32_t grantee_id;
-	/* Object id - is only defined for object type */
-	uint32_t object_id;
-	/* Object type - function, space, universe */
-	enum schema_object_type object_type;
-	/**
-	 * What is being granted, has been granted, or is being
-	 * revoked.
-	 */
-	uint8_t access;
-	/** To maintain a set of effective privileges. */
-	rb_node(struct priv_def) link;
-};
-
-/** Space options */
-struct space_opts {
-        /**
-	 * The space is a temporary:
-	 * - it is empty at server start
-	 * - changes are not written to WAL
-	 * - changes are not part of a snapshot
-	 */
-	bool temporary;
-};
-
-extern const struct space_opts space_opts_default;
-extern const struct opt_def space_opts_reg[];
-
-/** Space metadata. */
-struct space_def {
-	/** Space id. */
-	uint32_t id;
-	/** User id of the creator of the space */
-	uint32_t uid;
-	/**
-	 * If not set (is 0), any tuple in the
-	 * space can have any number of fields.
-	 * If set, each tuple
-	 * must have exactly this many fields.
-	 */
-	uint32_t exact_field_count;
-	char engine_name[ENGINE_NAME_MAX + 1];
-	struct space_opts opts;
-	char name[0];
-};
-
-/**
- * Size of the space_def, calculated using its name.
- * @param name_len Length of the space name.
- * @retval Size in bytes.
- */
-static inline size_t
-space_def_sizeof(uint32_t name_len)
-{
-	return sizeof(struct space_def) + name_len + 1;
-}
-
-/**
- * Delete the space_def object.
- * @param def Def to delete.
- */
-static inline void
-space_def_delete(struct space_def *def)
-{
-	free(def);
-}
-
-/**
- * API of C stored function.
- */
-typedef struct box_function_ctx box_function_ctx_t;
-typedef int (*box_function_f)(box_function_ctx_t *ctx,
-	     const char *args, const char *args_end);
-
 static inline size_t
 key_def_sizeof(uint32_t part_count)
 {
@@ -569,22 +148,6 @@ key_def_sizeof(uint32_t part_count)
 struct key_def *
 key_def_new(uint32_t part_count);
 
-/**
- * Create a new index definition definition.
- *
- * @param key_def  key definition, must be fully built
- * @param pk_def   primary key definition, pass non-NULL
- *                 for secondary keys to construct
- *                 index_def::cmp_def
- * @retval not NULL Success.
- * @retval NULL     Memory error.
- */
-struct index_def *
-index_def_new(uint32_t space_id, uint32_t iid, const char *name,
-	      uint32_t name_len, enum index_type type,
-	      const struct index_opts *opts,
-	      struct key_def *key_def, struct key_def *pk_def);
-
 /**
  * Set a single key part in a key def.
  * @pre part_no < part_count
@@ -709,37 +272,6 @@ key_mp_type_validate(enum field_type key_type, enum mp_type mp_type,
 	return 0;
 }
 
-#if defined(__cplusplus)
-} /* extern "C" */
-
-/**
- * Duplicate space_def object.
- * @param src Def to duplicate.
- * @retval Copy of the @src.
- */
-struct space_def *
-space_def_dup(const struct space_def *src); /* throws */
-
-/**
- * Create a new space definition.
- * @param id Space identifier.
- * @param uid Owner identifier.
- * @param exact_field_count Space tuples field count.
- *        0 for any count.
- * @param name Space name.
- * @param name_len Length of the @name.
- * @param engine_name Engine name.
- * @param engine_len Length of the @engine.
- * @param opts Space options.
- *
- * @retval Space definition.
- */
-struct space_def *
-space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
-	      const char *name, uint32_t name_len,
-	      const char *engine_name, uint32_t engine_len,
-	      const struct space_opts *opts); /* throws */
-
 /** Compare two key part arrays.
  *
  * This function is used to find out whether alteration
@@ -751,7 +283,6 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
  * One key part is considered to be greater than the other if:
  * - its fieldno is greater
  * - given the same fieldno, NUM < STRING
- *   (coarsely speaking, based on field_type_maxlen()).
  *
  * A key part array is considered greater than the other if all
  * its key parts are greater, or, all common key parts are equal
@@ -761,46 +292,8 @@ int
 key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	     const struct key_part *parts2, uint32_t part_count2);
 
-/**
- * One key definition is greater than the other if it's id is
- * greater, it's name is greater,  it's index type is greater
- * (HASH < TREE < BITSET) or its key part array is greater.
- */
-int
-index_def_cmp(const struct index_def *key1, const struct index_def *key2);
-
-/**
- * Check a key definition for violation of various limits.
- *
- * @param index_def   index definition
- * @param old_space   space definition
- */
-void
-index_def_check(struct index_def *index_def, const char *space_name);
-
-/**
- * Check object identifier for invalid symbols.
- * The function checks \a str for matching [a-zA-Z_][a-zA-Z0-9_]* expression.
- * Result is locale-dependent.
- */
-bool
-identifier_is_valid(const char *str);
-
-/**
- * Throw an error if identifier is not valid.
- */
-void
-identifier_check(const char *str);
-
-static inline struct index_def *
-index_def_dup_xc(const struct index_def *def)
-{
-	struct index_def *ret = index_def_dup(def);
-	if (ret == NULL)
-		diag_raise();
-	return ret;
-}
-
+#if defined(__cplusplus)
+} /* extern "C" */
 #endif /* defined(__cplusplus) */
 
 #endif /* TARANTOOL_BOX_KEY_DEF_H_INCLUDED */
diff --git a/src/box/opt_def.c b/src/box/opt_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..d15bc7017ae3e2ac1ae453c3672016e01fc61bf0
--- /dev/null
+++ b/src/box/opt_def.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "opt_def.h"
+
+const char *opt_type_strs[] = {
+	/* [OPT_BOOL]	= */ "boolean",
+	/* [OPT_INT]	= */ "integer",
+	/* [OPT_FLOAT]	= */ "float",
+	/* [OPT_STR]	= */ "string",
+};
diff --git a/src/box/opt_def.h b/src/box/opt_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..c519a1e43578b841aed4ca0156c88cf24b9564e5
--- /dev/null
+++ b/src/box/opt_def.h
@@ -0,0 +1,57 @@
+#ifndef TARANTOOL_BOX_OPT_DEF_H_INCLUDED
+#define TARANTOOL_BOX_OPT_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "trivia/util.h"
+#include <stddef.h>
+
+enum opt_type {
+	OPT_BOOL,	/* bool */
+	OPT_INT,	/* int64_t */
+	OPT_FLOAT,	/* double */
+	OPT_STR,	/* char[] */
+	opt_type_MAX,
+};
+
+extern const char *opt_type_strs[];
+
+struct opt_def {
+	const char *name;
+	enum opt_type type;
+	ptrdiff_t offset;
+	uint32_t len;
+};
+
+#define OPT_DEF(key, type, opts, field) \
+	{ key, type, offsetof(opts, field), sizeof(((opts *)0)->field) }
+
+#endif /* TARANTOOL_BOX_OPT_DEF_H_INCLUDED */
diff --git a/src/box/schema.cc b/src/box/schema.cc
index ff881b74a483a7ad3741ac6e21c9ed04927eda2a..91e4a0678665ee74d52ef26aeaa11f2aa04a6d7d 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -37,7 +37,6 @@
 #include "assoc.h"
 #include "lua/utils.h"
 #include "lua/space.h"
-#include "key_def.h"
 #include "alter.h"
 #include "scoped_guard.h"
 #include <stdio.h>
@@ -191,8 +190,8 @@ sc_space_new(uint32_t id, const char *name, struct key_def *key_def,
 	auto index_def_guard =
 		make_scoped_guard([=] { index_def_delete(index_def); });
 	struct space_def *def =
-		space_def_new(id, ADMIN, 0, name, strlen(name), "memtx",
-			      strlen("memtx"), &space_opts_default);
+		space_def_new_xc(id, ADMIN, 0, name, strlen(name), "memtx",
+				 strlen("memtx"), &space_opts_default);
 	auto def_guard = make_scoped_guard([=] { space_def_delete(def); });
 	struct rlist key_list;
 	rlist_create(&key_list);
diff --git a/src/box/schema.h b/src/box/schema.h
index d9fc81cd653223fefeff5c3839c548661a5e7aa9..091b8253302f15ec05baf298d875a46527b6f610 100644
--- a/src/box/schema.h
+++ b/src/box/schema.h
@@ -31,126 +31,20 @@
  * SUCH DAMAGE.
  */
 
-/** \cond public */
-enum {
-	/** Start of the reserved range of system spaces. */
-	BOX_SYSTEM_ID_MIN = 256,
-	/** Space id of _schema. */
-	BOX_SCHEMA_ID = 272,
-	/** Space id of _space. */
-	BOX_SPACE_ID = 280,
-	/** Space id of _vspace view. */
-	BOX_VSPACE_ID = 281,
-	/** Space id of _index. */
-	BOX_INDEX_ID = 288,
-	/** Space id of _vindex view. */
-	BOX_VINDEX_ID = 289,
-	/** Space id of _func. */
-	BOX_FUNC_ID = 296,
-	/** Space id of _vfunc view. */
-	BOX_VFUNC_ID = 297,
-	/** Space id of _user. */
-	BOX_USER_ID = 304,
-	/** Space id of _vuser view. */
-	BOX_VUSER_ID = 305,
-	/** Space id of _priv. */
-	BOX_PRIV_ID = 312,
-	/** Space id of _vpriv view. */
-	BOX_VPRIV_ID = 313,
-	/** Space id of _cluster. */
-	BOX_CLUSTER_ID = 320,
-	/** Space id of _truncate. */
-	BOX_TRUNCATE_ID = 330,
-	/** End of the reserved range of system spaces. */
-	BOX_SYSTEM_ID_MAX = 511,
-	BOX_ID_NIL = 2147483647
-};
-/** \endcond public */
-
-/** _space fields. */
-enum {
-	BOX_SPACE_FIELD_ID = 0,
-	BOX_SPACE_FIELD_UID = 1,
-	BOX_SPACE_FIELD_NAME = 2,
-	BOX_SPACE_FIELD_ENGINE = 3,
-	BOX_SPACE_FIELD_FIELD_COUNT = 4,
-	BOX_SPACE_FIELD_OPTS = 5,
-};
-
-/** _index fields. */
-enum {
-	BOX_INDEX_FIELD_SPACE_ID = 0,
-	BOX_INDEX_FIELD_ID = 1,
-	BOX_INDEX_FIELD_NAME = 2,
-	BOX_INDEX_FIELD_TYPE = 3,
-	BOX_INDEX_FIELD_OPTS = 4,
-	BOX_INDEX_FIELD_IS_UNIQUE_165 = 4,
-	BOX_INDEX_FIELD_PARTS = 5,
-	BOX_INDEX_FIELD_PART_COUNT_165 = 5,
-	BOX_INDEX_FIELD_PARTS_165 = 6,
-};
-
-/** _user fields. */
-enum {
-	BOX_USER_FIELD_ID = 0,
-	BOX_USER_FIELD_UID = 1,
-	BOX_USER_FIELD_NAME = 2,
-	BOX_USER_FIELD_TYPE = 3,
-	BOX_USER_FIELD_AUTH_MECH_LIST = 4,
-};
-
-/** _priv fields. */
-enum {
-	BOX_PRIV_FIELD_ID = 0,
-	BOX_PRIV_FIELD_UID = 1,
-	BOX_PRIV_FIELD_OBJECT_TYPE = 2,
-	BOX_PRIV_FIELD_OBJECT_ID = 3,
-	BOX_PRIV_FIELD_ACCESS = 4,
-};
-
-/** _func fields. */
-enum {
-	BOX_FUNC_FIELD_ID = 0,
-	BOX_FUNC_FIELD_UID = 1,
-	BOX_FUNC_FIELD_NAME = 2,
-	BOX_FUNC_FIELD_SETUID = 3,
-	BOX_FUNC_FIELD_LANGUAGE = 4,
-};
-
-/** _schema fields. */
-enum {
-	BOX_SCHEMA_FIELD_KEY = 0,
-};
-
-/** _cluster fields. */
-enum {
-	BOX_CLUSTER_FIELD_ID = 0,
-	BOX_CLUSTER_FIELD_UUID = 1,
-};
-
-/** _truncate fields. */
-enum {
-	BOX_TRUNCATE_FIELD_SPACE_ID = 0,
-	BOX_TRUNCATE_FIELD_COUNT = 1,
-};
-
 #include <stdint.h>
-
-extern uint32_t schema_version;
-
-#if defined(__cplusplus)
-
-#include "error.h"
 #include <stdio.h> /* snprintf */
+#include "error.h"
 #include "space.h"
 #include "latch.h"
 
+extern uint32_t schema_version;
+
 /**
  * Lock of schema modification
  */
 extern struct latch schema_lock;
 
-struct space;
+#if defined(__cplusplus)
 
 /** Call a visitor function on every space in the space cache. */
 void
@@ -254,6 +148,7 @@ func_by_name(const char *name, uint32_t name_len);
  */
 bool
 schema_find_grants(const char *type, uint32_t id);
+
 #endif /* defined(__cplusplus) */
 
 #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */
diff --git a/src/box/schema_def.c b/src/box/schema_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..0907292669dff49cf90ac949769f38b2e4bb19cf
--- /dev/null
+++ b/src/box/schema_def.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "schema_def.h"
+#include <wchar.h>
+#include <wctype.h>
+
+static const char *object_type_strs[] = {
+	"unknown", "universe", "space", "function", "user", "role" };
+
+enum schema_object_type
+schema_object_type(const char *name)
+{
+	/**
+	 * There may be other places in which we look object type by
+	 * name, and they are case-sensitive, so be case-sensitive
+	 * here too.
+	 */
+	int n_strs = sizeof(object_type_strs)/sizeof(*object_type_strs);
+	int index = strindex(object_type_strs, name, n_strs);
+	return (enum schema_object_type) (index == n_strs ? 0 : index);
+}
+
+const char *
+schema_object_name(enum schema_object_type type)
+{
+	return object_type_strs[type];
+}
+
+bool
+identifier_is_valid(const char *str)
+{
+	mbstate_t state;
+	memset(&state, 0, sizeof(state));
+	wchar_t w;
+	ssize_t len = mbrtowc(&w, str, MB_CUR_MAX, &state);
+	if (len <= 0)
+		return false; /* invalid character or zero-length string */
+	if (!iswalpha(w) && w != L'_')
+		return false; /* fail to match [a-zA-Z_] */
+
+	while ((len = mbrtowc(&w, str, MB_CUR_MAX, &state)) > 0) {
+		if (!iswalnum(w) && w != L'_')
+			return false; /* fail to match [a-zA-Z0-9_]* */
+		str += len;
+	}
+
+	if (len < 0)
+		return false; /* invalid character  */
+
+	return true;
+}
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..61b4c63682b0ccb02fcd3953848cbbf79a4cd40f
--- /dev/null
+++ b/src/box/schema_def.h
@@ -0,0 +1,211 @@
+#ifndef TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED
+#define TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "trivia/util.h"
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+enum {
+	BOX_ENGINE_MAX = 3, /* + 1 to the actual number of engines */
+	BOX_SPACE_MAX = INT32_MAX,
+	BOX_FUNCTION_MAX = 32000,
+	BOX_INDEX_MAX = 128,
+	BOX_NAME_MAX = 65000,
+	BOX_INVALID_NAME_MAX = 64,
+	ENGINE_NAME_MAX = 16,
+	FIELD_TYPE_NAME_MAX = 16,
+	GRANT_NAME_MAX = 16,
+	BOX_FIELD_MAX = INT32_MAX,
+	BOX_USER_MAX = 32,
+	/**
+	 * A fairly arbitrary limit which is still necessary
+	 * to keep tuple_format object small.
+	 */
+	BOX_INDEX_FIELD_MAX = INT16_MAX,
+	/** Yet another arbitrary limit which simply needs to
+	 * exist.
+	 */
+	BOX_INDEX_PART_MAX = UINT8_MAX
+};
+static_assert(BOX_INVALID_NAME_MAX <= BOX_NAME_MAX,
+	      "invalid name max is less than name max");
+
+/** \cond public */
+enum {
+	/** Start of the reserved range of system spaces. */
+	BOX_SYSTEM_ID_MIN = 256,
+	/** Space id of _schema. */
+	BOX_SCHEMA_ID = 272,
+	/** Space id of _space. */
+	BOX_SPACE_ID = 280,
+	/** Space id of _vspace view. */
+	BOX_VSPACE_ID = 281,
+	/** Space id of _index. */
+	BOX_INDEX_ID = 288,
+	/** Space id of _vindex view. */
+	BOX_VINDEX_ID = 289,
+	/** Space id of _func. */
+	BOX_FUNC_ID = 296,
+	/** Space id of _vfunc view. */
+	BOX_VFUNC_ID = 297,
+	/** Space id of _user. */
+	BOX_USER_ID = 304,
+	/** Space id of _vuser view. */
+	BOX_VUSER_ID = 305,
+	/** Space id of _priv. */
+	BOX_PRIV_ID = 312,
+	/** Space id of _vpriv view. */
+	BOX_VPRIV_ID = 313,
+	/** Space id of _cluster. */
+	BOX_CLUSTER_ID = 320,
+	/** Space id of _truncate. */
+	BOX_TRUNCATE_ID = 330,
+	/** End of the reserved range of system spaces. */
+	BOX_SYSTEM_ID_MAX = 511,
+	BOX_ID_NIL = 2147483647
+};
+/** \endcond public */
+
+/** _space fields. */
+enum {
+	BOX_SPACE_FIELD_ID = 0,
+	BOX_SPACE_FIELD_UID = 1,
+	BOX_SPACE_FIELD_NAME = 2,
+	BOX_SPACE_FIELD_ENGINE = 3,
+	BOX_SPACE_FIELD_FIELD_COUNT = 4,
+	BOX_SPACE_FIELD_OPTS = 5,
+};
+
+/** _index fields. */
+enum {
+	BOX_INDEX_FIELD_SPACE_ID = 0,
+	BOX_INDEX_FIELD_ID = 1,
+	BOX_INDEX_FIELD_NAME = 2,
+	BOX_INDEX_FIELD_TYPE = 3,
+	BOX_INDEX_FIELD_OPTS = 4,
+	BOX_INDEX_FIELD_IS_UNIQUE_165 = 4,
+	BOX_INDEX_FIELD_PARTS = 5,
+	BOX_INDEX_FIELD_PART_COUNT_165 = 5,
+	BOX_INDEX_FIELD_PARTS_165 = 6,
+};
+
+/** _user fields. */
+enum {
+	BOX_USER_FIELD_ID = 0,
+	BOX_USER_FIELD_UID = 1,
+	BOX_USER_FIELD_NAME = 2,
+	BOX_USER_FIELD_TYPE = 3,
+	BOX_USER_FIELD_AUTH_MECH_LIST = 4,
+};
+
+/** _priv fields. */
+enum {
+	BOX_PRIV_FIELD_ID = 0,
+	BOX_PRIV_FIELD_UID = 1,
+	BOX_PRIV_FIELD_OBJECT_TYPE = 2,
+	BOX_PRIV_FIELD_OBJECT_ID = 3,
+	BOX_PRIV_FIELD_ACCESS = 4,
+};
+
+/** _func fields. */
+enum {
+	BOX_FUNC_FIELD_ID = 0,
+	BOX_FUNC_FIELD_UID = 1,
+	BOX_FUNC_FIELD_NAME = 2,
+	BOX_FUNC_FIELD_SETUID = 3,
+	BOX_FUNC_FIELD_LANGUAGE = 4,
+};
+
+/** _schema fields. */
+enum {
+	BOX_SCHEMA_FIELD_KEY = 0,
+};
+
+/** _cluster fields. */
+enum {
+	BOX_CLUSTER_FIELD_ID = 0,
+	BOX_CLUSTER_FIELD_UUID = 1,
+};
+
+/** _truncate fields. */
+enum {
+	BOX_TRUNCATE_FIELD_SPACE_ID = 0,
+	BOX_TRUNCATE_FIELD_COUNT = 1,
+};
+
+/*
+ * Different objects which can be subject to access
+ * control.
+ *
+ * Use 0 for unknown to use the same index consistently
+ * even when there are more object types in the future.
+ */
+enum schema_object_type {
+	SC_UNKNOWN = 0, SC_UNIVERSE = 1, SC_SPACE = 2, SC_FUNCTION = 3,
+	SC_USER = 4, SC_ROLE = 5
+};
+
+enum schema_object_type
+schema_object_type(const char *name);
+
+const char *
+schema_object_name(enum schema_object_type type);
+
+/**
+ * Check object identifier for invalid symbols.
+ * The function checks \a str for matching [a-zA-Z_][a-zA-Z0-9_]* expression.
+ * Result is locale-dependent.
+ */
+bool
+identifier_is_valid(const char *str);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+#include "error.h"
+
+/**
+ * Throw an error if identifier is not valid.
+ */
+static inline void
+identifier_check_xc(const char *str)
+{
+	if (! identifier_is_valid(str))
+		tnt_raise(ClientError, ER_IDENTIFIER, str);
+}
+
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED */
diff --git a/src/box/space.cc b/src/box/space.cc
index e89afb32797d8e8418bc0890da12cabccae5025b..31fe296e9462d9d6c5b5ded3be32c965fa4b94da 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -85,7 +85,7 @@ space_new(struct space_def *def, struct rlist *key_list)
 	struct index_def *index_def;
 	MAYBE_UNUSED struct index_def *pk = rlist_empty(key_list) ? NULL :
 		rlist_first_entry(key_list, struct index_def, link);
-	def = space_def_dup(def);
+	def = space_def_dup_xc(def);
 	rlist_foreach_entry(index_def, key_list, link) {
 		index_count++;
 		index_id_max = MAX(index_id_max, index_def->iid);
diff --git a/src/box/space.h b/src/box/space.h
index b7b9fc63c8038d0762dd5bc0998159d8dfad9b8d..15e938d7a8dae744c72723fb85d658009b9521d1 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -30,8 +30,10 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "key_def.h"
+#include "user_def.h"
+#include "space_def.h"
 #include "small/rlist.h"
+#include "error.h"
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/src/box/space_def.c b/src/box/space_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..56583f1d385bd929082b07c3561ba52a30d390e0
--- /dev/null
+++ b/src/box/space_def.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "space_def.h"
+#include "diag.h"
+
+const struct space_opts space_opts_default = {
+	/* .temporary = */ false,
+};
+
+const struct opt_def space_opts_reg[] = {
+	OPT_DEF("temporary", OPT_BOOL, struct space_opts, temporary),
+	{ NULL, opt_type_MAX, 0, 0 }
+};
+
+struct space_def *
+space_def_dup(const struct space_def *src)
+{
+	size_t size = space_def_sizeof(strlen(src->name));
+	struct space_def *ret = (struct space_def *) malloc(size);
+	if (ret == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "ret");
+		return NULL;
+	}
+	memcpy(ret, src, size);
+	return ret;
+}
+
+struct space_def *
+space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+	      const char *name, uint32_t name_len,
+	      const char *engine_name, uint32_t engine_len,
+	      const struct space_opts *opts)
+{
+	size_t size = space_def_sizeof(name_len);
+	struct space_def *def = (struct space_def *) malloc(size);
+	if (def == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "def");
+		return NULL;
+	}
+	assert(name_len <= BOX_NAME_MAX);
+	assert(engine_len <= ENGINE_NAME_MAX);
+	def->id = id;
+	def->uid = uid;
+	def->exact_field_count = exact_field_count;
+	memcpy(def->name, name, name_len);
+	def->name[name_len] = 0;
+	memcpy(def->engine_name, engine_name, engine_len);
+	def->engine_name[engine_len] = 0;
+	def->opts = *opts;
+	return def;
+}
diff --git a/src/box/space_def.h b/src/box/space_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0eea5ca9427d66dab67017f58e141360c75a263
--- /dev/null
+++ b/src/box/space_def.h
@@ -0,0 +1,153 @@
+#ifndef TARANTOOL_BOX_SPACE_DEF_H_INCLUDED
+#define TARANTOOL_BOX_SPACE_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, 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 <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "trivia/util.h"
+#include "opt_def.h"
+#include "schema_def.h"
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/** Space options */
+struct space_opts {
+        /**
+	 * The space is a temporary:
+	 * - it is empty at server start
+	 * - changes are not written to WAL
+	 * - changes are not part of a snapshot
+	 */
+	bool temporary;
+};
+
+extern const struct space_opts space_opts_default;
+extern const struct opt_def space_opts_reg[];
+
+/** Space metadata. */
+struct space_def {
+	/** Space id. */
+	uint32_t id;
+	/** User id of the creator of the space */
+	uint32_t uid;
+	/**
+	 * If not set (is 0), any tuple in the
+	 * space can have any number of fields.
+	 * If set, each tuple
+	 * must have exactly this many fields.
+	 */
+	uint32_t exact_field_count;
+	char engine_name[ENGINE_NAME_MAX + 1];
+	struct space_opts opts;
+	char name[0];
+};
+
+/**
+ * Size of the space_def, calculated using its name.
+ * @param name_len Length of the space name.
+ * @retval Size in bytes.
+ */
+static inline size_t
+space_def_sizeof(uint32_t name_len)
+{
+	return sizeof(struct space_def) + name_len + 1;
+}
+
+/**
+ * Delete the space_def object.
+ * @param def Def to delete.
+ */
+static inline void
+space_def_delete(struct space_def *def)
+{
+	free(def);
+}
+
+/**
+ * Duplicate space_def object.
+ * @param src Def to duplicate.
+ * @retval Copy of the @src.
+ */
+struct space_def *
+space_def_dup(const struct space_def *src);
+
+/**
+ * Create a new space definition.
+ * @param id Space identifier.
+ * @param uid Owner identifier.
+ * @param exact_field_count Space tuples field count.
+ *        0 for any count.
+ * @param name Space name.
+ * @param name_len Length of the @name.
+ * @param engine_name Engine name.
+ * @param engine_len Length of the @engine.
+ * @param opts Space options.
+ *
+ * @retval Space definition.
+ */
+struct space_def *
+space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+	      const char *name, uint32_t name_len,
+	      const char *engine_name, uint32_t engine_len,
+	      const struct space_opts *opts);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+#include "diag.h"
+
+static inline struct space_def *
+space_def_dup_xc(const struct space_def *src)
+{
+	struct space_def *ret = space_def_dup(src);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+static inline struct space_def *
+space_def_new_xc(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+		 const char *name, uint32_t name_len,
+		 const char *engine_name, uint32_t engine_len,
+		 const struct space_opts *opts)
+{
+	struct space_def *ret = space_def_new(id, uid, exact_field_count, name,
+					      name_len, engine_name, engine_len,
+					      opts);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+#endif /* __cplusplus */
+
+#endif /* TARANTOOL_BOX_SPACE_DEF_H_INCLUDED */
diff --git a/src/box/user_def.h b/src/box/user_def.h
index 67f1d68e35c7ef863d73532ce73f3e5eb29e4582..602ac512faf8c330dbaf9f1ecce198393809ae95 100644
--- a/src/box/user_def.h
+++ b/src/box/user_def.h
@@ -30,13 +30,33 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "key_def.h" /* for SCHEMA_OBJECT_TYPE */
+#include "schema_def.h" /* for SCHEMA_OBJECT_TYPE */
 #include "scramble.h" /* for SCRAMBLE_SIZE */
+#define RB_COMPACT 1
+#include "small/rb.h"
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+/**
+ * Effective session user. A cache of user data
+ * and access stored in session and fiber local storage.
+ * Differs from the authenticated user when executing
+ * setuid functions.
+ */
+struct credentials {
+	/** A look up key to quickly find session user. */
+	uint8_t auth_token;
+	/**
+	 * Cached global grants, to avoid an extra look up
+	 * when checking global grants.
+	 */
+	uint8_t universal_access;
+	/** User id of the authenticated user. */
+	uint32_t uid;
+};
+
 enum {
 	/* SELECT */
 	PRIV_R = 1,
@@ -48,10 +68,51 @@ enum {
 	PRIV_ALL = PRIV_R + PRIV_W + PRIV_X
 };
 
+/**
+ * Definition of a privilege
+ */
+struct priv_def {
+	/** Who grants the privilege. */
+	uint32_t grantor_id;
+	/** Whom the privilege is granted. */
+	uint32_t grantee_id;
+	/* Object id - is only defined for object type */
+	uint32_t object_id;
+	/* Object type - function, space, universe */
+	enum schema_object_type object_type;
+	/**
+	 * What is being granted, has been granted, or is being
+	 * revoked.
+	 */
+	uint8_t access;
+	/** To maintain a set of effective privileges. */
+	rb_node(struct priv_def) link;
+};
+
 /* Privilege name for error messages */
 const char *
 priv_name(uint8_t access);
 
+/**
+ * Encapsulates privileges of a user on an object.
+ * I.e. "space" object has an instance of this
+ * structure for each user.
+ */
+struct access {
+	/**
+	 * Granted access has been given to a user explicitly
+	 * via some form of a grant.
+	 */
+	uint8_t granted;
+	/**
+	 * Effective access is a sum of granted access and
+	 * all privileges inherited by a user on this object
+	 * via some role. Since roles may be granted to other
+	 * roles, this may include indirect grants.
+	 */
+	uint8_t effective;
+};
+
 /**
  * A cache entry for an existing user. Entries for all existing
  * users are always present in the cache. The entry is maintained
diff --git a/src/box/vy_cache.c b/src/box/vy_cache.c
index b68e6b674350b53a9cb30a77e18014d7befdeeba..c1bba5fcd7d8ed01f4d27f6bb5a679f226599aa1 100644
--- a/src/box/vy_cache.c
+++ b/src/box/vy_cache.c
@@ -30,6 +30,7 @@
  */
 #include "vy_cache.h"
 #include "diag.h"
+#include "schema_def.h"
 
 #ifndef CT_ASSERT_G
 #define CT_ASSERT_G(e) typedef char CONCAT(__ct_assert_, __LINE__)[(e) ? 1 :-1]
diff --git a/src/box/vy_index.c b/src/box/vy_index.c
index 9e8bd1ad3f91615ccc280a26d361183404d112da..b9472e520b62901a690914627d02c0243d736f1b 100644
--- a/src/box/vy_index.c
+++ b/src/box/vy_index.c
@@ -30,28 +30,20 @@
  */
 #include "vy_index.h"
 
-#include <assert.h>
+#include "trivia/util.h"
 #include <stdbool.h>
 #include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <small/rlist.h>
-
 #include "assoc.h"
 #include "diag.h"
 #include "errcode.h"
 #include "histogram.h"
-#include "key_def.h"
+#include "index_def.h"
 #include "say.h"
 #include "schema.h"
-#include "trivia/util.h"
 #include "tuple.h"
-#include "tuple_format.h"
 #include "vy_log.h"
 #include "vy_mem.h"
 #include "vy_range.h"
diff --git a/src/box/vy_index.h b/src/box/vy_index.h
index 4a85393b70fb5a5d83efb5ff00d5319d3fd36548..f2b7c3f6c7a1a0ee301f4e4a69ba8b2aaca5e5c3 100644
--- a/src/box/vy_index.h
+++ b/src/box/vy_index.h
@@ -37,7 +37,7 @@
 
 #include <small/rlist.h>
 
-#include "key_def.h"
+#include "index_def.h"
 #define HEAP_FORWARD_DECLARATION
 #include "salad/heap.h"
 #include "vy_cache.h"
diff --git a/src/box/vy_run.h b/src/box/vy_run.h
index 772b69ab2f885ddebd68ffb95355147c737dd588..dad1092189541d5ad78aa88c81c812fa78fc9264 100644
--- a/src/box/vy_run.h
+++ b/src/box/vy_run.h
@@ -39,6 +39,7 @@
 #include "vy_stmt.h" /* for comparators */
 #include "vy_stmt_iterator.h" /* struct vy_stmt_iterator */
 #include "vy_stat.h"
+#include "index_def.h"
 
 #include "small/mempool.h"
 #include "salad/bloom.h"
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 8845258aab3979e808c4c7c52183c6ec0be86805..e30449ba47b9152241d4c2278fee635d4c22182b 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -157,6 +157,8 @@ add_executable(vy_point_iterator.test
     ${PROJECT_SOURCE_DIR}/src/box/vy_upsert.c
     ${PROJECT_SOURCE_DIR}/src/box/vy_index.c
     ${PROJECT_SOURCE_DIR}/src/box/vy_cache.c
+    ${PROJECT_SOURCE_DIR}/src/box/index_def.c
+    ${PROJECT_SOURCE_DIR}/src/box/schema_def.c
 )
 target_link_libraries(vy_point_iterator.test core tuple xrow xlog unit)