From 38b25832f9fb0c4908c0d4a463748f1e19ca0a42 Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov@tarantool.org>
Date: Fri, 22 Apr 2022 22:29:55 +0300
Subject: [PATCH] box: add space upgrade stubs

To implement online space upgrade, we need to add stub calls to the
following code paths and data structures:

 - struct space: add a pointer to space upgrade state.
 - struct space_opts: add a pointer to space upgrade definition.
 - CheckSpaceFormat::prepare: skip space check if the format is changed
   in the scope of space upgrade.
 - alter_space_do: check space upgrade state and fail alter if upgrade
   is in progress.
 - alter_space_commit: run background worker for space upgrade.
 - space_on_final_recovery_complete: restart upgrade after recovery.
 - result_processor: apply space upgrade transformations to tuples
   returned to the user by box functions.

We also need to:
 - Add a new error code ER_WRONG_SPACE_UPGRADE_OPTIONS, which we will
   use on error decoding upgrade options, stored in space options.
 - Load space upgrade Lua modules. The modules are supposed to define
   box.internal.space.upgrade method, which if available is used by
   box.schema.space.upgrade.
 - Add check_param, check_param_table and normalize_format helpers to
   box.internal, because we will use them from space.upgrade Lua code.

Note, the space upgrade state will be reference counted, because
background space upgrade may complete while some fiber is reading
from the upgraded space (there may be yields in Vinyl). For this fiber
to process the result correctly, it has to increment the reference
counter of the space upgrade state before reading from the space.

NO_DOC=ee
NO_TEST=ee
NO_CHANGELOG=ee
---
 src/box/CMakeLists.txt    |   9 +++
 src/box/alter.cc          |  13 ++++
 src/box/errcode.h         |   3 +-
 src/box/lua/init.c        |  14 ++++
 src/box/lua/schema.lua    |  15 +++++
 src/box/memtx_engine.cc   |   7 ++
 src/box/result.h          |  25 ++++++--
 src/box/space.c           |  20 ++++++
 src/box/space.h           |  11 ++++
 src/box/space_def.c       |  23 +++++++
 src/box/space_def.h       |   4 ++
 src/box/space_upgrade.c   |  46 +++++++++++++
 src/box/space_upgrade.h   | 131 ++++++++++++++++++++++++++++++++++++++
 src/trivia/config.h.cmake |   1 +
 test/box/error.result     |   1 +
 15 files changed, 315 insertions(+), 8 deletions(-)
 create mode 100644 src/box/space_upgrade.c
 create mode 100644 src/box/space_upgrade.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index c9db46dfe2..cbc354585a 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -17,6 +17,9 @@ endif()
 if(ENABLE_AUDIT_LOG)
     lua_source(lua_sources ${AUDIT_LOG_LUA_SOURCE} audit_lua)
 endif()
+if(ENABLE_SPACE_UPGRADE)
+    lua_source(lua_sources ${SPACE_UPGRADE_LUA_SOURCE} space_upgrade_lua)
+endif()
 lua_source(lua_sources lua/net_box.lua net_box_lua)
 lua_source(lua_sources lua/upgrade.lua upgrade_lua)
 lua_source(lua_sources lua/console.lua console_lua)
@@ -244,6 +247,12 @@ else()
     list(APPEND box_sources audit.c)
 endif()
 
+if(ENABLE_SPACE_UPGRADE)
+    list(APPEND box_sources ${SPACE_UPGRADE_SOURCES})
+else()
+    list(APPEND box_sources space_upgrade.c)
+endif()
+
 add_library(box STATIC ${box_sources})
 
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
diff --git a/src/box/alter.cc b/src/box/alter.cc
index caeadd2769..0683cfe2aa 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -57,6 +57,7 @@
 #include "sequence.h"
 #include "sql.h"
 #include "constraint_id.h"
+#include "space_upgrade.h"
 #include "box.h"
 
 /* {{{ Auxiliary functions and methods. */
@@ -823,6 +824,7 @@ alter_space_commit(struct trigger *trigger, void *event)
 		return -1;
 	}
 
