[Tarantool-patches] [PATCH v2 14/16] sql: introduce cache for prepared statemets

Nikita Pettik korablev at tarantool.org
Thu Nov 21 00:28:13 MSK 2019


This patch introduces cache (as data structure) to handle prepared
statements and a set of interface functions (insert, delete, find) to
operate on it. Cache under the hood uses LRU eviction policy. It is
implemented as a hash table with string keys (which contain original SQL
statements) and prepared statements (struct sql_stmt which is an alias
for struct Vdbe) as values. To realise LRU strategy we maintain list of
nodes: head is the newest prepared statement, tail a candidate to be
evicted. Size of cache is regulated via box.cfg{sql_cache_size}
parameter. Cache is global to all sessions. To erase session manually,
one can set its size to 0. Default cache size is assumed to be 5 Mb.

Part of #2592
---
 src/box/CMakeLists.txt          |   1 +
 src/box/box.cc                  |  20 +++++
 src/box/box.h                   |   1 +
 src/box/errcode.h               |   1 +
 src/box/lua/cfg.cc              |  12 +++
 src/box/lua/load_cfg.lua        |   3 +
 src/box/prep_stmt.c             | 186 ++++++++++++++++++++++++++++++++++++++++
 src/box/prep_stmt.h             | 112 ++++++++++++++++++++++++
 src/box/session.cc              |   1 +
 src/box/sql.c                   |   3 +
 test/app-tap/init_script.result |  37 ++++----
 test/box/admin.result           |   2 +
 test/box/cfg.result             |   7 ++
 test/box/cfg.test.lua           |   1 +
 test/box/misc.result            |   1 +
 15 files changed, 370 insertions(+), 18 deletions(-)
 create mode 100644 src/box/prep_stmt.c
 create mode 100644 src/box/prep_stmt.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 5cd5cba81..8be3d982d 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -126,6 +126,7 @@ add_library(box STATIC
     sql.c
     bind.c
     execute.c
+    prep_stmt.c
     wal.c
     call.c
     merger.c
diff --git a/src/box/box.cc b/src/box/box.cc
index b119c927b..7d0b36f13 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -74,6 +74,7 @@
 #include "call.h"
 #include "func.h"
 #include "sequence.h"
+#include "prep_stmt.h"
 
 static char status[64] = "unknown";
 
@@ -599,6 +600,15 @@ box_check_vinyl_options(void)
 	}
 }
 
+static void
+box_check_sql_cache_size(int size)
+{
+	if (size < 0) {
+		tnt_raise(ClientError, ER_CFG, "sql_cache_size",
+			  "must be non-negative");
+	}
+}
+
 void
 box_check_config()
 {
@@ -620,6 +630,7 @@ box_check_config()
 	box_check_memtx_memory(cfg_geti64("memtx_memory"));
 	box_check_memtx_min_tuple_size(cfg_geti64("memtx_min_tuple_size"));
 	box_check_vinyl_options();
+	box_check_sql_cache_size(cfg_geti("sql_cache_size"));
 }
 
 /*
@@ -886,6 +897,14 @@ box_set_net_msg_max(void)
 				IPROTO_FIBER_POOL_SIZE_FACTOR);
 }
 
+void
+box_set_prepared_stmt_cache_size(void)
+{
+	int cache_sz = cfg_geti("sql_cache_size");
+	box_check_sql_cache_size(cache_sz);
+	sql_prepared_stmt_cache_set_size(cache_sz);
+}
+
 /* }}} configuration bindings */
 
 /**
@@ -2096,6 +2115,7 @@ box_cfg_xc(void)
 	box_check_instance_uuid(&instance_uuid);
 	box_check_replicaset_uuid(&replicaset_uuid);
 
+	box_set_prepared_stmt_cache_size();
 	box_set_net_msg_max();
 	box_set_readahead();
 	box_set_too_long_threshold();
diff --git a/src/box/box.h b/src/box/box.h
index ccd527bd5..f2e88c8a9 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -235,6 +235,7 @@ void box_set_replication_sync_lag(void);
 void box_set_replication_sync_timeout(void);
 void box_set_replication_skip_conflict(void);
 void box_set_net_msg_max(void);
+void box_set_prepared_stmt_cache_size(void);
 
 extern "C" {
 #endif /* defined(__cplusplus) */