+	struct space *space = alter->new_space;
 	alter->new_space = NULL; /* for alter_space_delete(). */
 	/*
 	 * Delete the old version of the space, we are not
@@ -831,6 +833,8 @@ alter_space_commit(struct trigger *trigger, void *event)
 	space_delete(alter->old_space);
 	alter->old_space = NULL;
 	alter_space_delete(alter);
+
+	space_upgrade_run(space);
 	return 0;
 }
 
@@ -903,6 +907,8 @@ alter_space_rollback(struct trigger *trigger, void * /* event */)
 static void
 alter_space_do(struct txn_stmt *stmt, struct alter_space *alter)
 {
+	if (space_upgrade_check_alter(alter->old_space, alter->space_def) != 0)
+		diag_raise();
 	/**
 	 * AlterSpaceOp::prepare() may perform a potentially long
 	 * lasting operation that may yield, e.g. building of a new
@@ -1027,6 +1033,13 @@ CheckSpaceFormat::prepare(struct alter_space *alter)
 	struct space *old_space = alter->old_space;
 	struct tuple_format *new_format = new_space->format;
 	struct tuple_format *old_format = old_space->format;
+	if (new_space->upgrade != NULL) {
+		/*
+		 * Tuples stored in the space will be checked against
+		 * the new format during space upgrade.
+		 */
+		return;
+	}
 	if (old_format != NULL) {
 		assert(new_format != NULL);
 		for (uint32_t i = 0; i < old_space->index_count; i++) {
diff --git a/src/box/errcode.h b/src/box/errcode.h
index 9aedaaf39b..f47dda7afa 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -292,7 +292,8 @@ struct errcode_record {
 	/*237 */_(ER_CREATE_FOREIGN_KEY,	"Failed to create foreign key '%s' in space '%s': %s") \
 	/*238 */_(ER_FOREIGN_KEY_INTEGRITY,	"Foreign key '%s' integrity check failed: %s") \
 	/*239 */_(ER_FIELD_FOREIGN_KEY_FAILED,	"Foreign key constraint '%s' failed for field '%s': %s") \
-	/*239 */_(ER_COMPLEX_FOREIGN_KEY_FAILED, "Foreign key constraint '%s' failed: %s") \
+	/*240 */_(ER_COMPLEX_FOREIGN_KEY_FAILED, "Foreign key constraint '%s' failed: %s") \
+	/*241 */_(ER_WRONG_SPACE_UPGRADE_OPTIONS, "Wrong space upgrade options: %s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index f03aea7843..92f1d75ec4 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -72,6 +72,11 @@
 
 static uint32_t CTID_STRUCT_TXN_SAVEPOINT_PTR = 0;
 
+#if ENABLE_SPACE_UPGRADE
+void
+box_lua_space_upgrade_init(struct lua_State *L);
+#endif
+
 #if ENABLE_AUDIT_LOG
 void
 box_lua_audit_init(struct lua_State *L);
@@ -86,6 +91,9 @@ extern char session_lua[],
 #if ENABLE_FEEDBACK_DAEMON
 	feedback_daemon_lua[],
 #endif
+#if ENABLE_SPACE_UPGRADE
+	space_upgrade_lua[],
+#endif
 #if ENABLE_AUDIT_LOG
 	audit_lua[],
 #endif
@@ -106,6 +114,9 @@ static const char *lua_sources[] = {
 	 */
 	"box/feedback_daemon", feedback_daemon_lua,
 #endif
+#if ENABLE_SPACE_UPGRADE
+	"box/space_upgrade", space_upgrade_lua,
+#endif
 #if ENABLE_AUDIT_LOG
 	"box/audit", audit_lua,
 #endif
@@ -491,6 +502,9 @@ box_lua_init(struct lua_State *L)
 	box_lua_xlog_init(L);
 	box_lua_sql_init(L);
 	box_lua_watcher_init(L);
+#ifdef ENABLE_SPACE_UPGRADE
+	box_lua_space_upgrade_init(L);
+#endif
 #ifdef ENABLE_AUDIT_LOG
 	box_lua_audit_init(L);
 #endif
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 7da4bd5a68..6067123f55 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -297,6 +297,7 @@ local function check_param_table(table, template)
         end
     end
 end
+box.internal.check_param_table = check_param_table
 
 --[[
  @brief Common function to check type parameter (of function)
@@ -309,6 +310,7 @@ local function check_param(param, name, should_be_type)
                   name .. " should be a " .. should_be_type)
     end
 end
+box.internal.check_param = check_param
 
 --[[
  Adds to a table key-value pairs from defaults table
@@ -707,6 +709,7 @@ local function normalize_format(space_id, space_name, format)
     end
     return result
 end
+box.internal.space.normalize_format = normalize_format -- for space.upgrade
 
 box.schema.space = {}
 box.schema.space.create = function(name, options)
@@ -808,6 +811,14 @@ function box.schema.space.format(id, format)
     end
 end
 
+function box.schema.space.upgrade(id, ...)
+    check_param(id, 'id', 'number')
+    if not box.internal.space.upgrade then
+        box.error(box.error.UNSUPPORTED, "Community edition", "space upgrade")
+    end
+    return box.internal.space.upgrade(id, ...)
+end
+
 box.schema.create_space = box.schema.space.create
 
 box.schema.space.drop = function(space_id, space_name, opts)
@@ -2530,6 +2541,10 @@ space_mt.format = function(space, format)
     check_space_arg(space, 'format')
     return box.schema.space.format(space.id, format)
 end
+space_mt.upgrade = function(space, ...)
+    check_space_arg(space, 'upgrade')
+    return box.schema.space.upgrade(space.id, ...)
+end
 space_mt.drop = function(space)
     check_space_arg(space, 'drop')
     check_space_exists(space)
diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc
index d81e937b63..3299e11545 100644
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -491,6 +491,13 @@ memtx_engine_end_recovery(struct engine *engine)
 			return -1;
 	}
 	xdir_collect_inprogress(&memtx->snap_dir);
+
+	/* Complete space initialization. */
+	int rc = space_foreach(space_on_final_recovery_complete, NULL);
+	if (rc != 0) {
+		diag_log();
+		panic("Failed to complete recovery from WAL!");
+	}
 	return 0;
 }
 
diff --git a/src/box/result.h b/src/box/result.h
index 032a8ac11d..2158ccb899 100644
--- a/src/box/result.h
+++ b/src/box/result.h
@@ -5,8 +5,11 @@
  */
 #pragma once
 
-struct space;
-struct tuple;
+#include <stddef.h>
+
+#include "space.h"
+#include "space_upgrade.h"
+#include "trivia/util.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -31,21 +34,29 @@ extern "C" {
  * must be called as well, because it may need to free some resources.
  */
 struct result_processor {
+	/** Space upgrade state or NULL. */
+	struct space_upgrade *upgrade;
 };
 
 static inline void
 result_process_prepare(struct result_processor *p, struct space *space)
 {
-	(void)p;
-	(void)space;
+	p->upgrade = space->upgrade;
+	if (unlikely(p->upgrade != NULL))
+		space_upgrade_ref(p->upgrade);
 }
 
 static inline void
 result_process(struct result_processor *p, int *rc, struct tuple **result)
 {
-	(void)p;
-	(void)rc;
-	(void)result;
+	if (likely(p->upgrade == NULL))
+		return;
+	if (*rc == 0 && *result != NULL) {
+		*result = space_upgrade_apply(p->upgrade, *result);
+		if (*result == NULL)
+			*rc = -1;
+	}
+	space_upgrade_unref(p->upgrade);
 }
 
 #if defined(__cplusplus)
diff --git a/src/box/space.c b/src/box/space.c
index dbdc371737..ed5d8c2eff 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -49,6 +49,7 @@
 #include "assoc.h"
 #include "constraint_id.h"
 #include "box.h"
+#include "space_upgrade.h"
 #include "tuple_constraint.h"
 #include "tuple_constraint_func.h"
 #include "tuple_constraint_fkey.h"
@@ -231,6 +232,13 @@ space_create(struct space *space, struct engine *engine,
 
 	space->def = space_def_dup(def);
 
+	if (space->def->opts.upgrade_def != NULL) {
+		space->upgrade = space_upgrade_new(
+			space->def->opts.upgrade_def);
+		if (space->upgrade == NULL)
+			goto fail;
+	}
+
 	/* Create indexes and fill the index map. */
 	space->index_map = (struct index **)
 		calloc(index_count + index_id_max + 1, sizeof(struct index *));
@@ -303,6 +311,8 @@ space_create(struct space *space, struct engine *engine,
 fail:
 	free(space->index_map);
 	free(space->check_unique_constraint_map);
+	if (space->upgrade != NULL)
+		space_upgrade_unref(space->upgrade);
 	if (space->def != NULL)
 		space_def_delete(space->def);
 	if (space->format != NULL) {
@@ -319,6 +329,14 @@ space_on_initial_recovery_complete(struct space *space, void *nothing)
 	return space_init_constraints(space);
 }
 
+int
+space_on_final_recovery_complete(struct space *space, void *nothing)
+{
+	(void)nothing;
+	space_upgrade_run(space);
+	return 0;
+}
+
 struct space *
 space_new(struct space_def *def, struct rlist *key_list)
 {
@@ -358,6 +376,8 @@ space_delete(struct space *space)
 	}
 	trigger_destroy(&space->before_replace);
 	trigger_destroy(&space->on_replace);
+	if (space->upgrade != NULL)
+		space_upgrade_unref(space->upgrade);
 	space_def_delete(space->def);
 	/*
 	 * SQL triggers and constraints should be deleted with
diff --git a/src/box/space.h b/src/box/space.h
index d57401125e..2d6e8b502e 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -53,6 +53,7 @@ struct tuple;
 struct tuple_format;
 struct ck_constraint;
 struct constraint_id;
+struct space_upgrade;
 
 struct space_vtab {
 	/** Free a space instance. */
@@ -247,6 +248,8 @@ struct space {
 	 * List of all tx stories in the space.
 	 */
 	struct rlist memtx_stories;
+	/** Space upgrade state or NULL. */
+	struct space_upgrade *upgrade;
 };
 
 /** Initialize a base space instance. */
@@ -271,6 +274,14 @@ space_create(struct space *space, struct engine *engine,
 int
 space_on_initial_recovery_complete(struct space *space, void *nothing);
 
+/**
+ * Finish space initialization after finishing final recovery.
+ * See the comment to space_on_initial_recovery_complete() for
+ * the function semantics and rationale.
+ */
+int
+space_on_final_recovery_complete(struct space *space, void *nothing);
+
 /** Get space ordinal number. */
 static inline uint32_t
 space_id(struct space *space) { return space->def->id; }
diff --git a/src/box/space_def.c b/src/box/space_def.c
index f618bfe5b1..31d6ba03be 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -33,6 +33,7 @@
 #include "diag.h"
 #include "error.h"
 #include "msgpuck.h"
+#include "space_upgrade.h"
 #include "tt_static.h"
 #include "tuple_constraint_def.h"
 #include "tuple_format.h"
@@ -47,6 +48,7 @@ const struct space_opts space_opts_default = {
 	/* .sql        = */ NULL,
 	/* .constraint_def = */ NULL,
 	/* .constraint_count = */ 0,
+	/* .upgrade_def = */ NULL,
 };
 
 /**
@@ -65,6 +67,14 @@ static int
 space_opts_parse_foreign_key(const char **data, void *vopts,
 			     struct region *region, uint32_t errcode);
 
+/**
+ * Callback to parse a value with 'upgrade' key in msgpack space opts
+ * definition. See function definition below.
+ */
+static int
+space_opts_parse_upgrade(const char **data, void *vopts,
+			 struct region *region, uint32_t errcode);
+
 const struct opt_def space_opts_reg[] = {
 	OPT_DEF("group_id", OPT_UINT32, struct space_opts, group_id),
 	OPT_DEF("temporary", OPT_BOOL, struct space_opts, is_temporary),
@@ -74,6 +84,7 @@ const struct opt_def space_opts_reg[] = {
 	OPT_DEF("sql", OPT_STRPTR, struct space_opts, sql),
 	OPT_DEF_CUSTOM("constraint", space_opts_parse_constraint),
 	OPT_DEF_CUSTOM("foreign_key", space_opts_parse_foreign_key),
+	OPT_DEF_CUSTOM("upgrade", space_opts_parse_upgrade),
 	OPT_DEF_LEGACY("checks"),
 	OPT_END,
 };
@@ -106,6 +117,7 @@ space_def_dup_opts(struct space_def *def, const struct space_opts *opts)
 	def->opts.constraint_def =
 		tuple_constraint_def_array_dup(opts->constraint_def,
 					       opts->constraint_count);
+	def->opts.upgrade_def = space_upgrade_def_dup(opts->upgrade_def);
 }
 
 struct space_def *
@@ -178,6 +190,7 @@ space_def_delete(struct space_def *def)
 	tuple_dictionary_unref(def->dict);
 	free(def->opts.sql);
 	free(def->opts.constraint_def);
+	space_upgrade_def_delete(def->opts.upgrade_def);
 	TRASH(def);
 	free(def);
 }
@@ -221,3 +234,13 @@ space_opts_parse_foreign_key(const char **data, void *vopts,
 						&opts->constraint_count,
 						region, errcode, true);
 }
+
+static int
+space_opts_parse_upgrade(const char **data, void *vopts,
+			 struct region *region, uint32_t errcode)
+{
+	(void)errcode;
+	struct space_opts *opts = (struct space_opts *)vopts;
+	opts->upgrade_def = space_upgrade_def_decode(data, region);
+	return opts->upgrade_def == NULL ? -1 : 0;
+}
diff --git a/src/box/space_def.h b/src/box/space_def.h
index 0f954043e0..5db266e2ad 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -40,6 +40,8 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+struct space_upgrade_def;
+
 /** Space options */
 struct space_opts {
 	/**
@@ -85,6 +87,8 @@ struct space_opts {
 	struct tuple_constraint_def *constraint_def;
 	/** Number of constraints. */
 	uint32_t constraint_count;
+	/** Space upgrade definition or NULL. */
+	struct space_upgrade_def *upgrade_def;
 };
 
 extern const struct space_opts space_opts_default;
diff --git a/src/box/space_upgrade.c b/src/box/space_upgrade.c
new file mode 100644
index 0000000000..78c37cc9e7
--- /dev/null
+++ b/src/box/space_upgrade.c
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#include "space_upgrade.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "diag.h"
+#include "error.h"
+#include "space.h"
+#include "space_def.h"
+#include "trivia/config.h"
+
+#if defined(ENABLE_SPACE_UPGRADE)
+# error unimplemented
+#endif
+
+struct space_upgrade_def *
+space_upgrade_def_decode(const char **data, struct region *region)
+{
+	(void)data;
+	(void)region;
+	diag_set(ClientError, ER_UNSUPPORTED,
+		 "Community edition", "space upgrade");
+	return NULL;
+}
+
+int
+space_upgrade_check_alter(struct space *space, struct space_def *new_def)
+{
+	assert(space->upgrade == NULL);
+	assert(new_def->opts.upgrade_def == NULL);
+	(void)space;
+	(void)new_def;
+	return 0;
+}
+
+void
+space_upgrade_run(struct space *space)
+{
+	assert(space->def->opts.upgrade_def == NULL);
+	(void)space;
+}
diff --git a/src/box/space_upgrade.h b/src/box/space_upgrade.h
new file mode 100644
index 0000000000..c322793541
--- /dev/null
+++ b/src/box/space_upgrade.h
@@ -0,0 +1,131 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#pragma once
+
+#include "trivia/config.h"
+
+#if defined(ENABLE_SPACE_UPGRADE)
+# include "space_upgrade_impl.h"
+#else /* !defined(ENABLE_SPACE_UPGRADE) */
+
+#include <stddef.h>
+
+#include "trivia/util.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct region;
+struct space;
+struct space_def;
+struct space_upgrade;
+struct space_upgrade_def;
+struct tuple;
+
+/**
+ * Decodes space upgrade definition from MsgPack data.
+ * Returns a space_upgrade_def object allocated on the region on success,
+ * NULL on error.
+ */
+struct space_upgrade_def *
+space_upgrade_def_decode(const char **data, struct region *region);
+
+/**
+ * Duplicates the given space_upgrade_def object and returns a copy.
+ * The copy is allocated on malloc. It's okay to pass NULL to this
+ * function, in which case it returns NULL. The function never fails.
+ */
+static inline struct space_upgrade_def *
+space_upgrade_def_dup(const struct space_upgrade_def *def)
+{
+	assert(def == NULL);
+	(void)def;
+	return NULL;
+}
+
+/**
+ * Frees memory occupied by a space_upgrade_def object.
+ * It's okay to pass NULL to this function.
+ */
+static inline void
+space_upgrade_def_delete(struct space_upgrade_def *def)
+{
+	assert(def == NULL);
+	(void)def;
+}
+
+/**
+ * Creates a space upgrade state from a definition.
+ * Returns NULL and sets diag on error.
+ * The reference count of the new state is set to 1.
+ */
+static inline struct space_upgrade *
+space_upgrade_new(const struct space_upgrade_def *def)
+{
+	(void)def;
+	unreachable();
+	return NULL;
+}
+
+/**
+ * Increments the reference counter of a space upgrade state,
+ * thus preventing it from being freed.
+ *
+ * We need to reference a space upgrade state if we are going to yield,
+ * because the ongoing space upgrade may complete and delete the space
+ * struct.
+ */
+static inline void
+space_upgrade_ref(struct space_upgrade *upgrade)
+{
+	(void)upgrade;
+	unreachable();
+}
+
+/**
+ * Decrements the reference counter of a space upgrade state.
+ * The state is deleted when its reference count reaches zero.
+ */
+static inline void
+space_upgrade_unref(struct space_upgrade *upgrade)
+{
+	(void)upgrade;
+	unreachable();
+}
+
+/**
+ * Applies the given space upgrade function to a tuple.
+ * Returns the new tuple on success, NULL on error.
+ * The new tuple is referenced with tuple_bless.
+ */
+static inline struct tuple *
+space_upgrade_apply(struct space_upgrade *upgrade, struct tuple *tuple)
+{
+	(void)upgrade;
+	(void)tuple;
+	unreachable();
+	return NULL;
+}
+
+/**
+ * Checks if a space alter operation may proceed.
+ * Returns -1 and sets diag if it may not.
+ */
+int
+space_upgrade_check_alter(struct space *space, struct space_def *new_def);
+
+/**
+ * Starts space upgrade in the background if required.
+ */
+void
+space_upgrade_run(struct space *space);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* !defined(ENABLE_SPACE_UPGRADE) */
diff --git a/src/trivia/config.h.cmake b/src/trivia/config.h.cmake
index 058f9e39c9..2631d6c771 100644
--- a/src/trivia/config.h.cmake
+++ b/src/trivia/config.h.cmake
@@ -264,6 +264,7 @@
 #define CACHELINE_SIZE 64
 
 #cmakedefine ENABLE_TUPLE_COMPRESSION 1
+#cmakedefine ENABLE_SPACE_UPGRADE 1
 #cmakedefine ENABLE_SSL 1
 #cmakedefine ENABLE_AUDIT_LOG 1
 #cmakedefine ENABLE_FEEDBACK_DAEMON 1
diff --git a/test/box/error.result b/test/box/error.result
index 74c6b37004..f4c5304a7a 100644
--- a/test/box/error.result
+++ b/test/box/error.result
@@ -459,6 +459,7 @@ t;
  |   238: box.error.FOREIGN_KEY_INTEGRITY
  |   239: box.error.FIELD_FOREIGN_KEY_FAILED
  |   240: box.error.COMPLEX_FOREIGN_KEY_FAILED
+ |   241: box.error.WRONG_SPACE_UPGRADE_OPTIONS
  | ...
 
 test_run:cmd("setopt delimiter ''");
-- 
GitLab