diff --git a/src/box/errcode.h b/src/box/errcode.h
index c660b1c70..ee44f61b3 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -258,6 +258,7 @@ struct errcode_record {
 	/*203 */_(ER_BOOTSTRAP_READONLY,	"Trying to bootstrap a local read-only instance as master") \
 	/*204 */_(ER_SQL_FUNC_WRONG_RET_COUNT,	"SQL expects exactly one argument returned from %s, got %d")\
 	/*205 */_(ER_FUNC_INVALID_RETURN_TYPE,	"Function '%s' returned value of invalid type: expected %s got %s") \
+	/*206 */_(ER_SQL_PREPARE,		"Failed to prepare SQL statement: %s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc
index 4884ce013..42070fe49 100644
--- a/src/box/lua/cfg.cc
+++ b/src/box/lua/cfg.cc
@@ -274,6 +274,17 @@ lbox_cfg_set_net_msg_max(struct lua_State *L)
 	return 0;
 }
 
+static int
+lbox_set_prepared_stmt_cache_size(struct lua_State *L)
+{
+	try {
+		box_set_prepared_stmt_cache_size();
+	} catch (Exception *) {
+		luaT_error(L);
+	}
+	return 0;
+}
+
 static int
 lbox_cfg_set_worker_pool_threads(struct lua_State *L)
 {
@@ -378,6 +389,7 @@ box_lua_cfg_init(struct lua_State *L)
 		{"cfg_set_replication_sync_timeout", lbox_cfg_set_replication_sync_timeout},
 		{"cfg_set_replication_skip_conflict", lbox_cfg_set_replication_skip_conflict},
 		{"cfg_set_net_msg_max", lbox_cfg_set_net_msg_max},
+		{"cfg_set_sql_cache_size", lbox_set_prepared_stmt_cache_size},
 		{NULL, NULL}
 	};
 
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 85617c8f0..4463f989c 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -81,6 +81,7 @@ local default_cfg = {
     feedback_host         = "https://feedback.tarantool.io",
     feedback_interval     = 3600,
     net_msg_max           = 768,
+    sql_cache_size        = 5 * 1024 * 1024,
 }
 
 -- types of available options
@@ -144,6 +145,7 @@ local template_cfg = {
     feedback_host         = 'string',
     feedback_interval     = 'number',
     net_msg_max           = 'number',
+    sql_cache_size        = 'number',
 }
 
 local function normalize_uri(port)
@@ -250,6 +252,7 @@ local dynamic_cfg = {
     instance_uuid           = check_instance_uuid,
     replicaset_uuid         = check_replicaset_uuid,
     net_msg_max             = private.cfg_set_net_msg_max,
+    sql_cache_size          = private.cfg_set_sql_cache_size,
 }
 
 --
diff --git a/src/box/prep_stmt.c b/src/box/prep_stmt.c
new file mode 100644
index 000000000..fe3b8244e
--- /dev/null
+++ b/src/box/prep_stmt.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2010-2019, 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 "prep_stmt.h"
+
+#include "assoc.h"
+#include "error.h"
+#include "execute.h"
+#include "session.h"
+
+/** Default cache size is 5 Mb. */
+size_t prep_stmt_cache_size = 5 * 1024 * 1024;
+
+static struct prep_stmt_cache prep_stmt_cache;
+
+void
+sql_prepared_stmt_cache_init()
+{
+	prep_stmt_cache.hash = mh_strnptr_new();
+	if (prep_stmt_cache.hash == NULL)
+		panic("out of memory");
+	prep_stmt_cache.mem_quota = prep_stmt_cache_size;
+	prep_stmt_cache.mem_used = 0;
+	rlist_create(&prep_stmt_cache.cache_lru);
+}
+
+static size_t
+sql_cache_node_sizeof(struct sql_stmt *stmt)
+{
+	return sql_stmt_sizeof(stmt) + sizeof(struct stmt_cache_node *);
+}
+
+static void
+sql_cache_node_delete(struct prep_stmt_cache *cache,
+		      struct stmt_cache_node *node)
+{
+	cache->mem_used -= sql_cache_node_sizeof(node->stmt);
+	rlist_del(&node->in_lru);
+	sql_finalize(node->stmt);
+	TRASH(node);
+	free(node);
+}
+
+void
+sql_prepared_stmt_cache_delete(struct stmt_cache_node *node)
+{
+	struct prep_stmt_cache *cache = &prep_stmt_cache;
+	const char *sql_str = sql_stmt_query_str(node->stmt);
+	mh_int_t hash_id =
+		mh_strnptr_find_inp(cache->hash, sql_str, strlen(sql_str));
+	assert(hash_id != mh_end(cache->hash));
+	mh_strnptr_del(cache->hash, hash_id, NULL);
+	sql_cache_node_delete(cache, node);
+}
+
+void
+sql_cache_stmt_refresh(struct stmt_cache_node *node)
+{
+	rlist_move_entry(&prep_stmt_cache.cache_lru, node, in_lru);
+}
+
+static void
+sql_prepared_stmt_cache_gc()
+{
+	if (rlist_empty(&prep_stmt_cache.cache_lru)) {
+		assert(prep_stmt_cache.mem_used == 0);
+		return;
+	}
+	struct stmt_cache_node *node =
+		rlist_last_entry(&prep_stmt_cache.cache_lru, struct stmt_cache_node,
+				 in_lru);
+	/*
+	 * TODO: instead of following simple LRU rule it could turn
+	 * out to be reasonable to also account value of reference
+	 * counters.
+	 */
+	sql_prepared_stmt_cache_delete(node);
+}
+
+/**
+ * Allocate new cache node containing given prepared statement.
+ * Add it to the LRU cache list. Account cache size enlargement.
+ */
+static struct stmt_cache_node *
+sql_cache_node_new(struct sql_stmt *stmt)
+{
+	struct stmt_cache_node *node = malloc(sizeof(*node));
+	if (node == NULL) {
+		diag_set(OutOfMemory, sizeof(*node), "malloc",
+			 "struct stmt_cache_node");
+		return NULL;
+	}
+	node->stmt = stmt;
+	rlist_add(&prep_stmt_cache.cache_lru, &node->in_lru);
+	prep_stmt_cache.mem_used += sql_cache_node_sizeof(stmt);
+	return node;
+}
+
+/**
+ * Return true if used memory (accounting new node) for SQL
+ * prepared statement cache does not exceed the limit.
+ */
+static bool
+sql_cache_check_new_node_size(size_t size)
+{
+	return prep_stmt_cache.mem_used + size <= prep_stmt_cache.mem_quota;
+}
+
+int
+sql_prepared_stmt_cache_insert(struct sql_stmt *stmt)
+{
+	assert(stmt != NULL);
+	struct prep_stmt_cache *cache = &prep_stmt_cache;
+	size_t new_node_size = sql_cache_node_sizeof(stmt);
+	if (new_node_size > prep_stmt_cache.mem_quota) {
+		diag_set(ClientError, ER_SQL_PREPARE, "size of statement "\
+			"exceeds cache memory limit. Please, increase SQL "\
+			"cache size");
+		return -1;
+	}
+	while (! sql_cache_check_new_node_size(new_node_size))
+		sql_prepared_stmt_cache_gc();
+	struct mh_strnptr_t *hash = cache->hash;
+	const char *sql_str = sql_stmt_query_str(stmt);
+	assert(sql_prepared_stmt_cache_find(sql_str) == NULL);
+	struct stmt_cache_node *cache_node = sql_cache_node_new(stmt);
+	if (cache_node == NULL)
+		return -1;
+	uint32_t str_hash = mh_strn_hash(sql_str, strlen(sql_str));
+	const struct mh_strnptr_node_t hash_node = { sql_str, strlen(sql_str),
+						     str_hash, cache_node };
+	struct mh_strnptr_node_t *old_node = NULL;
+	mh_int_t i = mh_strnptr_put(hash, &hash_node, &old_node, NULL);
+	if (i == mh_end(hash)) {
+		sql_cache_node_delete(cache, cache_node);
+		diag_set(OutOfMemory, 0, "mh_strnptr_put", "mh_strnptr_node");
+		return -1;
+	}
+	assert(old_node == NULL);
+	return 0;
+}
+
+struct stmt_cache_node *
+sql_prepared_stmt_cache_find(const char *sql_str)
+{
+	struct mh_strnptr_t *hash = prep_stmt_cache.hash;
+	mh_int_t stmt = mh_strnptr_find_inp(hash, sql_str, strlen(sql_str));
+	if (stmt == mh_end(hash))
+		return NULL;
+	return mh_strnptr_node(hash, stmt)->val;
+}
+
+void
+sql_prepared_stmt_cache_set_size(size_t size)
+{
+	prep_stmt_cache.mem_quota = size;
+	while (prep_stmt_cache.mem_used > size)
+		sql_prepared_stmt_cache_gc();
+}
diff --git a/src/box/prep_stmt.h b/src/box/prep_stmt.h
new file mode 100644
index 000000000..31f3ff52b
--- /dev/null
+++ b/src/box/prep_stmt.h
@@ -0,0 +1,112 @@
+#ifndef INCLUDES_PREP_STMT_H
+#define INCLUDES_PREP_STMT_H
+/*
+ * Copyright 2010-2019, 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 <stdint.h>
+#include <stdio.h>
+
+#include "small/rlist.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern size_t prep_stmt_cache_size;
+
+/**
+ * Global prepared statement cache which follows LRU
+ * eviction policy. Implemented as hash <str : stmt>
+ * and double linked list.
+ */
+struct prep_stmt_cache {
+	/** Size of memory currently occupied by prepared statements. */
+	size_t mem_used;
+	/**
+	 * Max memory size that can be used for cache.
+	 */
+	size_t mem_quota;
+	/** Query hash -> struct prepared_stmt hash.*/
+	struct mh_strnptr_t *hash;
+	struct rlist cache_lru;
+};
+
+struct stmt_cache_node {
+	/** Prepared statement itself. */
+	struct sql_stmt *stmt;
+	/**
+	 * Link to the next node. Head is the newest, tail is
+	 * a candidate to be evicted.
+	 */
+	struct rlist in_lru;
+};
+
+struct sql_stmt;
+
+/**
+ * Initialize global cache for prepared statements. Called once
+ * in sql_init().
+ */
+void
+sql_prepared_stmt_cache_init();
+
+/** Remove statement node from cache and release all resources. */
+void
+sql_prepared_stmt_cache_delete(struct stmt_cache_node *node);
+
+/**
+ * Account LRU cache node as the newest one (i.e. move to the HEAD
+ * of LRU list).
+ */
+void
+sql_cache_stmt_refresh(struct stmt_cache_node *node);
+
+/**
+ * Save prepared statement to the prepared statement cache.
+ * Account cache size change. If the cache is full (i.e. memory
+ * quota is exceeded) diag error is raised. In case of success
+ * return id of prepared statement via output parameter @id.
+ */
+int
+sql_prepared_stmt_cache_insert(struct sql_stmt *stmt);
+
+/** Find entry by SQL string. In case of search fails it returns NULL. */
+struct stmt_cache_node *
+sql_prepared_stmt_cache_find(const char *sql_str);
+
+/** Set @prep_stmt_cache_size value. */
+void
+sql_prepared_stmt_cache_set_size(size_t size);
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif
+
+#endif
diff --git a/src/box/session.cc b/src/box/session.cc
index 461d1cf25..fe33ae6b6 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -36,6 +36,7 @@
 #include "user.h"
 #include "error.h"
 #include "tt_static.h"
+#include "execute.h"
 
 const char *session_type_strs[] = {
 	"background",
diff --git a/src/box/sql.c b/src/box/sql.c
index f1df55571..3a991ccd5 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -54,6 +54,7 @@
 #include "iproto_constants.h"
 #include "fk_constraint.h"
 #include "mpstream.h"
+#include "prep_stmt.h"
 
 static sql *db = NULL;
 
@@ -74,6 +75,8 @@ sql_init()
 	if (sql_init_db(&db) != 0)
 		panic("failed to initialize SQL subsystem");
 
+	sql_prepared_stmt_cache_init();
+
 	assert(db != NULL);
 }
 
diff --git a/test/app-tap/init_script.result b/test/app-tap/init_script.result
index 799297ba0..551a0bbeb 100644
--- a/test/app-tap/init_script.result
+++ b/test/app-tap/init_script.result
@@ -31,24 +31,25 @@ box.cfg
 26	replication_sync_timeout:300
 27	replication_timeout:1
 28	slab_alloc_factor:1.05
-29	strip_core:true
-30	too_long_threshold:0.5
-31	vinyl_bloom_fpr:0.05
-32	vinyl_cache:134217728
-33	vinyl_dir:.
-34	vinyl_max_tuple_size:1048576
-35	vinyl_memory:134217728
-36	vinyl_page_size:8192
-37	vinyl_read_threads:1
-38	vinyl_run_count_per_level:2
-39	vinyl_run_size_ratio:3.5
-40	vinyl_timeout:60
-41	vinyl_write_threads:4
-42	wal_dir:.
-43	wal_dir_rescan_delay:2
-44	wal_max_size:268435456
-45	wal_mode:write
-46	worker_pool_threads:4
+29	sql_cache_size:5242880
+30	strip_core:true
+31	too_long_threshold:0.5
+32	vinyl_bloom_fpr:0.05
+33	vinyl_cache:134217728
+34	vinyl_dir:.
+35	vinyl_max_tuple_size:1048576
+36	vinyl_memory:134217728
+37	vinyl_page_size:8192
+38	vinyl_read_threads:1
+39	vinyl_run_count_per_level:2
+40	vinyl_run_size_ratio:3.5
+41	vinyl_timeout:60
+42	vinyl_write_threads:4
+43	wal_dir:.
+44	wal_dir_rescan_delay:2
+45	wal_max_size:268435456
+46	wal_mode:write
+47	worker_pool_threads:4
 --
 -- Test insert from detached fiber
 --
diff --git a/test/box/admin.result b/test/box/admin.result
index 6126f3a97..852c1cde8 100644
--- a/test/box/admin.result
+++ b/test/box/admin.result
@@ -83,6 +83,8 @@ cfg_filter(box.cfg)
     - 1
   - - slab_alloc_factor
     - 1.05
+  - - sql_cache_size
+    - 5242880
   - - strip_core
     - true
   - - too_long_threshold
diff --git a/test/box/cfg.result b/test/box/cfg.result
index 5370bb870..9542e6375 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -71,6 +71,8 @@ cfg_filter(box.cfg)
  |     - 1
  |   - - slab_alloc_factor
  |     - 1.05
+ |   - - sql_cache_size
+ |     - 5242880
  |   - - strip_core
  |     - true
  |   - - too_long_threshold
@@ -170,6 +172,8 @@ cfg_filter(box.cfg)
  |     - 1
  |   - - slab_alloc_factor
  |     - 1.05
+ |   - - sql_cache_size
+ |     - 5242880
  |   - - strip_core
  |     - true
  |   - - too_long_threshold
@@ -315,6 +319,9 @@ box.cfg{memtx_memory = box.cfg.memtx_memory}
 box.cfg{vinyl_memory = box.cfg.vinyl_memory}
  | ---
  | ...
+box.cfg{sql_cache_size = 1024}
+ | ---
+ | ...
 
 --------------------------------------------------------------------------------
 -- Test of default cfg options
diff --git a/test/box/cfg.test.lua b/test/box/cfg.test.lua
index 56ccb6767..e129568e6 100644
--- a/test/box/cfg.test.lua
+++ b/test/box/cfg.test.lua
@@ -51,6 +51,7 @@ box.cfg{replicaset_uuid = '12345678-0123-5678-1234-abcdefabcdef'}
 
 box.cfg{memtx_memory = box.cfg.memtx_memory}
 box.cfg{vinyl_memory = box.cfg.vinyl_memory}
+box.cfg{sql_cache_size = 1024}
 
 --------------------------------------------------------------------------------
 -- Test of default cfg options
diff --git a/test/box/misc.result b/test/box/misc.result
index b2930515b..78ffbf1dc 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -535,6 +535,7 @@ t;
   203: box.error.BOOTSTRAP_READONLY
   204: box.error.SQL_FUNC_WRONG_RET_COUNT
   205: box.error.FUNC_INVALID_RETURN_TYPE
+  206: box.error.SQL_PREPARE
 ...
 test_run:cmd("setopt delimiter ''");
 ---
-- 
2.15.1



More information about the Tarantool-patches mailing list