Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute
@ 2019-03-22 10:50 imeevma
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
                   ` (7 more replies)
  0 siblings, 8 replies; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

The goal of this patch-set is to make functions from execute.c
the only way to execute SQL statements. This goal includes
similar output for executed SQL statements no matter how they
were executed: through net.box or through box.

https://github.com/tarantool/tarantool/issues/3505
https://github.com/tarantool/tarantool/tree/imeevma/gh-3505-no-sql-execute

General information of difference from previous version of
patch-set:
- Function box.sql.new_execute() was changed to box.execute().
- Removed box.sql.execute(), fixed all tests.
- Removed box.debug(). all indo were moved to box.info.
- Added additional check for lua_field_inspect_table().
- Fixed bug with absent type of pragma count_changes.
- Refactoring.

v1:
https://www.freelists.org/post/tarantool-patches/PATCH-v1-0010-sql-remove-boxsqlexecute
v2:
https://www.freelists.org/post/tarantool-patches/PATCH-v2-07-Remove-boxsqlexecute
v3:
https://www.freelists.org/post/tarantool-patches/PATCH-v3-07-Remove-boxsqlexecute
v4:
https://www.freelists.org/post/tarantool-patches/PATCH-v4-05-Remove-boxsqlexecute
v5:
https://www.freelists.org/post/tarantool-patches/PATCH-v5-05-sql-remove-boxsqlexecute
v6:
https://www.freelists.org/post/tarantool-patches/PATCH-v6-05-sql-remove-boxsqlexecute
v7:
https://www.freelists.org/post/tarantool-patches/PATCH-v7-06-sql-remove-boxsqlexecute
v8:
https://www.freelists.org/post/tarantool-patches/PATCH-v8-06-sql-remove-boxsqlexecute

Mergen Imeev (7):
  sql: add column name to SQL change counter
  sql: fix error code for SQL errors in execute.c
  sql: remove box.sql.debug()
  lua: remove exceptions from function luaL_tofield()
  iproto: create port_sql
  sql: create box.execute()
  sql: remove box.sql.execute()

 src/box/CMakeLists.txt                             |   1 -
 src/box/errcode.h                                  |   1 +
 src/box/execute.c                                  | 334 +++++++--
 src/box/execute.h                                  |  49 +-
 src/box/iproto.cc                                  |  12 +-
 src/box/lua/call.c                                 |   9 +-
 src/box/lua/console.lua                            |   2 +-
 src/box/lua/info.c                                 |  11 +
 src/box/lua/init.c                                 |  25 +-
 src/box/lua/load_cfg.lua                           |  13 +-
 src/box/lua/sql.c                                  | 139 ----
 src/box/lua/sql.h                                  |  46 --
 src/box/lua/tuple.c                                |   3 +-
 src/box/port.h                                     |   1 -
 src/box/sql/delete.c                               |   2 +
 src/box/sql/insert.c                               |   2 +
 src/box/sql/update.c                               |   2 +
 src/lua/msgpack.c                                  |  12 +-
 src/lua/utils.c                                    | 112 +--
 src/lua/utils.h                                    |   8 +-
 test/box/cfg.result                                |  17 -
 test/box/cfg.test.lua                              |  10 -
 test/box/info.result                               |   1 +
 test/box/misc.result                               |   3 +-
 test/box/sql-update-with-nested-select.result      |  25 +-
 test/box/sql-update-with-nested-select.test.lua    |  14 +-
 test/sql-tap/alter.test.lua                        |   8 +-
 test/sql-tap/alter2.test.lua                       |  12 +-
 test/sql-tap/analyze1.test.lua                     |   6 +-
 test/sql-tap/analyze3.test.lua                     |   9 -
 test/sql-tap/analyze9.test.lua                     |   2 +-
 test/sql-tap/autoinc.test.lua                      |   4 +-
 test/sql-tap/between.test.lua                      |   6 +-
 test/sql-tap/check.test.lua                        |  30 +-
 test/sql-tap/delete1.test.lua                      |   4 +-
 test/sql-tap/distinct.test.lua                     |   6 +-
 test/sql-tap/fkey1.test.lua                        |   4 +-
 test/sql-tap/fkey2.test.lua                        |  92 +--
 test/sql-tap/fkey3.test.lua                        |  18 +-
 test/sql-tap/fkey4.test.lua                        |  10 +-
 test/sql-tap/func.test.lua                         |  12 +-
 test/sql-tap/gh-2723-concurrency.test.lua          |  36 +-
 test/sql-tap/gh-2931-savepoints.test.lua           |   6 +-
 .../gh-3083-ephemeral-unref-tuples.test.lua        |   4 +-
 .../gh-3251-string-pattern-comparison.test.lua     |   6 +-
 .../gh-3307-xfer-optimization-issue.test.lua       |  14 +-
 test/sql-tap/gh-3332-tuple-format-leak.test.lua    |  12 +-
 test/sql-tap/gh2140-trans.test.lua                 |  26 +-
 test/sql-tap/gh2250-trigger-chain-limit.test.lua   |  21 +-
 test/sql-tap/gh2259-in-stmt-trans.test.lua         |  42 +-
 test/sql-tap/gh2548-select-compound-limit.test.lua |  14 +-
 test/sql-tap/gh2964-abort.test.lua                 |   2 +-
 test/sql-tap/index1.test.lua                       |   2 +-
 test/sql-tap/intpkey.test.lua                      |   2 +-
 test/sql-tap/limit.test.lua                        |  12 +-
 test/sql-tap/lua/sqltester.lua                     |  47 +-
 test/sql-tap/minmax2.test.lua                      |  36 +-
 test/sql-tap/minmax3.test.lua                      |   4 +-
 test/sql-tap/misc1.test.lua                        |   2 +-
 test/sql-tap/orderby9.test.lua                     |   2 +
 test/sql-tap/pragma.test.lua                       |   8 +-
 test/sql-tap/select1.test.lua                      |  20 +-
 test/sql-tap/select9.test.lua                      |   5 +-
 test/sql-tap/selectB.test.lua                      |   4 +-
 test/sql-tap/table.test.lua                        |  20 +-
 test/sql-tap/tkt-4a03edc4c8.test.lua               |   2 +-
 test/sql-tap/trigger1.test.lua                     |  12 +-
 test/sql-tap/triggerC.test.lua                     |  14 +-
 test/sql-tap/unique.test.lua                       |  10 +-
 test/sql-tap/where2.test.lua                       |  14 +-
 test/sql-tap/whereA.test.lua                       |   4 +-
 test/sql-tap/whereD.test.lua                       |   4 +-
 test/sql-tap/with2.test.lua                        |   4 +-
 test/sql/check-clear-ephemeral.result              |  22 +-
 test/sql/check-clear-ephemeral.test.lua            |  12 +-
 test/sql/checks.result                             |   9 +-
 test/sql/checks.test.lua                           |   8 +-
 test/sql/clear.result                              |  49 +-
 test/sql/clear.test.lua                            |  20 +-
 test/sql/collation.result                          | 819 +++++++++++++++------
 test/sql/collation.test.lua                        | 350 ++++-----
 test/sql/delete-multiple-idx.result                |  44 +-
 test/sql/delete-multiple-idx.test.lua              |  24 +-
 test/sql/delete.result                             | 114 ++-
 test/sql/delete.test.lua                           |  68 +-
 test/sql/drop-index.result                         |  35 +-
 test/sql/drop-index.test.lua                       |  24 +-
 test/sql/drop-table.result                         |  44 +-
 test/sql/drop-table.test.lua                       |  32 +-
 test/sql/engine.result                             |  24 +-
 test/sql/engine.test.lua                           |  16 +-
 test/sql/errinj.result                             | 156 ++--
 test/sql/errinj.test.lua                           |  86 +--
 test/sql/foreign-keys.result                       |  58 +-
 test/sql/foreign-keys.test.lua                     |  40 +-
 test/sql/func-recreate.result                      |  11 +-
 test/sql/func-recreate.test.lua                    |   4 +-
 test/sql/gh-2347-max-int-literals.result           |  23 +-
 test/sql/gh-2347-max-int-literals.test.lua         |  10 +-
 test/sql/gh-2362-select-access-rights.result       |  45 +-
 test/sql/gh-2362-select-access-rights.test.lua     |  22 +-
 test/sql/gh-2929-primary-key.result                |  19 +-
 test/sql/gh-2929-primary-key.test.lua              |  16 +-
 test/sql/gh-2981-check-autoinc.result              |  49 +-
 test/sql/gh-2981-check-autoinc.test.lua            |  30 +-
 test/sql/gh-3199-no-mem-leaks.result               | 188 +++--
 test/sql/gh-3199-no-mem-leaks.test.lua             |  40 +-
 test/sql/gh-3613-idx-alter-update-2.result         |  18 +-
 test/sql/gh-3613-idx-alter-update-2.test.lua       |  12 +-
 test/sql/gh-3613-idx-alter-update.result           |  26 +-
 test/sql/gh-3613-idx-alter-update.test.lua         |  18 +-
 test/sql/gh-3888-values-blob-assert.result         |  47 +-
 test/sql/gh-3888-values-blob-assert.test.lua       |  22 +-
 test/sql/gh2141-delete-trigger-drop-table.result   |  46 +-
 test/sql/gh2141-delete-trigger-drop-table.test.lua |  22 +-
 test/sql/gh2251-multiple-update.result             |  49 +-
 test/sql/gh2251-multiple-update.test.lua           |  26 +-
 test/sql/gh2483-remote-persistency-check.result    |  24 +-
 test/sql/gh2483-remote-persistency-check.test.lua  |  12 +-
 .../gh2808-inline-unique-persistency-check.result  |  37 +-
 ...gh2808-inline-unique-persistency-check.test.lua |  14 +-
 test/sql/icu-upper-lower.result                    | 161 +++-
 test/sql/icu-upper-lower.test.lua                  |  10 +-
 test/sql/insert-unique.result                      |  30 +-
 test/sql/insert-unique.test.lua                    |  18 +-
 test/sql/integer-overflow.result                   |  26 +-
 test/sql/integer-overflow.test.lua                 |  24 +-
 test/sql/iproto.result                             | 118 ++-
 test/sql/iproto.test.lua                           |  34 +-
 test/sql/max-on-index.result                       |  62 +-
 test/sql/max-on-index.test.lua                     |  32 +-
 test/sql/message-func-indexes.result               |  29 +-
 test/sql/message-func-indexes.test.lua             |  22 +-
 test/sql/min-on-index.result                       |  59 --
 test/sql/misc.result                               |  23 +-
 test/sql/misc.test.lua                             |  16 +-
 test/sql/no-pk-space.result                        |  19 +-
 test/sql/no-pk-space.test.lua                      |  16 +-
 test/sql/on-conflict.result                        | 130 +++-
 test/sql/on-conflict.test.lua                      |  70 +-
 test/sql/persistency.result                        | 425 ++++++++---
 test/sql/persistency.test.lua                      | 102 +--
 test/sql/row-count.result                          | 290 ++++++--
 test/sql/row-count.test.lua                        | 112 +--
 test/sql/savepoints.result                         |  31 +-
 test/sql/savepoints.test.lua                       |  28 +-
 test/sql/select-null.result                        |  31 +-
 test/sql/select-null.test.lua                      |  14 +-
 test/sql/sql-debug.result                          |  26 +-
 test/sql/sql-debug.test.lua                        |  10 +-
 test/sql/sql-statN-index-drop.result               | 197 +++--
 test/sql/sql-statN-index-drop.test.lua             |  58 +-
 test/sql/tokenizer.result                          |   3 +-
 test/sql/tokenizer.test.lua                        |   2 +-
 test/sql/transition.result                         | 387 +++++++---
 test/sql/transition.test.lua                       |  88 +--
 test/sql/transitive-transactions.result            |  48 +-
 test/sql/transitive-transactions.test.lua          |  42 +-
 test/sql/triggers.result                           | 245 ++++--
 test/sql/triggers.test.lua                         | 144 ++--
 test/sql/types.result                              | 104 ++-
 test/sql/types.test.lua                            |  66 +-
 test/sql/update-with-nested-select.result          |  28 +-
 test/sql/update-with-nested-select.test.lua        |  16 +-
 test/sql/upgrade.result                            |  40 +-
 test/sql/upgrade.test.lua                          |  20 +-
 test/sql/view.result                               | 131 ++--
 test/sql/view.test.lua                             |  94 +--
 test/sql/view_delayed_wal.result                   |  14 +-
 test/sql/view_delayed_wal.test.lua                 |  10 +-
 test/sql/vinyl-opts.result                         |   9 +-
 test/sql/vinyl-opts.test.lua                       |   6 +-
 172 files changed, 4987 insertions(+), 3105 deletions(-)
 delete mode 100644 src/box/lua/sql.c
 delete mode 100644 src/box/lua/sql.h
 delete mode 100644 test/sql/min-on-index.result

-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:42   ` [tarantool-patches] " Konstantin Osipov
  2019-03-29 12:00   ` Kirill Yukhin
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Currently, if the count_changes pragma is enabled, the INSERT,
REPLACE and UPDATE statements will return the number of changes at
execution time. This patch sets an INTEGER type for this result.

Follow up #3832
Needed from #3505
---
 src/box/sql/delete.c     |  2 ++
 src/box/sql/insert.c     |  2 ++
 src/box/sql/update.c     |  2 ++
 test/sql/iproto.result   | 31 +++++++++++++++++++++++++++++++
 test/sql/iproto.test.lua |  8 ++++++++
 5 files changed, 45 insertions(+)

diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index f4d0334..eb1c8aa 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -422,6 +422,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		sqlVdbeSetNumCols(v, 1);
 		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows deleted",
 				      SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  delete_from_cleanup:
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6f7f020..654b282 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -788,6 +788,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		sqlVdbeSetNumCols(v, 1);
 		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows inserted",
 				      SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  insert_cleanup:
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 05ceeb4..82de09c 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -530,6 +530,8 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		sqlVdbeSetNumCols(v, 1);
 		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows updated",
 				      SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  update_cleanup:
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 938aea9..c700a80 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -942,6 +942,37 @@ res.metadata
   - name: detail
     type: TEXT
 ...
+-- When pragma count_changes is on, statements INSERT, REPLACE and
+-- UPDATE returns number of changed columns. Make sure that this
+-- result has a column type.
+cn:execute("PRAGMA count_changes = 1;")
+---
+- rowcount: 0
+...
+cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
+---
+- metadata:
+  - name: rows inserted
+    type: INTEGER
+  rows:
+  - [3]
+...
+cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
+---
+- metadata:
+  - name: rows inserted
+    type: INTEGER
+  rows:
+  - [4]
+...
+cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
+---
+- metadata:
+  - name: rows updated
+    type: INTEGER
+  rows:
+  - [0]
+...
 cn:close()
 ---
 ...
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua
index fbdc5a2..3b36cc3 100644
--- a/test/sql/iproto.test.lua
+++ b/test/sql/iproto.test.lua
@@ -289,6 +289,14 @@ res.metadata
 res = cn:execute("EXPLAIN QUERY PLAN SELECT COUNT(*) FROM t1")
 res.metadata
 
+-- When pragma count_changes is on, statements INSERT, REPLACE and
+-- UPDATE returns number of changed columns. Make sure that this
+-- result has a column type.
+cn:execute("PRAGMA count_changes = 1;")
+cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
+cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
+cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
+
 cn:close()
 box.sql.execute('DROP TABLE t1')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:45   ` [tarantool-patches] " Konstantin Osipov
                     ` (2 more replies)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma
                   ` (5 subsequent siblings)
  7 siblings, 3 replies; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Currently, functions sql_execute() and sql_prepare_and_execute()
set the ER_SQL_EXECUTE code for all errors that occur during the
execution of a SQL command. This is considered incorrect because
some of these errors may have their own error code.

In addition, all errors that do not have an error code are VDBE
errors due to issue #3965, so it makes sense to set the error
code ER_VDBE_EXECUTE for all errors without an error code.

Part of #3505
---
 src/box/errcode.h                            |  1 +
 src/box/execute.c                            | 14 ++++++++--
 test/box/misc.result                         |  1 +
 test/sql/collation.result                    |  2 +-
 test/sql/gh-2362-select-access-rights.result | 12 +++------
 test/sql/iproto.result                       | 39 ++++++++++++----------------
 6 files changed, 35 insertions(+), 34 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 7764aa3..e39a89e 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -240,6 +240,7 @@ struct errcode_record {
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
+	/*188 */_(ER_VDBE_EXECUTE,		"Error during execution of VDBE byte-code: %s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/execute.c b/src/box/execute.c
index 7c77df2..5810086 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -523,7 +523,12 @@ sql_execute(sql *db, struct sql_stmt *stmt, struct port *port,
 		assert(rc != SQL_ROW && rc != SQL_OK);
 	}
 	if (rc != SQL_DONE) {
-		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+		}
 		return -1;
 	}
 	return 0;
@@ -541,7 +546,12 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
 		return -1;
 	}
 	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
-		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+		}
 		return -1;
 	}
 	assert(stmt != NULL);
diff --git a/test/box/misc.result b/test/box/misc.result
index c350bbd..4f1116e 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -516,6 +516,7 @@ t;
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
   187: box.error.SQL_ANALYZE_ARGUMENT
+  188: box.error.VDBE_EXECUTE
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 3794990..10872ca 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -102,7 +102,7 @@ cn = remote.connect(box.cfg.listen)
 ...
 cn:execute('select 1 limit ? collate not_exist', {1})
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''COLLATE'''
+- error: Syntax error near 'COLLATE'
 ...
 cn:close()
 ---
diff --git a/test/sql/gh-2362-select-access-rights.result b/test/sql/gh-2362-select-access-rights.result
index 0e5b9bf..8f1ecfa 100644
--- a/test/sql/gh-2362-select-access-rights.result
+++ b/test/sql/gh-2362-select-access-rights.result
@@ -46,8 +46,7 @@ c = nb.connect(box.cfg.listen)
 ...
 c:execute("SELECT * FROM t1;")
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.schema.user.grant('guest','read', 'space', 'T2')
 ---
@@ -57,8 +56,7 @@ c = nb.connect(box.cfg.listen)
 ...
 c:execute('SELECT * FROM t1, t2 WHERE t1.s1 = t2.s1')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.sql.execute("CREATE VIEW v AS SELECT * FROM t1")
 ---
@@ -71,8 +69,7 @@ v = nb.connect(box.cfg.listen)
 ...
 c:execute('SELECT * FROM v')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.sql.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
 ---
@@ -85,8 +82,7 @@ v = nb.connect(box.cfg.listen)
 ...
 c:execute('INSERT INTO t3 VALUES (1, 1)')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 -- Cleanup
 box.schema.user.revoke('guest','read','space', 'V')
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index c700a80..7ad61e5 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -92,11 +92,11 @@ cn:execute('delete from test where a = 12')
 -- SQL errors.
 cn:execute('insert into not_existing_table values ("kek")')
 ---
-- error: 'Failed to execute SQL statement: Space ''NOT_EXISTING_TABLE'' does not exist'
+- error: Space 'NOT_EXISTING_TABLE' does not exist
 ...
 cn:execute('insert qwerty gjsdjq  q  qwd qmq;; q;qwd;')
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''qwerty'''
+- error: Syntax error near 'qwerty'
 ...
 -- Empty result.
 cn:execute('select id as identifier from test where a = 5;')
@@ -109,7 +109,7 @@ cn:execute('select id as identifier from test where a = 5;')
 -- netbox API errors.
 cn:execute(100)
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''100'''
+- error: Syntax error near '100'
 ...
 cn:execute('select 1', nil, {dry_run = true})
 ---
@@ -118,11 +118,11 @@ cn:execute('select 1', nil, {dry_run = true})
 -- Empty request.
 cn:execute('')
 ---
-- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
+- error: Failed to execute an empty SQL statement
 ...
 cn:execute('   ;')
 ---
-- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
+- error: Failed to execute an empty SQL statement
 ...
 --
 -- gh-3467: allow only positive integers under limit clause.
@@ -154,18 +154,15 @@ cn:execute('select * from test limit ?', {2})
 ...
 cn:execute('select * from test limit ?', {-2})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit ?', {2.7})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit ?', {'Hello'})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit 1 offset ?', {2})
 ---
@@ -181,18 +178,15 @@ cn:execute('select * from test limit 1 offset ?', {2})
 ...
 cn:execute('select * from test limit 1 offset ?', {-2})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 cn:execute('select * from test limit 1 offset ?', {2.7})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 cn:execute('select * from test limit 1 offset ?', {'Hello'})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 --
 -- Parameters binding.
@@ -363,7 +357,7 @@ sql = 'select '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?'
 ...
 cn:execute(sql)
 ---
-- error: 'Failed to execute SQL statement: too many SQL variables'
+- error: too many SQL variables
 ...
 -- Try too many parameter values.
 sql = 'select ?'
@@ -567,12 +561,11 @@ cn:execute('drop table if exists test3')
 --
 cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''?1'''
+- error: Syntax error near '?1'
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: variable number must be between $1 and
-    $65000'
+- error: variable number must be between $1 and $65000
 ...
 parameters = {}
 ---
@@ -658,8 +651,8 @@ future1:wait_result()
 future2:wait_result()
 ---
 - null
-- 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_TEST_1''
-  in space ''TEST'''
+- 'Error during execution of VDBE byte-code: Duplicate key exists in unique index
+  ''pk_unnamed_TEST_1'' in space ''TEST'''
 ...
 future3:wait_result()
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:46   ` [tarantool-patches] " Konstantin Osipov
  2019-03-29 12:02   ` Kirill Yukhin
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Due to removing of box.sql.execute() it makes sense to remove
box.sql.debug() and move SQL info to box.info.

Part or #3505
---
 src/box/lua/info.c                                 | 11 +++++++
 src/box/lua/sql.c                                  | 10 ------
 test/box/info.result                               |  1 +
 test/sql-tap/analyze3.test.lua                     |  9 ------
 test/sql-tap/between.test.lua                      |  4 +--
 .../gh-3307-xfer-optimization-issue.test.lua       |  4 +--
 test/sql-tap/lua/sqltester.lua                     |  4 +--
 test/sql-tap/minmax2.test.lua                      | 36 +++++++++++-----------
 test/sql-tap/minmax3.test.lua                      |  4 +--
 test/sql-tap/where2.test.lua                       | 14 ++++-----
 test/sql-tap/whereA.test.lua                       |  4 +--
 test/sql-tap/whereD.test.lua                       |  4 +--
 test/sql-tap/with2.test.lua                        |  4 +--
 13 files changed, 51 insertions(+), 58 deletions(-)

diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 76b5646..707cd28 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -45,6 +45,7 @@
 #include "box/gc.h"
 #include "box/engine.h"
 #include "box/vinyl.h"
+#include "box/sql.h"
 #include "main.h"
 #include "version.h"
 #include "box/box.h"
@@ -201,6 +202,15 @@ lbox_info_replication(struct lua_State *L)
 }
 
 static int
+lbox_info_sql_call(struct lua_State *L)
+{
+	struct info_handler info;
+	luaT_info_handler_create(&info, L);
+	sql_debug_info(&info);
+	return 1;
+}
+
+static int
 lbox_info_id(struct lua_State *L)
 {
 	/*
@@ -497,6 +507,7 @@ static const struct luaL_Reg lbox_info_dynamic_meta[] = {
 	{"memory", lbox_info_memory},
 	{"gc", lbox_info_gc},
 	{"vinyl", lbox_info_vinyl},
+	{"sql", lbox_info_sql_call},
 	{NULL, NULL}
 };
 
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index ee20faa..cd6e75c 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -110,21 +110,11 @@ sqlerror:
 	return lua_error(L);
 }
 
-static int
-lua_sql_debug(struct lua_State *L)
-{
-	struct info_handler info;
-	luaT_info_handler_create(&info, L);
-	sql_debug_info(&info);
-	return 1;
-}
-
 void
 box_lua_sql_init(struct lua_State *L)
 {
 	static const struct luaL_Reg module_funcs [] = {
 		{"execute", lua_sql_execute},
-		{"debug", lua_sql_debug},
 		{NULL, NULL}
 	};
 
diff --git a/test/box/info.result b/test/box/info.result
index 3384631..26377b1 100644
--- a/test/box/info.result
+++ b/test/box/info.result
@@ -83,6 +83,7 @@ t
   - replication
   - ro
   - signature
+  - sql
   - status
   - uptime
   - uuid
diff --git a/test/sql-tap/analyze3.test.lua b/test/sql-tap/analyze3.test.lua
index 1396287..dcbea1d 100755
--- a/test/sql-tap/analyze3.test.lua
+++ b/test/sql-tap/analyze3.test.lua
@@ -48,15 +48,6 @@ testprefix = "analyze3"
 --               have been fixed.
 --
 
-local function eqp(sql)
-    return test:execsql("EXPLAIN QUERY PLAN"..sql)
-end
-
-local function sf_execsql(sql, db)
-    r = test:execsql(sql)
-    return {box.sql.debug().sql_search_count, r}
-end
-
 ---------------------------------------------------------------------------
 --
 -- analyze3-1.1.1: 
diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index d56de4e..79583af 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -51,10 +51,10 @@ test:do_test(
 -- is done.  Then it appends the names of the table and index used.
 --
 local function queryplan(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sqlite_sort_count = box.info.sql.sql_sort_count
     local data = test:execsql(sql)
     local x = "nosort"
-    if box.sql.debug().sql_sort_count - sql_sort_count then
+    if box.info.sql.sql_sort_count - sqlite_sort_count then
         x = "sort"
     end
     table.insert(data,x)
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 80a2a2d..57fbca6 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -5,9 +5,9 @@ test:plan(39)
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.info.sql.sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.info.sql.sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end
diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index 8aac64c..63c5d9b 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -326,9 +326,9 @@ function test.do_select_tests(self, label, tests)
 end
 
 function test.sf_execsql(self, sql)
-    local old = box.sql.debug().sql_search_count
+    local old = box.info.sql.sql_search_count
     local r = test:execsql(sql)
-    local new = box.sql.debug().sql_search_count - old
+    local new = box.info.sql.sql_search_count - old
 
     return {new, r}
 end
diff --git a/test/sql-tap/minmax2.test.lua b/test/sql-tap/minmax2.test.lua
index a6f840d..e75709a 100755
--- a/test/sql-tap/minmax2.test.lua
+++ b/test/sql-tap/minmax2.test.lua
@@ -59,7 +59,7 @@ test:do_execsql_test(
 test:do_test(
     "minmax2-1.1",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.1>
@@ -70,13 +70,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.2",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.3",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.3>
@@ -87,14 +87,14 @@ test:do_test(
 test:do_test(
     "minmax2-1.4",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.5",
     function()
         test:execsql "CREATE INDEX t1i1 ON t1(x DESC)"
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.5>
@@ -105,13 +105,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.6",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 1)
 
 test:do_test(
     "minmax2-1.7",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.7>
@@ -122,13 +122,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.8",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-1.9",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT max(y) FROM t1"
     end, {
         -- <minmax2-1.9>
@@ -139,7 +139,7 @@ test:do_test(
 test:do_test(
     "minmax2-1.10",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
@@ -149,7 +149,7 @@ test:do_test(
             CREATE TABLE t2(a INTEGER PRIMARY KEY, b INT );
             INSERT INTO t2 SELECT x, y FROM t1;
         ]]
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT min(a) FROM t2"
     end, {
         -- <minmax2-2.0>
@@ -160,13 +160,13 @@ test:do_test(
 test:do_test(
     "minmax2-2.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-2.2",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-2.2>
@@ -177,7 +177,7 @@ test:do_test(
 test:do_test(
     "minmax2-2.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -186,7 +186,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-3.0>
@@ -197,7 +197,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -206,7 +206,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.info.sql.sql_search_count
         return test:execsql " SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) "
 
 
@@ -220,7 +220,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.info.sql.sql_search_count - sql_search_count
     end, 1)
 
 test:do_execsql_test(
diff --git a/test/sql-tap/minmax3.test.lua b/test/sql-tap/minmax3.test.lua
index 3707575..8cefd63 100755
--- a/test/sql-tap/minmax3.test.lua
+++ b/test/sql-tap/minmax3.test.lua
@@ -24,9 +24,9 @@ test:plan(47)
 --
 
 local function count(sql)
-    local sql_search_count = box.sql.debug().sql_search_count
+    local sql_search_count = box.info.sql.sql_search_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+    table.insert(r, box.info.sql.sql_search_count - sql_search_count)
     return r
 end
 
diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua
index 8eaf405..831221c 100755
--- a/test/sql-tap/where2.test.lua
+++ b/test/sql-tap/where2.test.lua
@@ -62,10 +62,10 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.info.sql.sql_sort_count
     local r = test:execsql(sql)
-    print("lol "..(box.sql.debug().sql_sort_count - sql_sort_count))
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    print("lol "..(box.info.sql.sql_sort_count - sql_sort_count))
+    table.insert(r, box.info.sql.sql_sort_count - sql_sort_count)
     return r
 end
 
@@ -78,9 +78,9 @@ end
 --
 local function cksort(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.info.sql.sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.info.sql.sql_sort_count then
             sort = 'sort'
     end
     table.insert(data, sort)
@@ -97,9 +97,9 @@ end
 --
 local function queryplan(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.info.sql.sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.info.sql.sql_sort_count then
         sort = 'sort'
     end
     table.insert(data, sort)
diff --git a/test/sql-tap/whereA.test.lua b/test/sql-tap/whereA.test.lua
index b4a0f0a..34d70ef 100755
--- a/test/sql-tap/whereA.test.lua
+++ b/test/sql-tap/whereA.test.lua
@@ -193,9 +193,9 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.info.sql.sql_sort_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    table.insert(r, box.info.sql.sql_sort_count - sql_sort_count)
     return r
 end
 
diff --git a/test/sql-tap/whereD.test.lua b/test/sql-tap/whereD.test.lua
index b103f3c..0973208 100755
--- a/test/sql-tap/whereD.test.lua
+++ b/test/sql-tap/whereD.test.lua
@@ -234,10 +234,10 @@ local function do_searchcount_test(tn, sql, res)
     test:do_test(
         tn,
         function()
-            local sql_search_count = box.sql.debug().sql_search_count
+            local sql_search_count = box.info.sql.sql_search_count
             local r = test:execsql(sql)
             table.insert(r, "search")
-            table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+            table.insert(r, box.info.sql.sql_search_count - sql_search_count)
             return r
         end,
         res)
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index ca3f00e..df6b0ec 100755
--- a/test/sql-tap/with2.test.lua
+++ b/test/sql-tap/with2.test.lua
@@ -389,9 +389,9 @@ genstmt(255), {
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.info.sql.sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.info.sql.sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
                   ` (2 preceding siblings ...)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:53   ` [tarantool-patches] " Konstantin Osipov
  2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql imeevma
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

Hi! Thank you for review. My answers and new version below.
On 1/29/19 11:42 PM, Vladislav Shpilevoy wrote:
> Hi! Thanks for the patch! See 2 comments below.
>
>> @@ -525,94 +533,95 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
>>       case LUA_TBOOLEAN:
>>           field->type = MP_BOOL;
>>           field->bval = lua_toboolean(L, index);
>> -        return;
>> +        return 0;
>>       case LUA_TNIL:
>>           field->type = MP_NIL;
>> -        return;
>> +        return 0;
>>       case LUA_TSTRING:
>>           field->sval.data = lua_tolstring(L, index, &size);
>>           field->sval.len = (uint32_t) size;
>>           field->type = MP_STR;
>> -        return;
>> +        return 0;
>>       case LUA_TTABLE:
>>       {
>>           field->compact = false;
>> -        lua_field_inspect_table(L, cfg, index, field);
>> -        return;
>> +        if (lua_field_inspect_table(L, cfg, index, field) < 0)
>> +            return -1;
>> +        return 0; 
>
> 1. Why not simply 'return lua_field_inspect_table' ?
>
Fixed.

>>       }
>>       case LUA_TLIGHTUSERDATA:
>>       case LUA_TUSERDATA:
>> diff --git a/src/lua/utils.h b/src/lua/utils.h
>> index a47e3d2..fde3514 100644
>> --- a/src/lua/utils.h
>> +++ b/src/lua/utils.h
>> @@ -345,7 +348,8 @@ static inline void
>>   luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
>>           struct luaL_field *field)
>>   {
>> -    luaL_tofield(L, cfg, idx, field);
>> +    if (luaL_tofield(L, cfg, idx, field) < 0)
>> +        luaT_error(L);
>>       if (field->type != MP_EXT)
>>           return;
>>       luaL_convertfield(L, cfg, idx, field);
>> -- 
>> 2.7.4
>>
>>
>
> 2. Looking at lua_field_inspect_table I found that if a table has __serialize
> metamethod, it is called without a protection (utils.c:409). __serialize is an
> arbitrary unprotected user code, that can throw an error deliberately. What are
> we gonna do with that? Personally I've already faced with some user code, throwing
> an error from __serialize, deliberately. On not critical errors not meaning panic.
>
> As a solution we could 1) do not care, again; 2) finally accept the fact that
> wrap into a pcall was not so bad and use it; 3) use lua_pcall in that
> particular place. Please, consult Kostja, what does he choose.

I checked it this way:

diff --git a/src/lua/utils.c b/src/lua/utils.c
index bbfc549..eae3b5f 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -398,6 +398,20 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 	lua_settop(L, top); /* remove temporary objects */
 }
 
+/**
+ * Check that the field LUAL_SERIALIZE of the metatable is
+ * available.
+ *
+ * @param L Lua State.
+ */
+static int
+get_metafield_serialize(struct lua_State *L)
+{
+	int idx = *(int *)lua_topointer(L, -1);
+	luaL_getmetafield(L, idx, LUAL_SERIALIZE);
+	return 0;
+}
+
 static int
 lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 			int idx, struct luaL_field *field)
@@ -408,6 +422,9 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 	uint32_t max = 0;
 
 	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
+	if (!cfg->encode_load_metatables &&
+	    luaT_cpcall(L, get_metafield_serialize, &idx) != 0)
+		return -1;
 	if (!cfg->encode_load_metatables ||
 	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
 		goto skip;


New version:

commit e3395eb44aee7ce47c0191291708c585de596c6a
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Jan 11 21:16:12 2019 +0300

    lua: remove exceptions from function luaL_tofield()
    
    Before this commit, the luaL_tofield() function threw a Lua
    exception when an error occurred. This behavior has been changed
    in this patch. Now it sets diag_set() and returns -1 in case of
    an error.
    
    Needed for #3505

diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 52939ae..2049ee5 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -164,7 +164,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 		 */
 		for (int i = 1; i <= nrets; ++i) {
 			struct luaL_field field;
-			luaL_tofield(L, cfg, i, &field);
+			if (luaL_tofield(L, cfg, i, &field) < 0)
+				return luaT_error(L);
 			struct tuple *tuple;
 			if (field.type == MP_EXT &&
 			    (tuple = luaT_istuple(L, i)) != NULL) {
@@ -192,7 +193,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	 * Inspect the first result
 	 */
 	struct luaL_field root;
-	luaL_tofield(L, cfg, 1, &root);
+	if (luaL_tofield(L, cfg, 1, &root) < 0)
+		return luaT_error(L);
 	struct tuple *tuple;
 	if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) {
 		/* `return box.tuple()` */
@@ -221,7 +223,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	for (uint32_t t = 1; t <= root.size; t++) {
 		lua_rawgeti(L, 1, t);
 		struct luaL_field field;
-		luaL_tofield(L, cfg, -1, &field);
+		if (luaL_tofield(L, cfg, -1, &field) < 0)
+			return luaT_error(L);
 		if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) {
 			tuple_to_mpstream(tuple, stream);
 		} else if (field.type != MP_ARRAY) {
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 60c1a39..1903ee8 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -229,7 +229,8 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
 		return tuple_to_mpstream(tuple, stream);
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, index, &field);
+	if (luaL_tofield(L, cfg, index, &field) < 0)
+		luaT_error(L);
 	if (field.type == MP_ARRAY) {
 		lua_pushvalue(L, index);
 		luamp_encode_r(L, cfg, stream, &field, 0);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index b470060..1b1874e 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -149,10 +149,12 @@ restart: /* used by MP_EXT */
 		lua_pushnil(L);  /* first key */
 		while (lua_next(L, top) != 0) {
 			lua_pushvalue(L, -2); /* push a copy of key to top */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop a copy of key */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop value */
 		}
@@ -168,7 +170,8 @@ restart: /* used by MP_EXT */
 		mpstream_encode_array(stream, size);
 		for (uint32_t i = 0; i < size; i++) {
 			lua_rawgeti(L, top, i + 1);
-			luaL_tofield(L, cfg, top + 1, field);
+			if (luaL_tofield(L, cfg, top + 1, field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1);
 		}
@@ -203,7 +206,8 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
 	}
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, lua_gettop(L), &field);
+	if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0)
+		return luaT_error(L);
 	enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0);
 
 	if (!on_top) {
diff --git a/src/lua/utils.c b/src/lua/utils.c
index a418b95..eae3b5f 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -392,12 +392,27 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 		lua_pcall(L, 1, 1, 0);
 		/* replace obj with the unpacked value */
 		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
+		if (luaL_tofield(L, cfg, idx, field) < 0)
+			luaT_error(L);
 	} /* else ignore lua_gettable exceptions */
 	lua_settop(L, top); /* remove temporary objects */
 }
 
-static void
+/**
+ * Check that the field LUAL_SERIALIZE of the metatable is
+ * available.
+ *
+ * @param L Lua State.
+ */
+static int
+get_metafield_serialize(struct lua_State *L)
+{
+	int idx = *(int *)lua_topointer(L, -1);
+	luaL_getmetafield(L, idx, LUAL_SERIALIZE);
+	return 0;
+}
+
+static int
 lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 			int idx, struct luaL_field *field)
 {
@@ -407,6 +422,9 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 	uint32_t max = 0;
 
 	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
+	if (!cfg->encode_load_metatables &&
+	    luaT_cpcall(L, get_metafield_serialize, &idx) != 0)
+		return -1;
 	if (!cfg->encode_load_metatables ||
 	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
 		goto skip;
@@ -417,10 +435,10 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 		lua_call(L, 1, 1);
 		/* replace obj with the unpacked value */
 		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
-		return;
+		return luaL_tofield(L, cfg, idx, field);
 	} else if (!lua_isstring(L, -1)) {
-		luaL_error(L, "invalid " LUAL_SERIALIZE  " value");
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		return -1;
 	}
 
 	type = lua_tostring(L, -1);
@@ -433,7 +451,7 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 			field->compact = true;
 		lua_pop(L, 1); /* type */
 
-		return;
+		return 0;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
 		field->size = luaL_maplen(L, idx);
@@ -441,9 +459,10 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-		return;
+		return 0;
 	} else {
-		luaL_error(L, "invalid " LUAL_SERIALIZE "  value");
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		return -1;
 	}
 
 skip:
@@ -465,7 +484,7 @@ skip:
 			}
 			field->type = MP_MAP;
 			field->size = size;
-			return;
+			return 0;
 		}
 		if (k > max)
 			max = k;
@@ -475,15 +494,18 @@ skip:
 	if (cfg->encode_sparse_ratio > 0 &&
 	    max > size * (uint32_t)cfg->encode_sparse_ratio &&
 	    max > (uint32_t)cfg->encode_sparse_safe) {
-		if (!cfg->encode_sparse_convert)
-			luaL_error(L, "excessively sparse array");
+		if (!cfg->encode_sparse_convert) {
+			diag_set(LuajitError, "excessively sparse array");
+			return -1;
+		}
 		field->type = MP_MAP;
 		field->size = size;
-		return;
+		return 0;
 	}
 
 	assert(field->type == MP_ARRAY);
 	field->size = max;
+	return 0;
 }
 
 static void
@@ -496,12 +518,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 	lua_call(L, 1, 1);
 	lua_replace(L, idx);
 	lua_settop(L, top);
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 }
 
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
-		 struct luaL_field *field)
+	     struct luaL_field *field)
 {
 	if (index < 0)
 		index = lua_gettop(L) + index + 1;
@@ -510,10 +533,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	double intpart;
 	size_t size;
 
-#define CHECK_NUMBER(x) ({\
-	if (!isfinite(x) && !cfg->encode_invalid_numbers) {		\
-		if (!cfg->encode_invalid_as_nil)				\
-			luaL_error(L, "number must not be NaN or Inf");		\
+#define CHECK_NUMBER(x) ({							\
+	if (!isfinite(x) && !cfg->encode_invalid_numbers) {			\
+		if (!cfg->encode_invalid_as_nil) {				\
+			diag_set(LuajitError, "number must not be NaN or Inf");	\
+			return -1;						\
+		}								\
 		field->type = MP_NIL;						\
 	}})
 
@@ -534,94 +559,93 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 			field->dval = num;
 			CHECK_NUMBER(num);
 		}
-		return;
+		return 0;
 	case LUA_TCDATA:
 	{
-		uint32_t ctypeid = 0;
-		void *cdata = luaL_checkcdata(L, index, &ctypeid);
+		GCcdata *cd = cdataV(L->base + index - 1);
+		void *cdata = (void *)cdataptr(cd);
+
 		int64_t ival;
-		switch (ctypeid) {
+		switch (cd->ctypeid) {
 		case CTID_BOOL:
 			field->type = MP_BOOL;
 			field->bval = *(bool*) cdata;
-			return;
+			return 0;
 		case CTID_CCHAR:
 		case CTID_INT8:
 			ival = *(int8_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT16:
 			ival = *(int16_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT32:
 			ival = *(int32_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT64:
 			ival = *(int64_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_UINT8:
 			field->type = MP_UINT;
 			field->ival = *(uint8_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT16:
 			field->type = MP_UINT;
 			field->ival = *(uint16_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT32:
 			field->type = MP_UINT;
 			field->ival = *(uint32_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT64:
 			field->type = MP_UINT;
 			field->ival = *(uint64_t *) cdata;
-			return;
+			return 0;
 		case CTID_FLOAT:
 			field->type = MP_FLOAT;
 			field->fval = *(float *) cdata;
 			CHECK_NUMBER(field->fval);
-			return;
+			return 0;
 		case CTID_DOUBLE:
 			field->type = MP_DOUBLE;
 			field->dval = *(double *) cdata;
 			CHECK_NUMBER(field->dval);
-			return;
+			return 0;
 		case CTID_P_CVOID:
 		case CTID_P_VOID:
 			if (*(void **) cdata == NULL) {
 				field->type = MP_NIL;
-				return;
+				return 0;
 			}
 			/* Fall through */
 		default:
 			field->type = MP_EXT;
-			return;
 		}
-		return;
+		return 0;
 	}
 	case LUA_TBOOLEAN:
 		field->type = MP_BOOL;
 		field->bval = lua_toboolean(L, index);
-		return;
+		return 0;
 	case LUA_TNIL:
 		field->type = MP_NIL;
-		return;
+		return 0;
 	case LUA_TSTRING:
 		field->sval.data = lua_tolstring(L, index, &size);
 		field->sval.len = (uint32_t) size;
 		field->type = MP_STR;
-		return;
+		return 0;
 	case LUA_TTABLE:
 	{
 		field->compact = false;
-		lua_field_inspect_table(L, cfg, index, field);
-		return;
+		return lua_field_inspect_table(L, cfg, index, field);
 	}
 	case LUA_TLIGHTUSERDATA:
 	case LUA_TUSERDATA:
@@ -629,14 +653,14 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 		field->sval.len = 0;
 		if (lua_touserdata(L, index) == NULL) {
 			field->type = MP_NIL;
-			return;
+			return 0;
 		}
 		/* Fall through */
 	default:
 		field->type = MP_EXT;
-		return;
 	}
 #undef CHECK_NUMBER
+	return 0;
 }
 
 void
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 96311b7..df4d50e 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -311,8 +311,11 @@ struct luaL_field {
  * @param cfg configuration
  * @param index stack index
  * @param field conversion result
+ *
+ * @retval  0 Success.
+ * @retval -1 Error.
  */
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	     struct luaL_field *field);
 
@@ -352,7 +355,8 @@ static inline void
 luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 		struct luaL_field *field)
 {
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 	if (field->type != MP_EXT)
 		return;
 	luaL_convertfield(L, cfg, idx, field);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
                   ` (3 preceding siblings ...)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:55   ` [tarantool-patches] " Konstantin Osipov
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 6/7] sql: create box.execute() imeevma
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch creates port_sql implementation for the port. This will
allow us to dump sql responses to obuf or to Lua. Also this patch
defines methods dump_msgpack() and destroy() of port_sql.

Part of #3505
---
 src/box/execute.c | 263 +++++++++++++++++++++++++++++++++++-------------------
 src/box/execute.h |  49 +---------
 src/box/iproto.cc |  12 ++-
 src/box/port.h    |   1 -
 4 files changed, 182 insertions(+), 143 deletions(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index 5810086..460eebc 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -83,6 +83,92 @@ struct sql_bind {
 };
 
 /**
+ * Port implementation that is used to store SQL responses and
+ * output them to obuf or Lua. This port implementation is
+ * inherited from the port_tuple structure. This allows us to use
+ * this structure in the port_tuple methods instead of port_tuple
+ * itself.
+ *
+ * The methods of port_tuple are called via explicit access to
+ * port_tuple_vtab just like C++ does with BaseClass::method, when
+ * it is called in a child method.
+ */
+struct port_sql {
+	/* port_tuple to inherit from. */
+	struct port_tuple port_tuple;
+	/* Prepared SQL statement. */
+	struct sql_stmt *stmt;
+};
+
+static_assert(sizeof(struct port_sql) <= sizeof(struct port),
+	      "sizeof(struct port_sql) must be <= sizeof(struct port)");
+
+/**
+ * Dump data from port to buffer. Data in port contains tuples,
+ * metadata, or information obtained from an executed SQL query.
+ *
+ * Dumped msgpack structure:
+ * +----------------------------------------------+
+ * | IPROTO_BODY: {                               |
+ * |     IPROTO_METADATA: [                       |
+ * |         {IPROTO_FIELD_NAME: column name1},   |
+ * |         {IPROTO_FIELD_NAME: column name2},   |
+ * |         ...                                  |
+ * |     ],                                       |
+ * |                                              |
+ * |     IPROTO_DATA: [                           |
+ * |         tuple, tuple, tuple, ...             |
+ * |     ]                                        |
+ * | }                                            |
+ * +-------------------- OR ----------------------+
+ * | IPROTO_BODY: {                               |
+ * |     IPROTO_SQL_INFO: {                       |
+ * |         SQL_INFO_ROW_COUNT: number           |
+ * |         SQL_INFO_AUTOINCREMENT_IDS: [        |
+ * |             id, id, id, ...                  |
+ * |         ]                                    |
+ * |     }                                        |
+ * | }                                            |
+ * +-------------------- OR ----------------------+
+ * | IPROTO_BODY: {                               |
+ * |     IPROTO_SQL_INFO: {                       |
+ * |         SQL_INFO_ROW_COUNT: number           |
+ * |     }                                        |
+ * | }                                            |
+ * +----------------------------------------------+
+ * @param port Port that contains SQL response.
+ * @param[out] out Output buffer.
+ *
+ * @retval  0 Success.
+ * @retval -1 Memory error.
+ */
+static int
+port_sql_dump_msgpack(struct port *port, struct obuf *out);
+
+static void
+port_sql_destroy(struct port *base)
+{
+	port_tuple_vtab.destroy(base);
+	sql_finalize(((struct port_sql *)base)->stmt);
+}
+
+static const struct port_vtab port_sql_vtab = {
+	/* .dump_msgpack = */ port_sql_dump_msgpack,
+	/* .dump_msgpack_16 = */ NULL,
+	/* .dump_lua = */ NULL,
+	/* .dump_plain = */ NULL,
+	/* .destroy = */ port_sql_destroy,
+};
+
+static void
+port_sql_create(struct port *port, struct sql_stmt *stmt)
+{
+	port_tuple_create(port);
+	((struct port_sql *)port)->stmt = stmt;
+	port->vtab = &port_sql_vtab;
+}
+
+/**
  * Return a string name of a parameter marker.
  * @param Bind to get name.
  * @retval Zero terminated name.
@@ -504,108 +590,36 @@ sql_get_description(struct sql_stmt *stmt, struct obuf *out,
 	return 0;
 }
 
-static inline int
-sql_execute(sql *db, struct sql_stmt *stmt, struct port *port,
-	    struct region *region)
-{
-	int rc, column_count = sql_column_count(stmt);
-	if (column_count > 0) {
-		/* Either ROW or DONE or ERROR. */
-		while ((rc = sql_step(stmt)) == SQL_ROW) {
-			if (sql_row_to_port(stmt, column_count, region,
-					    port) != 0)
-				return -1;
-		}
-		assert(rc == SQL_DONE || rc != SQL_OK);
-	} else {
-		/* No rows. Either DONE or ERROR. */
-		rc = sql_step(stmt);
-		assert(rc != SQL_ROW && rc != SQL_OK);
-	}
-	if (rc != SQL_DONE) {
-		if (db->errCode != SQL_TARANTOOL_ERROR) {
-			const char *err = (char *)sql_value_text(db->pErr);
-			if (err == NULL)
-				err = sqlErrStr(db->errCode);
-			diag_set(ClientError, ER_VDBE_EXECUTE, err);
-		}
-		return -1;
-	}
-	return 0;
-}
-
-int
-sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
-			uint32_t bind_count, struct sql_response *response,
-			struct region *region)
-{
-	struct sql_stmt *stmt;
-	struct sql *db = sql_get();
-	if (db == NULL) {
-		diag_set(ClientError, ER_LOADING);
-		return -1;
-	}
-	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
-		if (db->errCode != SQL_TARANTOOL_ERROR) {
-			const char *err = (char *)sql_value_text(db->pErr);
-			if (err == NULL)
-				err = sqlErrStr(db->errCode);
-			diag_set(ClientError, ER_VDBE_EXECUTE, err);
-		}
-		return -1;
-	}
-	assert(stmt != NULL);
-	port_tuple_create(&response->port);
-	response->prep_stmt = stmt;
-	if (sql_bind(stmt, bind, bind_count) == 0 &&
-	    sql_execute(db, stmt, &response->port, region) == 0)
-		return 0;
-	port_destroy(&response->port);
-	sql_finalize(stmt);
-	return -1;
-}
-
-int
-sql_response_dump(struct sql_response *response, struct obuf *out)
+static int
+port_sql_dump_msgpack(struct port *port, struct obuf *out)
 {
+	assert(port->vtab == &port_sql_vtab);
 	sql *db = sql_get();
-	struct sql_stmt *stmt = (struct sql_stmt *) response->prep_stmt;
-	struct port_tuple *port_tuple = (struct port_tuple *) &response->port;
-	int rc = 0, column_count = sql_column_count(stmt);
+	struct sql_stmt *stmt = ((struct port_sql *)port)->stmt;
+	int column_count = sql_column_count(stmt);
 	if (column_count > 0) {
 		int keys = 2;
 		int size = mp_sizeof_map(keys);
 		char *pos = (char *) obuf_alloc(out, size);
 		if (pos == NULL) {
 			diag_set(OutOfMemory, size, "obuf_alloc", "pos");
-			goto err;
+			return -1;
 		}
 		pos = mp_encode_map(pos, keys);
-		if (sql_get_description(stmt, out, column_count) != 0) {
-err:
-			rc = -1;
-			goto finish;
-		}
-		size = mp_sizeof_uint(IPROTO_DATA) +
-		       mp_sizeof_array(port_tuple->size);
+		if (sql_get_description(stmt, out, column_count) != 0)
+			return -1;
+		size = mp_sizeof_uint(IPROTO_DATA);
 		pos = (char *) obuf_alloc(out, size);
 		if (pos == NULL) {
 			diag_set(OutOfMemory, size, "obuf_alloc", "pos");
-			goto err;
+			return -1;
 		}
 		pos = mp_encode_uint(pos, IPROTO_DATA);
-		pos = mp_encode_array(pos, port_tuple->size);
-		/*
-		 * Just like SELECT, SQL uses output format compatible
-		 * with Tarantool 1.6
-		 */
-		if (port_dump_msgpack_16(&response->port, out) < 0) {
-			/* Failed port dump destroyes the port. */
-			goto err;
-		}
+		if (port_tuple_vtab.dump_msgpack(port, out) < 0)
+			return -1;
 	} else {
 		int keys = 1;
-		assert(port_tuple->size == 0);
+		assert(((struct port_tuple *)port)->size == 0);
 		struct stailq *autoinc_id_list =
 			vdbe_autoinc_id_list((struct Vdbe *)stmt);
 		uint32_t map_size = stailq_empty(autoinc_id_list) ? 1 : 2;
@@ -615,7 +629,7 @@ err:
 		char *pos = (char *) obuf_alloc(out, size);
 		if (pos == NULL) {
 			diag_set(OutOfMemory, size, "obuf_alloc", "pos");
-			goto err;
+			return -1;
 		}
 		pos = mp_encode_map(pos, keys);
 		pos = mp_encode_uint(pos, IPROTO_SQL_INFO);
@@ -638,7 +652,7 @@ err:
 		char *buf = obuf_alloc(out, size);
 		if (buf == NULL) {
 			diag_set(OutOfMemory, size, "obuf_alloc", "buf");
-			goto err;
+			return -1;
 		}
 		buf = mp_encode_uint(buf, SQL_INFO_ROW_COUNT);
 		buf = mp_encode_uint(buf, changes);
@@ -653,8 +667,75 @@ err:
 			}
 		}
 	}
-finish:
-	port_destroy(&response->port);
-	sql_finalize(stmt);
-	return rc;
+	return 0;
+}
+
+/**
+ * Execute prepared SQL statement.
+ *
+ * This function uses region to allocate memory for temporary
+ * objects. After this function, region will be in the same state
+ * in which it was before this function.
+ *
+ * @param db SQL handle.
+ * @param stmt Prepared statement.
+ * @param port Port to store SQL response.
+ * @param region Region to allocate temporary objects.
+ *
+ * @retval  0 Success.
+ * @retval -1 Error.
+ */
+static inline int
+sql_execute(sql *db, struct sql_stmt *stmt, struct port *port,
+	    struct region *region)
+{
+	int rc, column_count = sql_column_count(stmt);
+	if (column_count > 0) {
+		/* Either ROW or DONE or ERROR. */
+		while ((rc = sql_step(stmt)) == SQL_ROW) {
+			if (sql_row_to_port(stmt, column_count, region,
+					    port) != 0)
+				return -1;
+		}
+		assert(rc == SQL_DONE || rc != SQL_OK);
+	} else {
+		/* No rows. Either DONE or ERROR. */
+		rc = sql_step(stmt);
+		assert(rc != SQL_ROW && rc != SQL_OK);
+	}
+	if (rc != SQL_DONE) {
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+		}
+		return -1;
+	}
+	return 0;
+}
+
+int
+sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
+			uint32_t bind_count, struct port *port,
+			struct region *region)
+{
+	struct sql_stmt *stmt;
+	struct sql *db = sql_get();
+	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+		}
+		return -1;
+	}
+	assert(stmt != NULL);
+	port_sql_create(port, stmt);
+	if (sql_bind(stmt, bind, bind_count) == 0 &&
+	    sql_execute(db, stmt, port, region) == 0)
+		return 0;
+	port_destroy(port);
+	return -1;
 }
diff --git a/src/box/execute.h b/src/box/execute.h
index 12d893a..52563cd 100644
--- a/src/box/execute.h
+++ b/src/box/execute.h
@@ -48,18 +48,9 @@ enum sql_info_key {
 
 extern const char *sql_info_key_strs[];
 
-struct obuf;
 struct region;
 struct sql_bind;
 
-/** Response on EXECUTE request. */
-struct sql_response {
-	/** Port with response data if any. */
-	struct port port;
-	/** Prepared SQL statement with metadata. */
-	void *prep_stmt;
-};
-
 /**
  * Parse MessagePack array of SQL parameters.
  * @param data MessagePack array of parameters. Each parameter
@@ -76,48 +67,12 @@ int
 sql_bind_list_decode(const char *data, struct sql_bind **out_bind);
 
 /**
- * Dump a built response into @an out buffer. The response is
- * destroyed.
- * Response structure:
- * +----------------------------------------------+
- * | IPROTO_OK, sync, schema_version   ...        | iproto_header
- * +----------------------------------------------+---------------
- * | Body - a map with one or two keys.           |
- * |                                              |
- * | IPROTO_BODY: {                               |
- * |     IPROTO_METADATA: [                       |
- * |         {IPROTO_FIELD_NAME: column name1},   |
- * |         {IPROTO_FIELD_NAME: column name2},   | iproto_body
- * |         ...                                  |
- * |     ],                                       |
- * |                                              |
- * |     IPROTO_DATA: [                           |
- * |         tuple, tuple, tuple, ...             |
- * |     ]                                        |
- * | }                                            |
- * +-------------------- OR ----------------------+
- * | IPROTO_BODY: {                               |
- * |     IPROTO_SQL_INFO: {                       |
- * |         SQL_INFO_ROW_COUNT: number           |
- * |     }                                        |
- * | }                                            |
- * +----------------------------------------------+
- * @param response EXECUTE response.
- * @param out Output buffer.
- *
- * @retval  0 Success.
- * @retval -1 Memory error.
- */
-int
-sql_response_dump(struct sql_response *response, struct obuf *out);
-
-/**
  * Prepare and execute an SQL statement.
  * @param sql SQL statement.
  * @param len Length of @a sql.
  * @param bind Array of parameters.
  * @param bind_count Length of @a bind.
- * @param[out] response Response to store result.
+ * @param[out] port Port to store SQL response.
  * @param region Runtime allocator for temporary objects
  *        (columns, tuples ...).
  *
@@ -126,7 +81,7 @@ sql_response_dump(struct sql_response *response, struct obuf *out);
  */
 int
 sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
-			uint32_t bind_count, struct sql_response *response,
+			uint32_t bind_count, struct port *port,
 			struct region *region);
 
 #if defined(__cplusplus)
diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index 3b0ba62..1e88bf0 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -1621,7 +1621,7 @@ tx_process_sql(struct cmsg *m)
 {
 	struct iproto_msg *msg = tx_accept_msg(m);
 	struct obuf *out;
-	struct sql_response response;
+	struct port port;
 	struct sql_bind *bind;
 	int bind_count;
 	const char *sql;
@@ -1638,7 +1638,7 @@ tx_process_sql(struct cmsg *m)
 		goto error;
 	sql = msg->sql.sql_text;
 	sql = mp_decode_str(&sql, &len);
-	if (sql_prepare_and_execute(sql, len, bind, bind_count, &response,
+	if (sql_prepare_and_execute(sql, len, bind, bind_count, &port,
 				    &fiber()->gc) != 0)
 		goto error;
 	/*
@@ -1648,12 +1648,16 @@ tx_process_sql(struct cmsg *m)
 	out = msg->connection->tx.p_obuf;
 	struct obuf_svp header_svp;
 	/* Prepare memory for the iproto header. */
-	if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0)
+	if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0) {
+		port_destroy(&port);
 		goto error;
-	if (sql_response_dump(&response, out) != 0) {
+	}
+	if (port_dump_msgpack(&port, out) != 0) {
+		port_destroy(&port);
 		obuf_rollback_to_svp(out, &header_svp);
 		goto error;
 	}
+	port_destroy(&port);
 	iproto_reply_sql(out, &header_svp, msg->header.sync, schema_version);
 	iproto_wpos_create(&msg->wpos, out);
 	return;
diff --git a/src/box/port.h b/src/box/port.h
index ad1b349..f188036 100644
--- a/src/box/port.h
+++ b/src/box/port.h
@@ -65,7 +65,6 @@ extern const struct port_vtab port_tuple_vtab;
 static inline struct port_tuple *
 port_tuple(struct port *port)
 {
-	assert(port->vtab == &port_tuple_vtab);
 	return (struct port_tuple *)port;
 }
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 6/7] sql: create box.execute()
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
                   ` (4 preceding siblings ...)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-22 15:57   ` [tarantool-patches] " Konstantin Osipov
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute() imeevma
  2019-03-29 21:07 ` [tarantool-patches] Re: [PATCH v9 0/7] sql: remove box.sql.execute Vladislav Shpilevoy
  7 siblings, 1 reply; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch creates the method dump_lua() for port_sql and uses it
in the new function box.execute(). The function box.execute()
replaces box.sql.execute() in the next patch.

Part of #3505
---
 src/box/execute.c    | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/box/lua/init.c   | 23 +++++++++++++++
 test/box/misc.result |  1 +
 3 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index 460eebc..ae3f7a4 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -43,6 +43,7 @@
 #include "port.h"
 #include "tuple.h"
 #include "sql/vdbe.h"
+#include "lua/utils.h"
 
 const char *sql_type_strs[] = {
 	NULL,
@@ -145,6 +146,16 @@ static_assert(sizeof(struct port_sql) <= sizeof(struct port),
 static int
 port_sql_dump_msgpack(struct port *port, struct obuf *out);
 
+/**
+ * Dump data from port to Lua stack. Data in port contains tuples,
+ * metadata, or information obtained from an executed SQL query.
+ *
+ * @param port Port that contains SQL response.
+ * @param L Lua stack.
+ */
+static void
+port_sql_dump_lua(struct port *port, struct lua_State *L);
+
 static void
 port_sql_destroy(struct port *base)
 {
@@ -155,7 +166,7 @@ port_sql_destroy(struct port *base)
 static const struct port_vtab port_sql_vtab = {
 	/* .dump_msgpack = */ port_sql_dump_msgpack,
 	/* .dump_msgpack_16 = */ NULL,
-	/* .dump_lua = */ NULL,
+	/* .dump_lua = */ port_sql_dump_lua,
 	/* .dump_plain = */ NULL,
 	/* .destroy = */ port_sql_destroy,
 };
@@ -671,6 +682,76 @@ port_sql_dump_msgpack(struct port *port, struct obuf *out)
 }
 
 /**
+ * Serialize a description of the prepared statement.
+ *
+ * @param stmt Prepared statement.
+ * @param L Lua stack.
+ * @param column_count Statement's column count.
+ */
+static inline void
+lua_sql_get_description(struct sql_stmt *stmt, struct lua_State *L,
+			int column_count)
+{
+	assert(column_count > 0);
+	lua_createtable(L, column_count, 0);
+	for (int i = 0; i < column_count; ++i) {
+		lua_createtable(L, 0, 2);
+		const char *name = sql_column_name(stmt, i);
+		const char *type = sql_column_datatype(stmt, i);
+		/*
+		 * Can not fail, since all column names are
+		 * preallocated during prepare phase and the
+		 * column_name simply returns them.
+		 */
+		assert(name != NULL);
+		assert(type != NULL);
+		lua_pushstring(L, name);
+		lua_setfield(L, -2, "name");
+		lua_pushstring(L, type);
+		lua_setfield(L, -2, "type");
+		lua_rawseti(L, -2, i + 1);
+	}
+}
+
+static void
+port_sql_dump_lua(struct port *port, struct lua_State *L)
+{
+	assert(port->vtab == &port_sql_vtab);
+	struct sql *db = sql_get();
+	struct sql_stmt *stmt = ((struct port_sql *)port)->stmt;
+	int column_count = sql_column_count(stmt);
+	if (column_count > 0) {
+		lua_createtable(L, 0, 2);
+		lua_sql_get_description(stmt, L, column_count);
+		lua_setfield(L, -2, "metadata");
+		port_tuple_vtab.dump_lua(port, L);
+		lua_setfield(L, -2, "rows");
+	} else {
+		assert(((struct port_tuple *)port)->size == 0);
+		struct stailq *autoinc_id_list =
+			vdbe_autoinc_id_list((struct Vdbe *)stmt);
+		lua_createtable(L, 0, stailq_empty(autoinc_id_list) ? 1 : 2);
+
+		luaL_pushuint64(L, db->nChange);
+		lua_setfield(L, -2, "rowcount");
+
+		if (!stailq_empty(autoinc_id_list)) {
+			lua_newtable(L);
+			int i = 1;
+			struct autoinc_id_entry *id_entry;
+			stailq_foreach_entry(id_entry, autoinc_id_list, link) {
+				if (id_entry->id >= 0)
+					luaL_pushuint64(L, id_entry->id);
+				else
+					luaL_pushint64(L, id_entry->id);
+				lua_rawseti(L, -2, i++);
+			}
+			lua_setfield(L, -2, "autoincrement_ids");
+		}
+	}
+}
+
+/**
  * Execute prepared SQL statement.
  *
  * This function uses region to allocate memory for temporary
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 744b2c8..eb2ca31 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -40,6 +40,7 @@
 #include "box/box.h"
 #include "box/txn.h"
 #include "box/vclock.h"
+#include "box/execute.h"
 
 #include "box/lua/error.h"
 #include "box/lua/tuple.h"
@@ -266,12 +267,34 @@ lbox_backup_stop(struct lua_State *L)
 	return 0;
 }
 
+static int
+lbox_execute(struct lua_State *L)
+{
+	struct sql_bind *bind = NULL;
+	int bind_count = 0;
+	size_t length;
+	struct port port;
+	int top = lua_gettop(L);
+
+	if (top != 1 || ! lua_isstring(L, 1))
+		return luaL_error(L, "Usage: box.execute(sqlstring)");
+
+	const char *sql = lua_tolstring(L, 1, &length);
+	if (sql_prepare_and_execute(sql, length, bind, bind_count, &port,
+				    &fiber()->gc) != 0)
+		return luaT_error(L);
+	port_dump_lua(&port, L);
+	port_destroy(&port);
+	return 1;
+}
+
 static const struct luaL_Reg boxlib[] = {
 	{"commit", lbox_commit},
 	{"rollback", lbox_rollback},
 	{"on_commit", lbox_on_commit},
 	{"on_rollback", lbox_on_rollback},
 	{"snapshot", lbox_snapshot},
+	{"execute", lbox_execute},
 	{NULL, NULL}
 };
 
diff --git a/test/box/misc.result b/test/box/misc.result
index 4f1116e..714d5de 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -63,6 +63,7 @@ t
   - commit
   - ctl
   - error
+  - execute
   - feedback
   - index
   - info
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute()
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
                   ` (5 preceding siblings ...)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 6/7] sql: create box.execute() imeevma
@ 2019-03-22 10:50 ` imeevma
  2019-03-26 21:48   ` [tarantool-patches] " Vladislav Shpilevoy
  2019-03-29 21:07 ` [tarantool-patches] Re: [PATCH v9 0/7] sql: remove box.sql.execute Vladislav Shpilevoy
  7 siblings, 1 reply; 36+ messages in thread
From: imeevma @ 2019-03-22 10:50 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

This patch replaces box.sql.execute() by box.execute() in tests
and removes box.sql.execute() from code.

Closes #3505
---
 src/box/CMakeLists.txt                             |   1 -
 src/box/lua/console.lua                            |   2 +-
 src/box/lua/init.c                                 |   2 -
 src/box/lua/load_cfg.lua                           |  13 +-
 src/box/lua/sql.c                                  | 129 ----
 src/box/lua/sql.h                                  |  46 --
 test/box/cfg.result                                |  17 -
 test/box/cfg.test.lua                              |  10 -
 test/box/misc.result                               |   1 -
 test/box/sql-update-with-nested-select.result      |  25 +-
 test/box/sql-update-with-nested-select.test.lua    |  14 +-
 test/sql-tap/alter.test.lua                        |   8 +-
 test/sql-tap/alter2.test.lua                       |  12 +-
 test/sql-tap/analyze1.test.lua                     |   6 +-
 test/sql-tap/analyze9.test.lua                     |   2 +-
 test/sql-tap/autoinc.test.lua                      |   4 +-
 test/sql-tap/between.test.lua                      |   2 +-
 test/sql-tap/check.test.lua                        |  30 +-
 test/sql-tap/delete1.test.lua                      |   4 +-
 test/sql-tap/distinct.test.lua                     |   6 +-
 test/sql-tap/fkey1.test.lua                        |   4 +-
 test/sql-tap/fkey2.test.lua                        |  92 +--
 test/sql-tap/fkey3.test.lua                        |  18 +-
 test/sql-tap/fkey4.test.lua                        |  10 +-
 test/sql-tap/func.test.lua                         |  12 +-
 test/sql-tap/gh-2723-concurrency.test.lua          |  36 +-
 test/sql-tap/gh-2931-savepoints.test.lua           |   6 +-
 .../gh-3083-ephemeral-unref-tuples.test.lua        |   4 +-
 .../gh-3251-string-pattern-comparison.test.lua     |   6 +-
 .../gh-3307-xfer-optimization-issue.test.lua       |  10 +-
 test/sql-tap/gh-3332-tuple-format-leak.test.lua    |  12 +-
 test/sql-tap/gh2140-trans.test.lua                 |  26 +-
 test/sql-tap/gh2250-trigger-chain-limit.test.lua   |  21 +-
 test/sql-tap/gh2259-in-stmt-trans.test.lua         |  42 +-
 test/sql-tap/gh2548-select-compound-limit.test.lua |  14 +-
 test/sql-tap/gh2964-abort.test.lua                 |   2 +-
 test/sql-tap/index1.test.lua                       |   2 +-
 test/sql-tap/intpkey.test.lua                      |   2 +-
 test/sql-tap/limit.test.lua                        |  12 +-
 test/sql-tap/lua/sqltester.lua                     |  43 +-
 test/sql-tap/misc1.test.lua                        |   2 +-
 test/sql-tap/orderby9.test.lua                     |   2 +
 test/sql-tap/pragma.test.lua                       |   8 +-
 test/sql-tap/select1.test.lua                      |  20 +-
 test/sql-tap/select9.test.lua                      |   5 +-
 test/sql-tap/selectB.test.lua                      |   4 +-
 test/sql-tap/table.test.lua                        |  20 +-
 test/sql-tap/tkt-4a03edc4c8.test.lua               |   2 +-
 test/sql-tap/trigger1.test.lua                     |  12 +-
 test/sql-tap/triggerC.test.lua                     |  14 +-
 test/sql-tap/unique.test.lua                       |  10 +-
 test/sql/check-clear-ephemeral.result              |  22 +-
 test/sql/check-clear-ephemeral.test.lua            |  12 +-
 test/sql/checks.result                             |   9 +-
 test/sql/checks.test.lua                           |   8 +-
 test/sql/clear.result                              |  49 +-
 test/sql/clear.test.lua                            |  20 +-
 test/sql/collation.result                          | 817 +++++++++++++++------
 test/sql/collation.test.lua                        | 350 ++++-----
 test/sql/delete-multiple-idx.result                |  44 +-
 test/sql/delete-multiple-idx.test.lua              |  24 +-
 test/sql/delete.result                             | 114 ++-
 test/sql/delete.test.lua                           |  68 +-
 test/sql/drop-index.result                         |  35 +-
 test/sql/drop-index.test.lua                       |  24 +-
 test/sql/drop-table.result                         |  44 +-
 test/sql/drop-table.test.lua                       |  32 +-
 test/sql/engine.result                             |  24 +-
 test/sql/engine.test.lua                           |  16 +-
 test/sql/errinj.result                             | 156 ++--
 test/sql/errinj.test.lua                           |  86 +--
 test/sql/foreign-keys.result                       |  58 +-
 test/sql/foreign-keys.test.lua                     |  40 +-
 test/sql/func-recreate.result                      |  11 +-
 test/sql/func-recreate.test.lua                    |   4 +-
 test/sql/gh-2347-max-int-literals.result           |  23 +-
 test/sql/gh-2347-max-int-literals.test.lua         |  10 +-
 test/sql/gh-2362-select-access-rights.result       |  33 +-
 test/sql/gh-2362-select-access-rights.test.lua     |  22 +-
 test/sql/gh-2929-primary-key.result                |  19 +-
 test/sql/gh-2929-primary-key.test.lua              |  16 +-
 test/sql/gh-2981-check-autoinc.result              |  49 +-
 test/sql/gh-2981-check-autoinc.test.lua            |  30 +-
 test/sql/gh-3199-no-mem-leaks.result               | 188 +++--
 test/sql/gh-3199-no-mem-leaks.test.lua             |  40 +-
 test/sql/gh-3613-idx-alter-update-2.result         |  18 +-
 test/sql/gh-3613-idx-alter-update-2.test.lua       |  12 +-
 test/sql/gh-3613-idx-alter-update.result           |  26 +-
 test/sql/gh-3613-idx-alter-update.test.lua         |  18 +-
 test/sql/gh-3888-values-blob-assert.result         |  47 +-
 test/sql/gh-3888-values-blob-assert.test.lua       |  22 +-
 test/sql/gh2141-delete-trigger-drop-table.result   |  46 +-
 test/sql/gh2141-delete-trigger-drop-table.test.lua |  22 +-
 test/sql/gh2251-multiple-update.result             |  49 +-
 test/sql/gh2251-multiple-update.test.lua           |  26 +-
 test/sql/gh2483-remote-persistency-check.result    |  24 +-
 test/sql/gh2483-remote-persistency-check.test.lua  |  12 +-
 .../gh2808-inline-unique-persistency-check.result  |  37 +-
 ...gh2808-inline-unique-persistency-check.test.lua |  14 +-
 test/sql/icu-upper-lower.result                    | 161 +++-
 test/sql/icu-upper-lower.test.lua                  |  10 +-
 test/sql/insert-unique.result                      |  30 +-
 test/sql/insert-unique.test.lua                    |  18 +-
 test/sql/integer-overflow.result                   |  26 +-
 test/sql/integer-overflow.test.lua                 |  24 +-
 test/sql/iproto.result                             |  48 +-
 test/sql/iproto.test.lua                           |  26 +-
 test/sql/max-on-index.result                       |  62 +-
 test/sql/max-on-index.test.lua                     |  32 +-
 test/sql/message-func-indexes.result               |  29 +-
 test/sql/message-func-indexes.test.lua             |  22 +-
 test/sql/min-on-index.result                       |  59 --
 test/sql/misc.result                               |  23 +-
 test/sql/misc.test.lua                             |  16 +-
 test/sql/no-pk-space.result                        |  19 +-
 test/sql/no-pk-space.test.lua                      |  16 +-
 test/sql/on-conflict.result                        | 130 +++-
 test/sql/on-conflict.test.lua                      |  70 +-
 test/sql/persistency.result                        | 425 ++++++++---
 test/sql/persistency.test.lua                      | 102 +--
 test/sql/row-count.result                          | 290 ++++++--
 test/sql/row-count.test.lua                        | 112 +--
 test/sql/savepoints.result                         |  31 +-
 test/sql/savepoints.test.lua                       |  28 +-
 test/sql/select-null.result                        |  31 +-
 test/sql/select-null.test.lua                      |  14 +-
 test/sql/sql-debug.result                          |  26 +-
 test/sql/sql-debug.test.lua                        |  10 +-
 test/sql/sql-statN-index-drop.result               | 197 +++--
 test/sql/sql-statN-index-drop.test.lua             |  58 +-
 test/sql/tokenizer.result                          |   3 +-
 test/sql/tokenizer.test.lua                        |   2 +-
 test/sql/transition.result                         | 387 +++++++---
 test/sql/transition.test.lua                       |  88 +--
 test/sql/transitive-transactions.result            |  48 +-
 test/sql/transitive-transactions.test.lua          |  42 +-
 test/sql/triggers.result                           | 245 ++++--
 test/sql/triggers.test.lua                         | 144 ++--
 test/sql/types.result                              | 104 ++-
 test/sql/types.test.lua                            |  66 +-
 test/sql/update-with-nested-select.result          |  28 +-
 test/sql/update-with-nested-select.test.lua        |  16 +-
 test/sql/upgrade.result                            |  40 +-
 test/sql/upgrade.test.lua                          |  20 +-
 test/sql/view.result                               | 131 ++--
 test/sql/view.test.lua                             |  94 +--
 test/sql/view_delayed_wal.result                   |  14 +-
 test/sql/view_delayed_wal.test.lua                 |  10 +-
 test/sql/vinyl-opts.result                         |   9 +-
 test/sql/vinyl-opts.test.lua                       |   6 +-
 150 files changed, 4491 insertions(+), 2828 deletions(-)
 delete mode 100644 src/box/lua/sql.c
 delete mode 100644 src/box/lua/sql.h
 delete mode 100644 test/sql/min-on-index.result

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 59e91b6..7675156 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -138,7 +138,6 @@ add_library(box STATIC
     lua/session.c
     lua/net_box.c
     lua/xlog.c
-    lua/sql.c
     ${bin_sources})
 
 target_link_libraries(box box_error tuple stat xrow xlog vclock crc32 scramble
diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua
index 33aefd6..f922f03 100644
--- a/src/box/lua/console.lua
+++ b/src/box/lua/console.lua
@@ -135,7 +135,7 @@ local function local_eval(storage, line)
         return preprocess(storage, line:sub(2))
     end
     if storage.language == 'sql' then
-        return format(pcall(box.sql.execute, line))
+        return format(pcall(box.execute, line))
     end
     --
     -- Attempt to append 'return ' before the chunk: if the chunk is
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index eb2ca31..af1bcdf 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -59,7 +59,6 @@
 #include "box/lua/xlog.h"
 #include "box/lua/console.h"
 #include "box/lua/tuple.h"
-#include "box/lua/sql.h"
 
 extern char session_lua[],
 	tuple_lua[],
@@ -330,7 +329,6 @@ box_lua_init(struct lua_State *L)
 	box_lua_ctl_init(L);
 	box_lua_session_init(L);
 	box_lua_xlog_init(L);
-	box_lua_sql_init(L);
 	luaopen_net_box(L);
 	lua_pop(L, 1);
 	tarantool_lua_console_init(L);
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 6c9a820..5530b2c 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -505,17 +505,14 @@ end
 box.cfg = locked(load_cfg)
 
 --
--- This makes possible do box.sql.execute without calling box.cfg
+-- This makes possible do box.execute without calling box.cfg
 -- manually. The load_cfg call overwrites following table and
 -- metatable.
 --
-box.sql = {}
-setmetatable(box.sql, {
-    __index = function(table, index)
-        load_cfg()
-        return box.sql[index]
-    end,
-})
+function box.execute(...)
+    load_cfg()
+    return box.execute(...)
+end
 
 -- gh-810:
 -- hack luajit default cpath
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
deleted file mode 100644
index cd6e75c..0000000
--- a/src/box/lua/sql.c
+++ /dev/null
@@ -1,129 +0,0 @@
-#include "sql.h"
-#include "box/sql.h"
-#include "lua/msgpack.h"
-
-#include "box/sql/sqlInt.h"
-#include "info/info.h"
-#include "lua/info.h"
-#include "lua/utils.h"
-
-static void
-lua_push_column_names(struct lua_State *L, struct sql_stmt *stmt)
-{
-	int column_count = sql_column_count(stmt);
-	lua_createtable(L, column_count, 0);
-	for (int i = 0; i < column_count; i++) {
-		const char *name = sql_column_name(stmt, i);
-		lua_pushstring(L, name == NULL ? "" : name);
-		lua_rawseti(L, -2, i+1);
-	}
-}
-
-static void
-lua_push_row(struct lua_State *L, struct sql_stmt *stmt)
-{
-	int column_count = sql_column_count(stmt);
-
-	lua_createtable(L, column_count, 0);
-	lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_array_metatable_ref);
-	lua_setmetatable(L, -2);
-
-	for (int i = 0; i < column_count; i++) {
-		int type = sql_column_type(stmt, i);
-		switch (type) {
-		case SQL_INTEGER:
-			luaL_pushint64(L, sql_column_int64(stmt, i));
-			break;
-		case SQL_FLOAT:
-			lua_pushnumber(L, sql_column_double(stmt, i));
-			break;
-		case SQL_TEXT: {
-			const void *text = sql_column_text(stmt, i);
-			lua_pushlstring(L, text,
-					sql_column_bytes(stmt, i));
-			break;
-		}
-		case SQL_BLOB: {
-			const void *blob = sql_column_blob(stmt, i);
-			if (sql_column_subtype(stmt,i) == SQL_SUBTYPE_MSGPACK) {
-				luamp_decode(L, luaL_msgpack_default,
-					     (const char **)&blob);
-			} else {
-				lua_pushlstring(L, blob,
-					sql_column_bytes(stmt, i));
-			}
-			break;
-		}
-		case SQL_NULL:
-			lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_nil_ref);
-			break;
-		default:
-			assert(0);
-		}
-		lua_rawseti(L, -2, i+1);
-	}
-}
-
-static int
-lua_sql_execute(struct lua_State *L)
-{
-	sql *db = sql_get();
-	if (db == NULL)
-		return luaL_error(L, "not ready");
-
-	size_t length;
-	const char *sql = lua_tolstring(L, 1, &length);
-	if (sql == NULL)
-		return luaL_error(L, "usage: box.sql.execute(sqlstring)");
-
-	struct sql_stmt *stmt;
-	if (sql_prepare_v2(db, sql, length, &stmt, &sql) != SQL_OK)
-		goto sqlerror;
-	assert(stmt != NULL);
-
-	int rc;
-	int retval_count;
-	if (sql_column_count(stmt) == 0) {
-		while ((rc = sql_step(stmt)) == SQL_ROW);
-		retval_count = 0;
-	} else {
-		lua_newtable(L);
-		lua_pushvalue(L, lua_upvalueindex(1));
-		lua_setmetatable(L, -2);
-		lua_push_column_names(L, stmt);
-		lua_rawseti(L, -2, 0);
-
-		int row_count = 0;
-		while ((rc = sql_step(stmt)) == SQL_ROW) {
-			lua_push_row(L, stmt);
-			lua_rawseti(L, -2, ++row_count);
-		}
-		retval_count = 1;
-	}
-        if (rc != SQL_OK && rc != SQL_DONE)
-		goto sqlerror;
-	sql_finalize(stmt);
-	return retval_count;
-sqlerror:
-	lua_pushstring(L, sql_errmsg(db));
-	sql_finalize(stmt);
-	return lua_error(L);
-}
-
-void
-box_lua_sql_init(struct lua_State *L)
-{
-	static const struct luaL_Reg module_funcs [] = {
-		{"execute", lua_sql_execute},
-		{NULL, NULL}
-	};
-
-	/* used by lua_sql_execute via upvalue */
-	lua_createtable(L, 0, 1);
-	lua_pushstring(L, "sequence");
-	lua_setfield(L, -2, "__serialize");
-
-	luaL_openlib(L, "box.sql", module_funcs, 1);
-	lua_pop(L, 1);
-}
-
diff --git a/src/box/lua/sql.h b/src/box/lua/sql.h
deleted file mode 100644
index 2543e94..0000000
--- a/src/box/lua/sql.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef INCLUDES_TARANTOOL_LUA_SQL_H
-#define INCLUDES_TARANTOOL_LUA_SQL_H
-/*
- * Copyright 2010-2015, 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.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct lua_State;
-
-void box_lua_sql_init(struct lua_State *L);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/test/box/cfg.result b/test/box/cfg.result
index 7778f82..66b02f5 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -528,23 +528,6 @@ box.cfg{net_msg_max = old + 1000}
 box.cfg{net_msg_max = old}
 ---
 ...
---
--- gh-3266: box.cfg{} still not optional on 2.0 brach
---
--- box.sql defined with __index function in metatable overridden
--- with first box.cfg() call
---
-box.cfg()
----
-...
-assert(next(box.sql) ~= nil)
----
-- true
-...
-assert(getmetatable(box.sql) == nil)
----
-- true
-...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/box/cfg.test.lua b/test/box/cfg.test.lua
index a641624..eddeab1 100644
--- a/test/box/cfg.test.lua
+++ b/test/box/cfg.test.lua
@@ -131,14 +131,4 @@ box.cfg{net_msg_max = 2}
 box.cfg{net_msg_max = old + 1000}
 box.cfg{net_msg_max = old}
 
---
--- gh-3266: box.cfg{} still not optional on 2.0 brach
---
--- box.sql defined with __index function in metatable overridden
--- with first box.cfg() call
---
-box.cfg()
-assert(next(box.sql) ~= nil)
-assert(getmetatable(box.sql) == nil)
-
 test_run:cmd("clear filter")
diff --git a/test/box/misc.result b/test/box/misc.result
index 714d5de..1b1a630 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -83,7 +83,6 @@ t
   - slab
   - snapshot
   - space
-  - sql
   - stat
   - tuple
 ...
diff --git a/test/box/sql-update-with-nested-select.result b/test/box/sql-update-with-nested-select.result
index 64a4fb6..24da618 100644
--- a/test/box/sql-update-with-nested-select.result
+++ b/test/box/sql-update-with-nested-select.result
@@ -3,31 +3,40 @@ test_run = require('test_run').new()
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE t1(a integer primary key, b int UNIQUE, e int);");
+box.execute("CREATE TABLE t1(a integer primary key, b int UNIQUE, e int);");
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);");
+box.execute("INSERT INTO t1 VALUES(1,4,6);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);");
+box.execute("INSERT INTO t1 VALUES(2,5,7);");
 ---
+- rowcount: 1
 ...
 -- Both entries must be updated
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 ---
+- rowcount: 2
 ...
 -- Check
-box.sql.execute("SELECT e FROM t1");
+box.execute("SELECT e FROM t1");
 ---
-- - [7]
+- metadata:
+  - name: E
+    type: INTEGER
+  rows:
+  - [7]
   - [8]
 ...
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/box/sql-update-with-nested-select.test.lua b/test/box/sql-update-with-nested-select.test.lua
index 9421a3b..5c5dd96 100644
--- a/test/box/sql-update-with-nested-select.test.lua
+++ b/test/box/sql-update-with-nested-select.test.lua
@@ -3,23 +3,23 @@ test_run = require('test_run').new()
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE t1(a integer primary key, b int UNIQUE, e int);");
+box.execute("CREATE TABLE t1(a integer primary key, b int UNIQUE, e int);");
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);");
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);");
+box.execute("INSERT INTO t1 VALUES(1,4,6);");
+box.execute("INSERT INTO t1 VALUES(2,5,7);");
 
 -- Both entries must be updated
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 
 -- Check
-box.sql.execute("SELECT e FROM t1");
+box.execute("SELECT e FROM t1");
 
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql-tap/alter.test.lua b/test/sql-tap/alter.test.lua
index 3b2eceb..2331cd0 100755
--- a/test/sql-tap/alter.test.lua
+++ b/test/sql-tap/alter.test.lua
@@ -359,7 +359,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 1, 3);
     ]], {
         -- <alter-7.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter-7.2>
     })
 
@@ -369,7 +369,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 2, 2);
     ]], {
         -- <alter-7.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter-7.3>
     })
 
@@ -435,7 +435,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(4, 5, 3);
     ]], {
         -- <alter-7.9>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter-7.9>
     })
 
@@ -490,7 +490,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(6, 5, 10);
     ]], {
         -- <alter-7.14>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter-7.14>
     })
 
diff --git a/test/sql-tap/alter2.test.lua b/test/sql-tap/alter2.test.lua
index beb6f5d..612dd74 100755
--- a/test/sql-tap/alter2.test.lua
+++ b/test/sql-tap/alter2.test.lua
@@ -24,7 +24,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(2, 3, 2);
     ]], {
         -- <alter2-1.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-1.2>
     })
 
@@ -45,7 +45,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(2, 3, 2);
     ]], {
         -- <alter2-1.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-1.4>
     })
 
@@ -83,7 +83,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(4, 2, 1);
     ]], {
         -- <alter2-1.6>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-1.6>
     })
 
@@ -143,7 +143,7 @@ test:do_catchsql_test(
         INSERT INTO parent VALUES(1, 2, 3);
     ]], {
         -- <alter2-2.1>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-2.1>
     })
 
@@ -154,7 +154,7 @@ test:do_catchsql_test(
         INSERT INTO child VALUES(2, 1, 1);
     ]], {
         -- <alter2-2.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-2.2>
     })
 
@@ -165,7 +165,7 @@ test:do_catchsql_test(
         INSERT INTO parent VALUES(3, 4, 2);
     ]], {
         -- <alter2-2.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </alter2-2.3>
     })
 
diff --git a/test/sql-tap/analyze1.test.lua b/test/sql-tap/analyze1.test.lua
index cc12593..f274cee 100755
--- a/test/sql-tap/analyze1.test.lua
+++ b/test/sql-tap/analyze1.test.lua
@@ -499,13 +499,13 @@ test:do_test(
         test:execsql("CREATE INDEX i2 ON t1(e);")
 
         for i = 0, 100 do
-            box.sql.execute(string.format("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', %s, %s);", i, math.floor(i / 2)))
+            box.execute(string.format("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', %s, %s);", i, math.floor(i / 2)))
         end;
         for i = 0, 20 do
-            box.sql.execute("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', 101, "..i..");")
+            box.execute("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', 101, "..i..");")
         end;
         for i = 102, 200 do
-            box.sql.execute(string.format("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', %s, %s);", i, math.floor(i / 2)))
+            box.execute(string.format("INSERT INTO t1 VALUES(null, 'x', 'y', 'z', %s, %s);", i, math.floor(i / 2)))
         end;
 
         test:execsql("ANALYZE;")
diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index 3969c45..02eb49f 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -240,7 +240,7 @@ insert_filler_rows_n = function(iStart, nCopy, nVal)
     for i = 0, nVal-1 do
         local iVal = iStart+i
         for j = 0, nCopy-1 do
-            box.sql.execute(string.format("INSERT INTO t1 VALUES (null, %s, %s, '%s')", iVal, iVal, iVal))
+            box.execute(string.format("INSERT INTO t1 VALUES (null, %s, %s, '%s')", iVal, iVal, iVal))
         end
     end
 end
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index dc2f60e..bdc0053 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -531,7 +531,7 @@ test:do_catchsql_test(
         INSERT INTO t6 VALUES(NULL,1);
     ]], {
         -- <autoinc-6.2>
-        1, "Sequence 'T6' has overflowed"
+        1, "Error during execution of VDBE byte-code: Sequence 'T6' has overflowed"
         -- </autoinc-6.2>
     })
 
@@ -810,7 +810,7 @@ test:do_catchsql_test(
         INSERT INTO t1 SELECT s2, s2 FROM t1;
     ]], {
         -- <autoinc-gh-3670>
-        1, "datatype mismatch"
+        1, "Error during execution of VDBE byte-code: datatype mismatch"
         -- </autoinc-gh-3670>
     })
 
diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index 79583af..4395b28 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -58,7 +58,7 @@ local function queryplan(sql)
         x = "sort"
     end
     table.insert(data,x)
-    local eqp = box.sql.execute("EXPLAIN QUERY PLAN "..sql.."")
+    local eqp = box.execute("EXPLAIN QUERY PLAN "..sql.."").rows
     -- puts eqp=$eqp
     for i, val in ipairs(eqp) do
         --local a = val[1]
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 0d8bf15..c7ef1a1 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -55,7 +55,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(6,7, 2);
     ]], {
         -- <check-1.3>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-1.3>
     })
 
@@ -75,7 +75,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(4,3, 2);
     ]], {
         -- <check-1.5>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-1.5>
     })
 
@@ -147,7 +147,7 @@ test:do_catchsql_test(
         UPDATE t1 SET x=7 WHERE x==2
     ]], {
         -- <check-1.12>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-1.12>
     })
 
@@ -167,7 +167,7 @@ test:do_catchsql_test(
         UPDATE t1 SET x=5 WHERE x==2
     ]], {
         -- <check-1.14>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-1.14>
     })
 
@@ -246,7 +246,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(3, 1.1, NULL, NULL);
     ]], {
         -- <check-2.4>
-        1, "CHECK constraint failed: ONE"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: ONE"
         -- </check-2.4>
     })
 
@@ -256,7 +256,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(4, NULL, 5, NULL);
     ]], {
         -- <check-2.5>
-        1, "CHECK constraint failed: TWO"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: TWO"
         -- </check-2.5>
     })
 
@@ -266,7 +266,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(5, NULL, NULL, 3.14159);
     ]], {
         -- <check-2.6>
-        1, "CHECK constraint failed: THREE"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: THREE"
         -- </check-2.6>
     })
 
@@ -413,7 +413,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(111,222,333);
     ]], {
         -- <check-3.9>
-        1, "CHECK constraint failed: T3"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T3"
         -- </check-3.9>
     })
 
@@ -484,7 +484,7 @@ test:do_catchsql_test(
         UPDATE t4 SET x=0, y=1;
     ]], {
         -- <check-4.6>
-        1, "CHECK constraint failed: T4"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T4"
         -- </check-4.6>
     })
 
@@ -504,7 +504,7 @@ test:do_catchsql_test(
         UPDATE t4 SET x=0, y=2;
     ]], {
         -- <check-4.9>
-        1, "CHECK constraint failed: T4"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T4"
         -- </check-4.9>
     })
 
@@ -581,7 +581,7 @@ test:do_catchsql_test(
         UPDATE OR FAIL t1 SET x=7-x, y=y+1;
     ]], {
         -- <check-6.5>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-6.5>
     })
 
@@ -603,7 +603,7 @@ test:do_catchsql_test(
         INSERT OR ROLLBACK INTO t1 VALUES(8,40.0, 10);
     ]], {
         -- <check-6.7>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-6.7>
     })
 
@@ -613,7 +613,7 @@ test:do_catchsql_test(
         COMMIT;
     ]], {
         -- <check-6.8>
-        1, "cannot commit - no transaction is active"
+        1, "Error during execution of VDBE byte-code: cannot commit - no transaction is active"
         -- </check-6.8>
     })
 
@@ -636,7 +636,7 @@ test:do_catchsql_test(
         REPLACE INTO t1 VALUES(6,7, 11);
     ]], {
         -- <check-6.12>
-        1, "CHECK constraint failed: T1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
         -- </check-6.12>
     })
 
@@ -700,7 +700,7 @@ test:do_catchsql_test(
     7.3,
     " INSERT INTO t6 VALUES(11) ", {
         -- <7.3>
-        1, "CHECK constraint failed: T6"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T6"
         -- </7.3>
     })
 
diff --git a/test/sql-tap/delete1.test.lua b/test/sql-tap/delete1.test.lua
index d6d4762..bfed3ba 100755
--- a/test/sql-tap/delete1.test.lua
+++ b/test/sql-tap/delete1.test.lua
@@ -151,7 +151,7 @@ test:do_test(
 test:do_test(
     "delete1-6.1.1",
     function()
-        box.sql.execute([[delete from "t" where "id"=2]])
+        box.execute([[delete from "t" where "id"=2]])
         return s:count()
     end,
     2)
@@ -159,7 +159,7 @@ test:do_test(
 test:do_test(
     "delete1-6.1",
     function()
-        box.sql.execute([[delete from "t"]])
+        box.execute([[delete from "t"]])
 	return s:count()
     end,
     0)
diff --git a/test/sql-tap/distinct.test.lua b/test/sql-tap/distinct.test.lua
index c663751..e697008 100755
--- a/test/sql-tap/distinct.test.lua
+++ b/test/sql-tap/distinct.test.lua
@@ -25,14 +25,14 @@ local function is_distinct_noop(sql)
     local sql2 = string.gsub(sql, "DISTINCT", "")
     local program1 = {  }
     local program2 = {  }
-    local r = box.sql.execute("EXPLAIN "..sql1)
+    local r = box.execute("EXPLAIN "..sql1).rows
     for _, val in ipairs(r) do
         local opcode = val[2]
         if opcode ~= "Noop" then
             table.insert(program1, opcode)
         end
     end
-    r = box.sql.execute("EXPLAIN "..sql2)
+    r = box.execute("EXPLAIN "..sql2).rows
     for _, val in ipairs(r) do
         local opcode = val[2]
         if opcode ~= "Noop" then
@@ -63,7 +63,7 @@ local function do_temptables_test(tn, sql, temptables)
         tn,
         function()
             local ret = {}
-            local r = box.sql.execute("EXPLAIN "..sql)
+            local r = box.execute("EXPLAIN "..sql).rows
             for _, val in ipairs(r) do
                 local opcode = val[2]
                 local p5 = val[7]
diff --git a/test/sql-tap/fkey1.test.lua b/test/sql-tap/fkey1.test.lua
index 0464f2d..f7c473f 100755
--- a/test/sql-tap/fkey1.test.lua
+++ b/test/sql-tap/fkey1.test.lua
@@ -158,7 +158,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO t11 VALUES (2, 3);
     ]], {
         -- <fkey1-5.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey1-5.2>
     })
 
@@ -194,7 +194,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3');
     ]], {
         -- <fkey1-5.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey1-5.5>
     })
 
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index d347e5a..525ef2a 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -26,7 +26,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,d) VALUES(1, 3);
     ]], {
         -- <fkey2-1.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.2>
     })
 
@@ -56,7 +56,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,d) VALUES(2, 4);
     ]], {
         -- <fkey2-1.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.5>
     })
 
@@ -77,7 +77,7 @@ test:do_catchsql_test(
         UPDATE t2 SET c = 2 WHERE d = 4;
     ]], {
         -- <fkey2-1.7>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.7>
     })
 
@@ -98,7 +98,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE a = 1;
     ]], {
         -- <fkey2-1.9>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.9>
     })
 
@@ -109,7 +109,7 @@ test:do_catchsql_test(
         UPDATE t1 SET a = 2;
     ]], {
         -- <fkey2-1.10>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.10>
     })
 
@@ -131,7 +131,7 @@ test:do_catchsql_test(
         INSERT INTO t4(c,d) values (1,3);
     ]], {
         -- <fkey2-1.12>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.12>
     })
 
@@ -161,7 +161,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (1,3);
     ]], {
         -- <fkey2-1.15>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.15>
     })
 
@@ -191,7 +191,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (2,4);
     ]], {
         -- <fkey2-1.18>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.18>
     })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (6,4);
     ]], {
         -- <fkey2-1.19>
-        1,"FOREIGN KEY constraint failed"
+        1,"Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.19>
     })
 
@@ -241,7 +241,7 @@ test:do_catchsql_test(
         DELETE FROM t7 WHERE b = 1;
     ]], {
         -- <fkey2-1.23>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.23>
     })
 
@@ -251,7 +251,7 @@ test:do_catchsql_test(
         UPDATE t7 SET b = 2;
     ]], {
         -- <fkey2-1.24>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.24>
     })
 
@@ -271,7 +271,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) VALUES(666, 54644);
     ]], {
         -- <fkey2-1.26>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.26>
     })
 
@@ -281,7 +281,7 @@ test:do_catchsql_test(
         UPDATE t7 SET b = 5;
     ]], {
         -- <fkey2-1.27>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-1.27>
     })
 
@@ -333,7 +333,7 @@ test:do_catchsql_test(
         DELETE FROM i;
     ]], {
         -- <fkey2-2.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-2.2>
     })
 
@@ -362,7 +362,7 @@ test:do_catchsql_test(
         UPDATE ab SET a = 5;
     ]], {
         -- <fkey2-3.2>
-        1, "CHECK constraint failed: EF"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: EF"
         -- </fkey2-3.2>
     })
 
@@ -382,7 +382,7 @@ test:do_catchsql_test(
         UPDATE ab SET a = 5;
     ]], {
         -- <fkey2-3.4>
-        1, "CHECK constraint failed: EF"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: EF"
         -- </fkey2-3.4>
     })
 
@@ -403,7 +403,7 @@ test:do_catchsql_test(
         DELETE FROM ab;
     ]], {
         -- <fkey2-3.6>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-3.6>
     })
 
@@ -540,7 +540,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,b) VALUES(1, 'A');
     ]], {
         -- <fkey2-5.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-5.2>
     })
 
@@ -562,7 +562,7 @@ test:do_catchsql_test(
         UPDATE t2 SET c = 3;
     ]], {
         -- <fkey2-5.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-5.4>
     })
 
@@ -572,7 +572,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE a = 2;
     ]], {
         -- <fkey2-5.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-5.5>
     })
 
@@ -591,7 +591,7 @@ test:do_catchsql_test(
         UPDATE t1 SET a = 3;
     ]], {
         -- <fkey2-5.7>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-5.7>
     })
 
@@ -656,7 +656,7 @@ test:do_catchsql_test(
         DELETE FROM t1;
     ]], {
         -- <fkey2-6.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-6.5>
     })
 
@@ -808,7 +808,7 @@ test:do_catchsql_test(
       UPDATE t1 SET b = 'five' WHERE b = 'two';
     ]], {
         -- <fkey2-9.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.2>
     })
 
@@ -818,7 +818,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE b = 'two';
     ]], {
         -- <fkey2-9.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.3>
     })
 
@@ -828,7 +828,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES('five');
     ]], {
         -- <fkey2-9.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.4>
     })
 
@@ -885,7 +885,7 @@ test:do_catchsql_test(
         DELETE FROM t1;
     ]], {
         -- <fkey2-9.8>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.8>
     })
 
@@ -942,7 +942,7 @@ test:do_catchsql_test(
         INSERT INTO down(c39, c38) VALUES('yes', 'no');
     ]], {
         -- <fkey2-9.12>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.12>
     })
 
@@ -954,7 +954,7 @@ test:do_catchsql_test(
         DELETE FROM up WHERE c34 = 'yes';
     ]], {
         -- <fkey2-9.13>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-9.13>
     })
 
@@ -1007,7 +1007,7 @@ test:do_execsql_test(
 --         INSERT INTO t3 VALUES(1, 2, 3);
 --     ]], {
 --         -- <fkey2-10.2>
---         1, "FOREIGN KEY constraint failed"
+--         1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
 --         -- </fkey2-10.2>
 --     })
 
@@ -1027,7 +1027,7 @@ test:do_execsql_test(
 --         UPDATE t4 SET b = 5;
 --     ]], {
 --         -- <fkey2-10.4>
---         1, "FOREIGN KEY constraint failed"
+--         1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
 --         -- </fkey2-10.4>
 --     })
 
@@ -1183,7 +1183,7 @@ test:do_catchsql_test(
         UPDATE self SET b = 15;
     ]], {
         -- <fkey2-11.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.2>
     })
 
@@ -1193,7 +1193,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15;
     ]], {
         -- <fkey2-11.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.3>
     })
 
@@ -1203,7 +1203,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15, b = 16;
     ]], {
         -- <fkey2-11.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.4>
     })
 
@@ -1231,7 +1231,7 @@ test:do_catchsql_test(
         INSERT INTO self(a,b) VALUES(20, 21);
     ]], {
         -- <fkey2-11.7>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.7>
     })
 
@@ -1253,7 +1253,7 @@ test:do_catchsql_test(
         UPDATE self SET b = 15;
     ]], {
         -- <fkey2-11.9>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.9>
     })
 
@@ -1263,7 +1263,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15;
     ]], {
         -- <fkey2-11.10>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.10>
     })
 
@@ -1273,7 +1273,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15, b = 16;
     ]], {
         -- <fkey2-11.11>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.11>
     })
 
@@ -1301,7 +1301,7 @@ test:do_catchsql_test(
         INSERT INTO self(a,b) VALUES(20, 21);
     ]], {
         -- <fkey2-11.14>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-11.14>
     })
 
@@ -1328,7 +1328,7 @@ test:do_catchsql_test(
         DELETE FROM tdd08;
     ]], {
         -- <fkey2-12.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-12.2>
     })
 
@@ -1348,7 +1348,7 @@ test:do_catchsql_test(
         INSERT INTO tdd08_b(w,x,y) VALUES(400,500,300);
     ]], {
         -- <fkey2-12.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-12.4>
     })
 
@@ -1358,7 +1358,7 @@ test:do_catchsql_test(
         UPDATE tdd08_b SET x=x+1;
     ]], {
         -- <fkey2-12.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-12.5>
     })
 
@@ -1368,7 +1368,7 @@ test:do_catchsql_test(
         UPDATE tdd08 SET a=a+1;
     ]], {
         -- <fkey2-12.6>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-12.6>
     })
 
@@ -1394,7 +1394,7 @@ test:do_catchsql_test(
         UPDATE tce71 set b = 201 where a = 100;
     ]], {
         -- <fkey2-13.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-13.2>
     })
 
@@ -1404,7 +1404,7 @@ test:do_catchsql_test(
         UPDATE tce71 set a = 101 where a = 100;
     ]], {
         -- <fkey2-13.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-13.3>
     })
 
@@ -1429,7 +1429,7 @@ test:do_catchsql_test(
         UPDATE tce73 set b = 201 where a = 100;
     ]], {
         -- <fkey2-14.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-14.2>
     })
 
@@ -1439,7 +1439,7 @@ test:do_catchsql_test(
         UPDATE tce71 set a = 101 where a = 100;
     ]], {
         -- <fkey2-14.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey2-14.3>
     })
 
diff --git a/test/sql-tap/fkey3.test.lua b/test/sql-tap/fkey3.test.lua
index 011402d..c66b5e0 100755
--- a/test/sql-tap/fkey3.test.lua
+++ b/test/sql-tap/fkey3.test.lua
@@ -122,7 +122,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(2, 2, 5, 2);
     ]], {
         -- <fkey3-3.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.2>
     })
 
@@ -132,7 +132,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(2, 3, 5, 2);
     ]], {
         -- <fkey3-3.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.3>
     })
 
@@ -151,7 +151,7 @@ test:do_catchsql_test(
         INSERT INTO t4 VALUES(2, 1);
     ]], {
         -- <fkey3-3.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.5>
     })
 
@@ -175,7 +175,7 @@ test:do_catchsql_test(
         INSERT INTO t6(a,b,c,d) VALUES(4, 'a', 65, 'a');
     ]], {
         -- <fkey3-3.7>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.7>
     })
 
@@ -197,7 +197,7 @@ test:do_catchsql_test(
         UPDATE t6 SET c = 1, d = 'a' WHERE a = 100;
     ]], {
         -- <fkey3-3.9>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.9>
     })
 
@@ -219,7 +219,7 @@ test:do_catchsql_test(
         INSERT INTO t7 VALUES('x', 450, 'x', 4);
     ]], {
         -- <fkey3-3.11>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.11>
     })
 
@@ -229,7 +229,7 @@ test:do_catchsql_test(
         INSERT INTO t7 VALUES('x', 450, 'x', 451);
     ]], {
         -- <fkey3-3.12>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-3.12>
     })
 
@@ -253,7 +253,7 @@ test:do_catchsql_test(
         UPDATE t8 SET d = 2;
     ]], {
         -- <fkey3-6.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-6.2>
     })
 
@@ -282,7 +282,7 @@ test:do_catchsql_test(
         UPDATE TestTable SET parent_id=1000 WHERE id=2;
     ]], {
         -- <fkey3-6.4>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey3-6.4>
     })
 
diff --git a/test/sql-tap/fkey4.test.lua b/test/sql-tap/fkey4.test.lua
index 13fd308..3a03cd5 100755
--- a/test/sql-tap/fkey4.test.lua
+++ b/test/sql-tap/fkey4.test.lua
@@ -30,7 +30,7 @@ test:do_catchsql_test(
         DELETE FROM p1 WHERE a = 2;
     ]], {
         -- <fkey8-1.2>
-        1, "NOT NULL constraint failed: C1.B"
+        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: C1.B"
         -- </fkey8-1.2>
     })
 
@@ -83,7 +83,7 @@ test:do_catchsql_test(
         DELETE FROM p1 WHERE a = 2;
     ]], {
         -- <fkey8-1.5>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey8-1.5>
     })
 
@@ -159,7 +159,7 @@ test:do_catchsql_test(
         UPDATE OR IGNORE p1 SET a = 4 WHERE a = 2;
     ]], {
         -- <fkey8-1.9>
-        1, "NOT NULL constraint failed: C1.B"
+        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: C1.B"
         -- </fkey8-1.9>
     })
 
@@ -203,7 +203,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO p1 VALUES(2, 'two');
     ]], {
         -- <fkey8-2.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey8-2.2>
     })
 
@@ -278,7 +278,7 @@ test:do_catchsql_test(
         DELETE FROM p3 WHERE a=1;
     ]], {
         -- <fkey8-4.3>
-        1, "FOREIGN KEY constraint failed"
+        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
         -- </fkey8-4.3>
     })
 
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 889fc58..a7fff6e 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -1598,7 +1598,7 @@ test:do_catchsql_test(
         SELECT sum(x) - ((1<<62)*2.0+1) from t6;
     ]], {
         -- <func-18.12>
-        1, "integer overflow"
+        1, "Error during execution of VDBE byte-code: integer overflow"
         -- </func-18.12>
     })
 
@@ -1653,7 +1653,7 @@ test:do_catchsql_test(
             SELECT 10 AS x);
     ]], {
         -- <func-18.15>
-        1, "integer overflow"
+        1, "Error during execution of VDBE byte-code: integer overflow"
         -- </func-18.15>
     })
 
@@ -1665,7 +1665,7 @@ test:do_catchsql_test(
             SELECT -10 AS x);
     ]], {
         -- <func-18.18>
-        1, "integer overflow"
+        1, "Error during execution of VDBE byte-code: integer overflow"
         -- </func-18.18>
     })
 
@@ -1730,7 +1730,7 @@ test:do_catchsql_test(
         SELECT abs(-9223372036854775807-1);
     ]], {
         -- <func-18.32>
-        1, "integer overflow"
+        1, "Error during execution of VDBE byte-code: integer overflow"
         -- </func-18.32>
     })
 
@@ -1752,7 +1752,7 @@ test:do_catchsql_test(
         SELECT 'abc' MATCH 'xyz';
     ]], {
         -- <func-19.2>
-        1, "unable to use function MATCH in the requested context"
+        1, "Error during execution of VDBE byte-code: unable to use function MATCH in the requested context"
         -- </func-19.2>
     })
 
@@ -1762,7 +1762,7 @@ test:do_catchsql_test(
         SELECT 'abc' NOT MATCH 'xyz';
     ]], {
         -- <func-19.3>
-        1, "unable to use function MATCH in the requested context"
+        1, "Error during execution of VDBE byte-code: unable to use function MATCH in the requested context"
         -- </func-19.3>
     })
 
diff --git a/test/sql-tap/gh-2723-concurrency.test.lua b/test/sql-tap/gh-2723-concurrency.test.lua
index 21912a0..842142d 100755
--- a/test/sql-tap/gh-2723-concurrency.test.lua
+++ b/test/sql-tap/gh-2723-concurrency.test.lua
@@ -11,11 +11,11 @@ for id = 1, N do
     fiber.create(
         function ()
             local table_name = "table2723"..id
-            box.sql.execute("create table "..table_name.."(id INT primary key, a integer unique, b INT)")
-            box.sql.execute("insert into "..table_name.." values(1, 2, 3)")
-            box.sql.execute("insert into "..table_name.." values(3, 4, 3)")
-            pcall( function() box.sql.execute("insert into "..table_name.." values(3, 4, 3)") end)
-            box.sql.execute("drop table "..table_name)
+            box.execute("create table "..table_name.."(id INT primary key, a integer unique, b INT)")
+            box.execute("insert into "..table_name.." values(1, 2, 3)")
+            box.execute("insert into "..table_name.." values(3, 4, 3)")
+            pcall( function() box.execute("insert into "..table_name.." values(3, 4, 3)") end)
+            box.execute("drop table "..table_name)
             ch:put(1)
         end
     )
@@ -32,16 +32,16 @@ test:do_test(
     0)
 
 ch = fiber.channel(N)
-box.sql.execute("create table t1(id INT primary key, a integer unique, b INT);")
-box.sql.execute("create index i1 on t1(b);")
+box.execute("create table t1(id INT primary key, a integer unique, b INT);")
+box.execute("create index i1 on t1(b);")
 for id = 1, N do
     fiber.create(
         function ()
-            box.sql.execute(string.format("insert into t1 values(%s, %s, 3)", id, id))
-            box.sql.execute(string.format("insert into t1 values(%s, %s, 3)", id+N, id+N))
-            box.sql.execute(string.format("delete from t1 where id = %s", id+N))
-            box.sql.execute(string.format("insert into t1 values(%s, %s, 3)", id+2*N, id+2*N))
-            box.sql.execute(string.format("delete from t1 where id = %s", id+2*N))
+            box.execute(string.format("insert into t1 values(%s, %s, 3)", id, id))
+            box.execute(string.format("insert into t1 values(%s, %s, 3)", id+N, id+N))
+            box.execute(string.format("delete from t1 where id = %s", id+N))
+            box.execute(string.format("insert into t1 values(%s, %s, 3)", id+2*N, id+2*N))
+            box.execute(string.format("delete from t1 where id = %s", id+2*N))
             ch:put(1)
         end
     )
@@ -55,19 +55,19 @@ test:do_test(
         return test:execsql("select count(*) from (select distinct * from t1);")[1]
     end,
     N)
-box.sql.execute("drop table t1;")
+box.execute("drop table t1;")
 
 
 ch = fiber.channel(N)
-box.sql.execute("create table t1(id INT primary key, a integer unique, b INT);")
-box.sql.execute("create index i1 on t1(b);")
+box.execute("create table t1(id INT primary key, a integer unique, b INT);")
+box.execute("create index i1 on t1(b);")
 for id = 1, N*N do
-    box.sql.execute(string.format("insert into t1 values(%s, %s, 3)", id, id))
+    box.execute(string.format("insert into t1 values(%s, %s, 3)", id, id))
 end
 for id = 1, N do
     fiber.create(
         function ()
-            box.sql.execute("delete from t1")
+            box.execute("delete from t1")
             ch:put(1)
         end
     )
@@ -81,6 +81,6 @@ test:do_test(
         return test:execsql("select count(*) from t1;")[1]
     end,
     0)
-box.sql.execute("drop table t1;")
+box.execute("drop table t1;")
 
 test:finish_test()
diff --git a/test/sql-tap/gh-2931-savepoints.test.lua b/test/sql-tap/gh-2931-savepoints.test.lua
index 2ce4145..85dc7a1 100755
--- a/test/sql-tap/gh-2931-savepoints.test.lua
+++ b/test/sql-tap/gh-2931-savepoints.test.lua
@@ -38,7 +38,7 @@ local testcases = {
 		{0,{1,1}}},
 	{"5",
 		[[rollback to savepoint s1_2;]],
-		{1, "no such savepoint: S1_2"}},
+		{1, "Error during execution of VDBE byte-code: no such savepoint: S1_2"}},
 	{"6",
 		[[insert into t1 values(2);
 		select * from t1 union all select * from t2;]],
@@ -80,13 +80,13 @@ local testcases = {
 		{0,{1,2,10,11,1,2,4,10,11}}},
 	{"14",
 		[[insert into t1 values(4);]],
-		{1,"Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
+		{1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
 	{"15",
 		[[select * from t1 union all select * from t2;]],
 		{0,{1,2,10,11,1,2,4,10,11}}},
 	{"16",
 		[[insert or rollback into t1 values(4);]],
-		{1,"Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
+		{1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
 	{"17",  -- should work as transaction is rolled back
 		[[insert or rollback into t1 values(4);
 		select * from t1 union all select * from t2;]],
diff --git a/test/sql-tap/gh-3083-ephemeral-unref-tuples.test.lua b/test/sql-tap/gh-3083-ephemeral-unref-tuples.test.lua
index de57e38..3807ba6 100755
--- a/test/sql-tap/gh-3083-ephemeral-unref-tuples.test.lua
+++ b/test/sql-tap/gh-3083-ephemeral-unref-tuples.test.lua
@@ -9,11 +9,11 @@ test:do_test(
 		test:execsql "CREATE TABLE test(id integer, k integer, primary key (id))"
 
 		for i = 1, 1000 do
-			box.sql.execute("insert into test(id, k) values(" .. i .. "," .. i .. ")")
+			box.execute("insert into test(id, k) values(" .. i .. "," .. i .. ")")
 		end
 
 		for i = 1, 10000 do
-			box.sql.execute("SELECT id, k FROM test WHERE k IN (5849, 4986, 4997, 5020, 5044, 4990, 5013, 4983)")
+			box.execute("SELECT id, k FROM test WHERE k IN (5849, 4986, 4997, 5020, 5044, 4990, 5013, 4983)")
 		end
 	end, {
 		-- <unref_before_delete-1.1>
diff --git a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
index 3d35743..0e0a003 100755
--- a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
+++ b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
@@ -142,17 +142,17 @@ for i, tested_string in ipairs(invalid_testcases) do
     local test_name = prefix .. "2." .. tostring(i)
     local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
 
     test_name = prefix .. "3." .. tostring(i)
     test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
 
     test_name = prefix .. "4." .. tostring(i)
     test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
 
     -- Just skipping if row value predicand contains invalid character.
 
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 57fbca6..eec7b53 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -139,7 +139,7 @@ test:do_catchsql_xfer_test(
         INSERT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.9>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.9>
     }, {
         exp_xfer_count = 0
@@ -210,7 +210,7 @@ test:do_catchsql_xfer_test(
             INSERT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.13>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.13>
     }, {
         exp_xfer_count = 0
@@ -245,7 +245,7 @@ test:do_catchsql_xfer_test(
             INSERT OR ABORT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.20>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.20>
     }, {
         exp_xfer_count = 1
@@ -313,7 +313,7 @@ test:do_catchsql_xfer_test(
             INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.24>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.24>
     }, {
         exp_xfer_count = 0
@@ -381,7 +381,7 @@ test:do_catchsql_xfer_test(
             INSERT OR FAIL INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.28>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.28>
     }, {
         exp_xfer_count = 1
diff --git a/test/sql-tap/gh-3332-tuple-format-leak.test.lua b/test/sql-tap/gh-3332-tuple-format-leak.test.lua
index f19b7bc..84e67b7 100755
--- a/test/sql-tap/gh-3332-tuple-format-leak.test.lua
+++ b/test/sql-tap/gh-3332-tuple-format-leak.test.lua
@@ -5,13 +5,13 @@ test:plan(2)
 test:do_test(
     "format-leak-prep",
     function()
-        box.sql.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY,\
+        box.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY,\
                          max_players INTEGER, n_players INTEGER, flags INTEGER);");
-        box.sql.execute("CREATE INDEX IDX_MAX_PLAYERS ON t1(max_players);");
-        box.sql.execute("CREATE INDEX IDX_N_PLAYERS ON t1(n_players);");
-        box.sql.execute("CREATE INDEX IDX_FLAGS ON t1(flags);");
+        box.execute("CREATE INDEX IDX_MAX_PLAYERS ON t1(max_players);");
+        box.execute("CREATE INDEX IDX_N_PLAYERS ON t1(n_players);");
+        box.execute("CREATE INDEX IDX_FLAGS ON t1(flags);");
         for i = 1, 10 do
-            box.sql.execute(string.format("INSERT INTO t1 VALUES (%s, %s, %s, %s);",
+            box.execute(string.format("INSERT INTO t1 VALUES (%s, %s, %s, %s);",
                                           i, 15, 6, 3));
         end
     end, {
@@ -22,7 +22,7 @@ test:do_test(
     "format-leak",
     function()
         for i = 1, 100000 do
-            box.sql.execute("SELECT id FROM t1 WHERE flags=3 ORDER BY id LIMIT 2");
+            box.execute("SELECT id FROM t1 WHERE flags=3 ORDER BY id LIMIT 2");
         end
     end, {
 
diff --git a/test/sql-tap/gh2140-trans.test.lua b/test/sql-tap/gh2140-trans.test.lua
index fe978d1..3843c97 100755
--- a/test/sql-tap/gh2140-trans.test.lua
+++ b/test/sql-tap/gh2140-trans.test.lua
@@ -2,14 +2,14 @@
 test = require("sqltester")
 test:plan(10)
 
-box.sql.execute("DROP TABLE IF EXISTS t1")
-box.sql.execute("DROP TABLE IF EXISTS t2")
+box.execute("DROP TABLE IF EXISTS t1")
+box.execute("DROP TABLE IF EXISTS t2")
 
-box.sql.execute("CREATE TABLE t1 (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
-box.sql.execute("CREATE TABLE t2 (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
+box.execute("CREATE TABLE t1 (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
+box.execute("CREATE TABLE t2 (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
 
-box.sql.execute("INSERT INTO t1 VALUES (1,1,1);")
-box.sql.execute("INSERT INTO t2 VALUES (1,1,1);")
+box.execute("INSERT INTO t1 VALUES (1,1,1);")
+box.execute("INSERT INTO t2 VALUES (1,1,1);")
 
 test:do_execsql_test('commit1_check',
                      [[START TRANSACTION;
@@ -28,8 +28,8 @@ test:do_execsql_test('rollback1_check',
                      {1, 1, 2, 2})
 
 for _, verb in ipairs({'ROLLBACK', 'ABORT'}) do
-    box.sql.execute('DELETE FROM t2')
-    answer = "Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
+    box.execute('DELETE FROM t2')
+    answer = "/Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'/"
     test:do_catchsql_test('insert1_'..verb,
                           [[START TRANSACTION;
                             INSERT INTO t2 VALUES (20, 2, 2);
@@ -39,13 +39,13 @@ for _, verb in ipairs({'ROLLBACK', 'ABORT'}) do
 
     local expect = {}
     if verb == 'ABORT' then
-         box.sql.execute('COMMIT')
+         box.execute('COMMIT')
          expect = {20, 2, 2}
     end
     test:do_execsql_test('insert1_'..verb..'_check',
                          'SELECT * FROM t2', expect)
 
-    box.sql.execute('DELETE FROM t2')
+    box.execute('DELETE FROM t2')
     test:do_catchsql_test('update1_'..verb,
                           [[START TRANSACTION;
                             INSERT INTO t2 VALUES (20, 2, 2);
@@ -57,9 +57,9 @@ for _, verb in ipairs({'ROLLBACK', 'ABORT'}) do
                          'SELECT * FROM t2', expect)
 end
 
-box.sql.execute('COMMIT')
+box.execute('COMMIT')
 -- Cleanup
-box.sql.execute('DROP TABLE t1')
-box.sql.execute('DROP TABLE t2')
+box.execute('DROP TABLE t1')
+box.execute('DROP TABLE t2')
 
 test:finish_test()
diff --git a/test/sql-tap/gh2250-trigger-chain-limit.test.lua b/test/sql-tap/gh2250-trigger-chain-limit.test.lua
index e38f286..0a8a6c5 100755
--- a/test/sql-tap/gh2250-trigger-chain-limit.test.lua
+++ b/test/sql-tap/gh2250-trigger-chain-limit.test.lua
@@ -7,23 +7,23 @@ for _, table_count in ipairs({30, 31}) do
     for i = 1,table_count do
         -- First table for uniform triggers check
         drop_string = 'DROP TABLE IF EXISTS t' .. i .. ';'
-        box.sql.execute(drop_string)
+        box.execute(drop_string)
 
         create_string = 'CREATE TABLE t' .. i .. ' (s1 INT UNIQUE, s2 INT, s3 INT PRIMARY KEY);'
-        box.sql.execute(create_string)
+        box.execute(create_string)
 
         insert_string = 'INSERT INTO t' .. i .. ' VALUES (0,' .. i .. ', 0);'
-        box.sql.execute(insert_string)
+        box.execute(insert_string)
 
         -- Second table for triggers mixture check
         drop_string = 'DROP TABLE IF EXISTS tt' .. i .. ';'
-        box.sql.execute(drop_string)
+        box.execute(drop_string)
 
         create_string = 'CREATE TABLE tt' .. i .. ' (s1 INT UNIQUE, s2 INT, s3 INT PRIMARY KEY);'
-        box.sql.execute(create_string)
+        box.execute(create_string)
 
         insert_string = 'INSERT INTO tt' .. i .. ' VALUES (0,' .. i .. ', 0);'
-        box.sql.execute(insert_string)
+        box.execute(insert_string)
     end
 
     -- And ON DELETE|UPDATE|INSERT triggers
@@ -33,21 +33,21 @@ for _, table_count in ipairs({30, 31}) do
             .. ' FOR EACH ROW '
         create_string = create_string .. ' BEGIN DELETE FROM t' .. i+1
             .. '; END'
-        box.sql.execute(create_string)
+        box.execute(create_string)
 
         create_string = 'CREATE TRIGGER tu' .. i
         create_string = create_string .. ' BEFORE UPDATE ON t' .. i
             .. ' FOR EACH ROW '
         create_string = create_string .. ' BEGIN UPDATE t' .. i+1 ..
             ' SET s1=s1+1; END'
-        box.sql.execute(create_string)
+        box.execute(create_string)
 
         create_string = 'CREATE TRIGGER ti' .. i
         create_string = create_string .. ' BEFORE INSERT ON t' .. i
             .. ' FOR EACH ROW '
         create_string = create_string .. ' BEGIN INSERT INTO t' .. i+1
             .. ' (s1) SELECT max(s1)+1 FROM t' .. i+1 .. '; END'
-        box.sql.execute(create_string)
+        box.execute(create_string)
 
         -- Try triggers mixture: DELETE triggers UPDATE, which triggers
         -- INSERT, which triggers DELETE etc.
@@ -70,12 +70,13 @@ for _, table_count in ipairs({30, 31}) do
                     ' SET s1=s1+1; END'
             end
         end
-        box.sql.execute(create_string)
+        box.execute(create_string)
     end
 
     function check(sql)
         msg = ''
         local _, msg = pcall(function () test:execsql(sql) end)
+        msg = tostring(msg)
         test:do_test(sql,
                      function()
                          return true
diff --git a/test/sql-tap/gh2259-in-stmt-trans.test.lua b/test/sql-tap/gh2259-in-stmt-trans.test.lua
index 95e0398..1ac0d53 100755
--- a/test/sql-tap/gh2259-in-stmt-trans.test.lua
+++ b/test/sql-tap/gh2259-in-stmt-trans.test.lua
@@ -2,23 +2,23 @@
 test = require("sqltester")
 test:plan(20)
 
-box.sql.execute("DROP TABLE IF EXISTS t1")
-box.sql.execute("DROP TABLE IF EXISTS t2")
+box.execute("DROP TABLE IF EXISTS t1")
+box.execute("DROP TABLE IF EXISTS t2")
 
-box.sql.execute("CREATE TABLE t1 (s1 INT UNIQUE, s2 INT PRIMARY KEY);")
-box.sql.execute("CREATE TABLE t2 (s1 INT UNIQUE, s2 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (s1 INT UNIQUE, s2 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2 (s1 INT UNIQUE, s2 INT PRIMARY KEY);")
 
-box.sql.execute("INSERT INTO t2 VALUES (1,1);")
-box.sql.execute("INSERT INTO t1 VALUES (3,3);")
+box.execute("INSERT INTO t2 VALUES (1,1);")
+box.execute("INSERT INTO t1 VALUES (3,3);")
 
 for _, prefix in pairs({"BEFORE", "AFTER"}) do
-    box.sql.execute('DROP TRIGGER IF EXISTS t1i')
-    box.sql.execute('CREATE TRIGGER t1i '..prefix..' INSERT ON t1 FOR EACH ROW \
+    box.execute('DROP TRIGGER IF EXISTS t1i')
+    box.execute('CREATE TRIGGER t1i '..prefix..' INSERT ON t1 FOR EACH ROW \
                      BEGIN INSERT INTO t2 VALUES (1,1); END')
 
     test:do_catchsql_test(prefix..'_insert1',
                           'INSERT INTO t1 VALUES(1, 2)',
-                          {1,"Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     test:do_execsql_test(prefix..'_insert1_check1',
                          'SELECT *  FROM t1',
@@ -28,13 +28,13 @@ for _, prefix in pairs({"BEFORE", "AFTER"}) do
                          'SELECT *  FROM t2',
                          {1, 1})
 
-    box.sql.execute('DROP TRIGGER IF EXISTS t1u')
-    box.sql.execute('CREATE TRIGGER t1u '..prefix..' UPDATE ON t1 FOR EACH ROW \
+    box.execute('DROP TRIGGER IF EXISTS t1u')
+    box.execute('CREATE TRIGGER t1u '..prefix..' UPDATE ON t1 FOR EACH ROW \
                      BEGIN INSERT INTO t2 VALUES (1,1); END')
 
     test:do_catchsql_test(prefix..'_update1',
                           'UPDATE t1 SET s1=1',
-                          {1,"Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     test:do_execsql_test(prefix..'_update1_check1',
                          'SELECT *  FROM t1',
@@ -44,15 +44,15 @@ for _, prefix in pairs({"BEFORE", "AFTER"}) do
                          'SELECT *  FROM t2',
                          {1, 1})
 
-    box.sql.execute('DROP TRIGGER IF EXISTS t1ds')
+    box.execute('DROP TRIGGER IF EXISTS t1ds')
     -- FOR EACH STATEMENT
-    box.sql.execute('CREATE TRIGGER t1ds '..prefix..' DELETE ON t1 FOR EACH ROW\
+    box.execute('CREATE TRIGGER t1ds '..prefix..' DELETE ON t1 FOR EACH ROW\
                        BEGIN INSERT INTO t2 VALUES (2,2); \
                              INSERT INTO t2 VALUES (2,2); END')
 
     test:do_catchsql_test(prefix..'delete1',
                           'DELETE FROM t1;',
-                          {1, "Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     -- Nothing should be inserted due to abort
     test:do_execsql_test('delete1_check1',
@@ -69,16 +69,16 @@ end
 -- Check multi-insert
 test:do_catchsql_test('insert2',
                       'INSERT INTO t1 VALUES (5, 6), (6, 7)',
-                      {1, "Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                      {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 test:do_execsql_test('insert2_check',
                      'SELECT * FROM t1;',
                      {3, 3})
 
 -- Cleanup
-box.sql.execute('DROP TRIGGER IF EXISTS t1i')
-box.sql.execute('DROP TRIGGER IF EXISTS t1u')
-box.sql.execute('DROP TRIGGER IF EXISTS t1ds')
-box.sql.execute('DROP TABLE t1')
-box.sql.execute('DROP TABLE t2')
+box.execute('DROP TRIGGER IF EXISTS t1i')
+box.execute('DROP TRIGGER IF EXISTS t1u')
+box.execute('DROP TRIGGER IF EXISTS t1ds')
+box.execute('DROP TABLE t1')
+box.execute('DROP TABLE t2')
 
 test:finish_test()
diff --git a/test/sql-tap/gh2548-select-compound-limit.test.lua b/test/sql-tap/gh2548-select-compound-limit.test.lua
index 5494a66..3881932 100755
--- a/test/sql-tap/gh2548-select-compound-limit.test.lua
+++ b/test/sql-tap/gh2548-select-compound-limit.test.lua
@@ -14,41 +14,41 @@ for _, term in ipairs({'UNION', 'UNION ALL', 'INTERSECT', 'EXCEPT'}) do
                  function()
                      for i = 1,table_count do
                          drop_string = 'DROP TABLE IF EXISTS t' .. i .. ';\n'
-                         box.sql.execute(drop_string)
+                         box.execute(drop_string)
                      end
 
                      for i = 1,table_count do
                          create_string = 'CREATE TABLE t' .. i .. ' (s1 int primary key, s2 int);\n'
-                         box.sql.execute(create_string)
+                         box.execute(create_string)
                      end
 
                      for i = 1,table_count do
                          insert_string = 'INSERT INTO t' .. i .. ' VALUES (0,' .. i .. ');\n'
-                         box.sql.execute(insert_string)
+                         box.execute(insert_string)
                      end
 
                      for i = 1,table_count-1 do
                          if i > 1 then select_string = select_string .. ' ' .. term .. ' ' end
                          select_string = select_string .. 'SELECT * FROM t' .. i
                      end
-                     return pcall( function() box.sql.execute(select_string) end)
+                     return pcall( function() box.execute(select_string) end)
                  end,
                  true)
     test:do_test("Negative COMPOUND "..term,
                  function()
                      select_string = select_string .. ' ' .. term ..' ' .. 'SELECT * FROM t' .. table_count
-                     return  pcall(function() box.sql.execute(select_string) end)
+                     return  pcall(function() box.execute(select_string) end)
                  end,
                  false)
 
     select_string_last = select_string
 
---    if not pcall(function() box.sql.execute(select_string) end) then
+--    if not pcall(function() box.execute(select_string) end) then
 --        print('not ok')
 --    end
 
 --    select_string = select_string .. ' ' .. term ..' ' .. 'SELECT * FROM t' .. table_count
---    if pcall(function() box.sql.execute(select_string) end) then
+--    if pcall(function() box.execute(select_string) end) then
 --        print('not ok')
 --    end
 end
diff --git a/test/sql-tap/gh2964-abort.test.lua b/test/sql-tap/gh2964-abort.test.lua
index d4fcebc..82aa3e4 100755
--- a/test/sql-tap/gh2964-abort.test.lua
+++ b/test/sql-tap/gh2964-abort.test.lua
@@ -12,7 +12,7 @@ test:do_catchsql_test(
     test_prefix.."1.0.2",
     "CREATE TABLE t2 (a int primary key);")
 
-local insert_err = {1, "Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}
+local insert_err = {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}
 local data = {
 --id|TRIG TYPE|INSERT TYPE|insert error|commit error| result
  {1, "AFTER", "or abort",   insert_err, {0},          {1,1,2}},
diff --git a/test/sql-tap/index1.test.lua b/test/sql-tap/index1.test.lua
index b23e9b3..b6943a5 100755
--- a/test/sql-tap/index1.test.lua
+++ b/test/sql-tap/index1.test.lua
@@ -112,7 +112,7 @@ test:do_test(
             end)
         v = v == true and {0} or {1}
         test:execsql("DROP TABLE test1")
-        return table.insert(v,msg) or v
+        return table.insert(v,tostring(msg)) or v
     end, {
         -- <index-2.2>
         1, "Can’t resolve field 'F4'"
diff --git a/test/sql-tap/intpkey.test.lua b/test/sql-tap/intpkey.test.lua
index bec2670..3ec3771 100755
--- a/test/sql-tap/intpkey.test.lua
+++ b/test/sql-tap/intpkey.test.lua
@@ -96,7 +96,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(5,'second','entry');
     ]], {
         -- <intpkey-1.6>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </intpkey-1.6>
     })
 
diff --git a/test/sql-tap/limit.test.lua b/test/sql-tap/limit.test.lua
index a0c9c9f..632c634 100755
--- a/test/sql-tap/limit.test.lua
+++ b/test/sql-tap/limit.test.lua
@@ -731,12 +731,10 @@ test:do_test(
     "limit-10.4",
     function()
         local limit = 1.5
-        return {pcall(function()
-            return test:execsql("SELECT x FROM t1 WHERE x<10 LIMIT "..limit)
-        end)}
+        return test:catchsql("SELECT x FROM t1 WHERE x<10 LIMIT "..limit)
     end, {
         -- <limit-10.4>
-        0, "Only positive integers are allowed in the LIMIT clause"
+        1, "Only positive integers are allowed in the LIMIT clause"
         -- </limit-10.4>
     })
 
@@ -744,12 +742,10 @@ test:do_test(
     "limit-10.5",
     function()
         local limit = "'hello world'"
-        return {pcall(function()
-            return test:execsql("SELECT x FROM t1 WHERE x<10 LIMIT "..limit)
-        end)}
+        return test:catchsql("SELECT x FROM t1 WHERE x<10 LIMIT "..limit)
     end, {
         -- <limit-10.5>
-        0, "Only positive integers are allowed in the LIMIT clause"
+        1, "Only positive integers are allowed in the LIMIT clause"
         -- </limit-10.5>
     })
 
diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index 63c5d9b..cb77bc4 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -16,6 +16,8 @@ local function flatten(arr)
         for _, v in ipairs(arr) do
             if type(v) == "table" then
                 flatten(v)
+            elseif box.tuple.is(v) then
+                flatten(v:totable())
             else
                 table.insert(result, v)
             end
@@ -169,11 +171,16 @@ test.do_test = do_test
 
 local function execsql_one_by_one(sql)
     local queries = sql_tokenizer.split_sql(sql)
-    local last_res = nil
+    local last_res_rows = nil
+    local last_res_metadata = nil
     for _, query in pairs(queries) do
-        last_res = box.sql.execute(query) or last_res
+        local new_res = box.execute(query)
+        if new_res ~= nil and new_res.rows ~= nil then
+            last_res_rows = new_res.rows
+            last_res_metadata = new_res.metadata
+        end
     end
-    return last_res
+    return last_res_rows, last_res_metadata
 end
 
 local function execsql(self, sql)
@@ -196,6 +203,9 @@ local function catchsql(self, sql, expect)
         r[1] = 0
     else
         r[1] = 1
+        if type(r[2]) == 'cdata' then
+            r[2] = tostring(r[2])
+        end
     end
     return r
 end
@@ -221,12 +231,11 @@ local function do_execsql2_test(self, label, sql, expect)
 end
 test.do_execsql2_test = do_execsql2_test
 
-local function flattern_with_column_names(result)
+local function flattern_with_column_names(result, metadata)
     local ret = {}
-    local columns = result[0]
     for i = 1, #result, 1 do
-        for j = 1, #columns, 1 do
-            table.insert(ret, columns[j])
+        for j = 1, #metadata, 1 do
+            table.insert(ret, metadata[j].name)
             table.insert(ret, result[i][j])
         end
     end
@@ -252,10 +261,10 @@ function test.do_catchsql_set_test(self, testcases, prefix)
 end
 
 local function execsql2(self, sql)
-    local result = execsql_one_by_one(sql)
+    local result, metadata = execsql_one_by_one(sql)
     if type(result) ~= 'table' then return end
     -- shift rows down, revealing column names
-    result = flattern_with_column_names(result)
+    result = flattern_with_column_names(result, metadata)
     return result
 end
 test.execsql2 = execsql2
@@ -272,6 +281,9 @@ local function catchsql2(self, sql)
     -- 0 means ok
     -- 1 means not ok
     r[1] = r[1] == true and 0 or 1
+    if r[1] == 1 then
+        r[2] = tostring(r[2])
+    end
     return r
 end
 test.catchsql2 = catchsql2
@@ -403,7 +415,16 @@ test.do_eqp_test = function (self, label, sql, result)
     test:do_test(
         label,
         function()
-            return execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
+            local result = execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
+            local res = {}
+            for k,v in pairs(result) do
+                res[k] = {}
+                res[k][1] = v[1]
+                res[k][2] = v[2]
+                res[k][3] = v[3]
+                res[k][4] = tostring(v[4])
+            end
+            return res
         end,
         result)
 end
@@ -424,7 +445,7 @@ box.cfg{
 }
 
 local engine = test_run and test_run:get_cfg('engine') or 'memtx'
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 function test.engine(self)
     return engine
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 1b288da..55c0c13 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -383,7 +383,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(1,2,4);
     ]], {
         -- <misc1-7.4>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T5_1' in space 'T5'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T5_1' in space 'T5'"
         -- </misc1-7.4>
     })
 
diff --git a/test/sql-tap/orderby9.test.lua b/test/sql-tap/orderby9.test.lua
index 191c21b..13f9ec6 100755
--- a/test/sql-tap/orderby9.test.lua
+++ b/test/sql-tap/orderby9.test.lua
@@ -41,6 +41,7 @@ test:do_test(
         -- separately for the result set and the ORDER BY clause, then the output
         -- order will be random.
         local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY 1;")
+        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
         local l2 = table.deepcopy(l1)
         table.sort(l1)
         return test.is_deeply_regex(l1, l2)
@@ -50,6 +51,7 @@ test:do_test(
     1.1,
     function()
         local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY random();")
+        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
         local l2 = table.deepcopy(l1)
         table.sort(l1)
         return test.is_deeply_regex(l1, l2)
diff --git a/test/sql-tap/pragma.test.lua b/test/sql-tap/pragma.test.lua
index 14d622f..8221d36 100755
--- a/test/sql-tap/pragma.test.lua
+++ b/test/sql-tap/pragma.test.lua
@@ -86,10 +86,10 @@ test:do_execsql_test(
 test:do_test(
 	"pragma-3.3",
 	function()
-		old_value = box.sql.execute('PRAGMA case_sensitive_like')
-		box.sql.execute('PRAGMA case_sensitive_like = 1')
-		new_value = box.sql.execute('PRAGMA case_sensitive_like')
-		box.sql.execute('PRAGMA case_sensitive_like = '.. old_value[1][1])
+		old_value = box.execute('PRAGMA case_sensitive_like').rows
+		box.execute('PRAGMA case_sensitive_like = 1')
+		new_value = box.execute('PRAGMA case_sensitive_like').rows
+		box.execute('PRAGMA case_sensitive_like = '.. old_value[1][1])
 		return new_value[1][1]
 	end,
 	-- <pragma-3.3>
diff --git a/test/sql-tap/select1.test.lua b/test/sql-tap/select1.test.lua
index d73429a..31955e3 100755
--- a/test/sql-tap/select1.test.lua
+++ b/test/sql-tap/select1.test.lua
@@ -1567,22 +1567,20 @@ test:do_execsql_test(
 test:do_test(
     "select1-9.2",
     function()
-        local r = box.sql.execute "SELECT * FROM test1 WHERE f1<0"
-        return r[0]
+        return box.execute("SELECT * FROM test1 WHERE f1<0").metadata
     end, {
         -- <select1-9.2>
-        "F1", "F2"
+        {name = F1, type = INTEGER},{name = F2, type = INTEGER}
         -- </select1-9.2>
     })
 
 test:do_test(
         "select1-9.3",
         function()
-            local r = box.sql.execute "SELECT * FROM test1 WHERE f1<(select count(*) from test2)"
-            return r[0]
+            return box.execute("SELECT * FROM test1 WHERE f1<(select count(*) from test2)").metadata
         end, {
             -- <select1-9.3>
-            "F1", "F2"
+            {name = F1, type = INTEGER},{name = F2, type = INTEGER}
             -- </select1-9.3>
         })
 
@@ -1591,22 +1589,20 @@ test:do_test(
 test:do_test(
     "select1-9.4",
     function()
-        local r = box.sql.execute "SELECT * FROM test1 ORDER BY f1"
-        return r[0]
+        return box.execute("SELECT * FROM test1 ORDER BY f1").metadata
     end, {
         -- <select1-9.4>
-        "F1", "F2"
+        {name = F1, type = INTEGER},{name = F2, type = INTEGER}
         -- </select1-9.4>
     })
 
 test:do_test(
     "select1-9.5",
     function()
-        local r = box.sql.execute "SELECT * FROM test1 WHERE f1<0 ORDER BY f1"
-        return r[0]
+        return box.execute("SELECT * FROM test1 WHERE f1<0 ORDER BY f1").metadata
     end, {
         -- <select1-9.5>
-        "F1", "F2"
+        {name = F1, type = INTEGER},{name = F2, type = INTEGER}
         -- </select1-9.5>
     })
 
diff --git a/test/sql-tap/select9.test.lua b/test/sql-tap/select9.test.lua
index d1883e9..a54010f 100755
--- a/test/sql-tap/select9.test.lua
+++ b/test/sql-tap/select9.test.lua
@@ -61,8 +61,9 @@ end
 
 local function test_compound_select(testname, sql, result)
     local nCol = 1
-    local A = box.sql.execute(sql) --test.box(sql)
-    nCol = #A[0]
+    local A = box.execute(sql) --test.box(sql)
+    nCol = #A.metadata
+    A = A.rows
     local nRow = #result / nCol
     local compound_sql = sql
     test:do_execsql_test(
diff --git a/test/sql-tap/selectB.test.lua b/test/sql-tap/selectB.test.lua
index 7f57cd2..56d4b9d 100755
--- a/test/sql-tap/selectB.test.lua
+++ b/test/sql-tap/selectB.test.lua
@@ -25,11 +25,11 @@ local function test_transform(testname, sql1, sql2, results)
     -- opcodes only (line[2]) of explain command)
     local vdbe1 = {  }
     local vdbe2 = {  }
-    local data = box.sql.execute("explain "..sql1)
+    local data = box.execute("explain "..sql1)
     for i, line in ipairs(data) do
         table.insert(vdbe1, line[2])
     end
-    data = box.sql.execute("explain "..sql2)
+    data = box.execute("explain "..sql2)
     for i, line in ipairs(data) do
         table.insert(vdbe2, line[2])
     end
diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
index c89d890..4470bce 100755
--- a/test/sql-tap/table.test.lua
+++ b/test/sql-tap/table.test.lua
@@ -722,7 +722,7 @@ test:do_catchsql_test(
         INSERT INTO t6 VALUES(NULL);
     ]], {
         -- <table-10.1>
-        1, "NOT NULL constraint failed: T6.A"
+        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: T6.A"
         -- </table-10.1>
     })
 
@@ -977,7 +977,7 @@ end
 -- MUST_WORK_TEST database should be locked #2554
 if 0>0 then
 local function try_drop_t9()
-    box.sql.execute("DROP TABLE t9;")
+    box.execute("DROP TABLE t9;")
     return 1
 end
 box.internal.sql_create_function("try_drop_t9", try_drop_t9)
@@ -1211,7 +1211,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, 2, 2);
     ]], {
         -- <table-21.2>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T21_1' in space 'T21'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T21_1' in space 'T21'"
         -- </table-21.2>
     })
 
@@ -1221,7 +1221,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, -1, 1);
     ]], {
         -- <table-21.3>
-        1, "CHECK constraint failed: T21"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T21"
         -- </table-21.3>
     })
 
@@ -1231,7 +1231,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, 1, -1);
     ]], {
         -- <table-21.4>
-        1, "CHECK constraint failed: T21"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T21"
         -- </table-21.4>
     })
 
@@ -1283,7 +1283,7 @@ test:do_catchsql_test(
         INSERT INTO T22 VALUES(2, 1, 1);
     ]], {
         -- <table-22.3>
-        1,"Duplicate key exists in unique index 'unique_ONE_2' in space 'T22'"
+        1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_ONE_2' in space 'T22'"
         -- </table-22.3>
     })
 
@@ -1308,7 +1308,7 @@ test:do_catchsql_test(
         INSERT INTO T24 VALUES(2, 1, 1);
     ]], {
         -- <table-22.5>
-        1, "Duplicate key exists in unique index 'unique_TWO_2' in space 'T24'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_TWO_2' in space 'T24'"
         -- </table-22.5>
     })
 
@@ -1362,7 +1362,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(11);
     ]], {
         -- <table-22.9>
-        1,"Duplicate key exists in unique index 'pk_unnamed_T28_1' in space 'T28'"
+        1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T28_1' in space 'T28'"
         -- </table-22.9>
     })
 
@@ -1372,7 +1372,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(0);
     ]], {
         -- <table-22.10>
-        1, "CHECK constraint failed: CHECK1"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: CHECK1"
         -- </table-22.10>
     })
 
@@ -1382,7 +1382,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(9);
     ]], {
         -- <table-22.11>
-        1, "CHECK constraint failed: CHECK2"
+        1, "Error during execution of VDBE byte-code: CHECK constraint failed: CHECK2"
         -- </table-22.11>
     })
 
diff --git a/test/sql-tap/tkt-4a03edc4c8.test.lua b/test/sql-tap/tkt-4a03edc4c8.test.lua
index cb7a982..703bf74 100755
--- a/test/sql-tap/tkt-4a03edc4c8.test.lua
+++ b/test/sql-tap/tkt-4a03edc4c8.test.lua
@@ -38,7 +38,7 @@ test:do_test(
         ]]
     end, {
         -- <tkt-4a03ed-1.1>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </tkt-4a03ed-1.1>
     })
 
diff --git a/test/sql-tap/trigger1.test.lua b/test/sql-tap/trigger1.test.lua
index 2984d4c..bf1857f 100755
--- a/test/sql-tap/trigger1.test.lua
+++ b/test/sql-tap/trigger1.test.lua
@@ -251,7 +251,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.12>
-        1, "SQL error: cannot create INSTEAD OF trigger on space: T1"
+        1, "Error during execution of VDBE byte-code: SQL error: cannot create INSTEAD OF trigger on space: T1"
         -- </trigger1-1.12>
     })
 
@@ -265,7 +265,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.13>
-        1, "SQL error: cannot create BEFORE trigger on view: V1"
+        1, "Error during execution of VDBE byte-code: SQL error: cannot create BEFORE trigger on view: V1"
         -- </trigger1-1.13>
     })
 
@@ -280,7 +280,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.14>
-        1, "SQL error: cannot create AFTER trigger on view: V1"
+        1, "Error during execution of VDBE byte-code: SQL error: cannot create AFTER trigger on view: V1"
         -- </trigger1-1.14>
     })
 
@@ -495,7 +495,7 @@ test:do_catchsql_test(
         DELETE FROM t2
     ]], {
         -- <trigger1-6.3>
-        1, "deletes are not permitted"
+        1, "Error during execution of VDBE byte-code: deletes are not permitted"
         -- </trigger1-6.3>
     })
 
@@ -857,7 +857,7 @@ test:do_catchsql_test(
             INSERT INTO t16 values(1);
           END;
    ]], {
-        1, [[Space _trigger does not support multi-statement transactions]]
+        1, [[Error during execution of VDBE byte-code: Space _trigger does not support multi-statement transactions]]
 })
 
 test:execsql [[
@@ -870,7 +870,7 @@ test:do_catchsql_test(
         START TRANSACTION;
           DROP TRIGGER t16err3;
    ]], {
-        1, [[Space _trigger does not support multi-statement transactions]]
+        1, [[Error during execution of VDBE byte-code: Space _trigger does not support multi-statement transactions]]
 })
 -- MUST_WORK_TEST
 -- #-------------------------------------------------------------------------
diff --git a/test/sql-tap/triggerC.test.lua b/test/sql-tap/triggerC.test.lua
index c0ef605..76558f1 100755
--- a/test/sql-tap/triggerC.test.lua
+++ b/test/sql-tap/triggerC.test.lua
@@ -165,7 +165,7 @@ test:do_test(
         return test:catchsql " DELETE FROM t4 "
     end, {
         -- <triggerC-1.9>
-        1, "delete is not supported"
+        1, "Error during execution of VDBE byte-code: delete is not supported"
         -- </triggerC-1.9>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1
     ]], {
         -- <triggerC-1.12>
-        1, "too many levels of trigger recursion"
+        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
         -- </triggerC-1.12>
     })
 
@@ -285,7 +285,7 @@ tests =   { {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
 
             {[[ CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN
                   INSERT INTO t2 VALUES(new.a - 1);
-                END;]], {1, "too many levels of trigger recursion"}},
+                END;]], {1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"}},
 
             {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
                   INSERT OR IGNORE INTO t2 VALUES(new.a);
@@ -293,7 +293,7 @@ tests =   { {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
 
             {[[  CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN
                    INSERT OR IGNORE INTO t2 VALUES(new.a);
-                 END;]], {1, "too many levels of trigger recursion"}}}
+                 END;]], {1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"}}}
 
 for n, v in ipairs(tests) do
     test:do_test(
@@ -382,7 +382,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(0,0)
     ]], {
         -- <triggerC-3.1.2>
-        1, "too many levels of trigger recursion"
+        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
         -- </triggerC-3.1.2>
     })
 
@@ -411,7 +411,7 @@ test:do_catchsql_test(
         INSERT INTO t3b VALUES(1);
     ]], {
         -- <triggerC-3.1.3>
-        1, "too many levels of trigger recursion"
+        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
         -- </triggerC-3.1.3>
     })
 
@@ -908,7 +908,7 @@ test:do_catchsql_test(
         UPDATE t12 SET a=a+1, b=b+1;
     ]], {
         -- <triggerC-13.2>
-        1, "too many levels of trigger recursion"
+        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
         -- </triggerC-13.2>
     })
 
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 56ac74a..927c419 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -70,7 +70,7 @@ test:do_catchsql_test(
         INSERT INTO t1(a,b,c) VALUES(1,3,4)
     ]], {
         -- <unique-1.3>
-        1, "Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </unique-1.3>
     })
 
@@ -91,7 +91,7 @@ test:do_catchsql_test(
         INSERT INTO t1(a,b,c) VALUES(3,2,4)
     ]], {
         -- <unique-1.5>
-        1, "Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
         -- </unique-1.5>
     })
 
@@ -167,7 +167,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(3, 1,5);
     ]], {
         -- <unique-2.3>
-        1, "Duplicate key exists in unique index 'I2' in space 'T2'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'I2' in space 'T2'"
         -- </unique-2.3>
     })
 
@@ -287,7 +287,7 @@ test:do_catchsql_test(
         SELECT a,b,c,d FROM t3 ORDER BY a,b,c,d;
     ]], {
         -- <unique-3.4>
-        1, "Duplicate key exists in unique index 'unique_unnamed_T3_2' in space 'T3'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T3_2' in space 'T3'"
         -- </unique-3.4>
     })
 
@@ -444,7 +444,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 1,2,3,4,5,6);
     ]], {
         -- <unique-5.2>
-        1, "Duplicate key exists in unique index 'unique_unnamed_T5_2' in space 'T5'"
+        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T5_2' in space 'T5'"
         -- </unique-5.2>
     })
 
diff --git a/test/sql/check-clear-ephemeral.result b/test/sql/check-clear-ephemeral.result
index 6fb0357..9fd6b87 100644
--- a/test/sql/check-clear-ephemeral.result
+++ b/test/sql/check-clear-ephemeral.result
@@ -4,24 +4,31 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
+box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
+box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
 ---
+- rowcount: 1000
 ...
 -- Ephemeral table is not belong to Tarantool, so must be cleared sql-way.
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
-- - [840]
+- metadata:
+  - name: A
+    type: BLOB
+  rows:
+  - [840]
   - [880]
   - [920]
   - [960]
@@ -33,8 +40,9 @@ box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
   - [161]
 ...
 -- Cleanup
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/check-clear-ephemeral.test.lua b/test/sql/check-clear-ephemeral.test.lua
index d6fb3a2..929a6c9 100644
--- a/test/sql/check-clear-ephemeral.test.lua
+++ b/test/sql/check-clear-ephemeral.test.lua
@@ -1,22 +1,22 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
+box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
+box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
 
 -- Ephemeral table is not belong to Tarantool, so must be cleared sql-way.
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 
 -- Cleanup
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/checks.result b/test/sql/checks.result
index 42df657..108fea6 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -11,8 +11,9 @@ test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 --
 -- gh-3272: Move SQL CHECK into server
@@ -109,18 +110,18 @@ s = box.space._space:insert(t)
 --
 -- gh-3611: Segfault on table creation with check referencing this table
 --
-box.sql.execute("CREATE TABLE w2 (s1 INT PRIMARY KEY, CHECK ((SELECT COUNT(*) FROM w2) = 0));")
+box.execute("CREATE TABLE w2 (s1 INT PRIMARY KEY, CHECK ((SELECT COUNT(*) FROM w2) = 0));")
 ---
 - error: 'Failed to create space ''W2'': Space ''W2'' does not exist'
 ...
-box.sql.execute("DROP TABLE w2;")
+box.execute("DROP TABLE w2;")
 ---
 - error: Space 'W2' does not exist
 ...
 --
 -- gh-3653: Dissallow bindings for DDL
 --
-box.sql.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
+box.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
 ---
 - error: 'Wrong space options (field 5): invalid expression specified (bindings are
     not allowed in DDL)'
diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
index 0582bbb..5bfcf12 100644
--- a/test/sql/checks.test.lua
+++ b/test/sql/checks.test.lua
@@ -2,7 +2,7 @@ env = require('test_run')
 test_run = env.new()
 test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 --
 -- gh-3272: Move SQL CHECK into server
@@ -47,13 +47,13 @@ s = box.space._space:insert(t)
 --
 -- gh-3611: Segfault on table creation with check referencing this table
 --
-box.sql.execute("CREATE TABLE w2 (s1 INT PRIMARY KEY, CHECK ((SELECT COUNT(*) FROM w2) = 0));")
-box.sql.execute("DROP TABLE w2;")
+box.execute("CREATE TABLE w2 (s1 INT PRIMARY KEY, CHECK ((SELECT COUNT(*) FROM w2) = 0));")
+box.execute("DROP TABLE w2;")
 
 --
 -- gh-3653: Dissallow bindings for DDL
 --
-box.sql.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
+box.execute("CREATE TABLE t5(x INT PRIMARY KEY, y INT, CHECK( x*y < ? ));")
 
 opts = {checks = {{expr = '?>5', name = 'ONE'}}}
 format = {{name = 'X', type = 'unsigned'}}
diff --git a/test/sql/clear.result b/test/sql/clear.result
index 9d4e9d3..0694a1e 100644
--- a/test/sql/clear.result
+++ b/test/sql/clear.result
@@ -4,27 +4,40 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON;")
+-- box.execute("PRAGMA vdbe_debug=ON;")
 -- Seed entry
-for i=1, 100 do box.sql.execute(string.format("INSERT INTO zoobar VALUES (%d, %d, 'c3', 444)", i+i, i)) end
+for i=1, 100 do box.execute(string.format("INSERT INTO zoobar VALUES (%d, %d, 'c3', 444)", i+i, i)) end
 ---
 ...
 -- Check table is not empty
-box.sql.execute("SELECT * FROM zoobar")
+box.execute("SELECT * FROM zoobar")
 ---
-- - [2, 1, 'c3', 444]
+- metadata:
+  - name: C1
+    type: INTEGER
+  - name: C2
+    type: INTEGER
+  - name: C3
+    type: TEXT
+  - name: C4
+    type: INTEGER
+  rows:
+  - [2, 1, 'c3', 444]
   - [4, 2, 'c3', 444]
   - [6, 3, 'c3', 444]
   - [8, 4, 'c3', 444]
@@ -126,20 +139,32 @@ box.sql.execute("SELECT * FROM zoobar")
   - [200, 100, 'c3', 444]
 ...
 -- Do clean up
-box.sql.execute("DELETE FROM zoobar")
+box.execute("DELETE FROM zoobar")
 ---
+- rowcount: 100
 ...
 -- Make sure table is empty
-box.sql.execute("SELECT * from zoobar")
+box.execute("SELECT * from zoobar")
 ---
-- []
+- metadata:
+  - name: C1
+    type: INTEGER
+  - name: C2
+    type: INTEGER
+  - name: C3
+    type: TEXT
+  - name: C4
+    type: INTEGER
+  rows: []
 ...
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zoobar")
+box.execute("DROP INDEX zoobar2 ON zoobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE zoobar")
+box.execute("DROP TABLE zoobar")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/clear.test.lua b/test/sql/clear.test.lua
index 78923f1..c67a447 100644
--- a/test/sql/clear.test.lua
+++ b/test/sql/clear.test.lua
@@ -1,31 +1,31 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
+box.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON;")
+-- box.execute("PRAGMA vdbe_debug=ON;")
 
 -- Seed entry
-for i=1, 100 do box.sql.execute(string.format("INSERT INTO zoobar VALUES (%d, %d, 'c3', 444)", i+i, i)) end
+for i=1, 100 do box.execute(string.format("INSERT INTO zoobar VALUES (%d, %d, 'c3', 444)", i+i, i)) end
 
 -- Check table is not empty
-box.sql.execute("SELECT * FROM zoobar")
+box.execute("SELECT * FROM zoobar")
 
 -- Do clean up
-box.sql.execute("DELETE FROM zoobar")
+box.execute("DELETE FROM zoobar")
 
 -- Make sure table is empty
-box.sql.execute("SELECT * from zoobar")
+box.execute("SELECT * from zoobar")
 
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zoobar")
-box.sql.execute("DROP TABLE zoobar")
+box.execute("DROP INDEX zoobar2 ON zoobar")
+box.execute("DROP TABLE zoobar")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 10872ca..cc1b1a9 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -7,63 +7,85 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- gh-3010: COLLATE after LIMIT should throw an error
 -- All of these tests should throw error "near "COLLATE": syntax error"
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY;")
 ---
 - error: Syntax error near 'COLLATE'
 ...
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
 ---
 - error: Syntax error near 'COLLATE'
 ...
-box.sql.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
 ---
 - error: Syntax error near 'COLLATE'
 ...
-box.sql.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
 ---
 - error: Syntax error near 'COLLATE'
 ...
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
 ---
 - error: Syntax error near 'COLLATE'
 ...
 -- gh-3052: upper/lower support only default locale
 -- For tr-TR result depends on collation
-box.sql.execute([[CREATE TABLE tu (descriptor VARCHAR(50) PRIMARY KEY, letter VARCHAR(50))]]);
+box.execute([[CREATE TABLE tu (descriptor VARCHAR(50) PRIMARY KEY, letter VARCHAR(50))]]);
 ---
+- rowcount: 1
 ...
 box.internal.collation.create('TURKISH', 'ICU', 'tr-TR', {strength='primary'});
 ---
 ...
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I U+0049','I');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I U+0049','I');]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Small Letter I U+0069','i');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Small Letter I U+0069','i');]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I With Dot Above U+0130','İ');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I With Dot Above U+0130','İ');]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Small Letter Dotless I U+0131','ı');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Small Letter Dotless I U+0131','ı');]])
 ---
+- rowcount: 1
 ...
 -- Without collation
-box.sql.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower FROM tu;]])
----
-- - ['Latin Capital Letter I U+0049', 'I', 'i']
+box.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower FROM tu;]])
+---
+- metadata:
+  - name: DESCRIPTOR
+    type: TEXT
+  - name: UPPER
+    type: TEXT
+  - name: LOWER
+    type: TEXT
+  rows:
+  - ['Latin Capital Letter I U+0049', 'I', 'i']
   - ['Latin Capital Letter I With Dot Above U+0130', 'İ', 'i̇']
   - ['Latin Small Letter Dotless I U+0131', 'I', 'ı']
   - ['Latin Small Letter I U+0069', 'I', 'i']
 ...
 -- With collation
-box.sql.execute([[SELECT descriptor, upper(letter COLLATE "TURKISH") AS upper,lower(letter COLLATE "TURKISH") AS lower FROM tu;]])
----
-- - ['Latin Capital Letter I U+0049', 'I', 'ı']
+box.execute([[SELECT descriptor, upper(letter COLLATE "TURKISH") AS upper,lower(letter COLLATE "TURKISH") AS lower FROM tu;]])
+---
+- metadata:
+  - name: DESCRIPTOR
+    type: TEXT
+  - name: UPPER
+    type: TEXT
+  - name: LOWER
+    type: TEXT
+  rows:
+  - ['Latin Capital Letter I U+0049', 'I', 'ı']
   - ['Latin Capital Letter I With Dot Above U+0130', 'İ', 'i']
   - ['Latin Small Letter Dotless I U+0131', 'I', 'ı']
   - ['Latin Small Letter I U+0069', 'İ', 'i']
@@ -75,24 +97,42 @@ box.internal.collation.drop('TURKISH')
 box.internal.collation.create('GERMAN', 'ICU', 'de-DE', {strength='primary'});
 ---
 ...
-box.sql.execute([[INSERT INTO tu VALUES ('German Small Letter Sharp S U+00DF','ß');]])
+box.execute([[INSERT INTO tu VALUES ('German Small Letter Sharp S U+00DF','ß');]])
 ---
+- rowcount: 1
 ...
 -- Without collation
-box.sql.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(letter) = 'SS';]])
----
-- - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
+box.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(letter) = 'SS';]])
+---
+- metadata:
+  - name: DESCRIPTOR
+    type: TEXT
+  - name: upper(letter)
+    type: TEXT
+  - name: LETTER
+    type: TEXT
+  rows:
+  - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
 ...
 -- With collation
-box.sql.execute([[SELECT descriptor, upper(letter COLLATE "GERMAN"), letter FROM tu where UPPER(letter COLLATE "GERMAN") = 'SS';]])
----
-- - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
+box.execute([[SELECT descriptor, upper(letter COLLATE "GERMAN"), letter FROM tu where UPPER(letter COLLATE "GERMAN") = 'SS';]])
+---
+- metadata:
+  - name: DESCRIPTOR
+    type: TEXT
+  - name: upper(letter COLLATE "GERMAN")
+    type: TEXT
+  - name: LETTER
+    type: TEXT
+  rows:
+  - ['German Small Letter Sharp S U+00DF', 'SS', 'ß']
 ...
 box.internal.collation.drop('GERMAN')
 ---
 ...
-box.sql.execute(([[DROP TABLE tu]]))
+box.execute(([[DROP TABLE tu]]))
 ---
+- rowcount: 1
 ...
 box.schema.user.grant('guest','read,write,execute', 'universe')
 ---
@@ -109,8 +149,9 @@ cn:close()
 ...
 -- Explicitly set BINARY collation is predifined and has ID.
 --
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\");")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\");")
 ---
+- rowcount: 1
 ...
 box.space.T:format()[2]['collation']
 ---
@@ -120,8 +161,9 @@ box.space.T:format()[3]['collation']
 ---
 - 3
 ...
-box.sql.execute("DROP TABLE t;")
+box.execute("DROP TABLE t;")
 ---
+- rowcount: 1
 ...
 -- BINARY collation works in the same way as there is no collation
 -- at all.
@@ -179,66 +221,116 @@ box.space._collation:delete{0}
 ...
 -- gh-3185: collations of LHS and RHS must be compatible.
 --
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\", c TEXT COLLATE \"unicode_ci\");")
----
-...
-box.sql.execute("SELECT * FROM t WHERE a = b;")
----
-- []
-...
-box.sql.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = b;")
----
-- []
-...
-box.sql.execute("SELECT * FROM t WHERE b = c;")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\", c TEXT COLLATE \"unicode_ci\");")
+---
+- rowcount: 1
+...
+box.execute("SELECT * FROM t WHERE a = b;")
+---
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: TEXT
+  - name: B
+    type: TEXT
+  - name: C
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = b;")
+---
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: TEXT
+  - name: B
+    type: TEXT
+  - name: C
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT * FROM t WHERE b = c;")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("SELECT * FROM t WHERE b COLLATE \"binary\" = c;")
----
-- []
-...
-box.sql.execute("SELECT * FROM t WHERE a = c;")
----
-- []
-...
-box.sql.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";")
+box.execute("SELECT * FROM t WHERE b COLLATE \"binary\" = c;")
+---
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: TEXT
+  - name: B
+    type: TEXT
+  - name: C
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT * FROM t WHERE a = c;")
+---
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: TEXT
+  - name: B
+    type: TEXT
+  - name: C
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";")
 ---
 - error: Illegal mix of collations
 ...
 -- Compound queries perform implicit comparisons between values.
 -- Hence, rules for collations compatibilities are the same.
 --
-box.sql.execute("SELECT 'abc' COLLATE \"binary\" UNION SELECT 'ABC' COLLATE \"unicode_ci\"")
+box.execute("SELECT 'abc' COLLATE \"binary\" UNION SELECT 'ABC' COLLATE \"unicode_ci\"")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("SELECT 'abc' COLLATE \"unicode_ci\" UNION SELECT 'ABC' COLLATE binary")
+box.execute("SELECT 'abc' COLLATE \"unicode_ci\" UNION SELECT 'ABC' COLLATE binary")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("SELECT c FROM t UNION SELECT b FROM t;")
+box.execute("SELECT c FROM t UNION SELECT b FROM t;")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("SELECT b FROM t UNION SELECT a FROM t;")
+box.execute("SELECT b FROM t UNION SELECT a FROM t;")
 ---
-- []
+- metadata:
+  - name: B
+    type: TEXT
+  rows: []
 ...
-box.sql.execute("SELECT a FROM t UNION SELECT c FROM t;")
+box.execute("SELECT a FROM t UNION SELECT c FROM t;")
 ---
-- []
+- metadata:
+  - name: A
+    type: TEXT
+  rows: []
 ...
-box.sql.execute("SELECT c COLLATE \"binary\" FROM t UNION SELECT a FROM t;")
+box.execute("SELECT c COLLATE \"binary\" FROM t UNION SELECT a FROM t;")
 ---
-- []
+- metadata:
+  - name: c COLLATE "binary"
+    type: TEXT
+  rows: []
 ...
-box.sql.execute("SELECT b COLLATE \"unicode\" FROM t UNION SELECT a FROM t;")
+box.execute("SELECT b COLLATE \"unicode\" FROM t UNION SELECT a FROM t;")
 ---
-- []
+- metadata:
+  - name: b COLLATE "unicode"
+    type: TEXT
+  rows: []
 ...
-box.sql.execute("DROP TABLE t;")
+box.execute("DROP TABLE t;")
 ---
+- rowcount: 1
 ...
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ---
@@ -251,7 +343,7 @@ box.session.su('tmp')
 ---
 ...
 -- Error: read access to space is denied.
-box.sql.execute("pragma collation_list")
+box.execute("pragma collation_list")
 ---
 - error: Read access to space '_collation' is denied for user 'tmp'
 ...
@@ -263,160 +355,237 @@ box.schema.user.drop('tmp')
 ...
 -- gh-3644 Foreign key update fails with "unicode_ci".
 -- Check that foreign key update doesn't fail with "unicode_ci".
-box.sql.execute('CREATE TABLE t0 (s1 VARCHAR(5) COLLATE "unicode_ci" UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
+box.execute('CREATE TABLE t0 (s1 VARCHAR(5) COLLATE "unicode_ci" UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) COLLATE "unicode_ci" REFERENCES t0(s1));')
+box.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) COLLATE "unicode_ci" REFERENCES t0(s1));')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t0(s1) VALUES ('a');")
+box.execute("INSERT INTO t0(s1) VALUES ('a');")
 ---
+- autoincrement_ids:
+  - 1
+  rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (1,'a');")
+box.execute("INSERT INTO t1 VALUES (1,'a');")
 ---
+- rowcount: 1
 ...
 -- Should't fail.
-box.sql.execute("UPDATE t0 SET s1 = 'A';")
+box.execute("UPDATE t0 SET s1 = 'A';")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT s1 FROM t0;")
+box.execute("SELECT s1 FROM t0;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
 ...
-box.sql.execute("SELECT * FROM t1;")
+box.execute("SELECT * FROM t1;")
 ---
-- - [1, 'a']
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S0
+    type: TEXT
+  rows:
+  - [1, 'a']
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t0;")
+box.execute("DROP TABLE t0;")
 ---
+- rowcount: 1
 ...
 -- Check that foreign key update fails with default collation.
-box.sql.execute('CREATE TABLE t0 (s1 VARCHAR(5) UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
+box.execute('CREATE TABLE t0 (s1 VARCHAR(5) UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) REFERENCES t0(s1));')
+box.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) REFERENCES t0(s1));')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t0(s1) VALUES ('a');")
+box.execute("INSERT INTO t0(s1) VALUES ('a');")
 ---
+- autoincrement_ids:
+  - 1
+  rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (1,'a');")
+box.execute("INSERT INTO t1 VALUES (1,'a');")
 ---
+- rowcount: 1
 ...
 -- Should fail.
-box.sql.execute("UPDATE t0 SET s1 = 'A';")
+box.execute("UPDATE t0 SET s1 = 'A';")
 ---
-- error: FOREIGN KEY constraint failed
+- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
 ...
-box.sql.execute("SELECT * FROM t1;")
+box.execute("SELECT * FROM t1;")
 ---
-- - [1, 'a']
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S0
+    type: TEXT
+  rows:
+  - [1, 'a']
 ...
-box.sql.execute("SELECT s1 FROM t0;")
+box.execute("SELECT s1 FROM t0;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t0;")
+box.execute("DROP TABLE t0;")
 ---
+- rowcount: 1
 ...
 -- gh-3937: result of concatination has derived collation.
 --
-box.sql.execute("CREATE TABLE t4a(a TEXT COLLATE \"unicode\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
+box.execute("CREATE TABLE t4a(a TEXT COLLATE \"unicode\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4a VALUES('ABC','abc',1);")
+box.execute("INSERT INTO t4a VALUES('ABC','abc',1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4a VALUES('ghi','ghi',3);")
+box.execute("INSERT INTO t4a VALUES('ghi','ghi',3);")
 ---
+- rowcount: 1
 ...
 -- Only LHS of concatenation has implicitly set collation.
 -- Hence, no collation is used.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||'') = b;")
+box.execute("SELECT c FROM t4a WHERE (a||'') = b;")
 ---
-- - [1]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [1]
   - [3]
 ...
 -- BINARY collation is forced for comparison operator as
 -- a derivation from concatenation.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b;")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b;")
 ---
-- - [3]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [3]
 ...
 -- Both operands of concatenation have explicit but different
 -- collations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'' COLLATE \"unicode_ci\") = b;")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'' COLLATE \"unicode_ci\") = b;")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b COLLATE \"unicode\";")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b COLLATE \"unicode\";")
 ---
 - error: Illegal mix of collations
 ...
 -- No collation is used since LHS and RHS of concatenation
 -- operator have different implicit collations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||'')=(b||'');")
+box.execute("SELECT c FROM t4a WHERE (a||'')=(b||'');")
 ---
-- - [3]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [3]
 ...
-box.sql.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 ---
-- - [3]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [3]
 ...
-box.sql.execute("CREATE TABLE t4b(a TEXT COLLATE \"unicode_ci\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
+box.execute("CREATE TABLE t4b(a TEXT COLLATE \"unicode_ci\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4b VALUES('BCD','bcd',1);")
+box.execute("INSERT INTO t4b VALUES('BCD','bcd',1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4b VALUES('ghi','ghi',3);")
+box.execute("INSERT INTO t4b VALUES('ghi','ghi',3);")
 ---
+- rowcount: 1
 ...
 -- Operands have the same implicit collation, so it is derived.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 ---
-- - [3]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [3]
 ...
 -- Couple of other possible combinations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b||a);")
 ---
-- - [3]
+- metadata:
+  - name: C
+    type: INTEGER
+  rows:
+  - [3]
 ...
-box.sql.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b COLLATE \"unicode_ci\"||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b COLLATE \"unicode_ci\"||a);")
 ---
 - error: Illegal mix of collations
 ...
-box.sql.execute("INSERT INTO t4b VALUES('abc', 'xxx', 2);")
+box.execute("INSERT INTO t4b VALUES('abc', 'xxx', 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4b VALUES('gHz', 'xxx', 4);")
+box.execute("INSERT INTO t4b VALUES('gHz', 'xxx', 4);")
 ---
+- rowcount: 1
 ...
 -- Results are sorted with case-insensitive order.
 -- Otherwise capital latters come first.
 --
-box.sql.execute("SELECT a FROM t4b ORDER BY a COLLATE \"unicode_ci\" || ''")
+box.execute("SELECT a FROM t4b ORDER BY a COLLATE \"unicode_ci\" || ''")
 ---
-- - ['abc']
+- metadata:
+  - name: A
+    type: TEXT
+  rows:
+  - ['abc']
   - ['BCD']
   - ['ghi']
   - ['gHz']
 ...
-box.sql.execute("SELECT a FROM t4b ORDER BY a || b")
+box.execute("SELECT a FROM t4b ORDER BY a || b")
 ---
-- - ['abc']
+- metadata:
+  - name: A
+    type: TEXT
+  rows:
+  - ['abc']
   - ['BCD']
   - ['ghi']
   - ['gHz']
@@ -429,358 +598,536 @@ box.space.T4B:drop()
 ...
 -- gh-3537 Duplicate key error for an index that is not unique
 -- pk - default, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3 (s1 VARCHAR(5) PRIMARY KEY);')
+box.execute('CREATE TABLE t3 (s1 VARCHAR(5) PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3 ON t3 (s1 collate "unicode_ci");')
+box.execute('CREATE INDEX i3 ON t3 (s1 collate "unicode_ci");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES ('a');")
+box.execute("INSERT INTO t3 VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES ('A');")
+box.execute("INSERT INTO t3 VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t3;")
+box.execute("SELECT * FROM t3;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
   - ['a']
 ...
-box.sql.execute("DROP TABLE t3;")
+box.execute("DROP TABLE t3;")
 ---
+- rowcount: 1
 ...
 -- pk - binary, sc - unicode
-box.sql.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
+box.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("INSERT INTO t3b VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3b VALUES ('A');")
+box.execute("INSERT INTO t3b VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t3b;")
+box.execute("SELECT * FROM t3b;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
   - ['a']
 ...
-box.sql.execute("DROP TABLE t3b;")
+box.execute("DROP TABLE t3b;")
 ---
+- rowcount: 1
 ...
 -- pk - binary, sc - unicode (make dup)
-box.sql.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
+box.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("INSERT INTO t3b VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3b VALUES ('A');")
+box.execute("INSERT INTO t3b VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("INSERT INTO t3b VALUES ('a');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3B_1' in space 'T3B'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3B_1'' in space ''T3B'''
 ...
-box.sql.execute("SELECT * FROM t3b;")
+box.execute("SELECT * FROM t3b;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
   - ['a']
 ...
-box.sql.execute("DROP TABLE t3b;")
+box.execute("DROP TABLE t3b;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode, sc - binary
-box.sql.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
+box.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("INSERT INTO t3c VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3c VALUES ('A');")
+box.execute("INSERT INTO t3c VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t3c;")
+box.execute("SELECT * FROM t3c;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
   - ['A']
 ...
-box.sql.execute("DROP TABLE t3c;")
+box.execute("DROP TABLE t3c;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode, sc - binary (make dup)
-box.sql.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
+box.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("INSERT INTO t3c VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3c VALUES ('A');")
+box.execute("INSERT INTO t3c VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("INSERT INTO t3c VALUES ('a');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3C_1' in space 'T3C'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3C_1'' in space ''T3C'''
 ...
-box.sql.execute("SELECT * FROM t3c;")
+box.execute("SELECT * FROM t3c;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
   - ['A']
 ...
-box.sql.execute("DROP TABLE t3c;")
+box.execute("DROP TABLE t3c;")
 ---
+- rowcount: 1
 ...
 -- pk - binary, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
+box.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("INSERT INTO t3d VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3d VALUES ('A');")
+box.execute("INSERT INTO t3d VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t3d;")
+box.execute("SELECT * FROM t3d;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
   - ['a']
 ...
-box.sql.execute("DROP TABLE t3d;")
+box.execute("DROP TABLE t3d;")
 ---
+- rowcount: 1
 ...
 -- pk - binary, sc - unicode_ci (make dup)
-box.sql.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
+box.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("INSERT INTO t3d VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3d VALUES ('A');")
+box.execute("INSERT INTO t3d VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("INSERT INTO t3d VALUES ('a');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3D_1' in space 'T3D'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3D_1'' in space ''T3D'''
 ...
-box.sql.execute("SELECT * FROM t3d;")
+box.execute("SELECT * FROM t3d;")
 ---
-- - ['A']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['A']
   - ['a']
 ...
-box.sql.execute("DROP TABLE t3d;")
+box.execute("DROP TABLE t3d;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode_ci, sc - binary (should fail)
-box.sql.execute('CREATE TABLE t3e (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
+box.execute('CREATE TABLE t3e (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3e ON t3e (s1 collate "binary");')
+box.execute('CREATE INDEX i3e ON t3e (s1 collate "binary");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3e VALUES ('a');")
+box.execute("INSERT INTO t3e VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3e VALUES ('A');")
+box.execute("INSERT INTO t3e VALUES ('A');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3E_1' in space 'T3E'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3E_1'' in space ''T3E'''
 ...
-box.sql.execute("SELECT * FROM t3e;")
+box.execute("SELECT * FROM t3e;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
 ...
-box.sql.execute("DROP TABLE t3e;")
+box.execute("DROP TABLE t3e;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
+box.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("INSERT INTO t3f VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3f VALUES ('A');")
+box.execute("INSERT INTO t3f VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t3f;")
+box.execute("SELECT * FROM t3f;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
   - ['A']
 ...
-box.sql.execute("DROP TABLE t3f;")
+box.execute("DROP TABLE t3f;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode, sc - unicode_ci (make dup)
-box.sql.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
+box.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("INSERT INTO t3f VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3f VALUES ('A');")
+box.execute("INSERT INTO t3f VALUES ('A');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("INSERT INTO t3f VALUES ('a');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3F_1' in space 'T3F'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3F_1'' in space ''T3F'''
 ...
-box.sql.execute("SELECT * FROM t3f;")
+box.execute("SELECT * FROM t3f;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
   - ['A']
 ...
-box.sql.execute("DROP TABLE t3f;")
+box.execute("DROP TABLE t3f;")
 ---
+- rowcount: 1
 ...
 -- pk - unicode_ci, sc - unicode (should fail)
-box.sql.execute('CREATE TABLE t3g (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
+box.execute('CREATE TABLE t3g (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i3g ON t3g (s1 collate "unicode");')
+box.execute('CREATE INDEX i3g ON t3g (s1 collate "unicode");')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3g VALUES ('a');")
+box.execute("INSERT INTO t3g VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3g VALUES ('A');")
+box.execute("INSERT INTO t3g VALUES ('A');")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_T3G_1' in space 'T3G'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_T3G_1'' in space ''T3G'''
 ...
-box.sql.execute("SELECT * FROM t3g;")
+box.execute("SELECT * FROM t3g;")
 ---
-- - ['a']
+- metadata:
+  - name: S1
+    type: TEXT
+  rows:
+  - ['a']
 ...
-box.sql.execute("DROP TABLE t3g;")
+box.execute("DROP TABLE t3g;")
 ---
+- rowcount: 1
 ...
 -- pk - default, sc - multipart
-box.sql.execute('CREATE TABLE qms1 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
+box.execute('CREATE TABLE qms1 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX iqms1 ON qms1 (w collate "unicode_ci", n);')
+box.execute('CREATE INDEX iqms1 ON qms1 (w collate "unicode_ci", n);')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms1 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms1 VALUES ('www', 'nnn', 'qqq', 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms1 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("INSERT INTO qms1 VALUES ('WWW', 'nnn', 'qqq', 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM qms1;")
+box.execute("SELECT * FROM qms1;")
 ---
-- - ['WWW', 'nnn', 'qqq', 2]
+- metadata:
+  - name: W
+    type: TEXT
+  - name: N
+    type: TEXT
+  - name: Q
+    type: TEXT
+  - name: S
+    type: INTEGER
+  rows:
+  - ['WWW', 'nnn', 'qqq', 2]
   - ['www', 'nnn', 'qqq', 1]
 ...
-box.sql.execute("DROP TABLE qms1;")
+box.execute("DROP TABLE qms1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE qms2 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
+box.execute('CREATE TABLE qms2 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX iqms2 ON qms2 (w collate "unicode", n);')
+box.execute('CREATE INDEX iqms2 ON qms2 (w collate "unicode", n);')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms2 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms2 VALUES ('www', 'nnn', 'qqq', 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms2 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("INSERT INTO qms2 VALUES ('WWW', 'nnn', 'qqq', 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM qms2;")
+box.execute("SELECT * FROM qms2;")
 ---
-- - ['WWW', 'nnn', 'qqq', 2]
+- metadata:
+  - name: W
+    type: TEXT
+  - name: N
+    type: TEXT
+  - name: Q
+    type: TEXT
+  - name: S
+    type: INTEGER
+  rows:
+  - ['WWW', 'nnn', 'qqq', 2]
   - ['www', 'nnn', 'qqq', 1]
 ...
-box.sql.execute("DROP TABLE qms2;")
+box.execute("DROP TABLE qms2;")
 ---
+- rowcount: 1
 ...
 -- pk - multipart, sc overlaps with pk
-box.sql.execute('CREATE TABLE qms3 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms3 PRIMARY KEY(w, n, q));')
+box.execute('CREATE TABLE qms3 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms3 PRIMARY KEY(w, n, q));')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX iqms3 ON qms3 (w collate "unicode_ci", s);')
+box.execute('CREATE INDEX iqms3 ON qms3 (w collate "unicode_ci", s);')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms3 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms3 VALUES ('www', 'nnn', 'qqq', 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms3 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("INSERT INTO qms3 VALUES ('WWW', 'nnn', 'qqq', 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM qms3;")
+box.execute("SELECT * FROM qms3;")
 ---
-- - ['WWW', 'nnn', 'qqq', 2]
+- metadata:
+  - name: W
+    type: TEXT
+  - name: N
+    type: TEXT
+  - name: Q
+    type: TEXT
+  - name: S
+    type: INTEGER
+  rows:
+  - ['WWW', 'nnn', 'qqq', 2]
   - ['www', 'nnn', 'qqq', 1]
 ...
-box.sql.execute("DROP TABLE qms3;")
+box.execute("DROP TABLE qms3;")
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE qms4 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms4 PRIMARY KEY(w, n, q));')
+box.execute('CREATE TABLE qms4 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms4 PRIMARY KEY(w, n, q));')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX iqms4 ON qms4 (w collate "unicode", s);')
+box.execute('CREATE INDEX iqms4 ON qms4 (w collate "unicode", s);')
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms4 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms4 VALUES ('www', 'nnn', 'qqq', 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO qms4 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("INSERT INTO qms4 VALUES ('WWW', 'nnn', 'qqq', 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM qms4;")
+box.execute("SELECT * FROM qms4;")
 ---
-- - ['WWW', 'nnn', 'qqq', 2]
+- metadata:
+  - name: W
+    type: TEXT
+  - name: N
+    type: TEXT
+  - name: Q
+    type: TEXT
+  - name: S
+    type: INTEGER
+  rows:
+  - ['WWW', 'nnn', 'qqq', 2]
   - ['www', 'nnn', 'qqq', 1]
 ...
-box.sql.execute("DROP TABLE qms4;")
+box.execute("DROP TABLE qms4;")
 ---
+- rowcount: 1
 ...
 -- gh-3932: make sure set build-in functions derive collation
 -- from their arguments.
 --
-box.sql.execute("CREATE TABLE jj (s1 INT PRIMARY KEY, s2 VARCHAR(3) COLLATE \"unicode_ci\");")
+box.execute("CREATE TABLE jj (s1 INT PRIMARY KEY, s2 VARCHAR(3) COLLATE \"unicode_ci\");")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO jj VALUES (1,'A'), (2,'a')")
+box.execute("INSERT INTO jj VALUES (1,'A'), (2,'a')")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT DISTINCT trim(s2) FROM jj;")
+box.execute("SELECT DISTINCT trim(s2) FROM jj;")
 ---
-- - ['A']
+- metadata:
+  - name: trim(s2)
+    type: TEXT
+  rows:
+  - ['A']
 ...
-box.sql.execute("INSERT INTO jj VALUES (3, 'aS'), (4, 'AS');")
+box.execute("INSERT INTO jj VALUES (3, 'aS'), (4, 'AS');")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT DISTINCT replace(s2, 'S', 's') FROM jj;")
+box.execute("SELECT DISTINCT replace(s2, 'S', 's') FROM jj;")
 ---
-- - ['A']
+- metadata:
+  - name: replace(s2, 'S', 's')
+    type: TEXT
+  rows:
+  - ['A']
   - ['as']
 ...
-box.sql.execute("SELECT DISTINCT substr(s2, 1, 1) FROM jj;")
+box.execute("SELECT DISTINCT substr(s2, 1, 1) FROM jj;")
 ---
-- - ['A']
+- metadata:
+  - name: substr(s2, 1, 1)
+    type: TEXT
+  rows:
+  - ['A']
 ...
 box.space.JJ:drop()
 ---
diff --git a/test/sql/collation.test.lua b/test/sql/collation.test.lua
index eba83e8..3a3f322 100644
--- a/test/sql/collation.test.lua
+++ b/test/sql/collation.test.lua
@@ -1,40 +1,40 @@
 remote = require('net.box')
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- gh-3010: COLLATE after LIMIT should throw an error
 
 -- All of these tests should throw error "near "COLLATE": syntax error"
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY;")
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
-box.sql.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
-box.sql.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
-box.sql.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY OFFSET 1;")
+box.execute("SELECT 1 LIMIT 1 OFFSET 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1, 1 COLLATE BINARY;")
+box.execute("SELECT 1 LIMIT 1 COLLATE BINARY, 1;")
 
 -- gh-3052: upper/lower support only default locale
 -- For tr-TR result depends on collation
-box.sql.execute([[CREATE TABLE tu (descriptor VARCHAR(50) PRIMARY KEY, letter VARCHAR(50))]]);
+box.execute([[CREATE TABLE tu (descriptor VARCHAR(50) PRIMARY KEY, letter VARCHAR(50))]]);
 box.internal.collation.create('TURKISH', 'ICU', 'tr-TR', {strength='primary'});
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I U+0049','I');]])
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Small Letter I U+0069','i');]])
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I With Dot Above U+0130','İ');]])
-box.sql.execute([[INSERT INTO tu VALUES ('Latin Small Letter Dotless I U+0131','ı');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I U+0049','I');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Small Letter I U+0069','i');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Capital Letter I With Dot Above U+0130','İ');]])
+box.execute([[INSERT INTO tu VALUES ('Latin Small Letter Dotless I U+0131','ı');]])
 -- Without collation
-box.sql.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower FROM tu;]])
+box.execute([[SELECT descriptor, upper(letter) AS upper,lower(letter) AS lower FROM tu;]])
 -- With collation
-box.sql.execute([[SELECT descriptor, upper(letter COLLATE "TURKISH") AS upper,lower(letter COLLATE "TURKISH") AS lower FROM tu;]])
+box.execute([[SELECT descriptor, upper(letter COLLATE "TURKISH") AS upper,lower(letter COLLATE "TURKISH") AS lower FROM tu;]])
 box.internal.collation.drop('TURKISH')
 
 -- For de-DE result is actually the same
 box.internal.collation.create('GERMAN', 'ICU', 'de-DE', {strength='primary'});
-box.sql.execute([[INSERT INTO tu VALUES ('German Small Letter Sharp S U+00DF','ß');]])
+box.execute([[INSERT INTO tu VALUES ('German Small Letter Sharp S U+00DF','ß');]])
 -- Without collation
-box.sql.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(letter) = 'SS';]])
+box.execute([[SELECT descriptor, upper(letter), letter FROM tu where UPPER(letter) = 'SS';]])
 -- With collation
-box.sql.execute([[SELECT descriptor, upper(letter COLLATE "GERMAN"), letter FROM tu where UPPER(letter COLLATE "GERMAN") = 'SS';]])
+box.execute([[SELECT descriptor, upper(letter COLLATE "GERMAN"), letter FROM tu where UPPER(letter COLLATE "GERMAN") = 'SS';]])
 box.internal.collation.drop('GERMAN')
-box.sql.execute(([[DROP TABLE tu]]))
+box.execute(([[DROP TABLE tu]]))
 
 box.schema.user.grant('guest','read,write,execute', 'universe')
 cn = remote.connect(box.cfg.listen)
@@ -45,10 +45,10 @@ cn:close()
 
 -- Explicitly set BINARY collation is predifined and has ID.
 --
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\");")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\");")
 box.space.T:format()[2]['collation']
 box.space.T:format()[3]['collation']
-box.sql.execute("DROP TABLE t;")
+box.execute("DROP TABLE t;")
 
 -- BINARY collation works in the same way as there is no collation
 -- at all.
@@ -73,236 +73,236 @@ box.space._collation:delete{0}
 
 -- gh-3185: collations of LHS and RHS must be compatible.
 --
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\", c TEXT COLLATE \"unicode_ci\");")
-box.sql.execute("SELECT * FROM t WHERE a = b;")
-box.sql.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = b;")
-box.sql.execute("SELECT * FROM t WHERE b = c;")
-box.sql.execute("SELECT * FROM t WHERE b COLLATE \"binary\" = c;")
-box.sql.execute("SELECT * FROM t WHERE a = c;")
-box.sql.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT COLLATE \"binary\", c TEXT COLLATE \"unicode_ci\");")
+box.execute("SELECT * FROM t WHERE a = b;")
+box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = b;")
+box.execute("SELECT * FROM t WHERE b = c;")
+box.execute("SELECT * FROM t WHERE b COLLATE \"binary\" = c;")
+box.execute("SELECT * FROM t WHERE a = c;")
+box.execute("SELECT * FROM t WHERE a COLLATE \"binary\" = c COLLATE \"unicode\";")
 
 -- Compound queries perform implicit comparisons between values.
 -- Hence, rules for collations compatibilities are the same.
 --
-box.sql.execute("SELECT 'abc' COLLATE \"binary\" UNION SELECT 'ABC' COLLATE \"unicode_ci\"")
-box.sql.execute("SELECT 'abc' COLLATE \"unicode_ci\" UNION SELECT 'ABC' COLLATE binary")
-box.sql.execute("SELECT c FROM t UNION SELECT b FROM t;")
-box.sql.execute("SELECT b FROM t UNION SELECT a FROM t;")
-box.sql.execute("SELECT a FROM t UNION SELECT c FROM t;")
-box.sql.execute("SELECT c COLLATE \"binary\" FROM t UNION SELECT a FROM t;")
-box.sql.execute("SELECT b COLLATE \"unicode\" FROM t UNION SELECT a FROM t;")
-
-box.sql.execute("DROP TABLE t;")
+box.execute("SELECT 'abc' COLLATE \"binary\" UNION SELECT 'ABC' COLLATE \"unicode_ci\"")
+box.execute("SELECT 'abc' COLLATE \"unicode_ci\" UNION SELECT 'ABC' COLLATE binary")
+box.execute("SELECT c FROM t UNION SELECT b FROM t;")
+box.execute("SELECT b FROM t UNION SELECT a FROM t;")
+box.execute("SELECT a FROM t UNION SELECT c FROM t;")
+box.execute("SELECT c COLLATE \"binary\" FROM t UNION SELECT a FROM t;")
+box.execute("SELECT b COLLATE \"unicode\" FROM t UNION SELECT a FROM t;")
+
+box.execute("DROP TABLE t;")
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 
 -- gh-3857 "PRAGMA collation_list" invokes segmentation fault.
 box.schema.user.create('tmp')
 box.session.su('tmp')
 -- Error: read access to space is denied.
-box.sql.execute("pragma collation_list")
+box.execute("pragma collation_list")
 box.session.su('admin')
 box.schema.user.drop('tmp')
 
 -- gh-3644 Foreign key update fails with "unicode_ci".
 -- Check that foreign key update doesn't fail with "unicode_ci".
-box.sql.execute('CREATE TABLE t0 (s1 VARCHAR(5) COLLATE "unicode_ci" UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
-box.sql.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) COLLATE "unicode_ci" REFERENCES t0(s1));')
-box.sql.execute("INSERT INTO t0(s1) VALUES ('a');")
-box.sql.execute("INSERT INTO t1 VALUES (1,'a');")
+box.execute('CREATE TABLE t0 (s1 VARCHAR(5) COLLATE "unicode_ci" UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
+box.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) COLLATE "unicode_ci" REFERENCES t0(s1));')
+box.execute("INSERT INTO t0(s1) VALUES ('a');")
+box.execute("INSERT INTO t1 VALUES (1,'a');")
 -- Should't fail.
-box.sql.execute("UPDATE t0 SET s1 = 'A';")
-box.sql.execute("SELECT s1 FROM t0;")
-box.sql.execute("SELECT * FROM t1;")
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t0;")
+box.execute("UPDATE t0 SET s1 = 'A';")
+box.execute("SELECT s1 FROM t0;")
+box.execute("SELECT * FROM t1;")
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t0;")
 -- Check that foreign key update fails with default collation.
-box.sql.execute('CREATE TABLE t0 (s1 VARCHAR(5) UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
-box.sql.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) REFERENCES t0(s1));')
-box.sql.execute("INSERT INTO t0(s1) VALUES ('a');")
-box.sql.execute("INSERT INTO t1 VALUES (1,'a');")
+box.execute('CREATE TABLE t0 (s1 VARCHAR(5) UNIQUE, id INT PRIMARY KEY AUTOINCREMENT);')
+box.execute('CREATE TABLE t1 (s1 INT PRIMARY KEY, s0 VARCHAR(5) REFERENCES t0(s1));')
+box.execute("INSERT INTO t0(s1) VALUES ('a');")
+box.execute("INSERT INTO t1 VALUES (1,'a');")
 -- Should fail.
-box.sql.execute("UPDATE t0 SET s1 = 'A';")
-box.sql.execute("SELECT * FROM t1;")
-box.sql.execute("SELECT s1 FROM t0;")
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t0;")
+box.execute("UPDATE t0 SET s1 = 'A';")
+box.execute("SELECT * FROM t1;")
+box.execute("SELECT s1 FROM t0;")
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t0;")
 
 -- gh-3937: result of concatination has derived collation.
 --
-box.sql.execute("CREATE TABLE t4a(a TEXT COLLATE \"unicode\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
-box.sql.execute("INSERT INTO t4a VALUES('ABC','abc',1);")
-box.sql.execute("INSERT INTO t4a VALUES('ghi','ghi',3);")
+box.execute("CREATE TABLE t4a(a TEXT COLLATE \"unicode\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
+box.execute("INSERT INTO t4a VALUES('ABC','abc',1);")
+box.execute("INSERT INTO t4a VALUES('ghi','ghi',3);")
 -- Only LHS of concatenation has implicitly set collation.
 -- Hence, no collation is used.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||'') = b;")
+box.execute("SELECT c FROM t4a WHERE (a||'') = b;")
 -- BINARY collation is forced for comparison operator as
 -- a derivation from concatenation.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b;")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b;")
 -- Both operands of concatenation have explicit but different
 -- collations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'' COLLATE \"unicode_ci\") = b;")
-box.sql.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b COLLATE \"unicode\";")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'' COLLATE \"unicode_ci\") = b;")
+box.execute("SELECT c FROM t4a WHERE (a COLLATE \"binary\"||'') = b COLLATE \"unicode\";")
 -- No collation is used since LHS and RHS of concatenation
 -- operator have different implicit collations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||'')=(b||'');")
-box.sql.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||'')=(b||'');")
+box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 
-box.sql.execute("CREATE TABLE t4b(a TEXT COLLATE \"unicode_ci\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
-box.sql.execute("INSERT INTO t4b VALUES('BCD','bcd',1);")
-box.sql.execute("INSERT INTO t4b VALUES('ghi','ghi',3);")
+box.execute("CREATE TABLE t4b(a TEXT COLLATE \"unicode_ci\", b TEXT COLLATE \"unicode_ci\", c INT PRIMARY KEY);")
+box.execute("INSERT INTO t4b VALUES('BCD','bcd',1);")
+box.execute("INSERT INTO t4b VALUES('ghi','ghi',3);")
 -- Operands have the same implicit collation, so it is derived.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b)=(b||a);")
 -- Couple of other possible combinations.
 --
-box.sql.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b||a);")
-box.sql.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b COLLATE \"unicode_ci\"||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b||a);")
+box.execute("SELECT c FROM t4a WHERE (a||b COLLATE \"binary\")=(b COLLATE \"unicode_ci\"||a);")
 
-box.sql.execute("INSERT INTO t4b VALUES('abc', 'xxx', 2);")
-box.sql.execute("INSERT INTO t4b VALUES('gHz', 'xxx', 4);")
+box.execute("INSERT INTO t4b VALUES('abc', 'xxx', 2);")
+box.execute("INSERT INTO t4b VALUES('gHz', 'xxx', 4);")
 -- Results are sorted with case-insensitive order.
 -- Otherwise capital latters come first.
 --
-box.sql.execute("SELECT a FROM t4b ORDER BY a COLLATE \"unicode_ci\" || ''")
-box.sql.execute("SELECT a FROM t4b ORDER BY a || b")
+box.execute("SELECT a FROM t4b ORDER BY a COLLATE \"unicode_ci\" || ''")
+box.execute("SELECT a FROM t4b ORDER BY a || b")
 
 box.space.T4A:drop()
 box.space.T4B:drop()
 
 -- gh-3537 Duplicate key error for an index that is not unique
 -- pk - default, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3 (s1 VARCHAR(5) PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3 ON t3 (s1 collate "unicode_ci");')
-box.sql.execute("INSERT INTO t3 VALUES ('a');")
-box.sql.execute("INSERT INTO t3 VALUES ('A');")
-box.sql.execute("SELECT * FROM t3;")
-box.sql.execute("DROP TABLE t3;")
+box.execute('CREATE TABLE t3 (s1 VARCHAR(5) PRIMARY KEY);')
+box.execute('CREATE INDEX i3 ON t3 (s1 collate "unicode_ci");')
+box.execute("INSERT INTO t3 VALUES ('a');")
+box.execute("INSERT INTO t3 VALUES ('A');")
+box.execute("SELECT * FROM t3;")
+box.execute("DROP TABLE t3;")
 
 -- pk - binary, sc - unicode
-box.sql.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
-box.sql.execute("INSERT INTO t3b VALUES ('A');")
-box.sql.execute("SELECT * FROM t3b;")
-box.sql.execute("DROP TABLE t3b;")
+box.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
+box.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("INSERT INTO t3b VALUES ('A');")
+box.execute("SELECT * FROM t3b;")
+box.execute("DROP TABLE t3b;")
 
 -- pk - binary, sc - unicode (make dup)
-box.sql.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
-box.sql.execute("INSERT INTO t3b VALUES ('A');")
-box.sql.execute("INSERT INTO t3b VALUES ('a');")
-box.sql.execute("SELECT * FROM t3b;")
-box.sql.execute("DROP TABLE t3b;")
+box.execute('CREATE TABLE t3b (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE INDEX i3b ON t3b (s1 collate "unicode");')
+box.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("INSERT INTO t3b VALUES ('A');")
+box.execute("INSERT INTO t3b VALUES ('a');")
+box.execute("SELECT * FROM t3b;")
+box.execute("DROP TABLE t3b;")
 
 -- pk - unicode, sc - binary
-box.sql.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
-box.sql.execute("INSERT INTO t3c VALUES ('A');")
-box.sql.execute("SELECT * FROM t3c;")
-box.sql.execute("DROP TABLE t3c;")
+box.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
+box.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("INSERT INTO t3c VALUES ('A');")
+box.execute("SELECT * FROM t3c;")
+box.execute("DROP TABLE t3c;")
 
 -- pk - unicode, sc - binary (make dup)
-box.sql.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
-box.sql.execute("INSERT INTO t3c VALUES ('A');")
-box.sql.execute("INSERT INTO t3c VALUES ('a');")
-box.sql.execute("SELECT * FROM t3c;")
-box.sql.execute("DROP TABLE t3c;")
+box.execute('CREATE TABLE t3c (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE INDEX i3c ON t3c (s1 collate "binary");')
+box.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("INSERT INTO t3c VALUES ('A');")
+box.execute("INSERT INTO t3c VALUES ('a');")
+box.execute("SELECT * FROM t3c;")
+box.execute("DROP TABLE t3c;")
 
 -- pk - binary, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
-box.sql.execute("INSERT INTO t3d VALUES ('A');")
-box.sql.execute("SELECT * FROM t3d;")
-box.sql.execute("DROP TABLE t3d;")
+box.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
+box.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("INSERT INTO t3d VALUES ('A');")
+box.execute("SELECT * FROM t3d;")
+box.execute("DROP TABLE t3d;")
 
 -- pk - binary, sc - unicode_ci (make dup)
-box.sql.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
-box.sql.execute("INSERT INTO t3d VALUES ('A');")
-box.sql.execute("INSERT INTO t3d VALUES ('a');")
-box.sql.execute("SELECT * FROM t3d;")
-box.sql.execute("DROP TABLE t3d;")
+box.execute('CREATE TABLE t3d (s1 VARCHAR(5) collate "binary" PRIMARY KEY);')
+box.execute('CREATE INDEX i3d ON t3d (s1 collate "unicode_ci");')
+box.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("INSERT INTO t3d VALUES ('A');")
+box.execute("INSERT INTO t3d VALUES ('a');")
+box.execute("SELECT * FROM t3d;")
+box.execute("DROP TABLE t3d;")
 
 -- pk - unicode_ci, sc - binary (should fail)
-box.sql.execute('CREATE TABLE t3e (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3e ON t3e (s1 collate "binary");')
-box.sql.execute("INSERT INTO t3e VALUES ('a');")
-box.sql.execute("INSERT INTO t3e VALUES ('A');")
-box.sql.execute("SELECT * FROM t3e;")
-box.sql.execute("DROP TABLE t3e;")
+box.execute('CREATE TABLE t3e (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
+box.execute('CREATE INDEX i3e ON t3e (s1 collate "binary");')
+box.execute("INSERT INTO t3e VALUES ('a');")
+box.execute("INSERT INTO t3e VALUES ('A');")
+box.execute("SELECT * FROM t3e;")
+box.execute("DROP TABLE t3e;")
 
 -- pk - unicode, sc - unicode_ci
-box.sql.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
-box.sql.execute("INSERT INTO t3f VALUES ('A');")
-box.sql.execute("SELECT * FROM t3f;")
-box.sql.execute("DROP TABLE t3f;")
+box.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
+box.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("INSERT INTO t3f VALUES ('A');")
+box.execute("SELECT * FROM t3f;")
+box.execute("DROP TABLE t3f;")
 
 -- pk - unicode, sc - unicode_ci (make dup)
-box.sql.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
-box.sql.execute("INSERT INTO t3f VALUES ('A');")
-box.sql.execute("INSERT INTO t3f VALUES ('a');")
-box.sql.execute("SELECT * FROM t3f;")
-box.sql.execute("DROP TABLE t3f;")
+box.execute('CREATE TABLE t3f (s1 VARCHAR(5) collate "unicode" PRIMARY KEY);')
+box.execute('CREATE INDEX i3f ON t3f (s1 collate "unicode_ci");')
+box.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("INSERT INTO t3f VALUES ('A');")
+box.execute("INSERT INTO t3f VALUES ('a');")
+box.execute("SELECT * FROM t3f;")
+box.execute("DROP TABLE t3f;")
 
 -- pk - unicode_ci, sc - unicode (should fail)
-box.sql.execute('CREATE TABLE t3g (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
-box.sql.execute('CREATE INDEX i3g ON t3g (s1 collate "unicode");')
-box.sql.execute("INSERT INTO t3g VALUES ('a');")
-box.sql.execute("INSERT INTO t3g VALUES ('A');")
-box.sql.execute("SELECT * FROM t3g;")
-box.sql.execute("DROP TABLE t3g;")
+box.execute('CREATE TABLE t3g (s1 VARCHAR(5) collate "unicode_ci" PRIMARY KEY);')
+box.execute('CREATE INDEX i3g ON t3g (s1 collate "unicode");')
+box.execute("INSERT INTO t3g VALUES ('a');")
+box.execute("INSERT INTO t3g VALUES ('A');")
+box.execute("SELECT * FROM t3g;")
+box.execute("DROP TABLE t3g;")
 
 -- pk - default, sc - multipart
-box.sql.execute('CREATE TABLE qms1 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
-box.sql.execute('CREATE INDEX iqms1 ON qms1 (w collate "unicode_ci", n);')
-box.sql.execute("INSERT INTO qms1 VALUES ('www', 'nnn', 'qqq', 1);")
-box.sql.execute("INSERT INTO qms1 VALUES ('WWW', 'nnn', 'qqq', 2);")
-box.sql.execute("SELECT * FROM qms1;")
-box.sql.execute("DROP TABLE qms1;")
-
-box.sql.execute('CREATE TABLE qms2 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
-box.sql.execute('CREATE INDEX iqms2 ON qms2 (w collate "unicode", n);')
-box.sql.execute("INSERT INTO qms2 VALUES ('www', 'nnn', 'qqq', 1);")
-box.sql.execute("INSERT INTO qms2 VALUES ('WWW', 'nnn', 'qqq', 2);")
-box.sql.execute("SELECT * FROM qms2;")
-box.sql.execute("DROP TABLE qms2;")
+box.execute('CREATE TABLE qms1 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
+box.execute('CREATE INDEX iqms1 ON qms1 (w collate "unicode_ci", n);')
+box.execute("INSERT INTO qms1 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms1 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("SELECT * FROM qms1;")
+box.execute("DROP TABLE qms1;")
+
+box.execute('CREATE TABLE qms2 (w VARCHAR(5) PRIMARY KEY, n VARCHAR(5), q VARCHAR(5), s INTEGER);')
+box.execute('CREATE INDEX iqms2 ON qms2 (w collate "unicode", n);')
+box.execute("INSERT INTO qms2 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms2 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("SELECT * FROM qms2;")
+box.execute("DROP TABLE qms2;")
 
 -- pk - multipart, sc overlaps with pk
-box.sql.execute('CREATE TABLE qms3 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms3 PRIMARY KEY(w, n, q));')
-box.sql.execute('CREATE INDEX iqms3 ON qms3 (w collate "unicode_ci", s);')
-box.sql.execute("INSERT INTO qms3 VALUES ('www', 'nnn', 'qqq', 1);")
-box.sql.execute("INSERT INTO qms3 VALUES ('WWW', 'nnn', 'qqq', 2);")
-box.sql.execute("SELECT * FROM qms3;")
-box.sql.execute("DROP TABLE qms3;")
-
-box.sql.execute('CREATE TABLE qms4 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms4 PRIMARY KEY(w, n, q));')
-box.sql.execute('CREATE INDEX iqms4 ON qms4 (w collate "unicode", s);')
-box.sql.execute("INSERT INTO qms4 VALUES ('www', 'nnn', 'qqq', 1);")
-box.sql.execute("INSERT INTO qms4 VALUES ('WWW', 'nnn', 'qqq', 2);")
-box.sql.execute("SELECT * FROM qms4;")
-box.sql.execute("DROP TABLE qms4;")
+box.execute('CREATE TABLE qms3 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms3 PRIMARY KEY(w, n, q));')
+box.execute('CREATE INDEX iqms3 ON qms3 (w collate "unicode_ci", s);')
+box.execute("INSERT INTO qms3 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms3 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("SELECT * FROM qms3;")
+box.execute("DROP TABLE qms3;")
+
+box.execute('CREATE TABLE qms4 (w VARCHAR(5), n VARCHAR(5), q VARCHAR(5), s INTEGER, CONSTRAINT pk_qms4 PRIMARY KEY(w, n, q));')
+box.execute('CREATE INDEX iqms4 ON qms4 (w collate "unicode", s);')
+box.execute("INSERT INTO qms4 VALUES ('www', 'nnn', 'qqq', 1);")
+box.execute("INSERT INTO qms4 VALUES ('WWW', 'nnn', 'qqq', 2);")
+box.execute("SELECT * FROM qms4;")
+box.execute("DROP TABLE qms4;")
 
 -- gh-3932: make sure set build-in functions derive collation
 -- from their arguments.
 --
-box.sql.execute("CREATE TABLE jj (s1 INT PRIMARY KEY, s2 VARCHAR(3) COLLATE \"unicode_ci\");")
-box.sql.execute("INSERT INTO jj VALUES (1,'A'), (2,'a')")
-box.sql.execute("SELECT DISTINCT trim(s2) FROM jj;")
-box.sql.execute("INSERT INTO jj VALUES (3, 'aS'), (4, 'AS');")
-box.sql.execute("SELECT DISTINCT replace(s2, 'S', 's') FROM jj;")
-box.sql.execute("SELECT DISTINCT substr(s2, 1, 1) FROM jj;")
+box.execute("CREATE TABLE jj (s1 INT PRIMARY KEY, s2 VARCHAR(3) COLLATE \"unicode_ci\");")
+box.execute("INSERT INTO jj VALUES (1,'A'), (2,'a')")
+box.execute("SELECT DISTINCT trim(s2) FROM jj;")
+box.execute("INSERT INTO jj VALUES (3, 'aS'), (4, 'AS');")
+box.execute("SELECT DISTINCT replace(s2, 'S', 's') FROM jj;")
+box.execute("SELECT DISTINCT substr(s2, 1, 1) FROM jj;")
 box.space.JJ:drop()
diff --git a/test/sql/delete-multiple-idx.result b/test/sql/delete-multiple-idx.result
index 27c352a..9614ead 100644
--- a/test/sql/delete-multiple-idx.result
+++ b/test/sql/delete-multiple-idx.result
@@ -4,47 +4,65 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- Create space.
-box.sql.execute("CREATE TABLE t3(id INT primary key,x INT,y INT);");
+box.execute("CREATE TABLE t3(id INT primary key,x INT,y INT);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX t3y ON t3(y);");
+box.execute("CREATE UNIQUE INDEX t3y ON t3(y);");
 ---
+- rowcount: 1
 ...
 -- Debug.
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries.
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, NULL);");
+box.execute("INSERT INTO t3 VALUES (1, 1, NULL);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES(2,9,NULL);");
+box.execute("INSERT INTO t3 VALUES(2,9,NULL);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES(3,5,NULL);");
+box.execute("INSERT INTO t3 VALUES(3,5,NULL);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES(6, 234,567);");
+box.execute("INSERT INTO t3 VALUES(6, 234,567);");
 ---
+- rowcount: 1
 ...
 -- Delete should be done from both trees..
-box.sql.execute("DELETE FROM t3 WHERE y IS NULL;");
+box.execute("DELETE FROM t3 WHERE y IS NULL;");
 ---
+- rowcount: 3
 ...
 -- Verify.
-box.sql.execute("SELECT * FROM t3;");
+box.execute("SELECT * FROM t3;");
 ---
-- - [6, 234, 567]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: INTEGER
+  rows:
+  - [6, 234, 567]
 ...
 -- Cleanup.
-box.sql.execute("DROP INDEX t3y ON t3");
+box.execute("DROP INDEX t3y ON t3");
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t3;");
+box.execute("DROP TABLE t3;");
 ---
+- rowcount: 1
 ...
 -- Debug.
 -- require("console").start()
diff --git a/test/sql/delete-multiple-idx.test.lua b/test/sql/delete-multiple-idx.test.lua
index 4ce7f2d..a81cccc 100644
--- a/test/sql/delete-multiple-idx.test.lua
+++ b/test/sql/delete-multiple-idx.test.lua
@@ -1,32 +1,32 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- Create space.
-box.sql.execute("CREATE TABLE t3(id INT primary key,x INT,y INT);");
-box.sql.execute("CREATE UNIQUE INDEX t3y ON t3(y);");
+box.execute("CREATE TABLE t3(id INT primary key,x INT,y INT);");
+box.execute("CREATE UNIQUE INDEX t3y ON t3(y);");
 
 -- Debug.
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries.
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, NULL);");
-box.sql.execute("INSERT INTO t3 VALUES(2,9,NULL);");
-box.sql.execute("INSERT INTO t3 VALUES(3,5,NULL);");
-box.sql.execute("INSERT INTO t3 VALUES(6, 234,567);");
+box.execute("INSERT INTO t3 VALUES (1, 1, NULL);");
+box.execute("INSERT INTO t3 VALUES(2,9,NULL);");
+box.execute("INSERT INTO t3 VALUES(3,5,NULL);");
+box.execute("INSERT INTO t3 VALUES(6, 234,567);");
 
 
 -- Delete should be done from both trees..
-box.sql.execute("DELETE FROM t3 WHERE y IS NULL;");
+box.execute("DELETE FROM t3 WHERE y IS NULL;");
 
 -- Verify.
-box.sql.execute("SELECT * FROM t3;");
+box.execute("SELECT * FROM t3;");
 
 -- Cleanup.
-box.sql.execute("DROP INDEX t3y ON t3");
-box.sql.execute("DROP TABLE t3;");
+box.execute("DROP INDEX t3y ON t3");
+box.execute("DROP TABLE t3;");
 
 -- Debug.
 -- require("console").start()
diff --git a/test/sql/delete.result b/test/sql/delete.result
index e024dd6..60eb6c3 100644
--- a/test/sql/delete.result
+++ b/test/sql/delete.result
@@ -4,134 +4,174 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
+box.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);");
+box.execute("INSERT INTO t1 VALUES(1, 2);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2, 4);");
+box.execute("INSERT INTO t1 VALUES(2, 4);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(1, 5);");
+box.execute("INSERT INTO t1 VALUES(1, 5);");
 ---
+- rowcount: 1
 ...
 -- Two rows to be removed.
-box.sql.execute("DELETE FROM t1 WHERE a=1;");
+box.execute("DELETE FROM t1 WHERE a=1;");
 ---
+- rowcount: 2
 ...
 -- Verify
-box.sql.execute("SELECT * FROM t1;");
+box.execute("SELECT * FROM t1;");
 ---
-- - [2, 4]
+- metadata:
+  - name: A
+    type: INTEGER
+  - name: B
+    type: INTEGER
+  rows:
+  - [2, 4]
 ...
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
 --
 -- gh-3535: Assertion with trigger and non existent table
 --
-box.sql.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t1;")
 ---
 - error: Space 'T1' does not exist
 ...
-box.sql.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END;")
+box.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES (0);")
+box.execute("INSERT INTO t2 VALUES (0);")
 ---
 - error: Space 'T1' does not exist
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
 --
 -- gh-2201: TRUNCATE TABLE operation.
 --
 -- can't truncate system table.
-box.sql.execute("TRUNCATE TABLE \"_sql_stat1\";")
+box.execute("TRUNCATE TABLE \"_sql_stat1\";")
 ---
 - error: Can't truncate a system space, space '_sql_stat1'
 ...
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b TEXT);")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b TEXT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
+box.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
+box.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
 ---
+- rowcount: 1
 ...
 -- Can't truncate in transaction.
-box.sql.execute("START TRANSACTION")
+box.execute("START TRANSACTION")
 ---
+- rowcount: 0
 ...
-box.sql.execute("TRUNCATE TABLE t1;")
+box.execute("TRUNCATE TABLE t1;")
 ---
 - error: DDL does not support multi-statement transactions
 ...
-box.sql.execute("ROLLBACK")
+box.execute("ROLLBACK")
 ---
+- rowcount: 0
 ...
 -- Can't truncate view.
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("TRUNCATE TABLE v1;")
+box.execute("TRUNCATE TABLE v1;")
 ---
 - error: 'SQL error: can not truncate space ''V1'' because it is a view'
 ...
 -- Can't truncate table with FK.
-box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
+box.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("TRUNCATE TABLE t1;")
+box.execute("TRUNCATE TABLE t1;")
 ---
 - error: 'SQL error: can not truncate space ''T1'' because other objects depend on
     it'
 ...
 -- Table triggers should be ignored.
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
+box.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("TRUNCATE TABLE t1;")
+box.execute("TRUNCATE TABLE t1;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT * FROM t1;")
+box.execute("SELECT * FROM t1;")
 ---
-- []
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  - name: B
+    type: TEXT
+  rows: []
 ...
-box.sql.execute("SELECT * FROM t2;")
+box.execute("SELECT * FROM t2;")
 ---
-- []
+- metadata:
+  - name: X
+    type: INTEGER
+  rows: []
 ...
 -- Cleanup.
-box.sql.execute("DROP VIEW v1");
+box.execute("DROP VIEW v1");
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/delete.test.lua b/test/sql/delete.test.lua
index 5a08130..5c72296 100644
--- a/test/sql/delete.test.lua
+++ b/test/sql/delete.test.lua
@@ -1,28 +1,28 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
+box.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);");
-box.sql.execute("INSERT INTO t1 VALUES(2, 4);");
-box.sql.execute("INSERT INTO t1 VALUES(1, 5);");
+box.execute("INSERT INTO t1 VALUES(1, 2);");
+box.execute("INSERT INTO t1 VALUES(2, 4);");
+box.execute("INSERT INTO t1 VALUES(1, 5);");
 
 -- Two rows to be removed.
-box.sql.execute("DELETE FROM t1 WHERE a=1;");
+box.execute("DELETE FROM t1 WHERE a=1;");
 
 -- Verify
-box.sql.execute("SELECT * FROM t1;");
+box.execute("SELECT * FROM t1;");
 
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 
 -- Debug
 -- require("console").start()
@@ -30,13 +30,13 @@ box.sql.execute("DROP TABLE t1;");
 --
 -- gh-3535: Assertion with trigger and non existent table
 --
-box.sql.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t1;")
 
-box.sql.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
-box.sql.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END;")
-box.sql.execute("INSERT INTO t2 VALUES (0);")
+box.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
+box.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END;")
+box.execute("INSERT INTO t2 VALUES (0);")
 
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 
 
 --
@@ -44,34 +44,34 @@ box.sql.execute("DROP TABLE t2;")
 --
 
 -- can't truncate system table.
-box.sql.execute("TRUNCATE TABLE \"_sql_stat1\";")
+box.execute("TRUNCATE TABLE \"_sql_stat1\";")
 
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b TEXT);")
-box.sql.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
-box.sql.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b TEXT);")
+box.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
+box.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
 
 -- Can't truncate in transaction.
-box.sql.execute("START TRANSACTION")
-box.sql.execute("TRUNCATE TABLE t1;")
-box.sql.execute("ROLLBACK")
+box.execute("START TRANSACTION")
+box.execute("TRUNCATE TABLE t1;")
+box.execute("ROLLBACK")
 
 -- Can't truncate view.
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
-box.sql.execute("TRUNCATE TABLE v1;")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("TRUNCATE TABLE v1;")
 
 -- Can't truncate table with FK.
-box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
-box.sql.execute("TRUNCATE TABLE t1;")
+box.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
+box.execute("TRUNCATE TABLE t1;")
 
 -- Table triggers should be ignored.
-box.sql.execute("DROP TABLE t2;")
-box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
-box.sql.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
-box.sql.execute("TRUNCATE TABLE t1;")
-box.sql.execute("SELECT * FROM t1;")
-box.sql.execute("SELECT * FROM t2;")
+box.execute("DROP TABLE t2;")
+box.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
+box.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
+box.execute("TRUNCATE TABLE t1;")
+box.execute("SELECT * FROM t1;")
+box.execute("SELECT * FROM t2;")
 
 -- Cleanup.
-box.sql.execute("DROP VIEW v1");
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP VIEW v1");
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
diff --git a/test/sql/drop-index.result b/test/sql/drop-index.result
index 2590b74..7693f9e 100644
--- a/test/sql/drop-index.result
+++ b/test/sql/drop-index.result
@@ -4,46 +4,57 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE zzoobar (c1 FLOAT, c2 INT PRIMARY KEY, c3 TEXT, c4 FLOAT)")
+box.execute("CREATE TABLE zzoobar (c1 FLOAT, c2 INT PRIMARY KEY, c3 TEXT, c4 FLOAT)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zzoobar(c1, c4)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zzoobar(c1, c4)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE        INDEX zoobar3 ON zzoobar(c3)")
+box.execute("CREATE        INDEX zoobar3 ON zzoobar(c3)")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
 -- Dummy entry
-box.sql.execute("INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP INDEX zoobar2 ON zzoobar")
+box.execute("DROP INDEX zoobar2 ON zzoobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP INDEX zoobar3 On zzoobar")
+box.execute("DROP INDEX zoobar3 On zzoobar")
 ---
+- rowcount: 1
 ...
 -- zoobar2 is dropped - should be OK
-box.sql.execute("INSERT INTO zzoobar VALUES (111, 223, 'c3', 444)")
+box.execute("INSERT INTO zzoobar VALUES (111, 223, 'c3', 444)")
 ---
+- rowcount: 1
 ...
 -- zoobar2 was dropped. Re-creation should  be OK
-box.sql.execute("CREATE INDEX zoobar2 ON zzoobar(c3)")
+box.execute("CREATE INDEX zoobar2 ON zzoobar(c3)")
 ---
+- rowcount: 1
 ...
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zzoobar")
+box.execute("DROP INDEX zoobar2 ON zzoobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE zzoobar")
+box.execute("DROP TABLE zzoobar")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/drop-index.test.lua b/test/sql/drop-index.test.lua
index 2493299..9ce3da0 100644
--- a/test/sql/drop-index.test.lua
+++ b/test/sql/drop-index.test.lua
@@ -1,33 +1,33 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE zzoobar (c1 FLOAT, c2 INT PRIMARY KEY, c3 TEXT, c4 FLOAT)")
+box.execute("CREATE TABLE zzoobar (c1 FLOAT, c2 INT PRIMARY KEY, c3 TEXT, c4 FLOAT)")
 
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zzoobar(c1, c4)")
-box.sql.execute("CREATE        INDEX zoobar3 ON zzoobar(c3)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zzoobar(c1, c4)")
+box.execute("CREATE        INDEX zoobar3 ON zzoobar(c3)")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
 
 -- Dummy entry
-box.sql.execute("INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzoobar VALUES (111, 222, 'c3', 444)")
 
-box.sql.execute("DROP INDEX zoobar2 ON zzoobar")
-box.sql.execute("DROP INDEX zoobar3 On zzoobar")
+box.execute("DROP INDEX zoobar2 ON zzoobar")
+box.execute("DROP INDEX zoobar3 On zzoobar")
 
 -- zoobar2 is dropped - should be OK
-box.sql.execute("INSERT INTO zzoobar VALUES (111, 223, 'c3', 444)")
+box.execute("INSERT INTO zzoobar VALUES (111, 223, 'c3', 444)")
 
 -- zoobar2 was dropped. Re-creation should  be OK
-box.sql.execute("CREATE INDEX zoobar2 ON zzoobar(c3)")
+box.execute("CREATE INDEX zoobar2 ON zzoobar(c3)")
 
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zzoobar")
-box.sql.execute("DROP TABLE zzoobar")
+box.execute("DROP INDEX zoobar2 ON zzoobar")
+box.execute("DROP TABLE zzoobar")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/drop-table.result b/test/sql/drop-table.result
index 0e14d8a..f752a92 100644
--- a/test/sql/drop-table.result
+++ b/test/sql/drop-table.result
@@ -4,47 +4,57 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE zzzoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE TABLE zzzoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
-box.sql.execute("CREATE INDEX zb ON zzzoobar(c1, c3)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("CREATE INDEX zb ON zzzoobar(c1, c3)")
 ---
+- rowcount: 1
 ...
 -- Dummy entry
-box.sql.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE zzzoobar")
+box.execute("DROP TABLE zzzoobar")
 ---
+- rowcount: 1
 ...
 -- Table does not exist anymore. Should error here.
-box.sql.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
 ---
 - error: Space 'ZZZOOBAR' does not exist
 ...
 -- gh-3712: if space features sequence, data from _sequence_data
 -- must be deleted before space is dropped.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY AUTOINCREMENT);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY AUTOINCREMENT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (NULL);")
+box.execute("INSERT INTO t1 VALUES (NULL);")
 ---
+- autoincrement_ids:
+  - 1
+  rowcount: 1
 ...
 box.snapshot()
 ---
 - ok
 ...
 test_run:cmd('restart server default')
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
 -- Cleanup
 -- DROP TABLE should do the job
@@ -85,12 +95,12 @@ box.session.su('tmp')
 -- Error: user do not have rights to write in box.space._index.
 -- Space that was already created should be automatically dropped.
 --
-box.sql.execute('CREATE TABLE t1 (id INT PRIMARY KEY, a INT)')
+box.execute('CREATE TABLE t1 (id INT PRIMARY KEY, a INT)')
 ---
 - error: Write access to space '_index' is denied for user 'tmp'
 ...
 -- Error: no such table.
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 ---
 - error: Space 'T1' does not exist
 ...
@@ -126,7 +136,7 @@ box.session.su('tmp')
 --
 -- Error: user do not have rights to write in _sequence.
 --
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY AUTOINCREMENT, a INT UNIQUE, b INT UNIQUE, c INT UNIQUE, d INT UNIQUE)')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY AUTOINCREMENT, a INT UNIQUE, b INT UNIQUE, c INT UNIQUE, d INT UNIQUE)')
 ---
 - error: Write access to space '_sequence' is denied for user 'tmp'
 ...
@@ -161,19 +171,21 @@ box.schema.user.grant('tmp', 'write', 'space')
 box.session.su('tmp')
 ---
 ...
-box.sql.execute('CREATE TABLE t3(a INTEGER PRIMARY KEY);')
+box.execute('CREATE TABLE t3(a INTEGER PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
 --
 -- Error: Failed to create foreign key constraint.
 --
-box.sql.execute('CREATE TABLE t4(x INTEGER PRIMARY KEY REFERENCES t3, a INT UNIQUE, c TEXT REFERENCES t3);')
+box.execute('CREATE TABLE t4(x INTEGER PRIMARY KEY REFERENCES t3, a INT UNIQUE, c TEXT REFERENCES t3);')
 ---
 - error: 'Failed to create foreign key constraint ''FK_CONSTRAINT_2_T4'': field type
     mismatch'
 ...
-box.sql.execute('DROP TABLE t3;')
+box.execute('DROP TABLE t3;')
 ---
+- rowcount: 1
 ...
 --
 -- Check that _space, _index and _sequence have the same number of
diff --git a/test/sql/drop-table.test.lua b/test/sql/drop-table.test.lua
index 808e8fe..a310db1 100644
--- a/test/sql/drop-table.test.lua
+++ b/test/sql/drop-table.test.lua
@@ -1,33 +1,33 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE zzzoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE TABLE zzzoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
 
-box.sql.execute("CREATE INDEX zb ON zzzoobar(c1, c3)")
+box.execute("CREATE INDEX zb ON zzzoobar(c1, c3)")
 
 -- Dummy entry
-box.sql.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
 
-box.sql.execute("DROP TABLE zzzoobar")
+box.execute("DROP TABLE zzzoobar")
 
 -- Table does not exist anymore. Should error here.
-box.sql.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zzzoobar VALUES (111, 222, 'c3', 444)")
 
 -- gh-3712: if space features sequence, data from _sequence_data
 -- must be deleted before space is dropped.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY AUTOINCREMENT);")
-box.sql.execute("INSERT INTO t1 VALUES (NULL);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY AUTOINCREMENT);")
+box.execute("INSERT INTO t1 VALUES (NULL);")
 box.snapshot()
 test_run:cmd('restart server default')
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 
 -- Cleanup
 -- DROP TABLE should do the job
@@ -56,9 +56,9 @@ box.session.su('tmp')
 -- Error: user do not have rights to write in box.space._index.
 -- Space that was already created should be automatically dropped.
 --
-box.sql.execute('CREATE TABLE t1 (id INT PRIMARY KEY, a INT)')
+box.execute('CREATE TABLE t1 (id INT PRIMARY KEY, a INT)')
 -- Error: no such table.
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 
 box.session.su('admin')
 
@@ -80,7 +80,7 @@ box.session.su('tmp')
 --
 -- Error: user do not have rights to write in _sequence.
 --
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY AUTOINCREMENT, a INT UNIQUE, b INT UNIQUE, c INT UNIQUE, d INT UNIQUE)')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY AUTOINCREMENT, a INT UNIQUE, b INT UNIQUE, c INT UNIQUE, d INT UNIQUE)')
 
 box.session.su('admin')
 
@@ -100,12 +100,12 @@ fk_constraint_count = #box.space._fk_constraint:select()
 box.schema.user.grant('tmp', 'write', 'space')
 box.session.su('tmp')
 
-box.sql.execute('CREATE TABLE t3(a INTEGER PRIMARY KEY);')
+box.execute('CREATE TABLE t3(a INTEGER PRIMARY KEY);')
 --
 -- Error: Failed to create foreign key constraint.
 --
-box.sql.execute('CREATE TABLE t4(x INTEGER PRIMARY KEY REFERENCES t3, a INT UNIQUE, c TEXT REFERENCES t3);')
-box.sql.execute('DROP TABLE t3;')
+box.execute('CREATE TABLE t4(x INTEGER PRIMARY KEY REFERENCES t3, a INT UNIQUE, c TEXT REFERENCES t3);')
+box.execute('DROP TABLE t3;')
 
 --
 -- Check that _space, _index and _sequence have the same number of
diff --git a/test/sql/engine.result b/test/sql/engine.result
index 935383a..bfbde14 100644
--- a/test/sql/engine.result
+++ b/test/sql/engine.result
@@ -4,20 +4,25 @@ env = require('test_run')
 test_run = env.new()
 ---
 ...
-box.sql.execute("pragma sql_default_engine='vinyl'")
+box.execute("pragma sql_default_engine='vinyl'")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE t1_vinyl(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("CREATE TABLE t1_vinyl(a INT PRIMARY KEY, b INT, c INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2_vinyl(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("CREATE TABLE t2_vinyl(a INT PRIMARY KEY, b INT, c INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("pragma sql_default_engine='memtx'")
+box.execute("pragma sql_default_engine='memtx'")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE t3_memtx(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("CREATE TABLE t3_memtx(a INT PRIMARY KEY, b INT, c INT);")
 ---
+- rowcount: 1
 ...
 assert(box.space.T1_VINYL.engine == 'vinyl')
 ---
@@ -31,12 +36,15 @@ assert(box.space.T3_MEMTX.engine == 'memtx')
 ---
 - true
 ...
-box.sql.execute("DROP TABLE t1_vinyl;")
+box.execute("DROP TABLE t1_vinyl;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2_vinyl;")
+box.execute("DROP TABLE t2_vinyl;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t3_memtx;")
+box.execute("DROP TABLE t3_memtx;")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/engine.test.lua b/test/sql/engine.test.lua
index 2f34b24..51d6939 100644
--- a/test/sql/engine.test.lua
+++ b/test/sql/engine.test.lua
@@ -1,17 +1,17 @@
 env = require('test_run')
 test_run = env.new()
 
-box.sql.execute("pragma sql_default_engine='vinyl'")
-box.sql.execute("CREATE TABLE t1_vinyl(a INT PRIMARY KEY, b INT, c INT);")
-box.sql.execute("CREATE TABLE t2_vinyl(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("pragma sql_default_engine='vinyl'")
+box.execute("CREATE TABLE t1_vinyl(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("CREATE TABLE t2_vinyl(a INT PRIMARY KEY, b INT, c INT);")
 
-box.sql.execute("pragma sql_default_engine='memtx'")
-box.sql.execute("CREATE TABLE t3_memtx(a INT PRIMARY KEY, b INT, c INT);")
+box.execute("pragma sql_default_engine='memtx'")
+box.execute("CREATE TABLE t3_memtx(a INT PRIMARY KEY, b INT, c INT);")
 
 assert(box.space.T1_VINYL.engine == 'vinyl')
 assert(box.space.T2_VINYL.engine == 'vinyl')
 assert(box.space.T3_MEMTX.engine == 'memtx')
 
-box.sql.execute("DROP TABLE t1_vinyl;")
-box.sql.execute("DROP TABLE t2_vinyl;")
-box.sql.execute("DROP TABLE t3_memtx;")
+box.execute("DROP TABLE t1_vinyl;")
+box.execute("DROP TABLE t2_vinyl;")
+box.execute("DROP TABLE t3_memtx;")
diff --git a/test/sql/errinj.result b/test/sql/errinj.result
index a1e7cc4..4b26560 100644
--- a/test/sql/errinj.result
+++ b/test/sql/errinj.result
@@ -7,8 +7,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 errinj = box.error.injection
 ---
@@ -18,17 +19,21 @@ fiber = require('fiber')
 ...
 -- gh-3924 Check that tuple_formats of ephemeral spaces are
 -- reused.
-box.sql.execute("CREATE TABLE t4 (id INTEGER PRIMARY KEY, a INTEGER);")
+box.execute("CREATE TABLE t4 (id INTEGER PRIMARY KEY, a INTEGER);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4 VALUES (1,1)")
+box.execute("INSERT INTO t4 VALUES (1,1)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4 VALUES (2,1)")
+box.execute("INSERT INTO t4 VALUES (2,1)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t4 VALUES (3,2)")
+box.execute("INSERT INTO t4 VALUES (3,2)")
 ---
+- rowcount: 1
 ...
 errinj.set('ERRINJ_TUPLE_FORMAT_COUNT', 200)
 ---
@@ -38,7 +43,7 @@ errinj.set('ERRINJ_MEMTX_DELAY_GC', true)
 ---
 - ok
 ...
-for i = 1, 201 do box.sql.execute("SELECT DISTINCT a FROM t4") end
+for i = 1, 201 do box.execute("SELECT DISTINCT a FROM t4") end
 ---
 ...
 errinj.set('ERRINJ_MEMTX_DELAY_GC', false)
@@ -49,11 +54,13 @@ errinj.set('ERRINJ_TUPLE_FORMAT_COUNT', -1)
 ---
 - ok
 ...
-box.sql.execute('DROP TABLE t4')
+box.execute('DROP TABLE t4')
 ---
+- rowcount: 1
 ...
-box.sql.execute('create table test (id int primary key, a float, b text)')
+box.execute('create table test (id int primary key, a float, b text)')
 ---
+- rowcount: 1
 ...
 box.schema.user.grant('guest','read,write,execute', 'universe')
 ---
@@ -122,16 +129,18 @@ select_res
 cn:close()
 ---
 ...
-box.sql.execute('drop table test')
+box.execute('drop table test')
 ---
+- rowcount: 1
 ...
 --
 -- gh-3326: after the iproto start using new buffers rotation
 -- policy, SQL responses could be corrupted, when DDL/DML is mixed
 -- with DQL. Same as gh-3255.
 --
-box.sql.execute('CREATE TABLE test (id integer primary key)')
+box.execute('CREATE TABLE test (id integer primary key)')
 ---
+- rowcount: 1
 ...
 cn = remote.connect(box.cfg.listen)
 ---
@@ -156,8 +165,9 @@ errinj.set("ERRINJ_IPROTO_TX_DELAY", false)
 ---
 - ok
 ...
-box.sql.execute('DROP TABLE test')
+box.execute('DROP TABLE test')
 ---
+- rowcount: 1
 ...
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ---
@@ -165,21 +175,23 @@ box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ----
 ---- gh-3273: Move SQL TRIGGERs into server.
 ----
-box.sql.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER);");
+box.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY, a INTEGER);");
+box.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY, a INTEGER);");
 ---
+- rowcount: 1
 ...
 box.error.injection.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-box.sql.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
+box.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
 ---
-- error: Failed to write to disk
+- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
 ...
-box.sql.execute("CREATE INDEX t1a ON t1(a);")
+box.execute("CREATE INDEX t1a ON t1(a);")
 ---
 - error: Failed to write to disk
 ...
@@ -187,19 +199,33 @@ box.error.injection.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-box.sql.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
+box.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (3, 3);")
+box.execute("INSERT INTO t1 VALUES (3, 3);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * from t1");
+box.execute("SELECT * from t1");
 ---
-- - [3, 3]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  rows:
+  - [3, 3]
 ...
-box.sql.execute("SELECT * from t2");
+box.execute("SELECT * from t2");
 ---
-- - [1, 1]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  rows:
+  - [1, 1]
 ...
 box.error.injection.set("ERRINJ_WAL_IO", true)
 ---
@@ -229,42 +255,60 @@ box.error.injection.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-box.sql.execute("DROP TRIGGER t1t;")
+box.execute("DROP TRIGGER t1t;")
 ---
-- error: Failed to write to disk
+- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
 ...
 box.error.injection.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-box.sql.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (3, 3);")
+box.execute("INSERT INTO t1 VALUES (3, 3);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * from t1");
+box.execute("SELECT * from t1");
 ---
-- - [3, 3]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  rows:
+  - [3, 3]
 ...
-box.sql.execute("SELECT * from t2");
+box.execute("SELECT * from t2");
 ---
-- - [1, 1]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  rows:
+  - [1, 1]
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
 -- Tests which are aimed at verifying work of commit/rollback
 -- triggers on _fk_constraint space.
 --
-box.sql.execute("CREATE TABLE t3 (id FLOAT PRIMARY KEY, a INT REFERENCES t3, b INT UNIQUE);")
+box.execute("CREATE TABLE t3 (id FLOAT PRIMARY KEY, a INT REFERENCES t3, b INT UNIQUE);")
 ---
+- rowcount: 1
 ...
 t = box.space._fk_constraint:select{}[1]:totable()
 ---
@@ -288,15 +332,15 @@ errinj.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-box.sql.execute("INSERT INTO t3 VALUES (1, 2, 2);")
+box.execute("INSERT INTO t3 VALUES (1, 2, 2);")
 ---
-- error: FOREIGN KEY constraint failed
+- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-box.sql.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
+box.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
 ---
 - error: Failed to write to disk
 ...
@@ -304,41 +348,45 @@ errinj.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DELETE FROM t3;")
+box.execute("DELETE FROM t3;")
 ---
+- rowcount: 1
 ...
 box.snapshot()
 ---
 - ok
 ...
-box.sql.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
+box.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 ---
-- error: FOREIGN KEY constraint failed
+- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-box.sql.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;")
+box.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;")
 ---
-- error: Failed to write to disk
+- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
 ...
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 ---
-- error: FOREIGN KEY constraint failed
+- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-box.sql.execute("DROP TABLE t3;")
+box.execute("DROP TABLE t3;")
 ---
+- rowcount: 1
 ...
 -- gh-3780: space without PK raises error if
 -- it is used in SQL queries.
@@ -349,11 +397,13 @@ errinj = box.error.injection
 fiber = require('fiber')
 ---
 ...
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t VALUES (1);")
+box.execute("INSERT INTO t VALUES (1);")
 ---
+- rowcount: 1
 ...
 errinj.set("ERRINJ_WAL_DELAY", true)
 ---
@@ -365,21 +415,21 @@ errinj.set("ERRINJ_WAL_DELAY", true)
 -- in yield, all operations on space will be blocked due to
 -- absence of primary key.
 --
-function drop_table_yield() box.sql.execute("DROP TABLE t;") end
+function drop_table_yield() box.execute("DROP TABLE t;") end
 ---
 ...
 f = fiber.create(drop_table_yield)
 ---
 ...
-box.sql.execute("SELECT * FROM t;")
+box.execute("SELECT * FROM t;")
 ---
 - error: SQL does not support spaces without primary key
 ...
-box.sql.execute("INSERT INTO t VALUES (2);")
+box.execute("INSERT INTO t VALUES (2);")
 ---
 - error: SQL does not support spaces without primary key
 ...
-box.sql.execute("UPDATE t SET id = 2;")
+box.execute("UPDATE t SET id = 2;")
 ---
 - error: SQL does not support spaces without primary key
 ...
diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua
index d8833fe..c0c2531 100644
--- a/test/sql/errinj.test.lua
+++ b/test/sql/errinj.test.lua
@@ -1,24 +1,24 @@
 remote = require('net.box')
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 errinj = box.error.injection
 fiber = require('fiber')
 
 -- gh-3924 Check that tuple_formats of ephemeral spaces are
 -- reused.
-box.sql.execute("CREATE TABLE t4 (id INTEGER PRIMARY KEY, a INTEGER);")
-box.sql.execute("INSERT INTO t4 VALUES (1,1)")
-box.sql.execute("INSERT INTO t4 VALUES (2,1)")
-box.sql.execute("INSERT INTO t4 VALUES (3,2)")
+box.execute("CREATE TABLE t4 (id INTEGER PRIMARY KEY, a INTEGER);")
+box.execute("INSERT INTO t4 VALUES (1,1)")
+box.execute("INSERT INTO t4 VALUES (2,1)")
+box.execute("INSERT INTO t4 VALUES (3,2)")
 errinj.set('ERRINJ_TUPLE_FORMAT_COUNT', 200)
 errinj.set('ERRINJ_MEMTX_DELAY_GC', true)
-for i = 1, 201 do box.sql.execute("SELECT DISTINCT a FROM t4") end
+for i = 1, 201 do box.execute("SELECT DISTINCT a FROM t4") end
 errinj.set('ERRINJ_MEMTX_DELAY_GC', false)
 errinj.set('ERRINJ_TUPLE_FORMAT_COUNT', -1)
-box.sql.execute('DROP TABLE t4')
+box.execute('DROP TABLE t4')
 
-box.sql.execute('create table test (id int primary key, a float, b text)')
+box.execute('create table test (id int primary key, a float, b text)')
 box.schema.user.grant('guest','read,write,execute', 'universe')
 cn = remote.connect(box.cfg.listen)
 cn:ping()
@@ -41,14 +41,14 @@ insert_res
 select_res
 
 cn:close()
-box.sql.execute('drop table test')
+box.execute('drop table test')
 
 --
 -- gh-3326: after the iproto start using new buffers rotation
 -- policy, SQL responses could be corrupted, when DDL/DML is mixed
 -- with DQL. Same as gh-3255.
 --
-box.sql.execute('CREATE TABLE test (id integer primary key)')
+box.execute('CREATE TABLE test (id integer primary key)')
 cn = remote.connect(box.cfg.listen)
 
 ch = fiber.channel(200)
@@ -58,22 +58,22 @@ for i = 1, 100 do fiber.create(function() for j = 1, 10 do cn.space.TEST:get{1}
 for i = 1, 200 do ch:get() end
 errinj.set("ERRINJ_IPROTO_TX_DELAY", false)
 
-box.sql.execute('DROP TABLE test')
+box.execute('DROP TABLE test')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 
 ----
 ---- gh-3273: Move SQL TRIGGERs into server.
 ----
-box.sql.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER);");
-box.sql.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY, a INTEGER);");
+box.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER);");
+box.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY, a INTEGER);");
 box.error.injection.set("ERRINJ_WAL_IO", true)
-box.sql.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
-box.sql.execute("CREATE INDEX t1a ON t1(a);")
+box.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
+box.execute("CREATE INDEX t1a ON t1(a);")
 box.error.injection.set("ERRINJ_WAL_IO", false)
-box.sql.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
-box.sql.execute("INSERT INTO t1 VALUES (3, 3);")
-box.sql.execute("SELECT * from t1");
-box.sql.execute("SELECT * from t2");
+box.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
+box.execute("INSERT INTO t1 VALUES (3, 3);")
+box.execute("SELECT * from t1");
+box.execute("SELECT * from t2");
 box.error.injection.set("ERRINJ_WAL_IO", true)
 t = box.space._trigger:get('T1T')
 t_new = t:totable()
@@ -82,20 +82,20 @@ _ = box.space._trigger:replace(t, t_new)
 box.error.injection.set("ERRINJ_WAL_IO", false)
 _ = box.space._trigger:replace(t, t_new)
 box.error.injection.set("ERRINJ_WAL_IO", true)
-box.sql.execute("DROP TRIGGER t1t;")
+box.execute("DROP TRIGGER t1t;")
 box.error.injection.set("ERRINJ_WAL_IO", false)
-box.sql.execute("DELETE FROM t1;")
-box.sql.execute("DELETE FROM t2;")
-box.sql.execute("INSERT INTO t1 VALUES (3, 3);")
-box.sql.execute("SELECT * from t1");
-box.sql.execute("SELECT * from t2");
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t2;")
+box.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t2;")
+box.execute("INSERT INTO t1 VALUES (3, 3);")
+box.execute("SELECT * from t1");
+box.execute("SELECT * from t2");
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
 
 -- Tests which are aimed at verifying work of commit/rollback
 -- triggers on _fk_constraint space.
 --
-box.sql.execute("CREATE TABLE t3 (id FLOAT PRIMARY KEY, a INT REFERENCES t3, b INT UNIQUE);")
+box.execute("CREATE TABLE t3 (id FLOAT PRIMARY KEY, a INT REFERENCES t3, b INT UNIQUE);")
 t = box.space._fk_constraint:select{}[1]:totable()
 errinj = box.error.injection
 errinj.set("ERRINJ_WAL_IO", true)
@@ -103,28 +103,28 @@ errinj.set("ERRINJ_WAL_IO", true)
 t[9] = {2}
 box.space._fk_constraint:replace(t)
 errinj.set("ERRINJ_WAL_IO", false)
-box.sql.execute("INSERT INTO t3 VALUES (1, 2, 2);")
+box.execute("INSERT INTO t3 VALUES (1, 2, 2);")
 errinj.set("ERRINJ_WAL_IO", true)
-box.sql.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
+box.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
 errinj.set("ERRINJ_WAL_IO", false)
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
-box.sql.execute("DELETE FROM t3;")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("DELETE FROM t3;")
 box.snapshot()
-box.sql.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 errinj.set("ERRINJ_WAL_IO", true)
-box.sql.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;")
-box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);")
+box.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;")
+box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 errinj.set("ERRINJ_WAL_IO", false)
-box.sql.execute("DROP TABLE t3;")
+box.execute("DROP TABLE t3;")
 
 -- gh-3780: space without PK raises error if
 -- it is used in SQL queries.
 --
 errinj = box.error.injection
 fiber = require('fiber')
-box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY);")
-box.sql.execute("INSERT INTO t VALUES (1);")
+box.execute("CREATE TABLE t (id INT PRIMARY KEY);")
+box.execute("INSERT INTO t VALUES (1);")
 errinj.set("ERRINJ_WAL_DELAY", true)
 -- DROP TABLE consists of several steps: firstly indexes
 -- are deleted, then space itself. Lets make sure that if
@@ -132,10 +132,10 @@ errinj.set("ERRINJ_WAL_DELAY", true)
 -- in yield, all operations on space will be blocked due to
 -- absence of primary key.
 --
-function drop_table_yield() box.sql.execute("DROP TABLE t;") end
+function drop_table_yield() box.execute("DROP TABLE t;") end
 f = fiber.create(drop_table_yield)
-box.sql.execute("SELECT * FROM t;")
-box.sql.execute("INSERT INTO t VALUES (2);")
-box.sql.execute("UPDATE t SET id = 2;")
+box.execute("SELECT * FROM t;")
+box.execute("INSERT INTO t VALUES (2);")
+box.execute("UPDATE t SET id = 2;")
 -- Finish drop space.
 errinj.set("ERRINJ_WAL_DELAY", false)
diff --git a/test/sql/foreign-keys.result b/test/sql/foreign-keys.result
index 3c6464e..293492e 100644
--- a/test/sql/foreign-keys.result
+++ b/test/sql/foreign-keys.result
@@ -8,17 +8,21 @@ test_run:cmd('restart server default with cleanup=1')
 -- Check that tuple inserted into _fk_constraint is FK constrains
 -- valid data.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b INT);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX i1 ON t1(a);")
+box.execute("CREATE UNIQUE INDEX i1 ON t1(a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2 (a INT, b INT, id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2 (a INT, b INT, id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ---
+- rowcount: 1
 ...
 -- Parent and child spaces must exist.
 --
@@ -56,8 +60,9 @@ box.space._fk_constraint:insert(t)
 - error: 'Failed to create foreign key constraint ''fk_1'': referencing space can''t
     be VIEW'
 ...
-box.sql.execute("DROP VIEW v1;")
+box.execute("DROP VIEW v1;")
 ---
+- rowcount: 1
 ...
 -- Match clause can be only one of: simple, partial, full.
 --
@@ -87,8 +92,9 @@ box.space._fk_constraint:insert(t)
 -- Temporary restriction (until SQL triggers work from Lua):
 -- referencing space must be empty.
 --
-box.sql.execute("INSERT INTO t2 VALUES (1, 2, 3);")
+box.execute("INSERT INTO t2 VALUES (1, 2, 3);")
 ---
+- rowcount: 1
 ...
 t = {'fk_1', child_id, parent_id, false, 'simple', 'restrict', 'restrict', {2}, {1}}
 ---
@@ -98,8 +104,9 @@ box.space._fk_constraint:insert(t)
 - error: 'Failed to create foreign key constraint ''fk_1'': referencing space must
     be empty'
 ...
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 ---
+- rowcount: 1
 ...
 -- Links must be specififed correctly.
 --
@@ -172,9 +179,10 @@ t = box.space._fk_constraint:insert(t)
 -- Implicitly referenced index can't be dropped,
 -- ergo - space can't be dropped until it is referenced.
 --
-box.sql.execute("DROP INDEX i1 on t1;")
+box.execute("DROP INDEX i1 on t1;")
 ---
-- error: 'Can''t modify space ''T1'': can not drop a referenced index'
+- error: 'Error during execution of VDBE byte-code: Can''t modify space ''T1'': can
+    not drop a referenced index'
 ...
 -- Referenced index can't be altered as well, if alter leads to
 -- rebuild of index (e.g. index still can be renamed).
@@ -213,11 +221,13 @@ box.space.T1:drop()
 -- Create several constraints to make sure that they are held
 -- as linked lists correctly including self-referencing constraints.
 --
-box.sql.execute("CREATE TABLE child (id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE child (id INT PRIMARY KEY, a INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE parent (a INT, id INT PRIMARY KEY);")
+box.execute("CREATE TABLE parent (a INT, id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
 parent_id = box.space._space.index.name:select('PARENT')[1]['id']
 ---
@@ -330,14 +340,17 @@ box.space.PARENT:drop()
 -- Check that parser correctly handles MATCH, ON DELETE and
 -- ON UPDATE clauses.
 --
-box.sql.execute('CREATE TABLE tp (id INT PRIMARY KEY, a INT UNIQUE)')
+box.execute('CREATE TABLE tp (id INT PRIMARY KEY, a INT UNIQUE)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE tc (id INT PRIMARY KEY, a INT REFERENCES tp(a) MATCH FULL ON DELETE SET NULL)')
+box.execute('CREATE TABLE tc (id INT PRIMARY KEY, a INT REFERENCES tp(a) MATCH FULL ON DELETE SET NULL)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('ALTER TABLE tc ADD CONSTRAINT fk1 FOREIGN KEY (id) REFERENCES tp(id) MATCH PARTIAL ON DELETE CASCADE ON UPDATE SET NULL')
+box.execute('ALTER TABLE tc ADD CONSTRAINT fk1 FOREIGN KEY (id) REFERENCES tp(id) MATCH PARTIAL ON DELETE CASCADE ON UPDATE SET NULL')
 ---
+- rowcount: 0
 ...
 box.space._fk_constraint:select{}
 ---
@@ -345,32 +358,35 @@ box.space._fk_constraint:select{}
   - ['FK_CONSTRAINT_1_TC', 518, 517, false, 'full', 'set_null', 'no_action', [1],
     [1]]
 ...
-box.sql.execute('DROP TABLE tc')
+box.execute('DROP TABLE tc')
 ---
+- rowcount: 1
 ...
-box.sql.execute('DROP TABLE tp')
+box.execute('DROP TABLE tp')
 ---
+- rowcount: 1
 ...
 -- gh-3475: ON UPDATE and ON DELETE clauses must appear once;
 -- MATCH clause must come first.
-box.sql.execute('CREATE TABLE t1 (id INT PRIMARY KEY);')
+box.execute('CREATE TABLE t1 (id INT PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE RESTRICT);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE RESTRICT);')
 ---
 - error: Keyword 'DELETE' is reserved. Please use double quotes if 'DELETE' is an
     identifier.
 ...
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE CASCADE);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE CASCADE);')
 ---
 - error: Keyword 'DELETE' is reserved. Please use double quotes if 'DELETE' is an
     identifier.
 ...
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON UPDATE RESTRICT ON DELETE RESTRICT);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON UPDATE RESTRICT ON DELETE RESTRICT);')
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE MATCH FULL);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE MATCH FULL);')
 ---
 - error: Keyword 'MATCH' is reserved. Please use double quotes if 'MATCH' is an identifier.
 ...
diff --git a/test/sql/foreign-keys.test.lua b/test/sql/foreign-keys.test.lua
index 3fb7cab..da0986d 100644
--- a/test/sql/foreign-keys.test.lua
+++ b/test/sql/foreign-keys.test.lua
@@ -6,10 +6,10 @@ test_run:cmd('restart server default with cleanup=1')
 -- Check that tuple inserted into _fk_constraint is FK constrains
 -- valid data.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b INT);")
-box.sql.execute("CREATE UNIQUE INDEX i1 ON t1(a);")
-box.sql.execute("CREATE TABLE t2 (a INT, b INT, id INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b INT);")
+box.execute("CREATE UNIQUE INDEX i1 ON t1(a);")
+box.execute("CREATE TABLE t2 (a INT, b INT, id INT PRIMARY KEY);")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 
 -- Parent and child spaces must exist.
 --
@@ -26,7 +26,7 @@ t = {'fk_1', child_id, view_id, false, 'simple', 'restrict', 'restrict', {0}, {1
 box.space._fk_constraint:insert(t)
 t = {'fk_1', view_id, parent_id, false, 'simple', 'restrict', 'restrict', {0}, {1}}
 box.space._fk_constraint:insert(t)
-box.sql.execute("DROP VIEW v1;")
+box.execute("DROP VIEW v1;")
 
 -- Match clause can be only one of: simple, partial, full.
 --
@@ -43,10 +43,10 @@ box.space._fk_constraint:insert(t)
 -- Temporary restriction (until SQL triggers work from Lua):
 -- referencing space must be empty.
 --
-box.sql.execute("INSERT INTO t2 VALUES (1, 2, 3);")
+box.execute("INSERT INTO t2 VALUES (1, 2, 3);")
 t = {'fk_1', child_id, parent_id, false, 'simple', 'restrict', 'restrict', {2}, {1}}
 box.space._fk_constraint:insert(t)
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 
 -- Links must be specififed correctly.
 --
@@ -84,7 +84,7 @@ t = box.space._fk_constraint:insert(t)
 -- Implicitly referenced index can't be dropped,
 -- ergo - space can't be dropped until it is referenced.
 --
-box.sql.execute("DROP INDEX i1 on t1;")
+box.execute("DROP INDEX i1 on t1;")
 
 -- Referenced index can't be altered as well, if alter leads to
 -- rebuild of index (e.g. index still can be renamed).
@@ -108,8 +108,8 @@ box.space.T1:drop()
 -- Create several constraints to make sure that they are held
 -- as linked lists correctly including self-referencing constraints.
 --
-box.sql.execute("CREATE TABLE child (id INT PRIMARY KEY, a INT);")
-box.sql.execute("CREATE TABLE parent (a INT, id INT PRIMARY KEY);")
+box.execute("CREATE TABLE child (id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE parent (a INT, id INT PRIMARY KEY);")
 
 parent_id = box.space._space.index.name:select('PARENT')[1]['id']
 child_id = box.space._space.index.name:select('CHILD')[1]['id']
@@ -153,20 +153,20 @@ box.space.PARENT:drop()
 -- Check that parser correctly handles MATCH, ON DELETE and
 -- ON UPDATE clauses.
 --
-box.sql.execute('CREATE TABLE tp (id INT PRIMARY KEY, a INT UNIQUE)')
-box.sql.execute('CREATE TABLE tc (id INT PRIMARY KEY, a INT REFERENCES tp(a) MATCH FULL ON DELETE SET NULL)')
-box.sql.execute('ALTER TABLE tc ADD CONSTRAINT fk1 FOREIGN KEY (id) REFERENCES tp(id) MATCH PARTIAL ON DELETE CASCADE ON UPDATE SET NULL')
+box.execute('CREATE TABLE tp (id INT PRIMARY KEY, a INT UNIQUE)')
+box.execute('CREATE TABLE tc (id INT PRIMARY KEY, a INT REFERENCES tp(a) MATCH FULL ON DELETE SET NULL)')
+box.execute('ALTER TABLE tc ADD CONSTRAINT fk1 FOREIGN KEY (id) REFERENCES tp(id) MATCH PARTIAL ON DELETE CASCADE ON UPDATE SET NULL')
 box.space._fk_constraint:select{}
-box.sql.execute('DROP TABLE tc')
-box.sql.execute('DROP TABLE tp')
+box.execute('DROP TABLE tc')
+box.execute('DROP TABLE tp')
 
 -- gh-3475: ON UPDATE and ON DELETE clauses must appear once;
 -- MATCH clause must come first.
-box.sql.execute('CREATE TABLE t1 (id INT PRIMARY KEY);')
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE RESTRICT);')
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE CASCADE);')
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON UPDATE RESTRICT ON DELETE RESTRICT);')
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE MATCH FULL);')
+box.execute('CREATE TABLE t1 (id INT PRIMARY KEY);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE RESTRICT);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON DELETE CASCADE);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE ON UPDATE RESTRICT ON DELETE RESTRICT);')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY REFERENCES t2 ON DELETE CASCADE MATCH FULL);')
 box.space.T1:drop()
 
 --- Clean-up SQL DD hash.
diff --git a/test/sql/func-recreate.result b/test/sql/func-recreate.result
index 22e169b..85c2c9c 100644
--- a/test/sql/func-recreate.result
+++ b/test/sql/func-recreate.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Check errors during function create process
 fiber = require('fiber')
@@ -17,7 +18,7 @@ box.internal.sql_create_function('WAITFOR', 'INT', function (n) fiber.sleep(n) r
 ch = fiber.channel(1)
 ---
 ...
-_ = fiber.create(function () ch:put(box.sql.execute('select WAITFOR(0.2)')) end)
+_ = fiber.create(function () ch:put(box.execute('select WAITFOR(0.2)')) end)
 ---
 ...
 fiber.sleep(0.1)
@@ -29,7 +30,11 @@ box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber')
 ...
 ch:get()
 ---
-- - [0.2]
+- metadata:
+  - name: WAITFOR(0.2)
+    type: INTEGER
+  rows:
+  - [0.2]
 ...
 box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
 ---
diff --git a/test/sql/func-recreate.test.lua b/test/sql/func-recreate.test.lua
index b4e1fbe..753e9ca 100644
--- a/test/sql/func-recreate.test.lua
+++ b/test/sql/func-recreate.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Check errors during function create process
 fiber = require('fiber')
@@ -8,7 +8,7 @@ box.internal.sql_create_function('WAITFOR', 'INT', function (n) fiber.sleep(n) r
 
 ch = fiber.channel(1)
 
-_ = fiber.create(function () ch:put(box.sql.execute('select WAITFOR(0.2)')) end)
+_ = fiber.create(function () ch:put(box.execute('select WAITFOR(0.2)')) end)
 fiber.sleep(0.1)
 
 box.internal.sql_create_function('WAITFOR', 'INT', function (n) require('fiber').sleep(n) return n end)
diff --git a/test/sql/gh-2347-max-int-literals.result b/test/sql/gh-2347-max-int-literals.result
index c289a80..635a9bc 100644
--- a/test/sql/gh-2347-max-int-literals.result
+++ b/test/sql/gh-2347-max-int-literals.result
@@ -4,25 +4,34 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 box.cfg{}
 ---
 ...
-box.sql.execute("select (9223372036854775807)")
+box.execute("select (9223372036854775807)")
 ---
-- - [9223372036854775807]
+- metadata:
+  - name: (9223372036854775807)
+    type: INTEGER
+  rows:
+  - [9223372036854775807]
 ...
-box.sql.execute("select (-9223372036854775808)")
+box.execute("select (-9223372036854775808)")
 ---
-- - [-9223372036854775808]
+- metadata:
+  - name: (-9223372036854775808)
+    type: NUMERIC
+  rows:
+  - [-9223372036854775808]
 ...
-box.sql.execute("select (9223372036854775808)")
+box.execute("select (9223372036854775808)")
 ---
 - error: 'oversized integer: 9223372036854775808'
 ...
-box.sql.execute("select (-9223372036854775809)")
+box.execute("select (-9223372036854775809)")
 ---
 - error: 'oversized integer: -9223372036854775809'
 ...
diff --git a/test/sql/gh-2347-max-int-literals.test.lua b/test/sql/gh-2347-max-int-literals.test.lua
index 4b1ef0d..8331f03 100644
--- a/test/sql/gh-2347-max-int-literals.test.lua
+++ b/test/sql/gh-2347-max-int-literals.test.lua
@@ -1,11 +1,11 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 box.cfg{}
 
-box.sql.execute("select (9223372036854775807)")
-box.sql.execute("select (-9223372036854775808)")
+box.execute("select (9223372036854775807)")
+box.execute("select (-9223372036854775808)")
 
-box.sql.execute("select (9223372036854775808)")
-box.sql.execute("select (-9223372036854775809)")
+box.execute("select (9223372036854775808)")
+box.execute("select (-9223372036854775809)")
diff --git a/test/sql/gh-2362-select-access-rights.result b/test/sql/gh-2362-select-access-rights.result
index 8f1ecfa..c6026c1 100644
--- a/test/sql/gh-2362-select-access-rights.result
+++ b/test/sql/gh-2362-select-access-rights.result
@@ -7,20 +7,25 @@ engine = test_run:get_cfg('engine')
 nb = require('net.box')
 ---
 ...
-box.sql.execute("PRAGMA sql_default_engine='"..engine.."'")
+box.execute("PRAGMA sql_default_engine='"..engine.."'")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE t1 (s1 INT PRIMARY KEY, s2 INT UNIQUE);")
+box.execute("CREATE TABLE t1 (s1 INT PRIMARY KEY, s2 INT UNIQUE);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (1, 1);")
+box.execute("INSERT INTO t1 VALUES (1, 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES (1);")
+box.execute("INSERT INTO t2 VALUES (1);")
 ---
+- rowcount: 1
 ...
 box.schema.user.grant('guest','read', 'space', 'T1')
 ---
@@ -58,8 +63,9 @@ c:execute('SELECT * FROM t1, t2 WHERE t1.s1 = t2.s1')
 ---
 - error: Read access to space 'T1' is denied for user 'guest'
 ...
-box.sql.execute("CREATE VIEW v AS SELECT * FROM t1")
+box.execute("CREATE VIEW v AS SELECT * FROM t1")
 ---
+- rowcount: 1
 ...
 box.schema.user.grant('guest','read', 'space', 'V')
 ---
@@ -71,8 +77,9 @@ c:execute('SELECT * FROM v')
 ---
 - error: Read access to space 'T1' is denied for user 'guest'
 ...
-box.sql.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
+box.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
 ---
+- rowcount: 1
 ...
 box.schema.user.grant('guest','read','space', 'T3')
 ---
@@ -94,15 +101,19 @@ box.schema.user.revoke('guest','read','space', 'T2')
 box.schema.user.revoke('guest','read','space', 'T3')
 ---
 ...
-box.sql.execute('DROP VIEW v')
+box.execute('DROP VIEW v')
 ---
+- rowcount: 1
 ...
-box.sql.execute('DROP TABLE t3')
+box.execute('DROP TABLE t3')
 ---
+- rowcount: 1
 ...
-box.sql.execute('DROP TABLE t2')
+box.execute('DROP TABLE t2')
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh-2362-select-access-rights.test.lua b/test/sql/gh-2362-select-access-rights.test.lua
index 9c50e19..f2b66b6 100644
--- a/test/sql/gh-2362-select-access-rights.test.lua
+++ b/test/sql/gh-2362-select-access-rights.test.lua
@@ -2,11 +2,11 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 nb = require('net.box')
 
-box.sql.execute("PRAGMA sql_default_engine='"..engine.."'")
-box.sql.execute("CREATE TABLE t1 (s1 INT PRIMARY KEY, s2 INT UNIQUE);")
-box.sql.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
-box.sql.execute("INSERT INTO t1 VALUES (1, 1);")
-box.sql.execute("INSERT INTO t2 VALUES (1);")
+box.execute("PRAGMA sql_default_engine='"..engine.."'")
+box.execute("CREATE TABLE t1 (s1 INT PRIMARY KEY, s2 INT UNIQUE);")
+box.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
+box.execute("INSERT INTO t1 VALUES (1, 1);")
+box.execute("INSERT INTO t2 VALUES (1);")
 
 box.schema.user.grant('guest','read', 'space', 'T1')
 c = nb.connect(box.cfg.listen)
@@ -20,13 +20,13 @@ box.schema.user.grant('guest','read', 'space', 'T2')
 c = nb.connect(box.cfg.listen)
 c:execute('SELECT * FROM t1, t2 WHERE t1.s1 = t2.s1')
 
-box.sql.execute("CREATE VIEW v AS SELECT * FROM t1")
+box.execute("CREATE VIEW v AS SELECT * FROM t1")
 
 box.schema.user.grant('guest','read', 'space', 'V')
 v = nb.connect(box.cfg.listen)
 c:execute('SELECT * FROM v')
 
-box.sql.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
+box.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
 box.schema.user.grant('guest','read','space', 'T3')
 v = nb.connect(box.cfg.listen)
 c:execute('INSERT INTO t3 VALUES (1, 1)')
@@ -36,7 +36,7 @@ box.schema.user.revoke('guest','read','space', 'V')
 box.schema.user.revoke('guest','read','space', 'T2')
 box.schema.user.revoke('guest','read','space', 'T3')
 
-box.sql.execute('DROP VIEW v')
-box.sql.execute('DROP TABLE t3')
-box.sql.execute('DROP TABLE t2')
-box.sql.execute("DROP TABLE t1")
+box.execute('DROP VIEW v')
+box.execute('DROP TABLE t3')
+box.execute('DROP TABLE t2')
+box.execute("DROP TABLE t1")
diff --git a/test/sql/gh-2929-primary-key.result b/test/sql/gh-2929-primary-key.result
index 4052665..1e01041 100644
--- a/test/sql/gh-2929-primary-key.result
+++ b/test/sql/gh-2929-primary-key.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- All tables in SQL are now WITHOUT ROW ID, so if user
 -- tries to create table without a primary key, an appropriate error message
@@ -13,32 +14,34 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 box.cfg{}
 ---
 ...
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
+box.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
 ---
 - error: PRIMARY KEY missing on table T2
 ...
-box.sql.execute("CREATE TABLE t3(a FLOAT)")
+box.execute("CREATE TABLE t3(a FLOAT)")
 ---
 - error: PRIMARY KEY missing on table T3
 ...
-box.sql.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
+box.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
 ---
 - error: PRIMARY KEY missing on table T4
 ...
-box.sql.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
+box.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
 ---
 - error: PRIMARY KEY missing on table T5
 ...
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
 --
 -- gh-3522: invalid primary key name
 --
-box.sql.execute("CREATE TABLE tx (a INT, PRIMARY KEY (b));")
+box.execute("CREATE TABLE tx (a INT, PRIMARY KEY (b));")
 ---
 - error: Can’t resolve field 'B'
 ...
diff --git a/test/sql/gh-2929-primary-key.test.lua b/test/sql/gh-2929-primary-key.test.lua
index 6c2f0eb..870e65a 100644
--- a/test/sql/gh-2929-primary-key.test.lua
+++ b/test/sql/gh-2929-primary-key.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- All tables in SQL are now WITHOUT ROW ID, so if user
 -- tries to create table without a primary key, an appropriate error message
@@ -8,16 +8,16 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 
 box.cfg{}
 
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
-box.sql.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE)")
+box.execute("CREATE TABLE t2(a INT UNIQUE, b INT)")
 
-box.sql.execute("CREATE TABLE t3(a FLOAT)")
-box.sql.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
-box.sql.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
+box.execute("CREATE TABLE t3(a FLOAT)")
+box.execute("CREATE TABLE t4(a FLOAT, b TEXT)")
+box.execute("CREATE TABLE t5(a FLOAT, b FLOAT UNIQUE)")
 
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 
 --
 -- gh-3522: invalid primary key name
 --
-box.sql.execute("CREATE TABLE tx (a INT, PRIMARY KEY (b));")
+box.execute("CREATE TABLE tx (a INT, PRIMARY KEY (b));")
diff --git a/test/sql/gh-2981-check-autoinc.result b/test/sql/gh-2981-check-autoinc.result
index b0f55e6..00bfe91 100644
--- a/test/sql/gh-2981-check-autoinc.result
+++ b/test/sql/gh-2981-check-autoinc.result
@@ -4,55 +4,66 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 box.cfg{}
 ---
 ...
-box.sql.execute("CREATE TABLE t1 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19));");
+box.execute("CREATE TABLE t1 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19));");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19 AND s1 <> 25));");
+box.execute("CREATE TABLE t2 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19 AND s1 <> 25));");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t3 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 < 10));");
+box.execute("CREATE TABLE t3 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 < 10));");
 ---
+- rowcount: 1
 ...
-box.sql.execute("insert into t1 values (18, null);")
+box.execute("insert into t1 values (18, null);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("insert into t1(s2) values (null);")
+box.execute("insert into t1(s2) values (null);")
 ---
-- error: 'CHECK constraint failed: T1'
+- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T1'
 ...
-box.sql.execute("insert into t2 values (18, null);")
+box.execute("insert into t2 values (18, null);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("insert into t2(s2) values (null);")
+box.execute("insert into t2(s2) values (null);")
 ---
-- error: 'CHECK constraint failed: T2'
+- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T2'
 ...
-box.sql.execute("insert into t2 values (24, null);")
+box.execute("insert into t2 values (24, null);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("insert into t2(s2) values (null);")
+box.execute("insert into t2(s2) values (null);")
 ---
-- error: 'CHECK constraint failed: T2'
+- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T2'
 ...
-box.sql.execute("insert into t3 values (9, null)")
+box.execute("insert into t3 values (9, null)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("insert into t3(s2) values (null)")
+box.execute("insert into t3(s2) values (null)")
 ---
-- error: 'CHECK constraint failed: T3'
+- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T3'
 ...
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2")
+box.execute("DROP TABLE t2")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t3")
+box.execute("DROP TABLE t3")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh-2981-check-autoinc.test.lua b/test/sql/gh-2981-check-autoinc.test.lua
index 98a5fb4..0eb8f73 100644
--- a/test/sql/gh-2981-check-autoinc.test.lua
+++ b/test/sql/gh-2981-check-autoinc.test.lua
@@ -1,25 +1,25 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 box.cfg{}
 
-box.sql.execute("CREATE TABLE t1 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19));");
-box.sql.execute("CREATE TABLE t2 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19 AND s1 <> 25));");
-box.sql.execute("CREATE TABLE t3 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 < 10));");
+box.execute("CREATE TABLE t1 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19));");
+box.execute("CREATE TABLE t2 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 <> 19 AND s1 <> 25));");
+box.execute("CREATE TABLE t3 (s1 INTEGER PRIMARY KEY AUTOINCREMENT, s2 INTEGER, CHECK (s1 < 10));");
 
-box.sql.execute("insert into t1 values (18, null);")
-box.sql.execute("insert into t1(s2) values (null);")
+box.execute("insert into t1 values (18, null);")
+box.execute("insert into t1(s2) values (null);")
 
-box.sql.execute("insert into t2 values (18, null);")
-box.sql.execute("insert into t2(s2) values (null);")
-box.sql.execute("insert into t2 values (24, null);")
-box.sql.execute("insert into t2(s2) values (null);")
+box.execute("insert into t2 values (18, null);")
+box.execute("insert into t2(s2) values (null);")
+box.execute("insert into t2 values (24, null);")
+box.execute("insert into t2(s2) values (null);")
 
-box.sql.execute("insert into t3 values (9, null)")
-box.sql.execute("insert into t3(s2) values (null)")
+box.execute("insert into t3 values (9, null)")
+box.execute("insert into t3(s2) values (null)")
 
-box.sql.execute("DROP TABLE t1")
-box.sql.execute("DROP TABLE t2")
-box.sql.execute("DROP TABLE t3")
+box.execute("DROP TABLE t1")
+box.execute("DROP TABLE t2")
+box.execute("DROP TABLE t3")
 
diff --git a/test/sql/gh-3199-no-mem-leaks.result b/test/sql/gh-3199-no-mem-leaks.result
index fff6abf..759645b 100644
--- a/test/sql/gh-3199-no-mem-leaks.result
+++ b/test/sql/gh-3199-no-mem-leaks.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 fiber = require('fiber')
 ---
@@ -14,57 +15,110 @@ fiber = require('fiber')
 -- executing SQL queries.
 --
 -- box.cfg()
-box.sql.execute('CREATE TABLE test (id INT PRIMARY KEY, x INTEGER, y INTEGER)')
+box.execute('CREATE TABLE test (id INT PRIMARY KEY, x INTEGER, y INTEGER)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('INSERT INTO test VALUES (1, 1, 1), (2, 2, 2)')
+box.execute('INSERT INTO test VALUES (1, 1, 1), (2, 2, 2)')
 ---
+- rowcount: 2
 ...
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 ---
-- - [1, 1, 2]
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: BLOB
+  - name: x + y
+    type: NUMERIC
+  rows:
+  - [1, 1, 2]
   - [2, 2, 4]
 ...
 fiber.info()[fiber.self().id()].memory.used
 ---
 - 0
 ...
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
----
-- - [1, 1, 2]
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: BLOB
+  - name: x + y
+    type: NUMERIC
+  rows:
+  - [1, 1, 2]
   - [2, 2, 4]
 ...
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
----
-- - [1, 1, 2]
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: BLOB
+  - name: x + y
+    type: NUMERIC
+  rows:
+  - [1, 1, 2]
   - [2, 2, 4]
 ...
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
----
-- - [1, 1, 2]
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: BLOB
+  - name: x + y
+    type: NUMERIC
+  rows:
+  - [1, 1, 2]
   - [2, 2, 4]
 ...
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
----
-- - [1, 1, 2]
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: Y
+    type: BLOB
+  - name: x + y
+    type: NUMERIC
+  rows:
+  - [1, 1, 2]
   - [2, 2, 4]
 ...
 fiber.info()[fiber.self().id()].memory.used
 ---
 - 0
 ...
-box.sql.execute('CREATE TABLE test2 (id INT PRIMARY KEY, a TEXT, b INTEGER)')
+box.execute('CREATE TABLE test2 (id INT PRIMARY KEY, a TEXT, b INTEGER)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('INSERT INTO test2 VALUES (1, \'abc\', 1), (2, \'hello\', 2)')
+box.execute('INSERT INTO test2 VALUES (1, \'abc\', 1), (2, \'hello\', 2)')
 ---
+- rowcount: 2
 ...
-box.sql.execute('INSERT INTO test2 VALUES (3, \'test\', 3), (4, \'xx\', 4)')
+box.execute('INSERT INTO test2 VALUES (3, \'test\', 3), (4, \'xx\', 4)')
 ---
+- rowcount: 2
 ...
-box.sql.execute('SELECT a, id + 2, b FROM test2 WHERE b < id * 2 ORDER BY a ')
+box.execute('SELECT a, id + 2, b FROM test2 WHERE b < id * 2 ORDER BY a ')
 ---
-- - ['abc', 3, 1]
+- metadata:
+  - name: A
+    type: BLOB
+  - name: id + 2
+    type: NUMERIC
+  - name: B
+    type: INTEGER
+  rows:
+  - ['abc', 3, 1]
   - ['hello', 4, 2]
   - ['test', 5, 3]
   - ['xx', 6, 4]
@@ -73,23 +127,47 @@ fiber.info()[fiber.self().id()].memory.used
 ---
 - 0
 ...
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
----
-- - ['abc', 3, 'abc']
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+---
+- metadata:
+  - name: A
+    type: TEXT
+  - name: id + 2 * b
+    type: NUMERIC
+  - name: A
+    type: BLOB
+  rows:
+  - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
   - ['test', 9, 'test']
   - ['xx', 12, 'xx']
 ...
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
----
-- - ['abc', 3, 'abc']
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+---
+- metadata:
+  - name: A
+    type: TEXT
+  - name: id + 2 * b
+    type: NUMERIC
+  - name: A
+    type: BLOB
+  rows:
+  - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
   - ['test', 9, 'test']
   - ['xx', 12, 'xx']
 ...
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
----
-- - ['abc', 3, 'abc']
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+---
+- metadata:
+  - name: A
+    type: TEXT
+  - name: id + 2 * b
+    type: NUMERIC
+  - name: A
+    type: BLOB
+  rows:
+  - ['abc', 3, 'abc']
   - ['hello', 6, 'hello']
   - ['test', 9, 'test']
   - ['xx', 12, 'xx']
@@ -98,19 +176,43 @@ fiber.info()[fiber.self().id()].memory.used
 ---
 - 0
 ...
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
----
-- - [1, 4, 1]
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: y + 3 * b
+    type: NUMERIC
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 4, 1]
   - [2, 8, 2]
 ...
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
----
-- - [1, 4, 1]
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: y + 3 * b
+    type: NUMERIC
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 4, 1]
   - [2, 8, 2]
 ...
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
----
-- - [1, 4, 1]
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+---
+- metadata:
+  - name: X
+    type: INTEGER
+  - name: y + 3 * b
+    type: NUMERIC
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 4, 1]
   - [2, 8, 2]
 ...
 fiber.info()[fiber.self().id()].memory.used
@@ -118,9 +220,11 @@ fiber.info()[fiber.self().id()].memory.used
 - 0
 ...
 -- Cleanup
-box.sql.execute('DROP TABLE test')
+box.execute('DROP TABLE test')
 ---
+- rowcount: 1
 ...
-box.sql.execute('DROP TABLE test2')
+box.execute('DROP TABLE test2')
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh-3199-no-mem-leaks.test.lua b/test/sql/gh-3199-no-mem-leaks.test.lua
index 1954e34..54a6ce5 100644
--- a/test/sql/gh-3199-no-mem-leaks.test.lua
+++ b/test/sql/gh-3199-no-mem-leaks.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 fiber = require('fiber')
 
 -- This test checks that no leaks of region memory happens during
@@ -10,39 +10,39 @@ fiber = require('fiber')
 -- box.cfg()
 
 
-box.sql.execute('CREATE TABLE test (id INT PRIMARY KEY, x INTEGER, y INTEGER)')
-box.sql.execute('INSERT INTO test VALUES (1, 1, 1), (2, 2, 2)')
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('CREATE TABLE test (id INT PRIMARY KEY, x INTEGER, y INTEGER)')
+box.execute('INSERT INTO test VALUES (1, 1, 1), (2, 2, 2)')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 
 fiber.info()[fiber.self().id()].memory.used
 
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
-box.sql.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
+box.execute('SELECT x, y, x + y FROM test ORDER BY y')
 
 fiber.info()[fiber.self().id()].memory.used
 
-box.sql.execute('CREATE TABLE test2 (id INT PRIMARY KEY, a TEXT, b INTEGER)')
-box.sql.execute('INSERT INTO test2 VALUES (1, \'abc\', 1), (2, \'hello\', 2)')
-box.sql.execute('INSERT INTO test2 VALUES (3, \'test\', 3), (4, \'xx\', 4)')
-box.sql.execute('SELECT a, id + 2, b FROM test2 WHERE b < id * 2 ORDER BY a ')
+box.execute('CREATE TABLE test2 (id INT PRIMARY KEY, a TEXT, b INTEGER)')
+box.execute('INSERT INTO test2 VALUES (1, \'abc\', 1), (2, \'hello\', 2)')
+box.execute('INSERT INTO test2 VALUES (3, \'test\', 3), (4, \'xx\', 4)')
+box.execute('SELECT a, id + 2, b FROM test2 WHERE b < id * 2 ORDER BY a ')
 
 fiber.info()[fiber.self().id()].memory.used
 
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
-box.sql.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
+box.execute('SELECT a, id + 2 * b, a FROM test2 WHERE b < id * 2 ORDER BY a ')
 
 fiber.info()[fiber.self().id()].memory.used
 
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
-box.sql.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
+box.execute('SELECT x, y + 3 * b, b FROM test2, test WHERE b = x')
 
 fiber.info()[fiber.self().id()].memory.used
 
 -- Cleanup
-box.sql.execute('DROP TABLE test')
-box.sql.execute('DROP TABLE test2')
+box.execute('DROP TABLE test')
+box.execute('DROP TABLE test2')
 
diff --git a/test/sql/gh-3613-idx-alter-update-2.result b/test/sql/gh-3613-idx-alter-update-2.result
index 234336c..d899121 100644
--- a/test/sql/gh-3613-idx-alter-update-2.result
+++ b/test/sql/gh-3613-idx-alter-update-2.result
@@ -4,25 +4,31 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
-box.sql.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
+box.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i ON t (s1)')
+box.execute('CREATE INDEX i ON t (s1)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('ALTER TABLE t RENAME TO j3')
+box.execute('ALTER TABLE t RENAME TO j3')
 ---
+- rowcount: 0
 ...
 -- After gh-3613 fix, bug in cmp_def was discovered.
 -- Comparison didn't take .opts.sql into account.
 test_run:cmd('restart server default')
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 ---
+- rowcount: 1
 ...
 -- Cleanup
-box.sql.execute('DROP TABLE j3')
+box.execute('DROP TABLE j3')
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh-3613-idx-alter-update-2.test.lua b/test/sql/gh-3613-idx-alter-update-2.test.lua
index e2beb7a..ff5b651 100644
--- a/test/sql/gh-3613-idx-alter-update-2.test.lua
+++ b/test/sql/gh-3613-idx-alter-update-2.test.lua
@@ -1,16 +1,16 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
-box.sql.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
-box.sql.execute('CREATE INDEX i ON t (s1)')
-box.sql.execute('ALTER TABLE t RENAME TO j3')
+box.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
+box.execute('CREATE INDEX i ON t (s1)')
+box.execute('ALTER TABLE t RENAME TO j3')
 
 -- After gh-3613 fix, bug in cmp_def was discovered.
 -- Comparison didn't take .opts.sql into account.
 test_run:cmd('restart server default')
 
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 
 -- Cleanup
-box.sql.execute('DROP TABLE j3')
+box.execute('DROP TABLE j3')
diff --git a/test/sql/gh-3613-idx-alter-update.result b/test/sql/gh-3613-idx-alter-update.result
index 2815428..85078df 100644
--- a/test/sql/gh-3613-idx-alter-update.result
+++ b/test/sql/gh-3613-idx-alter-update.result
@@ -4,21 +4,26 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
-box.sql.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
+box.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE INDEX i ON t (s1)')
+box.execute('CREATE INDEX i ON t (s1)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('ALTER TABLE t RENAME TO j3')
+box.execute('ALTER TABLE t RENAME TO j3')
 ---
+- rowcount: 0
 ...
 -- Due to gh-3613, next stmt caused segfault
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 ---
+- rowcount: 1
 ...
 -- Make sure that no artifacts remain after restart.
 box.snapshot()
@@ -26,12 +31,13 @@ box.snapshot()
 - ok
 ...
 test_run:cmd('restart server default')
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 ---
 - error: No index 'I' is defined in space 'J3'
 ...
-box.sql.execute('CREATE INDEX i ON j3 (s1)')
+box.execute('CREATE INDEX i ON j3 (s1)')
 ---
+- rowcount: 1
 ...
 -- Check that _index was altered properly
 box.snapshot()
@@ -39,10 +45,12 @@ box.snapshot()
 - ok
 ...
 test_run:cmd('restart server default')
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 ---
+- rowcount: 1
 ...
 -- Cleanup
-box.sql.execute('DROP TABLE j3')
+box.execute('DROP TABLE j3')
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh-3613-idx-alter-update.test.lua b/test/sql/gh-3613-idx-alter-update.test.lua
index 49b81d7..3027182 100644
--- a/test/sql/gh-3613-idx-alter-update.test.lua
+++ b/test/sql/gh-3613-idx-alter-update.test.lua
@@ -1,26 +1,26 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
-box.sql.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
-box.sql.execute('CREATE INDEX i ON t (s1)')
-box.sql.execute('ALTER TABLE t RENAME TO j3')
+box.execute('CREATE TABLE t (s1 INT PRIMARY KEY)')
+box.execute('CREATE INDEX i ON t (s1)')
+box.execute('ALTER TABLE t RENAME TO j3')
 
 -- Due to gh-3613, next stmt caused segfault
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 
 -- Make sure that no artifacts remain after restart.
 box.snapshot()
 test_run:cmd('restart server default')
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 
-box.sql.execute('CREATE INDEX i ON j3 (s1)')
+box.execute('CREATE INDEX i ON j3 (s1)')
 
 -- Check that _index was altered properly
 box.snapshot()
 test_run:cmd('restart server default')
 
-box.sql.execute('DROP INDEX i ON j3')
+box.execute('DROP INDEX i ON j3')
 
 -- Cleanup
-box.sql.execute('DROP TABLE j3')
+box.execute('DROP TABLE j3')
diff --git a/test/sql/gh-3888-values-blob-assert.result b/test/sql/gh-3888-values-blob-assert.result
index 611c10d..e7f3e38 100644
--- a/test/sql/gh-3888-values-blob-assert.result
+++ b/test/sql/gh-3888-values-blob-assert.result
@@ -10,52 +10,69 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- check 'VALUES' against typedef keywords (should fail)
-box.sql.execute('VALUES(scalar)')
+box.execute('VALUES(scalar)')
 ---
 - error: Syntax error near 'scalar'
 ...
-box.sql.execute('VALUES(float)')
+box.execute('VALUES(float)')
 ---
 - error: Syntax error near 'float'
 ...
 -- check 'SELECT' against typedef keywords (should fail)
-box.sql.execute('SELECT scalar')
+box.execute('SELECT scalar')
 ---
 - error: Syntax error near 'scalar'
 ...
-box.sql.execute('SELECT float')
+box.execute('SELECT float')
 ---
 - error: Syntax error near 'float'
 ...
 -- check 'VALUES' against ID (should fail)
-box.sql.execute('VALUES(TheColumnName)')
+box.execute('VALUES(TheColumnName)')
 ---
 - error: Can’t resolve field 'THECOLUMNNAME'
 ...
 -- check 'SELECT' against ID (should fail)
-box.sql.execute('SELECT TheColumnName')
+box.execute('SELECT TheColumnName')
 ---
 - error: Can’t resolve field 'THECOLUMNNAME'
 ...
 -- check 'VALUES' well-formed expression  (returns value)
-box.sql.execute('VALUES(-0.5e-2)')
+box.execute('VALUES(-0.5e-2)')
 ---
-- - [-0.005]
+- metadata:
+  - name: column1
+    type: NUMERIC
+  rows:
+  - [-0.005]
 ...
-box.sql.execute('SELECT X\'507265766564\'')
+box.execute('SELECT X\'507265766564\'')
 ---
-- - ['Preved']
+- metadata:
+  - name: X'507265766564'
+    type: BLOB
+  rows:
+  - ['Preved']
 ...
 -- check 'SELECT' well-formed expression  (return value)
-box.sql.execute('SELECT 3.14')
+box.execute('SELECT 3.14')
 ---
-- - [3.14]
+- metadata:
+  - name: '3.14'
+    type: INTEGER
+  rows:
+  - [3.14]
 ...
-box.sql.execute('SELECT X\'4D6564766564\'')
+box.execute('SELECT X\'4D6564766564\'')
 ---
-- - ['Medved']
+- metadata:
+  - name: X'4D6564766564'
+    type: BLOB
+  rows:
+  - ['Medved']
 ...
diff --git a/test/sql/gh-3888-values-blob-assert.test.lua b/test/sql/gh-3888-values-blob-assert.test.lua
index 6e916c2..0b7c385 100644
--- a/test/sql/gh-3888-values-blob-assert.test.lua
+++ b/test/sql/gh-3888-values-blob-assert.test.lua
@@ -6,28 +6,28 @@
 --
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- check 'VALUES' against typedef keywords (should fail)
-box.sql.execute('VALUES(scalar)')
-box.sql.execute('VALUES(float)')
+box.execute('VALUES(scalar)')
+box.execute('VALUES(float)')
 
 -- check 'SELECT' against typedef keywords (should fail)
-box.sql.execute('SELECT scalar')
-box.sql.execute('SELECT float')
+box.execute('SELECT scalar')
+box.execute('SELECT float')
 
 -- check 'VALUES' against ID (should fail)
-box.sql.execute('VALUES(TheColumnName)')
+box.execute('VALUES(TheColumnName)')
 
 -- check 'SELECT' against ID (should fail)
-box.sql.execute('SELECT TheColumnName')
+box.execute('SELECT TheColumnName')
 
 -- check 'VALUES' well-formed expression  (returns value)
-box.sql.execute('VALUES(-0.5e-2)')
-box.sql.execute('SELECT X\'507265766564\'')
+box.execute('VALUES(-0.5e-2)')
+box.execute('SELECT X\'507265766564\'')
 
 -- check 'SELECT' well-formed expression  (return value)
-box.sql.execute('SELECT 3.14')
-box.sql.execute('SELECT X\'4D6564766564\'')
+box.execute('SELECT 3.14')
+box.execute('SELECT X\'4D6564766564\'')
 
 
diff --git a/test/sql/gh2141-delete-trigger-drop-table.result b/test/sql/gh2141-delete-trigger-drop-table.result
index 82ff51a..f1fefc7 100644
--- a/test/sql/gh2141-delete-trigger-drop-table.result
+++ b/test/sql/gh2141-delete-trigger-drop-table.result
@@ -4,35 +4,49 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- create space
-box.sql.execute("CREATE TABLE t(id INT PRIMARY KEY)")
+box.execute("CREATE TABLE t(id INT PRIMARY KEY)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_bu BEFORE UPDATE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bu BEFORE UPDATE ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_au AFTER UPDATE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_au AFTER UPDATE ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_bi BEFORE INSERT ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bi BEFORE INSERT ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_ai AFTER INSERT ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_ai AFTER INSERT ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_bd BEFORE DELETE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bd BEFORE DELETE ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END")
 ---
+- rowcount: 1
 ...
 -- check that these triggers exist
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
-- - ['TT_AD', {'sql': 'CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END'}]
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows:
+  - ['TT_AD', {'sql': 'CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END'}]
   - ['TT_AI', {'sql': 'CREATE TRIGGER tt_ai AFTER INSERT ON t BEGIN SELECT 1; END'}]
   - ['TT_AU', {'sql': 'CREATE TRIGGER tt_au AFTER UPDATE ON t BEGIN SELECT 1; END'}]
   - ['TT_BD', {'sql': 'CREATE TRIGGER tt_bd BEFORE DELETE ON t BEGIN SELECT 1; END'}]
@@ -40,11 +54,17 @@ box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
   - ['TT_BU', {'sql': 'CREATE TRIGGER tt_bu BEFORE UPDATE ON t BEGIN SELECT 1; END'}]
 ...
 -- drop table
-box.sql.execute("DROP TABLE t")
+box.execute("DROP TABLE t")
 ---
+- rowcount: 1
 ...
 -- check that triggers were dropped with deleted table
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
-- []
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows: []
 ...
diff --git a/test/sql/gh2141-delete-trigger-drop-table.test.lua b/test/sql/gh2141-delete-trigger-drop-table.test.lua
index be3adc3..6fc9d99 100644
--- a/test/sql/gh2141-delete-trigger-drop-table.test.lua
+++ b/test/sql/gh2141-delete-trigger-drop-table.test.lua
@@ -1,22 +1,22 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- create space
-box.sql.execute("CREATE TABLE t(id INT PRIMARY KEY)")
+box.execute("CREATE TABLE t(id INT PRIMARY KEY)")
 
-box.sql.execute("CREATE TRIGGER tt_bu BEFORE UPDATE ON t BEGIN SELECT 1; END")
-box.sql.execute("CREATE TRIGGER tt_au AFTER UPDATE ON t BEGIN SELECT 1; END")
-box.sql.execute("CREATE TRIGGER tt_bi BEFORE INSERT ON t BEGIN SELECT 1; END")
-box.sql.execute("CREATE TRIGGER tt_ai AFTER INSERT ON t BEGIN SELECT 1; END")
-box.sql.execute("CREATE TRIGGER tt_bd BEFORE DELETE ON t BEGIN SELECT 1; END")
-box.sql.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bu BEFORE UPDATE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_au AFTER UPDATE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bi BEFORE INSERT ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_ai AFTER INSERT ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_bd BEFORE DELETE ON t BEGIN SELECT 1; END")
+box.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t BEGIN SELECT 1; END")
 
 -- check that these triggers exist
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 
 -- drop table
-box.sql.execute("DROP TABLE t")
+box.execute("DROP TABLE t")
 
 -- check that triggers were dropped with deleted table
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
diff --git a/test/sql/gh2251-multiple-update.result b/test/sql/gh2251-multiple-update.result
index e6380a0..cc1a4c8 100644
--- a/test/sql/gh2251-multiple-update.result
+++ b/test/sql/gh2251-multiple-update.result
@@ -5,49 +5,68 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
-box.sql.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);")
+box.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);")
+box.execute("INSERT INTO t1 VALUES(1,4,6);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);")
+box.execute("INSERT INTO t1 VALUES(2,5,7);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);")
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT e FROM t1")
+box.execute("SELECT e FROM t1")
 ---
-- - [7]
+- metadata:
+  - name: E
+    type: INTEGER
+  rows:
+  - [7]
   - [8]
 ...
-box.sql.execute("CREATE TABLE t2(a integer primary key, b INT UNIQUE, c FLOAT, d FLOAT, e INT,  UNIQUE(c,d));")
+box.execute("CREATE TABLE t2(a integer primary key, b INT UNIQUE, c FLOAT, d FLOAT, e INT,  UNIQUE(c,d));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES(1,2,3,4,5);")
+box.execute("INSERT INTO t2 VALUES(1,2,3,4,5);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES(2,3,4,4,6);")
+box.execute("INSERT INTO t2 VALUES(2,3,4,4,6);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE t2 SET e=e+1 WHERE b IN (SELECT b FROM t2);")
+box.execute("UPDATE t2 SET e=e+1 WHERE b IN (SELECT b FROM t2);")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT e FROM t2")
+box.execute("SELECT e FROM t2")
 ---
-- - [6]
+- metadata:
+  - name: E
+    type: INTEGER
+  rows:
+  - [6]
   - [7]
 ...
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2")
+box.execute("DROP TABLE t2")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/gh2251-multiple-update.test.lua b/test/sql/gh2251-multiple-update.test.lua
index 656ed72..a852c4b 100644
--- a/test/sql/gh2251-multiple-update.test.lua
+++ b/test/sql/gh2251-multiple-update.test.lua
@@ -1,28 +1,28 @@
 -- Regression test for #2251
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
-box.sql.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);")
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);")
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);")
+box.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);")
+box.execute("INSERT INTO t1 VALUES(1,4,6);")
+box.execute("INSERT INTO t1 VALUES(2,5,7);")
 
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);")
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);")
 
-box.sql.execute("SELECT e FROM t1")
+box.execute("SELECT e FROM t1")
 
-box.sql.execute("CREATE TABLE t2(a integer primary key, b INT UNIQUE, c FLOAT, d FLOAT, e INT,  UNIQUE(c,d));")
-box.sql.execute("INSERT INTO t2 VALUES(1,2,3,4,5);")
-box.sql.execute("INSERT INTO t2 VALUES(2,3,4,4,6);")
+box.execute("CREATE TABLE t2(a integer primary key, b INT UNIQUE, c FLOAT, d FLOAT, e INT,  UNIQUE(c,d));")
+box.execute("INSERT INTO t2 VALUES(1,2,3,4,5);")
+box.execute("INSERT INTO t2 VALUES(2,3,4,4,6);")
 
-box.sql.execute("UPDATE t2 SET e=e+1 WHERE b IN (SELECT b FROM t2);")
+box.execute("UPDATE t2 SET e=e+1 WHERE b IN (SELECT b FROM t2);")
 
-box.sql.execute("SELECT e FROM t2")
+box.execute("SELECT e FROM t2")
 
-box.sql.execute("DROP TABLE t1")
-box.sql.execute("DROP TABLE t2")
+box.execute("DROP TABLE t1")
+box.execute("DROP TABLE t2")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/gh2483-remote-persistency-check.result b/test/sql/gh2483-remote-persistency-check.result
index 50e65f2..0a45db4 100644
--- a/test/sql/gh2483-remote-persistency-check.result
+++ b/test/sql/gh2483-remote-persistency-check.result
@@ -8,23 +8,30 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 box.schema.user.grant('guest', 'read,write,execute', 'universe')
 ---
 ...
 -- Create a table and insert a datum
-box.sql.execute([[CREATE TABLE t(id int PRIMARY KEY)]])
+box.execute([[CREATE TABLE t(id int PRIMARY KEY)]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[INSERT INTO t (id) VALUES (1)]])
+box.execute([[INSERT INTO t (id) VALUES (1)]])
 ---
+- rowcount: 1
 ...
 -- Sanity check
-box.sql.execute([[SELECT * FROM t]])
+box.execute([[SELECT * FROM t]])
 ---
-- - [1]
+- metadata:
+  - name: ID
+    type: INTEGER
+  rows:
+  - [1]
 ...
 test_run:cmd('restart server default');
 -- Connect to ourself
@@ -34,13 +41,14 @@ c = require('net.box').connect(os.getenv("LISTEN"))
 -- This segfaults due to gh-2483 since
 -- before the patch sql schema was read on-demand.
 -- Which could obviously lead to access denied error.
-c:eval([[ return box.sql.execute('SELECT * FROM t') ]])
+c:eval([[ return box.execute('SELECT * FROM t') ]])
 ---
-- [[1]]
+- {'metadata': [{'name': 'ID', 'type': 'INTEGER'}], 'rows': [[1]]}
 ...
 -- sql.execute([[SELECT * FROM t]])
-box.sql.execute([[DROP TABLE t]])
+box.execute([[DROP TABLE t]])
 ---
+- rowcount: 1
 ...
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ---
diff --git a/test/sql/gh2483-remote-persistency-check.test.lua b/test/sql/gh2483-remote-persistency-check.test.lua
index b952f6b..7db1602 100644
--- a/test/sql/gh2483-remote-persistency-check.test.lua
+++ b/test/sql/gh2483-remote-persistency-check.test.lua
@@ -2,16 +2,16 @@
 env = require('test_run')
 test_run = env.new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 box.schema.user.grant('guest', 'read,write,execute', 'universe')
 
 -- Create a table and insert a datum
-box.sql.execute([[CREATE TABLE t(id int PRIMARY KEY)]])
-box.sql.execute([[INSERT INTO t (id) VALUES (1)]])
+box.execute([[CREATE TABLE t(id int PRIMARY KEY)]])
+box.execute([[INSERT INTO t (id) VALUES (1)]])
 
 -- Sanity check
-box.sql.execute([[SELECT * FROM t]])
+box.execute([[SELECT * FROM t]])
 
 test_run:cmd('restart server default');
 
@@ -21,8 +21,8 @@ c = require('net.box').connect(os.getenv("LISTEN"))
 -- This segfaults due to gh-2483 since
 -- before the patch sql schema was read on-demand.
 -- Which could obviously lead to access denied error.
-c:eval([[ return box.sql.execute('SELECT * FROM t') ]])
+c:eval([[ return box.execute('SELECT * FROM t') ]])
 -- sql.execute([[SELECT * FROM t]])
 
-box.sql.execute([[DROP TABLE t]])
+box.execute([[DROP TABLE t]])
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
diff --git a/test/sql/gh2808-inline-unique-persistency-check.result b/test/sql/gh2808-inline-unique-persistency-check.result
index 6754af6..d2440da 100644
--- a/test/sql/gh2808-inline-unique-persistency-check.result
+++ b/test/sql/gh2808-inline-unique-persistency-check.result
@@ -8,35 +8,52 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Create a table and insert a datum
-box.sql.execute([[CREATE TABLE t1(a INT PRIMARY KEY, b INT, UNIQUE(b));]])
+box.execute([[CREATE TABLE t1(a INT PRIMARY KEY, b INT, UNIQUE(b));]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[INSERT INTO t1 VALUES(1,2);]])
+box.execute([[INSERT INTO t1 VALUES(1,2);]])
 ---
+- rowcount: 1
 ...
 -- Sanity check
-box.sql.execute([[SELECT * FROM t1]])
+box.execute([[SELECT * FROM t1]])
 ---
-- - [1, 2]
+- metadata:
+  - name: A
+    type: INTEGER
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 2]
 ...
 test_run:cmd('restart server default');
 -- This cmd should not fail
 -- before this fix, unique index was notrecovered
 -- correctly after restart (#2808)
-box.sql.execute([[INSERT INTO t1 VALUES(2,3);]])
+box.execute([[INSERT INTO t1 VALUES(2,3);]])
 ---
+- rowcount: 1
 ...
 -- Sanity check
-box.sql.execute([[SELECT * FROM t1]])
----
-- - [1, 2]
+box.execute([[SELECT * FROM t1]])
+---
+- metadata:
+  - name: A
+    type: INTEGER
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 2]
   - [2, 3]
 ...
 -- Cleanup
-box.sql.execute([[drop table t1;]])
+box.execute([[drop table t1;]])
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/gh2808-inline-unique-persistency-check.test.lua b/test/sql/gh2808-inline-unique-persistency-check.test.lua
index 81e2af5..26b646a 100644
--- a/test/sql/gh2808-inline-unique-persistency-check.test.lua
+++ b/test/sql/gh2808-inline-unique-persistency-check.test.lua
@@ -2,23 +2,23 @@
 env = require('test_run')
 test_run = env.new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Create a table and insert a datum
-box.sql.execute([[CREATE TABLE t1(a INT PRIMARY KEY, b INT, UNIQUE(b));]])
-box.sql.execute([[INSERT INTO t1 VALUES(1,2);]])
+box.execute([[CREATE TABLE t1(a INT PRIMARY KEY, b INT, UNIQUE(b));]])
+box.execute([[INSERT INTO t1 VALUES(1,2);]])
 
 -- Sanity check
-box.sql.execute([[SELECT * FROM t1]])
+box.execute([[SELECT * FROM t1]])
 
 test_run:cmd('restart server default');
 -- This cmd should not fail
 -- before this fix, unique index was notrecovered
 -- correctly after restart (#2808)
-box.sql.execute([[INSERT INTO t1 VALUES(2,3);]])
+box.execute([[INSERT INTO t1 VALUES(2,3);]])
 
 -- Sanity check
-box.sql.execute([[SELECT * FROM t1]])
+box.execute([[SELECT * FROM t1]])
 
 -- Cleanup
-box.sql.execute([[drop table t1;]])
+box.execute([[drop table t1;]])
diff --git a/test/sql/icu-upper-lower.result b/test/sql/icu-upper-lower.result
index 61d6546..c062beb 100644
--- a/test/sql/icu-upper-lower.result
+++ b/test/sql/icu-upper-lower.result
@@ -4,15 +4,16 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 test_run:cmd("setopt delimiter ';'")
 ---
 - true
 ...
 upper_lower_test = function (str)
-    return box.sql.execute(string.format("select lower('%s'), upper('%s')", str, str))
+    return box.execute(string.format("select lower('%s'), upper('%s')", str, str))
 end;
 ---
 ...
@@ -22,14 +23,28 @@ upper_lower_test([[
     Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq.
 ]]);
 ---
-- - ['     zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq. ',
+- metadata:
+  - name: lower('     Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq
+      olacaq. ')
+    type: TEXT
+  - name: upper('     Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq
+      olacaq. ')
+    type: TEXT
+  rows:
+  - ['     zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq. ',
     '     ZƏFƏR, JAKETINI DƏ, PAPAĞINI DA GÖTÜR, BU AXŞAM HAVA ÇOX SOYUQ OLACAQ. ']
 ...
 upper_lower_test([[
     The quick brown fox jumps over the lazy dog.
 ]]);
 ---
-- - ['     the quick brown fox jumps over the lazy dog. ', '     THE QUICK BROWN FOX
+- metadata:
+  - name: lower('     The quick brown fox jumps over the lazy dog. ')
+    type: TEXT
+  - name: upper('     The quick brown fox jumps over the lazy dog. ')
+    type: TEXT
+  rows:
+  - ['     the quick brown fox jumps over the lazy dog. ', '     THE QUICK BROWN FOX
       JUMPS OVER THE LAZY DOG. ']
 ...
 -- English
@@ -37,7 +52,13 @@ upper_lower_test([[
     The quick brown fox jumps over the lazy dog.
 ]]);
 ---
-- - ['     the quick brown fox jumps over the lazy dog. ', '     THE QUICK BROWN FOX
+- metadata:
+  - name: lower('     The quick brown fox jumps over the lazy dog. ')
+    type: TEXT
+  - name: upper('     The quick brown fox jumps over the lazy dog. ')
+    type: TEXT
+  rows:
+  - ['     the quick brown fox jumps over the lazy dog. ', '     THE QUICK BROWN FOX
       JUMPS OVER THE LAZY DOG. ']
 ...
 -- Armenian
@@ -45,7 +66,15 @@ upper_lower_test([[
     Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք
 ]]);
 ---
-- - ['     բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք ', '     ԲԵԼ
+- metadata:
+  - name: lower('     Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ
+      փառք ')
+    type: TEXT
+  - name: upper('     Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ
+      փառք ')
+    type: TEXT
+  rows:
+  - ['     բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք ', '     ԲԵԼ
       ԴՂՅԱԿԻ ՁԱԽ ԺԱՄՆ ՕՖ ԱԶԳՈՒԹՅԱՆԸ ՑՊԱՀԱՆՋ ՉՃՇՏԱԾ ՎՆԱՍ ԷՐ ԵՒ ՓԱՌՔ ']
 ...
 -- Belarussian
@@ -53,7 +82,15 @@ upper_lower_test([[
     У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай
 ]]);
 ---
-- - ['     у іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай ',
+- metadata:
+  - name: lower('     У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу
+      з юшкай ')
+    type: TEXT
+  - name: upper('     У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу
+      з юшкай ')
+    type: TEXT
+  rows:
+  - ['     у іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай ',
     '     У ІЎІ ХУДЫ ЖВАВЫ ЧОРТ У ЗЯЛЁНАЙ КАМІЗЭЛЬЦЫ ПАБЕГ ПАД’ЕСЦІ ФАРШУ З ЮШКАЙ ']
 ...
 -- Greek
@@ -61,7 +98,15 @@ upper_lower_test([[
     Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός
 ]]);
 ---
-- - ['     τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός ', '     ΤΆΧΙΣΤΗ
+- metadata:
+  - name: lower('     Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός
+      ')
+    type: TEXT
+  - name: upper('     Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός
+      ')
+    type: TEXT
+  rows:
+  - ['     τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός ', '     ΤΆΧΙΣΤΗ
       ΑΛΏΠΗΞ ΒΑΦΉΣ ΨΗΜΈΝΗ ΓΗ, ΔΡΑΣΚΕΛΊΖΕΙ ΥΠΈΡ ΝΩΘΡΟΎ ΚΥΝΌΣ ']
 ...
 -- Irish
@@ -69,7 +114,15 @@ upper_lower_test([[
     Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig
 ]]);
 ---
-- - ['     chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig ',
+- metadata:
+  - name: lower('     Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin
+      bhig ')
+    type: TEXT
+  - name: upper('     Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin
+      bhig ')
+    type: TEXT
+  rows:
+  - ['     chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig ',
     '     CHUAIGH BÉ MHÓRSHÁCH LE DLÚTHSPÁD FÍORFHINN TRÍ HATA MO DHEA-PHORCÁIN BHIG ']
 ...
 -- Spain
@@ -77,7 +130,13 @@ upper_lower_test([[
     Quiere la boca exhausta vid, kiwi, piña y fugaz jamón
 ]]);
 ---
-- - ['     quiere la boca exhausta vid, kiwi, piña y fugaz jamón ', '     QUIERE LA
+- metadata:
+  - name: lower('     Quiere la boca exhausta vid, kiwi, piña y fugaz jamón ')
+    type: TEXT
+  - name: upper('     Quiere la boca exhausta vid, kiwi, piña y fugaz jamón ')
+    type: TEXT
+  rows:
+  - ['     quiere la boca exhausta vid, kiwi, piña y fugaz jamón ', '     QUIERE LA
       BOCA EXHAUSTA VID, KIWI, PIÑA Y FUGAZ JAMÓN ']
 ...
 -- Korean
@@ -85,7 +144,13 @@ upper_lower_test([[
     키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다
 ]]);
 ---
-- - ['     키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다 ', '     키스의 고유조건은 입술끼리 만나야 하고 특별한
+- metadata:
+  - name: lower('     키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다 ')
+    type: TEXT
+  - name: upper('     키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다 ')
+    type: TEXT
+  rows:
+  - ['     키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다 ', '     키스의 고유조건은 입술끼리 만나야 하고 특별한
       기술은 필요치 않다 ']
 ...
 -- Latvian
@@ -93,7 +158,13 @@ upper_lower_test([[
     Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus
 ]]);
 ---
-- - ['     glāžšķūņa rūķīši dzērumā čiepj baha koncertflīģeļu vākus ', '     GLĀŽŠĶŪŅA
+- metadata:
+  - name: lower('     Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus ')
+    type: TEXT
+  - name: upper('     Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus ')
+    type: TEXT
+  rows:
+  - ['     glāžšķūņa rūķīši dzērumā čiepj baha koncertflīģeļu vākus ', '     GLĀŽŠĶŪŅA
       RŪĶĪŠI DZĒRUMĀ ČIEPJ BAHA KONCERTFLĪĢEĻU VĀKUS ']
 ...
 -- German
@@ -101,7 +172,15 @@ upper_lower_test([[
     Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich
 ]]);
 ---
-- - ['     zwölf große boxkämpfer jagen viktor quer über den sylter deich ', '     ZWÖLF
+- metadata:
+  - name: lower('     Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich
+      ')
+    type: TEXT
+  - name: upper('     Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich
+      ')
+    type: TEXT
+  rows:
+  - ['     zwölf große boxkämpfer jagen viktor quer über den sylter deich ', '     ZWÖLF
       GROSSE BOXKÄMPFER JAGEN VIKTOR QUER ÜBER DEN SYLTER DEICH ']
 ...
 -- Polish
@@ -109,7 +188,13 @@ upper_lower_test([[
     Pchnąć w tę łódź jeża lub ośm skrzyń fig.
 ]]);
 ---
-- - ['     pchnąć w tę łódź jeża lub ośm skrzyń fig. ', '     PCHNĄĆ W TĘ ŁÓDŹ JEŻA
+- metadata:
+  - name: lower('     Pchnąć w tę łódź jeża lub ośm skrzyń fig. ')
+    type: TEXT
+  - name: upper('     Pchnąć w tę łódź jeża lub ośm skrzyń fig. ')
+    type: TEXT
+  rows:
+  - ['     pchnąć w tę łódź jeża lub ośm skrzyń fig. ', '     PCHNĄĆ W TĘ ŁÓDŹ JEŻA
       LUB OŚM SKRZYŃ FIG. ']
 ...
 -- Ukrainian
@@ -117,7 +202,13 @@ upper_lower_test([[
     Чуєш їх, доцю, га? Кумедна ж ти, прощайся без ґольфів!
 ]]);
 ---
-- - ['     чуєш їх, доцю, га? кумедна ж ти, прощайся без ґольфів! ', '     ЧУЄШ ЇХ,
+- metadata:
+  - name: lower('     Чуєш їх, доцю, га? Кумедна ж ти, прощайся без ґольфів! ')
+    type: TEXT
+  - name: upper('     Чуєш їх, доцю, га? Кумедна ж ти, прощайся без ґольфів! ')
+    type: TEXT
+  rows:
+  - ['     чуєш їх, доцю, га? кумедна ж ти, прощайся без ґольфів! ', '     ЧУЄШ ЇХ,
       ДОЦЮ, ГА? КУМЕДНА Ж ТИ, ПРОЩАЙСЯ БЕЗ ҐОЛЬФІВ! ']
 ...
 -- Czech
@@ -125,7 +216,13 @@ upper_lower_test([[
     Příliš žluťoučký kůň úpěl ďábelské ódy
 ]]);
 ---
-- - ['     příliš žluťoučký kůň úpěl ďábelské ódy ', '     PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL
+- metadata:
+  - name: lower('     Příliš žluťoučký kůň úpěl ďábelské ódy ')
+    type: TEXT
+  - name: upper('     Příliš žluťoučký kůň úpěl ďábelské ódy ')
+    type: TEXT
+  rows:
+  - ['     příliš žluťoučký kůň úpěl ďábelské ódy ', '     PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL
       ĎÁBELSKÉ ÓDY ']
 ...
 -- Esperanto
@@ -133,7 +230,15 @@ upper_lower_test([[
     Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj
 ]]);
 ---
-- - ['     laŭ ludoviko zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj ', '     LAŬ
+- metadata:
+  - name: lower('     Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj
+      ')
+    type: TEXT
+  - name: upper('     Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj
+      ')
+    type: TEXT
+  rows:
+  - ['     laŭ ludoviko zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj ', '     LAŬ
       LUDOVIKO ZAMENHOF BONGUSTAS FREŜA ĈEĤA MANĜAĴO KUN SPICOJ ']
 ...
 -- Japanese
@@ -141,7 +246,13 @@ upper_lower_test([[
     いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす
 ]]);
 ---
-- - ['     いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ', '     いろはにほへと
+- metadata:
+  - name: lower('     いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ')
+    type: TEXT
+  - name: upper('     いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ')
+    type: TEXT
+  rows:
+  - ['     いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ', '     いろはにほへと
       ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ']
 ...
 -- Turkish
@@ -149,7 +260,13 @@ upper_lower_test([[
     Pijamalı hasta yağız şoföre çabucak güvendi. EXTRA: İ
 ]]);
 ---
-- - ['     pijamalı hasta yağız şoföre çabucak güvendi. extra: i̇ ', '     PIJAMALI
+- metadata:
+  - name: 'lower(''     Pijamalı hasta yağız şoföre çabucak güvendi. EXTRA: İ '')'
+    type: TEXT
+  - name: 'upper(''     Pijamalı hasta yağız şoföre çabucak güvendi. EXTRA: İ '')'
+    type: TEXT
+  rows:
+  - ['     pijamalı hasta yağız şoföre çabucak güvendi. extra: i̇ ', '     PIJAMALI
       HASTA YAĞIZ ŞOFÖRE ÇABUCAK GÜVENDI. EXTRA: İ ']
 ...
 test_run:cmd("setopt delimiter ''");
@@ -157,15 +274,15 @@ test_run:cmd("setopt delimiter ''");
 - true
 ...
 -- Bad test cases
-box.sql.execute("select upper('1', 2)")
+box.execute("select upper('1', 2)")
 ---
 - error: wrong number of arguments to function UPPER()
 ...
-box.sql.execute("select upper(\"1\")")
+box.execute("select upper(\"1\")")
 ---
 - error: Can’t resolve field '1'
 ...
-box.sql.execute("select upper()")
+box.execute("select upper()")
 ---
 - error: wrong number of arguments to function UPPER()
 ...
diff --git a/test/sql/icu-upper-lower.test.lua b/test/sql/icu-upper-lower.test.lua
index 6629a74..00e9699 100644
--- a/test/sql/icu-upper-lower.test.lua
+++ b/test/sql/icu-upper-lower.test.lua
@@ -1,11 +1,11 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 test_run:cmd("setopt delimiter ';'")
 
 upper_lower_test = function (str)
-    return box.sql.execute(string.format("select lower('%s'), upper('%s')", str, str))
+    return box.execute(string.format("select lower('%s'), upper('%s')", str, str))
 end;
 
 -- Some pangrams
@@ -80,7 +80,7 @@ upper_lower_test([[
 test_run:cmd("setopt delimiter ''");
 
 -- Bad test cases
-box.sql.execute("select upper('1', 2)")
-box.sql.execute("select upper(\"1\")")
-box.sql.execute("select upper()")
+box.execute("select upper('1', 2)")
+box.execute("select upper(\"1\")")
+box.execute("select upper()")
 
diff --git a/test/sql/insert-unique.result b/test/sql/insert-unique.result
index adfa60f..a18f9ff 100644
--- a/test/sql/insert-unique.result
+++ b/test/sql/insert-unique.result
@@ -4,39 +4,47 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entry
-box.sql.execute("INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 ---
+- rowcount: 1
 ...
 -- PK must be unique
-box.sql.execute("INSERT INTO zoobar VALUES (112, 222, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (112, 222, 'c3', 444)")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_ZOOBAR_1' in space 'ZOOBAR'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_ZOOBAR_1'' in space ''ZOOBAR'''
 ...
 -- Unique index must be respected
-box.sql.execute("INSERT INTO zoobar VALUES (111, 223, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (111, 223, 'c3', 444)")
 ---
-- error: Duplicate key exists in unique index 'ZOOBAR2' in space 'ZOOBAR'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''ZOOBAR2'' in space ''ZOOBAR'''
 ...
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zoobar")
+box.execute("DROP INDEX zoobar2 ON zoobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE zoobar")
+box.execute("DROP TABLE zoobar")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/insert-unique.test.lua b/test/sql/insert-unique.test.lua
index b44a6e2..026bc9d 100644
--- a/test/sql/insert-unique.test.lua
+++ b/test/sql/insert-unique.test.lua
@@ -1,28 +1,28 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
-box.sql.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
+box.execute("CREATE TABLE zoobar (c1 INT, c2 INT PRIMARY KEY, c3 TEXT, c4 INT)")
+box.execute("CREATE UNIQUE INDEX zoobar2 ON zoobar(c1, c4)")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entry
-box.sql.execute("INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- PK must be unique
-box.sql.execute("INSERT INTO zoobar VALUES (112, 222, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (112, 222, 'c3', 444)")
 
 -- Unique index must be respected
-box.sql.execute("INSERT INTO zoobar VALUES (111, 223, 'c3', 444)")
+box.execute("INSERT INTO zoobar VALUES (111, 223, 'c3', 444)")
 
 -- Cleanup
-box.sql.execute("DROP INDEX zoobar2 ON zoobar")
-box.sql.execute("DROP TABLE zoobar")
+box.execute("DROP INDEX zoobar2 ON zoobar")
+box.execute("DROP TABLE zoobar")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/integer-overflow.result b/test/sql/integer-overflow.result
index 4754c04..2a9bbbd 100644
--- a/test/sql/integer-overflow.result
+++ b/test/sql/integer-overflow.result
@@ -4,45 +4,46 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- gh-3735: make sure that integer overflows errors are
 -- handled during VDBE execution.
 --
-box.sql.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
+box.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
 ---
 - error: 'Failed to execute SQL statement: integer is overflowed'
 ...
-box.sql.execute('SELECT (-9223372036854775808 / -1);')
+box.execute('SELECT (-9223372036854775808 / -1);')
 ---
 - error: 'Failed to execute SQL statement: integer is overflowed'
 ...
-box.sql.execute('SELECT (-9223372036854775808 - 1);')
+box.execute('SELECT (-9223372036854775808 - 1);')
 ---
 - error: 'Failed to execute SQL statement: integer is overflowed'
 ...
-box.sql.execute('SELECT (9223372036854775807 + 1);')
+box.execute('SELECT (9223372036854775807 + 1);')
 ---
 - error: 'Failed to execute SQL statement: integer is overflowed'
 ...
 -- Literals are checked right after parsing.
 --
-box.sql.execute('SELECT 9223372036854775808;')
+box.execute('SELECT 9223372036854775808;')
 ---
 - error: 'oversized integer: 9223372036854775808'
 ...
-box.sql.execute('SELECT -9223372036854775809;')
+box.execute('SELECT -9223372036854775809;')
 ---
 - error: 'oversized integer: -9223372036854775809'
 ...
-box.sql.execute('SELECT 9223372036854775808 - 1;')
+box.execute('SELECT 9223372036854775808 - 1;')
 ---
 - error: 'oversized integer: 9223372036854775808'
 ...
 -- Test that CAST may also leads to overflow.
 --
-box.sql.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
+box.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
 ---
 - error: 'Type mismatch: can not convert 9223372036854775808 to integer'
 ...
@@ -52,7 +53,7 @@ box.sql.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
 -- float 9223372036854775800 -> int (9223372036854775808),
 -- with error due to conversion = 8.
 --
-box.sql.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
+box.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
 ---
 - error: 'Type mismatch: can not convert 9.22337203685478e+18 to integer'
 ...
@@ -60,14 +61,15 @@ box.sql.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
 -- [INT64_MAX, UINT64_MAX], they are handled inside SQL in a
 -- proper way, which now means that an error is raised.
 --
-box.sql.execute('CREATE TABLE t (id INT PRIMARY KEY);')
+box.execute('CREATE TABLE t (id INT PRIMARY KEY);')
 ---
+- rowcount: 1
 ...
 box.space.T:insert({9223372036854775809})
 ---
 - [9223372036854775808]
 ...
-box.sql.execute('SELECT * FROM t;')
+box.execute('SELECT * FROM t;')
 ---
 - error: 'Failed to execute SQL statement: integer is overflowed'
 ...
diff --git a/test/sql/integer-overflow.test.lua b/test/sql/integer-overflow.test.lua
index 45fc209..4339edf 100644
--- a/test/sql/integer-overflow.test.lua
+++ b/test/sql/integer-overflow.test.lua
@@ -1,34 +1,34 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- gh-3735: make sure that integer overflows errors are
 -- handled during VDBE execution.
 --
-box.sql.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
-box.sql.execute('SELECT (-9223372036854775808 / -1);')
-box.sql.execute('SELECT (-9223372036854775808 - 1);')
-box.sql.execute('SELECT (9223372036854775807 + 1);')
+box.execute('SELECT (2147483647 * 2147483647 * 2147483647);')
+box.execute('SELECT (-9223372036854775808 / -1);')
+box.execute('SELECT (-9223372036854775808 - 1);')
+box.execute('SELECT (9223372036854775807 + 1);')
 -- Literals are checked right after parsing.
 --
-box.sql.execute('SELECT 9223372036854775808;')
-box.sql.execute('SELECT -9223372036854775809;')
-box.sql.execute('SELECT 9223372036854775808 - 1;')
+box.execute('SELECT 9223372036854775808;')
+box.execute('SELECT -9223372036854775809;')
+box.execute('SELECT 9223372036854775808 - 1;')
 -- Test that CAST may also leads to overflow.
 --
-box.sql.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
+box.execute('SELECT CAST(\'9223372036854775808\' AS INTEGER);')
 -- Due to inexact represantation of large integers in terms of
 -- floating point numbers, numerics with value < INT64_MAX
 -- have INT64_MAX + 1 value in integer representation:
 -- float 9223372036854775800 -> int (9223372036854775808),
 -- with error due to conversion = 8.
 --
-box.sql.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
+box.execute('SELECT CAST(9223372036854775807.0 AS INTEGER);')
 -- gh-3810: make sure that if space contains integers in range
 -- [INT64_MAX, UINT64_MAX], they are handled inside SQL in a
 -- proper way, which now means that an error is raised.
 --
-box.sql.execute('CREATE TABLE t (id INT PRIMARY KEY);')
+box.execute('CREATE TABLE t (id INT PRIMARY KEY);')
 box.space.T:insert({9223372036854775809})
-box.sql.execute('SELECT * FROM t;')
+box.execute('SELECT * FROM t;')
 box.space.T:drop()
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 7ad61e5..0ab8558 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -7,11 +7,13 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
-box.sql.execute('create table test (id int primary key, a float, b text)')
+box.execute('create table test (id int primary key, a float, b text)')
 ---
+- rowcount: 1
 ...
 space = box.space.TEST
 ---
@@ -28,9 +30,17 @@ space:replace{7, 8.5, '9'}
 ---
 - [7, 8.5, '9']
 ...
-box.sql.execute('select * from test')
+box.execute('select * from test')
 ---
-- - [1, 2, '3']
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: NUMERIC
+  - name: B
+    type: TEXT
+  rows:
+  - [1, 2, '3']
   - [4, 5, '6']
   - [7, 8.5, '9']
 ...
@@ -619,8 +629,9 @@ res.metadata
   - name: B
     type: TEXT
 ...
-box.sql.execute('drop table test')
+box.execute('drop table test')
 ---
+- rowcount: 1
 ...
 cn:close()
 ---
@@ -678,13 +689,15 @@ future4:wait_result()
 cn:close()
 ---
 ...
-box.sql.execute('drop table test')
+box.execute('drop table test')
 ---
+- rowcount: 1
 ...
 -- gh-2618 Return generated columns after INSERT in IPROTO.
 -- Return all ids generated in current INSERT statement.
-box.sql.execute('create table test (id integer primary key autoincrement, a integer)')
+box.execute('create table test (id integer primary key autoincrement, a integer)')
 ---
+- rowcount: 1
 ...
 cn = remote.connect(box.cfg.listen)
 ---
@@ -763,8 +776,9 @@ cn:execute('insert into test values (null, 1)')
   - 2
   rowcount: 1
 ...
-box.sql.execute('create table test3 (id int primary key autoincrement)')
+box.execute('create table test3 (id int primary key autoincrement)')
 ---
+- rowcount: 1
 ...
 box.schema.sequence.alter('TEST3', {min=-10000, step=-10})
 ---
@@ -778,8 +792,9 @@ cn:execute('insert into TEST3 values (null), (null), (null), (null)')
   - -29
   rowcount: 4
 ...
-box.sql.execute('drop table test')
+box.execute('drop table test')
 ---
+- rowcount: 1
 ...
 s:drop()
 ---
@@ -787,8 +802,9 @@ s:drop()
 sq:drop()
 ---
 ...
-box.sql.execute('drop table test3')
+box.execute('drop table test3')
 ---
+- rowcount: 1
 ...
 --
 -- Ensure that FK inside CREATE TABLE does not affect rowcount.
@@ -857,8 +873,9 @@ s:drop()
 cn = remote.connect(box.cfg.listen)
 ---
 ...
-box.sql.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT)')
+box.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT)')
 ---
+- rowcount: 1
 ...
 for i = 0, 1000 do cn:execute("INSERT INTO t1 VALUES (null)") end
 ---
@@ -866,15 +883,17 @@ for i = 0, 1000 do cn:execute("INSERT INTO t1 VALUES (null)") end
 _ = cn:execute("INSERT INTO t1 SELECT NULL from t1")
 ---
 ...
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 ---
+- rowcount: 1
 ...
 cn:close()
 ---
 ...
 -- gh-3832: Some statements do not return column type
-box.sql.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY)')
+box.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY)')
 ---
+- rowcount: 1
 ...
 cn = remote.connect(box.cfg.listen)
 ---
@@ -969,8 +988,9 @@ cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
 cn:close()
 ---
 ...
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 ---
+- rowcount: 1
 ...
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ---
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua
index 3b36cc3..6a6c24a 100644
--- a/test/sql/iproto.test.lua
+++ b/test/sql/iproto.test.lua
@@ -1,14 +1,14 @@
 remote = require('net.box')
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
-box.sql.execute('create table test (id int primary key, a float, b text)')
+box.execute('create table test (id int primary key, a float, b text)')
 space = box.space.TEST
 space:replace{1, 2, '3'}
 space:replace{4, 5, '6'}
 space:replace{7, 8.5, '9'}
-box.sql.execute('select * from test')
+box.execute('select * from test')
 box.schema.user.grant('guest','read,write,execute', 'universe')
 box.schema.user.grant('guest', 'create', 'space')
 cn = remote.connect(box.cfg.listen)
@@ -194,7 +194,7 @@ cn:execute('select * from test where id = :1', {1})
 _ = space:replace{1, 1, string.rep('a', 4 * 1024 * 1024)}
 res = cn:execute('select * from test')
 res.metadata
-box.sql.execute('drop table test')
+box.execute('drop table test')
 cn:close()
 
 --
@@ -212,11 +212,11 @@ future3:wait_result()
 future4 = cn:execute('select * from test', nil, nil, {is_async = true})
 future4:wait_result()
 cn:close()
-box.sql.execute('drop table test')
+box.execute('drop table test')
 
 -- gh-2618 Return generated columns after INSERT in IPROTO.
 -- Return all ids generated in current INSERT statement.
-box.sql.execute('create table test (id integer primary key autoincrement, a integer)')
+box.execute('create table test (id integer primary key autoincrement, a integer)')
 
 cn = remote.connect(box.cfg.listen)
 cn:execute('insert into test values (1, 1)')
@@ -233,14 +233,14 @@ function push_id() s:replace{box.NULL} s:replace{box.NULL} end
 _ = box.space.TEST:on_replace(push_id)
 cn:execute('insert into test values (null, 1)')
 
-box.sql.execute('create table test3 (id int primary key autoincrement)')
+box.execute('create table test3 (id int primary key autoincrement)')
 box.schema.sequence.alter('TEST3', {min=-10000, step=-10})
 cn:execute('insert into TEST3 values (null), (null), (null), (null)')
 
-box.sql.execute('drop table test')
+box.execute('drop table test')
 s:drop()
 sq:drop()
-box.sql.execute('drop table test3')
+box.execute('drop table test3')
 
 --
 -- Ensure that FK inside CREATE TABLE does not affect rowcount.
@@ -268,15 +268,15 @@ s:drop()
 
 -- Too many autogenerated ids leads to SEGFAULT.
 cn = remote.connect(box.cfg.listen)
-box.sql.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT)')
+box.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT)')
 for i = 0, 1000 do cn:execute("INSERT INTO t1 VALUES (null)") end
 _ = cn:execute("INSERT INTO t1 SELECT NULL from t1")
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 
 cn:close()
 
 -- gh-3832: Some statements do not return column type
-box.sql.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY)')
+box.execute('CREATE TABLE t1(id INTEGER PRIMARY KEY)')
 cn = remote.connect(box.cfg.listen)
 
 -- PRAGMA:
@@ -298,7 +298,7 @@ cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
 cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
 
 cn:close()
-box.sql.execute('DROP TABLE t1')
+box.execute('DROP TABLE t1')
 
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 box.schema.user.revoke('guest', 'create', 'space')
diff --git a/test/sql/max-on-index.result b/test/sql/max-on-index.result
index c4b5900..de1fbb3 100644
--- a/test/sql/max-on-index.result
+++ b/test/sql/max-on-index.result
@@ -4,62 +4,86 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
 -- scalar affinity
-box.sql.execute("CREATE TABLE test1 (f1 INT, f2 INT, PRIMARY KEY(f1))")
+box.execute("CREATE TABLE test1 (f1 INT, f2 INT, PRIMARY KEY(f1))")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX test1_index ON test1 (f2)")
+box.execute("CREATE INDEX test1_index ON test1 (f2)")
 ---
+- rowcount: 1
 ...
 -- integer affinity
-box.sql.execute("CREATE TABLE test2 (f1 INT, f2 INT, PRIMARY KEY(f1))")
+box.execute("CREATE TABLE test2 (f1 INT, f2 INT, PRIMARY KEY(f1))")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("INSERT INTO test1 VALUES(1, 2)");
+box.execute("INSERT INTO test1 VALUES(1, 2)");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO test1 VALUES(2, NULL)");
+box.execute("INSERT INTO test1 VALUES(2, NULL)");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO test1 VALUES(3, NULL)");
+box.execute("INSERT INTO test1 VALUES(3, NULL)");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO test1 VALUES(4, 3)");
+box.execute("INSERT INTO test1 VALUES(4, 3)");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO test2 VALUES(1, 2)");
+box.execute("INSERT INTO test2 VALUES(1, 2)");
 ---
+- rowcount: 1
 ...
 -- Select must return properly decoded `NULL`
-box.sql.execute("SELECT MAX(f1) FROM test1")
+box.execute("SELECT MAX(f1) FROM test1")
 ---
-- - [4]
+- metadata:
+  - name: MAX(f1)
+    type: BLOB
+  rows:
+  - [4]
 ...
-box.sql.execute("SELECT MAX(f2) FROM test1")
+box.execute("SELECT MAX(f2) FROM test1")
 ---
-- - [3]
+- metadata:
+  - name: MAX(f2)
+    type: BLOB
+  rows:
+  - [3]
 ...
-box.sql.execute("SELECT MAX(f1) FROM test2")
+box.execute("SELECT MAX(f1) FROM test2")
 ---
-- - [1]
+- metadata:
+  - name: MAX(f1)
+    type: BLOB
+  rows:
+  - [1]
 ...
 -- Cleanup
-box.sql.execute("DROP INDEX test1_index ON test1")
+box.execute("DROP INDEX test1_index ON test1")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE test1")
+box.execute("DROP TABLE test1")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE test2")
+box.execute("DROP TABLE test2")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/max-on-index.test.lua b/test/sql/max-on-index.test.lua
index 7d89c3a..4cceaa7 100644
--- a/test/sql/max-on-index.test.lua
+++ b/test/sql/max-on-index.test.lua
@@ -1,38 +1,38 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
 -- scalar affinity
-box.sql.execute("CREATE TABLE test1 (f1 INT, f2 INT, PRIMARY KEY(f1))")
-box.sql.execute("CREATE INDEX test1_index ON test1 (f2)")
+box.execute("CREATE TABLE test1 (f1 INT, f2 INT, PRIMARY KEY(f1))")
+box.execute("CREATE INDEX test1_index ON test1 (f2)")
 
 -- integer affinity
-box.sql.execute("CREATE TABLE test2 (f1 INT, f2 INT, PRIMARY KEY(f1))")
+box.execute("CREATE TABLE test2 (f1 INT, f2 INT, PRIMARY KEY(f1))")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("INSERT INTO test1 VALUES(1, 2)");
-box.sql.execute("INSERT INTO test1 VALUES(2, NULL)");
-box.sql.execute("INSERT INTO test1 VALUES(3, NULL)");
-box.sql.execute("INSERT INTO test1 VALUES(4, 3)");
+box.execute("INSERT INTO test1 VALUES(1, 2)");
+box.execute("INSERT INTO test1 VALUES(2, NULL)");
+box.execute("INSERT INTO test1 VALUES(3, NULL)");
+box.execute("INSERT INTO test1 VALUES(4, 3)");
 
-box.sql.execute("INSERT INTO test2 VALUES(1, 2)");
+box.execute("INSERT INTO test2 VALUES(1, 2)");
 
 -- Select must return properly decoded `NULL`
-box.sql.execute("SELECT MAX(f1) FROM test1")
-box.sql.execute("SELECT MAX(f2) FROM test1")
+box.execute("SELECT MAX(f1) FROM test1")
+box.execute("SELECT MAX(f2) FROM test1")
 
-box.sql.execute("SELECT MAX(f1) FROM test2")
+box.execute("SELECT MAX(f1) FROM test2")
 
 -- Cleanup
-box.sql.execute("DROP INDEX test1_index ON test1")
-box.sql.execute("DROP TABLE test1")
-box.sql.execute("DROP TABLE test2")
+box.execute("DROP INDEX test1_index ON test1")
+box.execute("DROP TABLE test1")
+box.execute("DROP TABLE test2")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/message-func-indexes.result b/test/sql/message-func-indexes.result
index 5928a8e..04e7a52 100644
--- a/test/sql/message-func-indexes.result
+++ b/test/sql/message-func-indexes.result
@@ -4,44 +4,51 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Creating tables.
-box.sql.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER)")
+box.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(object INTEGER PRIMARY KEY, price INTEGER, count INTEGER)")
+box.execute("CREATE TABLE t2(object INTEGER PRIMARY KEY, price INTEGER, count INTEGER)")
 ---
+- rowcount: 1
 ...
 -- Expressions that're supposed to create functional indexes
 -- should return certain message.
-box.sql.execute("CREATE INDEX i1 ON t1(a+1)")
+box.execute("CREATE INDEX i1 ON t1(a+1)")
 ---
 - error: Tarantool does not support functional indexes
 ...
-box.sql.execute("CREATE INDEX i2 ON t1(a)")
+box.execute("CREATE INDEX i2 ON t1(a)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i3 ON t2(price + 100)")
+box.execute("CREATE INDEX i3 ON t2(price + 100)")
 ---
 - error: Tarantool does not support functional indexes
 ...
-box.sql.execute("CREATE INDEX i4 ON t2(price)")
+box.execute("CREATE INDEX i4 ON t2(price)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i5 ON t2(count + 1)")
+box.execute("CREATE INDEX i5 ON t2(count + 1)")
 ---
 - error: Tarantool does not support functional indexes
 ...
-box.sql.execute("CREATE INDEX i6 ON t2(count * price)")
+box.execute("CREATE INDEX i6 ON t2(count * price)")
 ---
 - error: Tarantool does not support functional indexes
 ...
 -- Cleaning up.
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2")
+box.execute("DROP TABLE t2")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/message-func-indexes.test.lua b/test/sql/message-func-indexes.test.lua
index e0eae76..9ac5f47 100644
--- a/test/sql/message-func-indexes.test.lua
+++ b/test/sql/message-func-indexes.test.lua
@@ -1,20 +1,20 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Creating tables.
-box.sql.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER)")
-box.sql.execute("CREATE TABLE t2(object INTEGER PRIMARY KEY, price INTEGER, count INTEGER)")
+box.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a INTEGER)")
+box.execute("CREATE TABLE t2(object INTEGER PRIMARY KEY, price INTEGER, count INTEGER)")
 
 -- Expressions that're supposed to create functional indexes
 -- should return certain message.
-box.sql.execute("CREATE INDEX i1 ON t1(a+1)")
-box.sql.execute("CREATE INDEX i2 ON t1(a)")
-box.sql.execute("CREATE INDEX i3 ON t2(price + 100)")
-box.sql.execute("CREATE INDEX i4 ON t2(price)")
-box.sql.execute("CREATE INDEX i5 ON t2(count + 1)")
-box.sql.execute("CREATE INDEX i6 ON t2(count * price)")
+box.execute("CREATE INDEX i1 ON t1(a+1)")
+box.execute("CREATE INDEX i2 ON t1(a)")
+box.execute("CREATE INDEX i3 ON t2(price + 100)")
+box.execute("CREATE INDEX i4 ON t2(price)")
+box.execute("CREATE INDEX i5 ON t2(count + 1)")
+box.execute("CREATE INDEX i6 ON t2(count * price)")
 
 -- Cleaning up.
-box.sql.execute("DROP TABLE t1")
-box.sql.execute("DROP TABLE t2")
+box.execute("DROP TABLE t1")
+box.execute("DROP TABLE t2")
diff --git a/test/sql/min-on-index.result b/test/sql/min-on-index.result
deleted file mode 100644
index 1b2aadf..0000000
--- a/test/sql/min-on-index.result
+++ /dev/null
@@ -1,59 +0,0 @@
-test_run = require('test_run').new()
----
-...
--- box.cfg()
--- create space
--- scalar affinity
-box.sql.execute("CREATE TABLE test1 (f1, f2 INT, PRIMARY KEY(f1))")
----
-...
-box.sql.execute("CREATE INDEX test1_index ON test1 (f2)")
----
-...
--- integer affinity
-box.sql.execute("CREATE TABLE test2 (f1 INT, f2 INT, PRIMARY KEY(f1))")
----
-...
--- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
--- Seed entries
-box.sql.execute("INSERT INTO test1 VALUES(1, 2)");
----
-...
-box.sql.execute("INSERT INTO test1 VALUES(2, NULL)");
----
-...
-box.sql.execute("INSERT INTO test1 VALUES(3, NULL)");
----
-...
-box.sql.execute("INSERT INTO test1 VALUES(4, 3)");
----
-...
-box.sql.execute("INSERT INTO test2 VALUES(1, 2)");
----
-...
--- Select must return properly decoded `NULL`
-box.sql.execute("SELECT MIN(f1) FROM test1")
----
-- - [1]
-...
-box.sql.execute("SELECT MIN(f2) FROM test1")
----
-- - [2]
-...
-box.sql.execute("SELECT MIN(f1) FROM test2")
----
-- - [1]
-...
--- Cleanup
-box.sql.execute("DROP INDEX test1_index")
----
-...
-box.sql.execute("DROP TABLE test1")
----
-...
-box.sql.execute("DROP TABLE test2")
----
-...
--- Debug
--- require("console").start()
diff --git a/test/sql/misc.result b/test/sql/misc.result
index 9a8aa8d..b20818d 100644
--- a/test/sql/misc.result
+++ b/test/sql/misc.result
@@ -4,20 +4,25 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Forbid multistatement queries.
-box.sql.execute('select 1;')
+box.execute('select 1;')
 ---
-- - [1]
+- metadata:
+  - name: '1'
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute('select 1; select 2;')
+box.execute('select 1; select 2;')
 ---
 - error: Keyword 'select' is reserved. Please use double quotes if 'select' is an
     identifier.
 ...
-box.sql.execute('create table t1 (id INT primary key); select 100;')
+box.execute('create table t1 (id INT primary key); select 100;')
 ---
 - error: Keyword 'select' is reserved. Please use double quotes if 'select' is an
     identifier.
@@ -26,19 +31,19 @@ box.space.t1 == nil
 ---
 - true
 ...
-box.sql.execute(';')
+box.execute(';')
 ---
 - error: Failed to execute an empty SQL statement
 ...
-box.sql.execute('')
+box.execute('')
 ---
 - error: Failed to execute an empty SQL statement
 ...
-box.sql.execute('     ;')
+box.execute('     ;')
 ---
 - error: Failed to execute an empty SQL statement
 ...
-box.sql.execute('\n\n\n\t\t\t   ')
+box.execute('\n\n\n\t\t\t   ')
 ---
 - error: Failed to execute an empty SQL statement
 ...
diff --git a/test/sql/misc.test.lua b/test/sql/misc.test.lua
index 994e64f..1c2e6ce 100644
--- a/test/sql/misc.test.lua
+++ b/test/sql/misc.test.lua
@@ -1,13 +1,13 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Forbid multistatement queries.
-box.sql.execute('select 1;')
-box.sql.execute('select 1; select 2;')
-box.sql.execute('create table t1 (id INT primary key); select 100;')
+box.execute('select 1;')
+box.execute('select 1; select 2;')
+box.execute('create table t1 (id INT primary key); select 100;')
 box.space.t1 == nil
-box.sql.execute(';')
-box.sql.execute('')
-box.sql.execute('     ;')
-box.sql.execute('\n\n\n\t\t\t   ')
+box.execute(';')
+box.execute('')
+box.execute('     ;')
+box.execute('\n\n\n\t\t\t   ')
diff --git a/test/sql/no-pk-space.result b/test/sql/no-pk-space.result
index 1d57d16..b270b1f 100644
--- a/test/sql/no-pk-space.result
+++ b/test/sql/no-pk-space.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 format = {}
 ---
@@ -16,19 +17,19 @@ format[1] = {'id', 'integer'}
 s = box.schema.create_space('test', {format = format})
 ---
 ...
-box.sql.execute("SELECT * FROM \"test\";")
+box.execute("SELECT * FROM \"test\";")
 ---
 - error: SQL does not support spaces without primary key
 ...
-box.sql.execute("INSERT INTO \"test\" VALUES (1);")
+box.execute("INSERT INTO \"test\" VALUES (1);")
 ---
 - error: SQL does not support spaces without primary key
 ...
-box.sql.execute("DELETE FROM \"test\";")
+box.execute("DELETE FROM \"test\";")
 ---
 - error: SQL does not support spaces without primary key
 ...
-box.sql.execute("UPDATE \"test\" SET id = 3;")
+box.execute("UPDATE \"test\" SET id = 3;")
 ---
 - error: SQL does not support spaces without primary key
 ...
@@ -39,17 +40,19 @@ s:drop()
 -- after drop of indexes. So, if space:drop() fails due to being
 -- referenced by a view, space becomes unusable in SQL terms.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 ---
+- rowcount: 1
 ...
 box.space.T1:drop()
 ---
 - error: 'Can''t drop space ''T1'': other views depend on this space'
 ...
-box.sql.execute("SELECT * FROM v1;")
+box.execute("SELECT * FROM v1;")
 ---
 - error: SQL does not support spaces without primary key
 ...
diff --git a/test/sql/no-pk-space.test.lua b/test/sql/no-pk-space.test.lua
index 2828777..318c2ac 100644
--- a/test/sql/no-pk-space.test.lua
+++ b/test/sql/no-pk-space.test.lua
@@ -1,14 +1,14 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 format = {}
 format[1] = {'id', 'integer'}
 s = box.schema.create_space('test', {format = format})
-box.sql.execute("SELECT * FROM \"test\";")
-box.sql.execute("INSERT INTO \"test\" VALUES (1);")
-box.sql.execute("DELETE FROM \"test\";")
-box.sql.execute("UPDATE \"test\" SET id = 3;")
+box.execute("SELECT * FROM \"test\";")
+box.execute("INSERT INTO \"test\" VALUES (1);")
+box.execute("DELETE FROM \"test\";")
+box.execute("UPDATE \"test\" SET id = 3;")
 
 s:drop()
 
@@ -16,9 +16,9 @@ s:drop()
 -- after drop of indexes. So, if space:drop() fails due to being
 -- referenced by a view, space becomes unusable in SQL terms.
 --
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY);")
+box.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
 box.space.T1:drop()
-box.sql.execute("SELECT * FROM v1;")
+box.execute("SELECT * FROM v1;")
 box.space.V1:drop()
 box.space.T1:drop()
diff --git a/test/sql/on-conflict.result b/test/sql/on-conflict.result
index 6d37e69..253b356 100644
--- a/test/sql/on-conflict.result
+++ b/test/sql/on-conflict.result
@@ -4,145 +4,195 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 --
 -- Check that original sql ON CONFLICT clause is really
 -- disabled.
 --
-box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT ABORT)")
+box.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT ABORT)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE q (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT FAIL)")
+box.execute("CREATE TABLE q (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT FAIL)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE p (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT IGNORE)")
+box.execute("CREATE TABLE p (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT IGNORE)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE g (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT REPLACE)")
+box.execute("CREATE TABLE g (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT REPLACE)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE e (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, v INTEGER)")
+box.execute("CREATE TABLE e (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, v INTEGER)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY ON CONFLICT REPLACE)")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY ON CONFLICT REPLACE)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
-box.sql.execute("CREATE TABLE t2(a INT PRIMARY KEY ON CONFLICT IGNORE)")
+box.execute("CREATE TABLE t2(a INT PRIMARY KEY ON CONFLICT IGNORE)")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 -- CHECK constraint is illegal with REPLACE option.
 --
-box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER CHECK (a > 5) ON CONFLICT REPLACE);")
+box.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER CHECK (a > 5) ON CONFLICT REPLACE);")
 ---
 - error: Keyword 'ON' is reserved. Please use double quotes if 'ON' is an identifier.
 ...
 --
 -- gh-3473: Primary key can't be declared with NULL.
 --
-box.sql.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY NOT NULL);")
+box.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY NOT NULL);")
 ---
 - error: Primary index of the space 'TE17' can not contain nullable parts
 ...
-box.sql.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY);")
+box.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY);")
 ---
 - error: Primary index of the space 'TE17' can not contain nullable parts
 ...
-box.sql.execute("CREATE TABLE test (a int PRIMARY KEY, b int NULL ON CONFLICT IGNORE);")
+box.execute("CREATE TABLE test (a int PRIMARY KEY, b int NULL ON CONFLICT IGNORE);")
 ---
 - error: 'SQL error: NULL declaration for column ''B'' of table ''TEST'' has been
     already set to ''none'''
 ...
-box.sql.execute("CREATE TABLE test (a int, b int NULL, c int, PRIMARY KEY(a, b, c))")
+box.execute("CREATE TABLE test (a int, b int NULL, c int, PRIMARY KEY(a, b, c))")
 ---
 - error: Primary index of the space 'TEST' can not contain nullable parts
 ...
 -- Several NOT NULL REPLACE constraints work
 --
-box.sql.execute("CREATE TABLE a (id INT PRIMARY KEY, a INT NOT NULL ON CONFLICT REPLACE DEFAULT 1, b INT NOT NULL ON CONFLICT REPLACE DEFAULT 2);")
+box.execute("CREATE TABLE a (id INT PRIMARY KEY, a INT NOT NULL ON CONFLICT REPLACE DEFAULT 1, b INT NOT NULL ON CONFLICT REPLACE DEFAULT 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO a VALUES(1, NULL, NULL);")
+box.execute("INSERT INTO a VALUES(1, NULL, NULL);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO a VALUES(2, NULL, NULL);")
+box.execute("INSERT INTO a VALUES(2, NULL, NULL);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM a;")
+box.execute("SELECT * FROM a;")
 ---
-- - [1, 1, 2]
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: INTEGER
+  - name: B
+    type: INTEGER
+  rows:
+  - [1, 1, 2]
   - [2, 1, 2]
 ...
-box.sql.execute("DROP TABLE a;")
+box.execute("DROP TABLE a;")
 ---
+- rowcount: 1
 ...
 -- gh-3566: UPDATE OR IGNORE causes deletion of old entry.
 --
-box.sql.execute("CREATE TABLE tj (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
+box.execute("CREATE TABLE tj (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO tj VALUES (1, 1, 2), (2, 2, 3);")
+box.execute("INSERT INTO tj VALUES (1, 1, 2), (2, 2, 3);")
 ---
+- rowcount: 2
 ...
-box.sql.execute("CREATE UNIQUE INDEX i ON tj (s2);")
+box.execute("CREATE UNIQUE INDEX i ON tj (s2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE OR IGNORE tj SET s1 = s1 + 1;")
+box.execute("UPDATE OR IGNORE tj SET s1 = s1 + 1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT s1, s2 FROM tj;")
+box.execute("SELECT s1, s2 FROM tj;")
 ---
-- - [1, 2]
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S2
+    type: INTEGER
+  rows:
+  - [1, 2]
   - [3, 3]
 ...
-box.sql.execute("UPDATE OR IGNORE tj SET s2 = s2 + 1;")
+box.execute("UPDATE OR IGNORE tj SET s2 = s2 + 1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT s1, s2 FROM tj;")
+box.execute("SELECT s1, s2 FROM tj;")
 ---
-- - [1, 2]
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S2
+    type: INTEGER
+  rows:
+  - [1, 2]
   - [3, 4]
 ...
 -- gh-3565: INSERT OR REPLACE causes assertion fault.
 --
-box.sql.execute("DROP TABLE tj;")
+box.execute("DROP TABLE tj;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE tj (s1 INT PRIMARY KEY, s2 INT);")
+box.execute("CREATE TABLE tj (s1 INT PRIMARY KEY, s2 INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO tj VALUES (1, 2),(2, 3);")
+box.execute("INSERT INTO tj VALUES (1, 2),(2, 3);")
 ---
+- rowcount: 2
 ...
-box.sql.execute("CREATE UNIQUE INDEX i ON tj (s2);")
+box.execute("CREATE UNIQUE INDEX i ON tj (s2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("REPLACE INTO tj VALUES (1, 3);")
+box.execute("REPLACE INTO tj VALUES (1, 3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("SELECT * FROM tj;")
+box.execute("SELECT * FROM tj;")
 ---
-- - [1, 3]
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S2
+    type: INTEGER
+  rows:
+  - [1, 3]
 ...
-box.sql.execute("INSERT INTO tj VALUES (2, 4), (3, 5);")
+box.execute("INSERT INTO tj VALUES (2, 4), (3, 5);")
 ---
+- rowcount: 2
 ...
-box.sql.execute("UPDATE OR REPLACE tj SET s2 = s2 + 1;")
+box.execute("UPDATE OR REPLACE tj SET s2 = s2 + 1;")
 ---
+- rowcount: 5
 ...
-box.sql.execute("SELECT * FROM tj;")
+box.execute("SELECT * FROM tj;")
 ---
-- - [1, 4]
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S2
+    type: INTEGER
+  rows:
+  - [1, 4]
   - [3, 6]
 ...
-box.sql.execute("DROP TABLE tj;")
+box.execute("DROP TABLE tj;")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/on-conflict.test.lua b/test/sql/on-conflict.test.lua
index 982612e..1aa4d1b 100644
--- a/test/sql/on-conflict.test.lua
+++ b/test/sql/on-conflict.test.lua
@@ -1,58 +1,58 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 --
 -- Check that original sql ON CONFLICT clause is really
 -- disabled.
 --
-box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT ABORT)")
-box.sql.execute("CREATE TABLE q (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT FAIL)")
-box.sql.execute("CREATE TABLE p (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT IGNORE)")
-box.sql.execute("CREATE TABLE g (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT REPLACE)")
-box.sql.execute("CREATE TABLE e (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, v INTEGER)")
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY ON CONFLICT REPLACE)")
-box.sql.execute("CREATE TABLE t2(a INT PRIMARY KEY ON CONFLICT IGNORE)")
+box.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT ABORT)")
+box.execute("CREATE TABLE q (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT FAIL)")
+box.execute("CREATE TABLE p (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT IGNORE)")
+box.execute("CREATE TABLE g (id INTEGER PRIMARY KEY, v INTEGER UNIQUE ON CONFLICT REPLACE)")
+box.execute("CREATE TABLE e (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, v INTEGER)")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY ON CONFLICT REPLACE)")
+box.execute("CREATE TABLE t2(a INT PRIMARY KEY ON CONFLICT IGNORE)")
 
 -- CHECK constraint is illegal with REPLACE option.
 --
-box.sql.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER CHECK (a > 5) ON CONFLICT REPLACE);")
+box.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER CHECK (a > 5) ON CONFLICT REPLACE);")
 
 --
 -- gh-3473: Primary key can't be declared with NULL.
 --
-box.sql.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY NOT NULL);")
-box.sql.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY);")
-box.sql.execute("CREATE TABLE test (a int PRIMARY KEY, b int NULL ON CONFLICT IGNORE);")
-box.sql.execute("CREATE TABLE test (a int, b int NULL, c int, PRIMARY KEY(a, b, c))")
+box.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY NOT NULL);")
+box.execute("CREATE TABLE te17 (s1 INT NULL PRIMARY KEY);")
+box.execute("CREATE TABLE test (a int PRIMARY KEY, b int NULL ON CONFLICT IGNORE);")
+box.execute("CREATE TABLE test (a int, b int NULL, c int, PRIMARY KEY(a, b, c))")
 
 -- Several NOT NULL REPLACE constraints work
 --
-box.sql.execute("CREATE TABLE a (id INT PRIMARY KEY, a INT NOT NULL ON CONFLICT REPLACE DEFAULT 1, b INT NOT NULL ON CONFLICT REPLACE DEFAULT 2);")
-box.sql.execute("INSERT INTO a VALUES(1, NULL, NULL);")
-box.sql.execute("INSERT INTO a VALUES(2, NULL, NULL);")
-box.sql.execute("SELECT * FROM a;")
-box.sql.execute("DROP TABLE a;")
+box.execute("CREATE TABLE a (id INT PRIMARY KEY, a INT NOT NULL ON CONFLICT REPLACE DEFAULT 1, b INT NOT NULL ON CONFLICT REPLACE DEFAULT 2);")
+box.execute("INSERT INTO a VALUES(1, NULL, NULL);")
+box.execute("INSERT INTO a VALUES(2, NULL, NULL);")
+box.execute("SELECT * FROM a;")
+box.execute("DROP TABLE a;")
 
 -- gh-3566: UPDATE OR IGNORE causes deletion of old entry.
 --
-box.sql.execute("CREATE TABLE tj (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
-box.sql.execute("INSERT INTO tj VALUES (1, 1, 2), (2, 2, 3);")
-box.sql.execute("CREATE UNIQUE INDEX i ON tj (s2);")
-box.sql.execute("UPDATE OR IGNORE tj SET s1 = s1 + 1;")
-box.sql.execute("SELECT s1, s2 FROM tj;")
-box.sql.execute("UPDATE OR IGNORE tj SET s2 = s2 + 1;")
-box.sql.execute("SELECT s1, s2 FROM tj;")
+box.execute("CREATE TABLE tj (s0 INT PRIMARY KEY, s1 INT UNIQUE, s2 INT);")
+box.execute("INSERT INTO tj VALUES (1, 1, 2), (2, 2, 3);")
+box.execute("CREATE UNIQUE INDEX i ON tj (s2);")
+box.execute("UPDATE OR IGNORE tj SET s1 = s1 + 1;")
+box.execute("SELECT s1, s2 FROM tj;")
+box.execute("UPDATE OR IGNORE tj SET s2 = s2 + 1;")
+box.execute("SELECT s1, s2 FROM tj;")
 
 -- gh-3565: INSERT OR REPLACE causes assertion fault.
 --
-box.sql.execute("DROP TABLE tj;")
-box.sql.execute("CREATE TABLE tj (s1 INT PRIMARY KEY, s2 INT);")
-box.sql.execute("INSERT INTO tj VALUES (1, 2),(2, 3);")
-box.sql.execute("CREATE UNIQUE INDEX i ON tj (s2);")
-box.sql.execute("REPLACE INTO tj VALUES (1, 3);")
-box.sql.execute("SELECT * FROM tj;")
-box.sql.execute("INSERT INTO tj VALUES (2, 4), (3, 5);")
-box.sql.execute("UPDATE OR REPLACE tj SET s2 = s2 + 1;")
-box.sql.execute("SELECT * FROM tj;")
+box.execute("DROP TABLE tj;")
+box.execute("CREATE TABLE tj (s1 INT PRIMARY KEY, s2 INT);")
+box.execute("INSERT INTO tj VALUES (1, 2),(2, 3);")
+box.execute("CREATE UNIQUE INDEX i ON tj (s2);")
+box.execute("REPLACE INTO tj VALUES (1, 3);")
+box.execute("SELECT * FROM tj;")
+box.execute("INSERT INTO tj VALUES (2, 4), (3, 5);")
+box.execute("UPDATE OR REPLACE tj SET s2 = s2 + 1;")
+box.execute("SELECT * FROM tj;")
 
-box.sql.execute("DROP TABLE tj;")
+box.execute("DROP TABLE tj;")
diff --git a/test/sql/persistency.result b/test/sql/persistency.result
index 31c844f..9e5994b 100644
--- a/test/sql/persistency.result
+++ b/test/sql/persistency.result
@@ -7,159 +7,343 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- create space
-box.sql.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
+box.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
 ---
+- rowcount: 1
 ...
 -- prepare data
-box.sql.execute("INSERT INTO foobar VALUES (1, 'foo')")
+box.execute("INSERT INTO foobar VALUES (1, 'foo')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (2, 'bar')")
+box.execute("INSERT INTO foobar VALUES (2, 'bar')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
+box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
+box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_FOOBAR_1' in space 'FOOBAR'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_FOOBAR_1'' in space ''FOOBAR'''
 ...
 -- simple select
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
----
-- - ['bar', 2, 42, 'awesome']
-...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
----
-- - ['foobar', 1000, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
----
-- - ['bar', 2, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
----
-- []
-...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
----
-- []
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
+  - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
 ...
-box.sql.execute("SELECT count(*) FROM foobar")
+box.execute("SELECT count(*) FROM foobar")
 ---
-- - [3]
+- metadata:
+  - name: count(*)
+    type: INTEGER
+  rows:
+  - [3]
 ...
-box.sql.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
+box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
 ---
-- - [1]
+- metadata:
+  - name: count(*)
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
----
-- - ['bar', 2, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
+---
+- metadata:
+  - name: BAR
+    type: BLOB
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
   - ['foo', 1, 42, 'awesome']
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
----
-- - ['foobar', 1000, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
+---
+- metadata:
+  - name: BAR
+    type: BLOB
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foobar', 1000, 42, 'awesome']
   - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
 -- updates
-box.sql.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
+box.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
+box.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
 ---
-- - [1]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 ---
-- - [1]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
+box.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 ---
-- - [0]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [0]
 ...
 -- multi-index
 -- create space
-box.sql.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
+box.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
+box.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
 ---
+- rowcount: 1
 ...
 -- prepare data
-box.sql.execute("INSERT INTO barfoo VALUES ('foo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('foo', 1)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO barfoo VALUES ('bar', 2)")
+box.execute("INSERT INTO barfoo VALUES ('bar', 2)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
+box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 ---
+- rowcount: 1
 ...
 -- create a trigger
-box.sql.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT INTO barfoo VALUES ('trigger test', 9999); END")
+box.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT INTO barfoo VALUES ('trigger test', 9999); END")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
 ---
-- - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows:
+  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
         INTO barfoo VALUES (''trigger test'', 9999); END'}]
 ...
 -- Many entries
-box.sql.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
+box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
+box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
 ---
+- rowcount: 1000
 ...
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
-- - [840]
+- metadata:
+  - name: A
+    type: BLOB
+  rows:
+  - [840]
   - [880]
   - [920]
   - [960]
@@ -172,59 +356,99 @@ box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ...
 test_run:cmd('restart server default');
 -- prove that trigger survived
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
----
-- - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+---
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows:
+  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
         INTO barfoo VALUES (''trigger test'', 9999); END'}]
 ...
 -- ... functional
-box.sql.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
+box.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
 ---
 - error: 'Type mismatch: can not convert foobar trigger test to integer'
 ...
-box.sql.execute("SELECT * FROM barfoo WHERE foo = 9999");
+box.execute("SELECT * FROM barfoo WHERE foo = 9999");
 ---
-- []
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: NUMERIC
+  rows: []
 ...
 -- and still persistent
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
----
-- - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+---
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows:
+  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT
         INTO barfoo VALUES (''trigger test'', 9999); END'}]
 ...
 -- and can be dropped just once
-box.sql.execute("DROP TRIGGER tfoobar")
+box.execute("DROP TRIGGER tfoobar")
 ---
+- rowcount: 1
 ...
 -- Should error
-box.sql.execute("DROP TRIGGER tfoobar")
+box.execute("DROP TRIGGER tfoobar")
 ---
 - error: Trigger 'TFOOBAR' doesn't exist
 ...
 -- Should be empty
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 ---
-- []
+- metadata:
+  - name: name
+    type: TEXT
+  - name: opts
+    type: UNKNOWN
+  rows: []
 ...
 -- prove barfoo2 still exists
-box.sql.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_BARFOO_1' in space 'BARFOO'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_BARFOO_1'' in space ''BARFOO'''
 ...
-box.sql.execute("SELECT * FROM barfoo")
+box.execute("SELECT * FROM barfoo")
 ---
-- - ['foo', 1]
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: NUMERIC
+  rows:
+  - ['foo', 1]
   - ['bar', 2]
   - ['foobar', 1000]
 ...
-box.sql.execute("SELECT * FROM foobar");
+box.execute("SELECT * FROM foobar");
 ---
-- - [2, 'bar']
+- metadata:
+  - name: FOO
+    type: INTEGER
+  - name: BAR
+    type: TEXT
+  rows:
+  - [2, 'bar']
   - [1000, 'foobar']
 ...
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ---
-- - [840]
+- metadata:
+  - name: A
+    type: BLOB
+  rows:
+  - [840]
   - [880]
   - [920]
   - [960]
@@ -236,12 +460,15 @@ box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
   - [161]
 ...
 -- cleanup
-box.sql.execute("DROP TABLE foobar")
+box.execute("DROP TABLE foobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE barfoo")
+box.execute("DROP TABLE barfoo")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE t1")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/persistency.test.lua b/test/sql/persistency.test.lua
index 67659e8..bad2d69 100644
--- a/test/sql/persistency.test.lua
+++ b/test/sql/persistency.test.lua
@@ -1,90 +1,90 @@
 env = require('test_run')
 test_run = env.new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- create space
-box.sql.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
+box.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
 
 -- prepare data
-box.sql.execute("INSERT INTO foobar VALUES (1, 'foo')")
-box.sql.execute("INSERT INTO foobar VALUES (2, 'bar')")
-box.sql.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
+box.execute("INSERT INTO foobar VALUES (1, 'foo')")
+box.execute("INSERT INTO foobar VALUES (2, 'bar')")
+box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 
-box.sql.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
+box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 
 -- simple select
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
-box.sql.execute("SELECT count(*) FROM foobar")
-box.sql.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
+box.execute("SELECT count(*) FROM foobar")
+box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
 
 -- updates
-box.sql.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
-box.sql.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
+box.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 
 -- multi-index
 
 -- create space
-box.sql.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
-box.sql.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
+box.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
+box.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
 
 -- prepare data
-box.sql.execute("INSERT INTO barfoo VALUES ('foo', 1)")
-box.sql.execute("INSERT INTO barfoo VALUES ('bar', 2)")
-box.sql.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
+box.execute("INSERT INTO barfoo VALUES ('foo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('bar', 2)")
+box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 
 -- create a trigger
-box.sql.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT INTO barfoo VALUES ('trigger test', 9999); END")
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar BEGIN INSERT INTO barfoo VALUES ('trigger test', 9999); END")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
 
 -- Many entries
-box.sql.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
-box.sql.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
+box.execute("WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000) INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;")
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 
 test_run:cmd('restart server default');
 
 -- prove that trigger survived
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
 
 -- ... functional
-box.sql.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
-box.sql.execute("SELECT * FROM barfoo WHERE foo = 9999");
+box.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
+box.execute("SELECT * FROM barfoo WHERE foo = 9999");
 
 -- and still persistent
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 
 -- and can be dropped just once
-box.sql.execute("DROP TRIGGER tfoobar")
+box.execute("DROP TRIGGER tfoobar")
 -- Should error
-box.sql.execute("DROP TRIGGER tfoobar")
+box.execute("DROP TRIGGER tfoobar")
 -- Should be empty
-box.sql.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 
 -- prove barfoo2 still exists
-box.sql.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 
-box.sql.execute("SELECT * FROM barfoo")
-box.sql.execute("SELECT * FROM foobar");
-box.sql.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
+box.execute("SELECT * FROM barfoo")
+box.execute("SELECT * FROM foobar");
+box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 
 -- cleanup
-box.sql.execute("DROP TABLE foobar")
-box.sql.execute("DROP TABLE barfoo")
-box.sql.execute("DROP TABLE t1")
+box.execute("DROP TABLE foobar")
+box.execute("DROP TABLE barfoo")
+box.execute("DROP TABLE t1")
diff --git a/test/sql/row-count.result b/test/sql/row-count.result
index b75298f..fe48430 100644
--- a/test/sql/row-count.result
+++ b/test/sql/row-count.result
@@ -4,208 +4,338 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Test cases concerning row count calculations.
 --
-box.sql.execute("CREATE TABLE t1 (s1 VARCHAR(10) PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (s1 VARCHAR(10) PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("CREATE TABLE t2 (s1 VARCHAR(10) PRIMARY KEY, s2 VARCHAR(10) REFERENCES t1 ON DELETE CASCADE);")
+box.execute("CREATE TABLE t2 (s1 VARCHAR(10) PRIMARY KEY, s2 VARCHAR(10) REFERENCES t1 ON DELETE CASCADE);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("CREATE TABLE t3 (i1 INT UNIQUE, i2 INT, i3 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t3 (i1 INT UNIQUE, i2 INT, i3 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES (0, 0, 0);")
+box.execute("INSERT INTO t3 VALUES (0, 0, 0);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("CREATE TRIGGER x AFTER DELETE ON t1 FOR EACH ROW BEGIN UPDATE t3 SET i1 = i1 + ROW_COUNT(); END;")
+box.execute("CREATE TRIGGER x AFTER DELETE ON t1 FOR EACH ROW BEGIN UPDATE t3 SET i1 = i1 + ROW_COUNT(); END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("INSERT INTO t1 VALUES ('a');")
+box.execute("INSERT INTO t1 VALUES ('a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("INSERT INTO t2 VALUES ('a','a');")
+box.execute("INSERT INTO t2 VALUES ('a','a');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
+box.execute("INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
 ---
+- rowcount: 3
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [3]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [3]
 ...
 -- REPLACE is accounted for two operations: DELETE + INSERT.
-box.sql.execute("REPLACE INTO t2 VALUES('a', 'c');")
+box.execute("REPLACE INTO t2 VALUES('a', 'c');")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [2]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [2]
 ...
-box.sql.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t1;")
 ---
+- rowcount: 4
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [4]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [4]
 ...
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("TRUNCATE TABLE t3;")
+box.execute("TRUNCATE TABLE t3;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("UPDATE t3 SET i2 = 666;")
+box.execute("UPDATE t3 SET i2 = 666;")
 ---
+- rowcount: 3
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [3]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [3]
 ...
 -- gh-3816: DELETE optimization returns valid number of
 -- deleted tuples.
 --
-box.sql.execute("DELETE FROM t3 WHERE 0 = 0;")
+box.execute("DELETE FROM t3 WHERE 0 = 0;")
 ---
+- rowcount: 3
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [3]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [3]
 ...
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("DELETE FROM t3")
+box.execute("DELETE FROM t3")
 ---
+- rowcount: 3
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [3]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [3]
 ...
 -- But triggers still should't be accounted.
 --
-box.sql.execute("CREATE TABLE tt1 (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE tt1 (id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE tt2 (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE tt2 (id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tr1 AFTER DELETE ON tt1 BEGIN DELETE FROM tt2; END;")
+box.execute("CREATE TRIGGER tr1 AFTER DELETE ON tt1 BEGIN DELETE FROM tt2; END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO tt1 VALUES (1), (2), (3);")
+box.execute("INSERT INTO tt1 VALUES (1), (2), (3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("INSERT INTO tt2 VALUES (1), (2), (3);")
+box.execute("INSERT INTO tt2 VALUES (1), (2), (3);")
 ---
+- rowcount: 3
 ...
-box.sql.execute("DELETE FROM tt1 WHERE id = 2;")
+box.execute("DELETE FROM tt1 WHERE id = 2;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [1]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT * FROM tt2;")
+box.execute("SELECT * FROM tt2;")
 ---
-- []
+- metadata:
+  - name: ID
+    type: INTEGER
+  rows: []
 ...
-box.sql.execute("DROP TABLE tt1;")
+box.execute("DROP TABLE tt1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE tt2;")
+box.execute("DROP TABLE tt2;")
 ---
+- rowcount: 1
 ...
 -- All statements which are not accounted as DML should
 -- return 0 (zero) as a row count.
 --
-box.sql.execute("START TRANSACTION;")
+box.execute("START TRANSACTION;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("COMMIT;")
+box.execute("COMMIT;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("COMMIT;")
+box.execute("COMMIT;")
 ---
-- error: cannot commit - no transaction is active
+- error: 'Error during execution of VDBE byte-code: cannot commit - no transaction
+    is active'
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("ANALYZE;")
+box.execute("ANALYZE;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute("EXPLAIN QUERY PLAN INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
+box.execute("EXPLAIN QUERY PLAN INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
 ---
-- - [0, 0, 0, 'SCAN TABLE T2']
+- metadata:
+  - name: selectid
+    type: INTEGER
+  - name: order
+    type: INTEGER
+  - name: from
+    type: INTEGER
+  - name: detail
+    type: TEXT
+  rows:
+  - [0, 0, 0, 'SCAN TABLE T2']
 ...
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
 ---
-- - [0]
+- metadata:
+  - name: ROW_COUNT()
+    type: INTEGER
+  rows:
+  - [0]
 ...
-box.sql.execute('PRAGMA recursive_triggers')
+box.execute('PRAGMA recursive_triggers')
 ---
-- - [1]
+- metadata:
+  - name: recursive_triggers
+    type: INTEGER
+  rows:
+  - [1]
 ...
 -- Clean-up.
 --
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t3;")
+box.execute("DROP TABLE t3;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/row-count.test.lua b/test/sql/row-count.test.lua
index 89476c7..590e5d0 100644
--- a/test/sql/row-count.test.lua
+++ b/test/sql/row-count.test.lua
@@ -1,75 +1,75 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Test cases concerning row count calculations.
 --
-box.sql.execute("CREATE TABLE t1 (s1 VARCHAR(10) PRIMARY KEY);")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("CREATE TABLE t2 (s1 VARCHAR(10) PRIMARY KEY, s2 VARCHAR(10) REFERENCES t1 ON DELETE CASCADE);")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("CREATE TABLE t3 (i1 INT UNIQUE, i2 INT, i3 INT PRIMARY KEY);")
-box.sql.execute("INSERT INTO t3 VALUES (0, 0, 0);")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("CREATE TRIGGER x AFTER DELETE ON t1 FOR EACH ROW BEGIN UPDATE t3 SET i1 = i1 + ROW_COUNT(); END;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t1 VALUES ('a');")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t2 VALUES ('a','a');")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("CREATE TABLE t1 (s1 VARCHAR(10) PRIMARY KEY);")
+box.execute("SELECT ROW_COUNT();")
+box.execute("SELECT ROW_COUNT();")
+box.execute("CREATE TABLE t2 (s1 VARCHAR(10) PRIMARY KEY, s2 VARCHAR(10) REFERENCES t1 ON DELETE CASCADE);")
+box.execute("SELECT ROW_COUNT();")
+box.execute("CREATE TABLE t3 (i1 INT UNIQUE, i2 INT, i3 INT PRIMARY KEY);")
+box.execute("INSERT INTO t3 VALUES (0, 0, 0);")
+box.execute("SELECT ROW_COUNT();")
+box.execute("CREATE TRIGGER x AFTER DELETE ON t1 FOR EACH ROW BEGIN UPDATE t3 SET i1 = i1 + ROW_COUNT(); END;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t1 VALUES ('a');")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t2 VALUES ('a','a');")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
+box.execute("SELECT ROW_COUNT();")
 -- REPLACE is accounted for two operations: DELETE + INSERT.
-box.sql.execute("REPLACE INTO t2 VALUES('a', 'c');")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("DELETE FROM t1;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
-box.sql.execute("TRUNCATE TABLE t3;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
-box.sql.execute("UPDATE t3 SET i2 = 666;")
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("REPLACE INTO t2 VALUES('a', 'c');")
+box.execute("SELECT ROW_COUNT();")
+box.execute("DELETE FROM t1;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("TRUNCATE TABLE t3;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("UPDATE t3 SET i2 = 666;")
+box.execute("SELECT ROW_COUNT();")
 -- gh-3816: DELETE optimization returns valid number of
 -- deleted tuples.
 --
-box.sql.execute("DELETE FROM t3 WHERE 0 = 0;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
-box.sql.execute("DELETE FROM t3")
-box.sql.execute("SELECT ROW_COUNT();")
+box.execute("DELETE FROM t3 WHERE 0 = 0;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("INSERT INTO t3 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);")
+box.execute("DELETE FROM t3")
+box.execute("SELECT ROW_COUNT();")
 -- But triggers still should't be accounted.
 --
-box.sql.execute("CREATE TABLE tt1 (id INT PRIMARY KEY);")
-box.sql.execute("CREATE TABLE tt2 (id INT PRIMARY KEY);")
-box.sql.execute("CREATE TRIGGER tr1 AFTER DELETE ON tt1 BEGIN DELETE FROM tt2; END;")
-box.sql.execute("INSERT INTO tt1 VALUES (1), (2), (3);")
-box.sql.execute("INSERT INTO tt2 VALUES (1), (2), (3);")
-box.sql.execute("DELETE FROM tt1 WHERE id = 2;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("SELECT * FROM tt2;")
-box.sql.execute("DROP TABLE tt1;")
-box.sql.execute("DROP TABLE tt2;")
+box.execute("CREATE TABLE tt1 (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE tt2 (id INT PRIMARY KEY);")
+box.execute("CREATE TRIGGER tr1 AFTER DELETE ON tt1 BEGIN DELETE FROM tt2; END;")
+box.execute("INSERT INTO tt1 VALUES (1), (2), (3);")
+box.execute("INSERT INTO tt2 VALUES (1), (2), (3);")
+box.execute("DELETE FROM tt1 WHERE id = 2;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("SELECT * FROM tt2;")
+box.execute("DROP TABLE tt1;")
+box.execute("DROP TABLE tt2;")
 
 -- All statements which are not accounted as DML should
 -- return 0 (zero) as a row count.
 --
-box.sql.execute("START TRANSACTION;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("COMMIT;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("COMMIT;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("ANALYZE;")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute("EXPLAIN QUERY PLAN INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
-box.sql.execute("SELECT ROW_COUNT();")
-box.sql.execute('PRAGMA recursive_triggers')
+box.execute("START TRANSACTION;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("COMMIT;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("COMMIT;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("ANALYZE;")
+box.execute("SELECT ROW_COUNT();")
+box.execute("EXPLAIN QUERY PLAN INSERT INTO t1 VALUES ('b'), ('c'), ('d');")
+box.execute("SELECT ROW_COUNT();")
+box.execute('PRAGMA recursive_triggers')
 
 -- Clean-up.
 --
-box.sql.execute("DROP TABLE t2;")
-box.sql.execute("DROP TABLE t3;")
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t3;")
+box.execute("DROP TABLE t1;")
 
diff --git a/test/sql/savepoints.result b/test/sql/savepoints.result
index 8553dd8..d378172 100644
--- a/test/sql/savepoints.result
+++ b/test/sql/savepoints.result
@@ -4,32 +4,33 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- These tests check that SQL savepoints properly work outside
 -- transactions as well as inside transactions started in Lua.
 -- gh-3313
 --
-box.sql.execute('SAVEPOINT t1;');
+box.execute('SAVEPOINT t1;');
 ---
 - error: Can not set a savepoint in absence of active transaction
 ...
-box.sql.execute('RELEASE SAVEPOINT t1;');
+box.execute('RELEASE SAVEPOINT t1;');
 ---
 - error: Can not set a savepoint in absence of active transaction
 ...
-box.sql.execute('ROLLBACK TO SAVEPOINT t1;');
+box.execute('ROLLBACK TO SAVEPOINT t1;');
 ---
 - error: Can not set a savepoint in absence of active transaction
 ...
-box.begin() box.sql.execute('SAVEPOINT t1;') box.sql.execute('RELEASE SAVEPOINT t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.execute('RELEASE SAVEPOINT t1;') box.commit();
 ---
 ...
-box.begin() box.sql.execute('SAVEPOINT t1;') box.sql.execute('ROLLBACK TO t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.execute('ROLLBACK TO t1;') box.commit();
 ---
 ...
-box.begin() box.sql.execute('SAVEPOINT t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.commit();
 ---
 ...
 box.commit();
@@ -43,8 +44,8 @@ test_run:cmd("setopt delimiter ';'")
 ...
 release_sv = function()
     box.begin()
-    box.sql.execute('SAVEPOINT t1;')
-    box.sql.execute('RELEASE SAVEPOINT t1;')
+    box.execute('SAVEPOINT t1;')
+    box.execute('RELEASE SAVEPOINT t1;')
 end;
 ---
 ...
@@ -56,17 +57,17 @@ box.commit();
 ...
 release_sv_fail = function()
     box.begin()
-    box.sql.execute('SAVEPOINT t1;')
-    box.sql.execute('SAVEPOINT t2;')
-    box.sql.execute('RELEASE SAVEPOINT t2;')
-    box.sql.execute('RELEASE SAVEPOINT t1;')
-    box.sql.execute('ROLLBACK TO t1;')
+    box.execute('SAVEPOINT t1;')
+    box.execute('SAVEPOINT t2;')
+    box.execute('RELEASE SAVEPOINT t2;')
+    box.execute('RELEASE SAVEPOINT t1;')
+    box.execute('ROLLBACK TO t1;')
 end;
 ---
 ...
 release_sv_fail();
 ---
-- error: 'no such savepoint: T1'
+- error: 'Error during execution of VDBE byte-code: no such savepoint: T1'
 ...
 box.commit();
 ---
diff --git a/test/sql/savepoints.test.lua b/test/sql/savepoints.test.lua
index a4ed061..b4c6fee 100644
--- a/test/sql/savepoints.test.lua
+++ b/test/sql/savepoints.test.lua
@@ -1,21 +1,21 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- These tests check that SQL savepoints properly work outside
 -- transactions as well as inside transactions started in Lua.
 -- gh-3313
 --
 
-box.sql.execute('SAVEPOINT t1;');
-box.sql.execute('RELEASE SAVEPOINT t1;');
-box.sql.execute('ROLLBACK TO SAVEPOINT t1;');
+box.execute('SAVEPOINT t1;');
+box.execute('RELEASE SAVEPOINT t1;');
+box.execute('ROLLBACK TO SAVEPOINT t1;');
 
-box.begin() box.sql.execute('SAVEPOINT t1;') box.sql.execute('RELEASE SAVEPOINT t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.execute('RELEASE SAVEPOINT t1;') box.commit();
 
-box.begin() box.sql.execute('SAVEPOINT t1;') box.sql.execute('ROLLBACK TO t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.execute('ROLLBACK TO t1;') box.commit();
 
-box.begin() box.sql.execute('SAVEPOINT t1;') box.commit();
+box.begin() box.execute('SAVEPOINT t1;') box.commit();
 
 box.commit();
 
@@ -25,19 +25,19 @@ test_run:cmd("setopt delimiter ';'")
 
 release_sv = function()
     box.begin()
-    box.sql.execute('SAVEPOINT t1;')
-    box.sql.execute('RELEASE SAVEPOINT t1;')
+    box.execute('SAVEPOINT t1;')
+    box.execute('RELEASE SAVEPOINT t1;')
 end;
 release_sv();
 box.commit();
 
 release_sv_fail = function()
     box.begin()
-    box.sql.execute('SAVEPOINT t1;')
-    box.sql.execute('SAVEPOINT t2;')
-    box.sql.execute('RELEASE SAVEPOINT t2;')
-    box.sql.execute('RELEASE SAVEPOINT t1;')
-    box.sql.execute('ROLLBACK TO t1;')
+    box.execute('SAVEPOINT t1;')
+    box.execute('SAVEPOINT t2;')
+    box.execute('RELEASE SAVEPOINT t2;')
+    box.execute('RELEASE SAVEPOINT t1;')
+    box.execute('ROLLBACK TO t1;')
 end;
 release_sv_fail();
 box.commit();
diff --git a/test/sql/select-null.result b/test/sql/select-null.result
index 5ea23d0..948d53d 100644
--- a/test/sql/select-null.result
+++ b/test/sql/select-null.result
@@ -4,32 +4,45 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE t3(id INT, a text, b TEXT, PRIMARY KEY(id))")
+box.execute("CREATE TABLE t3(id INT, a text, b TEXT, PRIMARY KEY(id))")
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("INSERT INTO t3 VALUES(1, 'abc',NULL)");
+box.execute("INSERT INTO t3 VALUES(1, 'abc',NULL)");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t3 VALUES(2, NULL,'xyz')");
+box.execute("INSERT INTO t3 VALUES(2, NULL,'xyz')");
 ---
+- rowcount: 1
 ...
 -- Select must return properly decoded `NULL`
-box.sql.execute("SELECT * FROM t3")
----
-- - [1, 'abc', null]
+box.execute("SELECT * FROM t3")
+---
+- metadata:
+  - name: ID
+    type: INTEGER
+  - name: A
+    type: TEXT
+  - name: B
+    type: TEXT
+  rows:
+  - [1, 'abc', null]
   - [2, null, 'xyz']
 ...
 -- Cleanup
-box.sql.execute("DROP TABLE t3")
+box.execute("DROP TABLE t3")
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/select-null.test.lua b/test/sql/select-null.test.lua
index ccbc030..a49eb43 100644
--- a/test/sql/select-null.test.lua
+++ b/test/sql/select-null.test.lua
@@ -1,24 +1,24 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE t3(id INT, a text, b TEXT, PRIMARY KEY(id))")
+box.execute("CREATE TABLE t3(id INT, a text, b TEXT, PRIMARY KEY(id))")
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("INSERT INTO t3 VALUES(1, 'abc',NULL)");
-box.sql.execute("INSERT INTO t3 VALUES(2, NULL,'xyz')");
+box.execute("INSERT INTO t3 VALUES(1, 'abc',NULL)");
+box.execute("INSERT INTO t3 VALUES(2, NULL,'xyz')");
 
 -- Select must return properly decoded `NULL`
-box.sql.execute("SELECT * FROM t3")
+box.execute("SELECT * FROM t3")
 
 -- Cleanup
-box.sql.execute("DROP TABLE t3")
+box.execute("DROP TABLE t3")
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/sql-debug.result b/test/sql/sql-debug.result
index a857392..a2aca8b 100644
--- a/test/sql/sql-debug.result
+++ b/test/sql/sql-debug.result
@@ -8,25 +8,37 @@ test_run = require('test_run').new()
 -- gh-3832: Some statements do not return column type
 -- Check that "PRAGMA parser_trace" returns 0 or 1 if called
 -- without parameter.
-result = box.sql.execute('PRAGMA parser_trace')
+result = box.execute('PRAGMA parser_trace').rows
 ---
 ...
-box.sql.execute('PRAGMA parser_trace = 1')
+box.execute('PRAGMA parser_trace = 1')
 ---
+- rowcount: 0
 ...
-box.sql.execute('PRAGMA parser_trace')
+box.execute('PRAGMA parser_trace')
 ---
-- - [1]
+- metadata:
+  - name: parser_trace
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute('PRAGMA parser_trace = '.. result[1][1])
+box.execute('PRAGMA parser_trace = '.. result[1][1])
 ---
+- rowcount: 0
 ...
 --
 -- Make PRAGMA command return the result as a result set.
 --
-box.sql.execute('PRAGMA')
+box.execute('PRAGMA')
 ---
-- - ['case_sensitive_like', 0]
+- metadata:
+  - name: pragma_name
+    type: TEXT
+  - name: pragma_value
+    type: INTEGER
+  rows:
+  - ['case_sensitive_like', 0]
   - ['count_changes', 0]
   - ['defer_foreign_keys', 0]
   - ['full_column_names', 0]
diff --git a/test/sql/sql-debug.test.lua b/test/sql/sql-debug.test.lua
index e429c38..edd0ef4 100644
--- a/test/sql/sql-debug.test.lua
+++ b/test/sql/sql-debug.test.lua
@@ -6,12 +6,12 @@ test_run = require('test_run').new()
 
 -- Check that "PRAGMA parser_trace" returns 0 or 1 if called
 -- without parameter.
-result = box.sql.execute('PRAGMA parser_trace')
-box.sql.execute('PRAGMA parser_trace = 1')
-box.sql.execute('PRAGMA parser_trace')
-box.sql.execute('PRAGMA parser_trace = '.. result[1][1])
+result = box.execute('PRAGMA parser_trace').rows
+box.execute('PRAGMA parser_trace = 1')
+box.execute('PRAGMA parser_trace')
+box.execute('PRAGMA parser_trace = '.. result[1][1])
 
 --
 -- Make PRAGMA command return the result as a result set.
 --
-box.sql.execute('PRAGMA')
+box.execute('PRAGMA')
diff --git a/test/sql/sql-statN-index-drop.result b/test/sql/sql-statN-index-drop.result
index 7605951..0636cd9 100644
--- a/test/sql/sql-statN-index-drop.result
+++ b/test/sql/sql-statN-index-drop.result
@@ -4,130 +4,239 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Initializing some things.
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i1 ON t1(a);")
+box.execute("CREATE INDEX i1 ON t1(a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i1 ON t2(a);")
+box.execute("CREATE INDEX i1 ON t2(a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
+box.execute("INSERT INTO t1 VALUES(1, 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+box.execute("INSERT INTO t2 VALUES(1, 2);")
 ---
+- rowcount: 1
 ...
 -- Analyze.
-box.sql.execute("ANALYZE;")
+box.execute("ANALYZE;")
 ---
+- rowcount: 0
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
----
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
+box.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: neq
+    type: TEXT
+  - name: nlt
+    type: TEXT
+  - name: ndlt
+    type: TEXT
+  - name: sample
+    type: BLOB
+  rows:
+  - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
   - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
----
-- - ['T1', 'I1', '1 1']
+box.execute("SELECT * FROM \"_sql_stat1\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: stat
+    type: TEXT
+  rows:
+  - ['T1', 'I1', '1 1']
   - ['T1', 'T1', '1 1']
   - ['T2', 'I1', '1 1']
   - ['T2', 'T2', '1 1']
 ...
 -- Dropping an index.
-box.sql.execute("DROP INDEX i1 ON t1;")
+box.execute("DROP INDEX i1 ON t1;")
 ---
+- rowcount: 3
 ...
 -- Checking the DROP INDEX results.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
----
-- - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
+box.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: neq
+    type: TEXT
+  - name: nlt
+    type: TEXT
+  - name: ndlt
+    type: TEXT
+  - name: sample
+    type: BLOB
+  rows:
+  - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
   - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.execute("SELECT * FROM \"_sql_stat1\";")
 ---
-- - ['T1', 'T1', '1 1']
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: stat
+    type: TEXT
+  rows:
+  - ['T1', 'T1', '1 1']
   - ['T2', 'I1', '1 1']
   - ['T2', 'T2', '1 1']
 ...
 --Cleaning up.
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 3
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 5
 ...
 -- Same test but dropping an INDEX ON t2.
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i1 ON t1(a);")
+box.execute("CREATE INDEX i1 ON t1(a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i1 ON t2(a);")
+box.execute("CREATE INDEX i1 ON t2(a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
+box.execute("INSERT INTO t1 VALUES(1, 2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+box.execute("INSERT INTO t2 VALUES(1, 2);")
 ---
+- rowcount: 1
 ...
 -- Analyze.
-box.sql.execute("ANALYZE;")
+box.execute("ANALYZE;")
 ---
+- rowcount: 0
 ...
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
----
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
+box.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: neq
+    type: TEXT
+  - name: nlt
+    type: TEXT
+  - name: ndlt
+    type: TEXT
+  - name: sample
+    type: BLOB
+  rows:
+  - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
   - ['T2', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
----
-- - ['T1', 'I1', '1 1']
+box.execute("SELECT * FROM \"_sql_stat1\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: stat
+    type: TEXT
+  rows:
+  - ['T1', 'I1', '1 1']
   - ['T1', 'T1', '1 1']
   - ['T2', 'I1', '1 1']
   - ['T2', 'T2', '1 1']
 ...
 -- Dropping an index.
-box.sql.execute("DROP INDEX i1 ON t2;")
+box.execute("DROP INDEX i1 ON t2;")
 ---
+- rowcount: 3
 ...
 -- Checking the DROP INDEX results.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
----
-- - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
+box.execute("SELECT * FROM \"_sql_stat4\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: neq
+    type: TEXT
+  - name: nlt
+    type: TEXT
+  - name: ndlt
+    type: TEXT
+  - name: sample
+    type: BLOB
+  rows:
+  - ['T1', 'I1', '1', '0', '0', !!binary kQI=]
   - ['T1', 'T1', '1', '0', '0', !!binary kQE=]
   - ['T2', 'T2', '1', '0', '0', !!binary kQE=]
 ...
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
----
-- - ['T1', 'I1', '1 1']
+box.execute("SELECT * FROM \"_sql_stat1\";")
+---
+- metadata:
+  - name: tbl
+    type: TEXT
+  - name: idx
+    type: TEXT
+  - name: stat
+    type: TEXT
+  rows:
+  - ['T1', 'I1', '1 1']
   - ['T1', 'T1', '1 1']
   - ['T2', 'T2', '1 1']
 ...
 --Cleaning up.
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 5
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 3
 ...
diff --git a/test/sql/sql-statN-index-drop.test.lua b/test/sql/sql-statN-index-drop.test.lua
index 35f2291..5477a2a 100644
--- a/test/sql/sql-statN-index-drop.test.lua
+++ b/test/sql/sql-statN-index-drop.test.lua
@@ -1,56 +1,56 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Initializing some things.
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
-box.sql.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
-box.sql.execute("CREATE INDEX i1 ON t1(a);")
-box.sql.execute("CREATE INDEX i1 ON t2(a);")
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
-box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE INDEX i1 ON t1(a);")
+box.execute("CREATE INDEX i1 ON t2(a);")
+box.execute("INSERT INTO t1 VALUES(1, 2);")
+box.execute("INSERT INTO t2 VALUES(1, 2);")
 
 -- Analyze.
-box.sql.execute("ANALYZE;")
+box.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.execute("SELECT * FROM \"_sql_stat4\";")
+box.execute("SELECT * FROM \"_sql_stat1\";")
 
 -- Dropping an index.
-box.sql.execute("DROP INDEX i1 ON t1;")
+box.execute("DROP INDEX i1 ON t1;")
 
 -- Checking the DROP INDEX results.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.execute("SELECT * FROM \"_sql_stat4\";")
+box.execute("SELECT * FROM \"_sql_stat1\";")
 
 --Cleaning up.
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
 
 -- Same test but dropping an INDEX ON t2.
 
-box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
-box.sql.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
-box.sql.execute("CREATE INDEX i1 ON t1(a);")
-box.sql.execute("CREATE INDEX i1 ON t2(a);")
-box.sql.execute("INSERT INTO t1 VALUES(1, 2);")
-box.sql.execute("INSERT INTO t2 VALUES(1, 2);")
+box.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t2(id INT PRIMARY KEY, a INT);")
+box.execute("CREATE INDEX i1 ON t1(a);")
+box.execute("CREATE INDEX i1 ON t2(a);")
+box.execute("INSERT INTO t1 VALUES(1, 2);")
+box.execute("INSERT INTO t2 VALUES(1, 2);")
 
 -- Analyze.
-box.sql.execute("ANALYZE;")
+box.execute("ANALYZE;")
 
 -- Checking the data.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.execute("SELECT * FROM \"_sql_stat4\";")
+box.execute("SELECT * FROM \"_sql_stat1\";")
 
 -- Dropping an index.
-box.sql.execute("DROP INDEX i1 ON t2;")
+box.execute("DROP INDEX i1 ON t2;")
 
 -- Checking the DROP INDEX results.
-box.sql.execute("SELECT * FROM \"_sql_stat4\";")
-box.sql.execute("SELECT * FROM \"_sql_stat1\";")
+box.execute("SELECT * FROM \"_sql_stat4\";")
+box.execute("SELECT * FROM \"_sql_stat1\";")
 
 --Cleaning up.
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
diff --git a/test/sql/tokenizer.result b/test/sql/tokenizer.result
index 95063b5..47fb9d4 100644
--- a/test/sql/tokenizer.result
+++ b/test/sql/tokenizer.result
@@ -7,8 +7,9 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 sql_tokenizer = require('sql_tokenizer')
 ---
diff --git a/test/sql/tokenizer.test.lua b/test/sql/tokenizer.test.lua
index 4a4e289..3f5dd12 100644
--- a/test/sql/tokenizer.test.lua
+++ b/test/sql/tokenizer.test.lua
@@ -1,7 +1,7 @@
 env = require('test_run')
 test_run = env.new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 sql_tokenizer = require('sql_tokenizer')
 
diff --git a/test/sql/transition.result b/test/sql/transition.result
index ea110c0..b348730 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -4,188 +4,395 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- create space
-box.sql.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
+box.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
 ---
+- rowcount: 1
 ...
 -- prepare data
-box.sql.execute("INSERT INTO foobar VALUES (1, 'foo')")
+box.execute("INSERT INTO foobar VALUES (1, 'foo')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (2, 'bar')")
+box.execute("INSERT INTO foobar VALUES (2, 'bar')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
+box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
+box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_FOOBAR_1' in space 'FOOBAR'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_FOOBAR_1'' in space ''FOOBAR'''
 ...
 -- simple select
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
----
-- - ['bar', 2, 42, 'awesome']
-...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
----
-- - ['foobar', 1000, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
----
-- - ['bar', 2, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
----
-- []
-...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
----
-- []
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
+  - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows: []
+...
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
----
-- - ['foo', 1, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
----
-- - ['foo', 1, 42, 'awesome']
-...
-box.sql.execute("SELECT count(*) FROM foobar")
----
-- - [3]
-...
-box.sql.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
----
-- - [1]
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
+---
+- metadata:
+  - name: BAR
+    type: TEXT
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foo', 1, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
----
-- - ['bar', 2, 42, 'awesome']
+box.execute("SELECT count(*) FROM foobar")
+---
+- metadata:
+  - name: count(*)
+    type: INTEGER
+  rows:
+  - [3]
+...
+box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
+---
+- metadata:
+  - name: count(*)
+    type: INTEGER
+  rows:
+  - [1]
+...
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
+---
+- metadata:
+  - name: BAR
+    type: BLOB
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['bar', 2, 42, 'awesome']
   - ['foo', 1, 42, 'awesome']
   - ['foobar', 1000, 42, 'awesome']
 ...
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
----
-- - ['foobar', 1000, 42, 'awesome']
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
+---
+- metadata:
+  - name: BAR
+    type: BLOB
+  - name: FOO
+    type: INTEGER
+  - name: '42'
+    type: INTEGER
+  - name: '''awesome'''
+    type: TEXT
+  rows:
+  - ['foobar', 1000, 42, 'awesome']
   - ['foo', 1, 42, 'awesome']
   - ['bar', 2, 42, 'awesome']
 ...
 -- updates
-box.sql.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
+box.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
 ---
+- rowcount: 2
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
+box.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
 ---
-- - [1]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 ---
-- - [1]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
+box.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 ---
-- - [0]
+- metadata:
+  - name: COUNT(*)
+    type: INTEGER
+  rows:
+  - [0]
 ...
 -- multi-index
 -- create space
-box.sql.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
+box.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
+box.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
 ---
+- rowcount: 1
 ...
 -- prepare data
-box.sql.execute("INSERT INTO barfoo VALUES ('foo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('foo', 1)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO barfoo VALUES ('bar', 2)")
+box.execute("INSERT INTO barfoo VALUES ('bar', 2)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
+box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 ---
+- rowcount: 1
 ...
 -- prove barfoo2 was created
-box.sql.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 ---
-- error: Duplicate key exists in unique index 'pk_unnamed_BARFOO_1' in space 'BARFOO'
+- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
+    index ''pk_unnamed_BARFOO_1'' in space ''BARFOO'''
 ...
-box.sql.execute("SELECT foo, bar FROM barfoo")
+box.execute("SELECT foo, bar FROM barfoo")
 ---
-- - [1, 'foo']
+- metadata:
+  - name: FOO
+    type: NUMERIC
+  - name: BAR
+    type: TEXT
+  rows:
+  - [1, 'foo']
   - [2, 'bar']
   - [1000, 'foobar']
 ...
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo==2")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo==2")
 ---
-- - [2, 'bar']
+- metadata:
+  - name: FOO
+    type: NUMERIC
+  - name: BAR
+    type: TEXT
+  rows:
+  - [2, 'bar']
 ...
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE bar=='foobar'")
+box.execute("SELECT foo, bar FROM barfoo WHERE bar=='foobar'")
 ---
-- - [1000, 'foobar']
+- metadata:
+  - name: FOO
+    type: NUMERIC
+  - name: BAR
+    type: TEXT
+  rows:
+  - [1000, 'foobar']
 ...
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
 ---
-- - [2, 'bar']
+- metadata:
+  - name: FOO
+    type: NUMERIC
+  - name: BAR
+    type: TEXT
+  rows:
+  - [2, 'bar']
   - [1000, 'foobar']
 ...
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo<=2")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo<=2")
 ---
-- - [1, 'foo']
+- metadata:
+  - name: FOO
+    type: NUMERIC
+  - name: BAR
+    type: TEXT
+  rows:
+  - [1, 'foo']
   - [2, 'bar']
 ...
 -- cleanup
-box.sql.execute("DROP INDEX barfoo2 ON barfoo")
+box.execute("DROP INDEX barfoo2 ON barfoo")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE foobar")
+box.execute("DROP TABLE foobar")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE barfoo")
+box.execute("DROP TABLE barfoo")
 ---
+- rowcount: 1
 ...
 -- attempt to create a table lacking PRIMARY KEY
-box.sql.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
+box.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
 ---
 - error: PRIMARY KEY missing on table WITHOUT_ROWID_LACKING_PRIMARY_KEY
 ...
 -- create a table with implicit indices (used to SEGFAULT)
-box.sql.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
+box.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE implicit_indices")
+box.execute("DROP TABLE implicit_indices")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/transition.test.lua b/test/sql/transition.test.lua
index 2fbee62..c76ab93 100644
--- a/test/sql/transition.test.lua
+++ b/test/sql/transition.test.lua
@@ -1,70 +1,70 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- create space
-box.sql.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
+box.execute("CREATE TABLE foobar (foo INT PRIMARY KEY, bar TEXT)")
 
 -- prepare data
-box.sql.execute("INSERT INTO foobar VALUES (1, 'foo')")
-box.sql.execute("INSERT INTO foobar VALUES (2, 'bar')")
-box.sql.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
+box.execute("INSERT INTO foobar VALUES (1, 'foo')")
+box.execute("INSERT INTO foobar VALUES (2, 'bar')")
+box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 
-box.sql.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
+box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 
 -- simple select
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
-box.sql.execute("SELECT count(*) FROM foobar")
-box.sql.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
-box.sql.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar LIMIT 2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo=10000")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo>10000")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<2.001")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<=2")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE foo<100")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar WHERE bar='foo'")
+box.execute("SELECT count(*) FROM foobar")
+box.execute("SELECT count(*) FROM foobar WHERE bar='foo'")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar")
+box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar ORDER BY bar DESC")
 
 -- updates
-box.sql.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
-box.sql.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
-box.sql.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("REPLACE INTO foobar VALUES (1, 'cacodaemon')")
+box.execute("SELECT COUNT(*) FROM foobar WHERE foo=1")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
+box.execute("DELETE FROM foobar WHERE bar='cacodaemon'")
+box.execute("SELECT COUNT(*) FROM foobar WHERE bar='cacodaemon'")
 
 -- multi-index
 
 -- create space
-box.sql.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
-box.sql.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
+box.execute("CREATE TABLE barfoo (bar TEXT, foo FLOAT PRIMARY KEY)")
+box.execute("CREATE UNIQUE INDEX barfoo2 ON barfoo(bar)")
 
 -- prepare data
-box.sql.execute("INSERT INTO barfoo VALUES ('foo', 1)")
-box.sql.execute("INSERT INTO barfoo VALUES ('bar', 2)")
-box.sql.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
+box.execute("INSERT INTO barfoo VALUES ('foo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('bar', 2)")
+box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 
 -- prove barfoo2 was created
-box.sql.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
+box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 
-box.sql.execute("SELECT foo, bar FROM barfoo")
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo==2")
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE bar=='foobar'")
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
-box.sql.execute("SELECT foo, bar FROM barfoo WHERE foo<=2")
+box.execute("SELECT foo, bar FROM barfoo")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo==2")
+box.execute("SELECT foo, bar FROM barfoo WHERE bar=='foobar'")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo>=2")
+box.execute("SELECT foo, bar FROM barfoo WHERE foo<=2")
 
 -- cleanup
-box.sql.execute("DROP INDEX barfoo2 ON barfoo")
-box.sql.execute("DROP TABLE foobar")
-box.sql.execute("DROP TABLE barfoo")
+box.execute("DROP INDEX barfoo2 ON barfoo")
+box.execute("DROP TABLE foobar")
+box.execute("DROP TABLE barfoo")
 
 -- attempt to create a table lacking PRIMARY KEY
-box.sql.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
+box.execute("CREATE TABLE without_rowid_lacking_primary_key(x SCALAR)")
 
 -- create a table with implicit indices (used to SEGFAULT)
-box.sql.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
-box.sql.execute("DROP TABLE implicit_indices")
+box.execute("CREATE TABLE implicit_indices(a INT PRIMARY KEY,b INT,c INT,d TEXT UNIQUE)")
+box.execute("DROP TABLE implicit_indices")
diff --git a/test/sql/transitive-transactions.result b/test/sql/transitive-transactions.result
index 88685b4..82e0406 100644
--- a/test/sql/transitive-transactions.result
+++ b/test/sql/transitive-transactions.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute("pragma sql_default_engine=\'"..engine.."\'")
+box.execute("pragma sql_default_engine=\'"..engine.."\'")
 ---
+- rowcount: 0
 ...
 test_run:cmd("setopt delimiter ';'")
 ---
@@ -15,28 +16,30 @@ test_run:cmd("setopt delimiter ';'")
 -- between SQL and Lua. In particular, make sure that deferred foreign keys
 -- violations are passed correctly.
 --
-box.begin() box.sql.execute('COMMIT');
+box.begin() box.execute('COMMIT');
 ---
 ...
-box.begin() box.sql.execute('ROLLBACK');
+box.begin() box.execute('ROLLBACK');
 ---
 ...
-box.sql.execute('START TRANSACTION;') box.commit();
+box.execute('START TRANSACTION;') box.commit();
 ---
 ...
-box.sql.execute('START TRANSACTION;') box.rollback();
+box.execute('START TRANSACTION;') box.rollback();
 ---
 ...
-box.sql.execute('CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);');
+box.execute('CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);');
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y) DEFERRABLE INITIALLY DEFERRED);');
+box.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y) DEFERRABLE INITIALLY DEFERRED);');
 ---
+- rowcount: 1
 ...
 fk_violation_1 = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
-    box.sql.execute('COMMIT;')
+    box.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('COMMIT;')
 end;
 ---
 ...
@@ -49,8 +52,8 @@ box.space.CHILD:select();
 - []
 ...
 fk_violation_2 = function()
-    box.sql.execute('START TRANSACTION;')
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('START TRANSACTION;')
+    box.execute('INSERT INTO child VALUES (1, 1);')
     box.commit()
 end;
 ---
@@ -65,8 +68,8 @@ box.space.CHILD:select();
 ...
 fk_violation_3 = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
-    box.sql.execute('INSERT INTO parent VALUES (1, 1);')
+    box.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('INSERT INTO parent VALUES (1, 1);')
     box.commit()
 end;
 ---
@@ -84,20 +87,20 @@ box.space.PARENT:select();
 ...
 -- Make sure that 'PRAGMA defer_foreign_keys' works.
 --
-box.sql.execute('DROP TABLE child;')
-box.sql.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y))')
+box.execute('DROP TABLE child;')
+box.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y))')
 
 fk_defer = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 2);')
-    box.sql.execute('INSERT INTO parent VALUES (2, 2);')
+    box.execute('INSERT INTO child VALUES (1, 2);')
+    box.execute('INSERT INTO parent VALUES (2, 2);')
     box.commit()
 end;
 ---
 ...
 fk_defer();
 ---
-- error: FOREIGN KEY constraint failed
+- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
 ...
 box.space.CHILD:select();
 ---
@@ -107,7 +110,7 @@ box.space.PARENT:select();
 ---
 - - [1, 1]
 ...
-box.sql.execute('PRAGMA defer_foreign_keys = 1;')
+box.execute('PRAGMA defer_foreign_keys = 1;')
 box.rollback()
 fk_defer();
 ---
@@ -122,11 +125,12 @@ box.space.PARENT:select();
   - [2, 2]
 ...
 -- Cleanup
-box.sql.execute('PRAGMA defer_foreign_keys = 0;')
+box.execute('PRAGMA defer_foreign_keys = 0;')
 
-box.sql.execute('DROP TABLE child;');
+box.execute('DROP TABLE child;');
 ---
 ...
-box.sql.execute('DROP TABLE parent;');
+box.execute('DROP TABLE parent;');
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/transitive-transactions.test.lua b/test/sql/transitive-transactions.test.lua
index 5127202..97b06e5 100644
--- a/test/sql/transitive-transactions.test.lua
+++ b/test/sql/transitive-transactions.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute("pragma sql_default_engine=\'"..engine.."\'")
+box.execute("pragma sql_default_engine=\'"..engine.."\'")
 test_run:cmd("setopt delimiter ';'")
 
 -- These tests are aimed at checking transitive transactions
@@ -8,25 +8,25 @@ test_run:cmd("setopt delimiter ';'")
 -- violations are passed correctly.
 --
 
-box.begin() box.sql.execute('COMMIT');
-box.begin() box.sql.execute('ROLLBACK');
-box.sql.execute('START TRANSACTION;') box.commit();
-box.sql.execute('START TRANSACTION;') box.rollback();
+box.begin() box.execute('COMMIT');
+box.begin() box.execute('ROLLBACK');
+box.execute('START TRANSACTION;') box.commit();
+box.execute('START TRANSACTION;') box.rollback();
 
-box.sql.execute('CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);');
-box.sql.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y) DEFERRABLE INITIALLY DEFERRED);');
+box.execute('CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);');
+box.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y) DEFERRABLE INITIALLY DEFERRED);');
 
 fk_violation_1 = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
-    box.sql.execute('COMMIT;')
+    box.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('COMMIT;')
 end;
 fk_violation_1();
 box.space.CHILD:select();
 
 fk_violation_2 = function()
-    box.sql.execute('START TRANSACTION;')
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('START TRANSACTION;')
+    box.execute('INSERT INTO child VALUES (1, 1);')
     box.commit()
 end;
 fk_violation_2();
@@ -34,8 +34,8 @@ box.space.CHILD:select();
 
 fk_violation_3 = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 1);')
-    box.sql.execute('INSERT INTO parent VALUES (1, 1);')
+    box.execute('INSERT INTO child VALUES (1, 1);')
+    box.execute('INSERT INTO parent VALUES (1, 1);')
     box.commit()
 end;
 fk_violation_3();
@@ -44,26 +44,26 @@ box.space.PARENT:select();
 
 -- Make sure that 'PRAGMA defer_foreign_keys' works.
 --
-box.sql.execute('DROP TABLE child;')
-box.sql.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y))')
+box.execute('DROP TABLE child;')
+box.execute('CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y))')
 
 fk_defer = function()
     box.begin()
-    box.sql.execute('INSERT INTO child VALUES (1, 2);')
-    box.sql.execute('INSERT INTO parent VALUES (2, 2);')
+    box.execute('INSERT INTO child VALUES (1, 2);')
+    box.execute('INSERT INTO parent VALUES (2, 2);')
     box.commit()
 end;
 fk_defer();
 box.space.CHILD:select();
 box.space.PARENT:select();
-box.sql.execute('PRAGMA defer_foreign_keys = 1;')
+box.execute('PRAGMA defer_foreign_keys = 1;')
 box.rollback()
 fk_defer();
 box.space.CHILD:select();
 box.space.PARENT:select();
 
-box.sql.execute('PRAGMA defer_foreign_keys = 0;')
+box.execute('PRAGMA defer_foreign_keys = 0;')
 
 -- Cleanup
-box.sql.execute('DROP TABLE child;');
-box.sql.execute('DROP TABLE parent;');
+box.execute('DROP TABLE child;');
+box.execute('DROP TABLE parent;');
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 826e998..3f0f388 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -7,8 +7,9 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Get invariant part of the tuple; name and opts don't change.
  function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.opts}) end return r end
@@ -17,14 +18,17 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 --
 -- gh-3273: Move Triggers to server
 --
-box.sql.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t2(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t2(x INTEGER PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
+box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
 ---
+- rowcount: 1
 ...
 immutable_part(box.space._trigger:select())
 ---
@@ -64,18 +68,21 @@ immutable_part(box.space._trigger:select())
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1);
         END; '}
 ...
-box.sql.execute("DROP TABLE T1;")
+box.execute("DROP TABLE T1;")
 ---
+- rowcount: 1
 ...
 immutable_part(box.space._trigger:select())
 ---
 - []
 ...
-box.sql.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
+box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
 ---
+- rowcount: 1
 ...
 immutable_part(box.space._trigger:select())
 ---
@@ -87,15 +94,21 @@ space_id = box.space._space.index["name"]:get('T1').id
 ---
 ...
 -- Test, didn't trigger t1t degrade.
-box.sql.execute("INSERT INTO t1 VALUES(1);")
+box.execute("INSERT INTO t1 VALUES(1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t2;")
+box.execute("SELECT * FROM t2;")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 ---
+- rowcount: 1
 ...
 -- Test triggers.
 tuple = {"T2T", space_id, {sql = "CREATE TRIGGER t2t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(2); END;"}}
@@ -122,21 +135,28 @@ immutable_part(box.space._trigger:select())
     - {'sql': 'CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3);
         END;'}
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2);")
+box.execute("INSERT INTO t1 VALUES(2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t2;")
+box.execute("SELECT * FROM t2;")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
   - [2]
   - [3]
 ...
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 ---
+- rowcount: 3
 ...
 -- Test t1t after t2t and t3t drop.
-box.sql.execute("DROP TRIGGER T2T;")
+box.execute("DROP TRIGGER T2T;")
 ---
+- rowcount: 1
 ...
 _ = box.space._trigger:delete("T3T")
 ---
@@ -147,22 +167,30 @@ immutable_part(box.space._trigger:select())
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1);
         END; '}
 ...
-box.sql.execute("INSERT INTO t1 VALUES(3);")
+box.execute("INSERT INTO t1 VALUES(3);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t2;")
+box.execute("SELECT * FROM t2;")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("DELETE FROM t2;")
+box.execute("DELETE FROM t2;")
 ---
+- rowcount: 1
 ...
 -- Insert new SQL t2t and t3t.
-box.sql.execute([[CREATE TRIGGER t2t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(2); END; ]])
+box.execute([[CREATE TRIGGER t2t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(2); END; ]])
 ---
+- rowcount: 1
 ...
-box.sql.execute([[CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3); END; ]])
+box.execute([[CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3); END; ]])
 ---
+- rowcount: 1
 ...
 immutable_part(box.space._trigger:select())
 ---
@@ -176,29 +204,37 @@ immutable_part(box.space._trigger:select())
     - {'sql': 'CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3);
         END; '}
 ...
-box.sql.execute("INSERT INTO t1 VALUES(4);")
+box.execute("INSERT INTO t1 VALUES(4);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t2;")
+box.execute("SELECT * FROM t2;")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
   - [2]
   - [3]
 ...
 -- Clean up.
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
+- rowcount: 1
 ...
 immutable_part(box.space._trigger:select())
 ---
 - []
 ...
 -- Test target tables restricts.
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
 ---
+- rowcount: 1
 ...
 space_id = box.space.T1.id
 ---
@@ -210,8 +246,9 @@ box.space._trigger:insert(tuple)
 ---
 - error: 'SQL error: cannot create INSTEAD OF trigger on space: T1'
 ...
-box.sql.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
+box.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
 ---
+- rowcount: 1
 ...
 space_id = box.space.V1.id
 ---
@@ -240,165 +277,216 @@ box.space._trigger:insert(tuple)
 ---
 - error: 'SQL error: cannot create trigger on system table'
 ...
-box.sql.execute("DROP VIEW V1;")
+box.execute("DROP VIEW V1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE T1;")
+box.execute("DROP TABLE T1;")
 ---
+- rowcount: 1
 ...
 --
 -- gh-3531: Assertion with trigger and two storage engines
 --
 -- Case 1: Src 'vinyl' table; Dst 'memtx' table
-box.sql.execute("PRAGMA sql_default_engine ('vinyl');")
+box.execute("PRAGMA sql_default_engine ('vinyl');")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
+box.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
+box.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("PRAGMA sql_default_engine('memtx');")
+box.execute("PRAGMA sql_default_engine('memtx');")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
+box.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO m VALUES (0, '0');")
+box.execute("INSERT INTO m VALUES (0, '0');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO n VALUES (0, '',null);")
+box.execute("INSERT INTO n VALUES (0, '',null);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';")
+box.execute("UPDATE m SET s1 = 'The Rain In Spain';")
 ---
 - error: A multi-statement transaction can not use multiple storage engines
 ...
 -- ANALYZE operates with _sql_stat{1,4} tables should work
-box.sql.execute("ANALYZE m;")
+box.execute("ANALYZE m;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("DROP TABLE m;")
+box.execute("DROP TABLE m;")
 ---
+- rowcount: 5
 ...
-box.sql.execute("DROP TABLE n;")
+box.execute("DROP TABLE n;")
 ---
+- rowcount: 1
 ...
 -- Case 2: Src 'memtx' table; Dst 'vinyl' table
-box.sql.execute("PRAGMA sql_default_engine ('memtx');")
+box.execute("PRAGMA sql_default_engine ('memtx');")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
+box.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
+box.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("PRAGMA sql_default_engine('vinyl');")
+box.execute("PRAGMA sql_default_engine('vinyl');")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
+box.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO m VALUES (0, '0');")
+box.execute("INSERT INTO m VALUES (0, '0');")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO n VALUES (0, '',null);")
+box.execute("INSERT INTO n VALUES (0, '',null);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';")
+box.execute("UPDATE m SET s1 = 'The Rain In Spain';")
 ---
 - error: A multi-statement transaction can not use multiple storage engines
 ...
 -- ANALYZE operates with _sql_stat{1,4} tables should work
-box.sql.execute("ANALYZE n;")
+box.execute("ANALYZE n;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("DROP TABLE m;")
+box.execute("DROP TABLE m;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE n;")
+box.execute("DROP TABLE n;")
 ---
+- rowcount: 5
 ...
 -- Test SQL Transaction with LUA
-box.sql.execute("PRAGMA sql_default_engine ('memtx');")
+box.execute("PRAGMA sql_default_engine ('memtx');")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE test (id INT PRIMARY KEY)")
+box.execute("CREATE TABLE test (id INT PRIMARY KEY)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("PRAGMA sql_default_engine='vinyl'")
+box.execute("PRAGMA sql_default_engine='vinyl'")
 ---
+- rowcount: 0
 ...
-box.sql.execute("CREATE TABLE test2 (id INT PRIMARY KEY)")
+box.execute("CREATE TABLE test2 (id INT PRIMARY KEY)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO test2 VALUES (2)")
+box.execute("INSERT INTO test2 VALUES (2)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("START TRANSACTION")
+box.execute("START TRANSACTION")
 ---
+- rowcount: 0
 ...
-box.sql.execute("INSERT INTO test VALUES (1)")
+box.execute("INSERT INTO test VALUES (1)")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM test2")
+box.execute("SELECT * FROM test2")
 ---
 - error: A multi-statement transaction can not use multiple storage engines
 ...
-box.sql.execute("ROLLBACK;")
+box.execute("ROLLBACK;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("DROP TABLE test;")
+box.execute("DROP TABLE test;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE test2;")
+box.execute("DROP TABLE test2;")
 ---
+- rowcount: 1
 ...
 --
 -- gh-3536: Some triggers cause error messages and/or half-finished updates
 --
-box.sql.execute("CREATE TABLE t (s1 INT, s2 INT, s3 INT, s4 INT PRIMARY KEY);")
+box.execute("CREATE TABLE t (s1 INT, s2 INT, s3 INT, s4 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v AS SELECT s1, s2 FROM t;")
+box.execute("CREATE VIEW v AS SELECT s1, s2 FROM t;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER tv INSTEAD OF UPDATE ON v BEGIN UPDATE t SET s3 = new.s1 WHERE s1 = old.s1; END;")
+box.execute("CREATE TRIGGER tv INSTEAD OF UPDATE ON v BEGIN UPDATE t SET s3 = new.s1 WHERE s1 = old.s1; END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t VALUES (1,1,1,1);")
+box.execute("INSERT INTO t VALUES (1,1,1,1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("UPDATE v SET s2 = s1 + 1;")
+box.execute("UPDATE v SET s2 = s1 + 1;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("UPDATE v SET s1 = s1 + 5;")
+box.execute("UPDATE v SET s1 = s1 + 5;")
 ---
+- rowcount: 0
 ...
-box.sql.execute("SELECT * FROM t;")
+box.execute("SELECT * FROM t;")
 ---
-- - [1, 1, 6, 1]
+- metadata:
+  - name: S1
+    type: INTEGER
+  - name: S2
+    type: INTEGER
+  - name: S3
+    type: INTEGER
+  - name: S4
+    type: INTEGER
+  rows:
+  - [1, 1, 6, 1]
 ...
-box.sql.execute("DROP VIEW v;")
+box.execute("DROP VIEW v;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t;")
+box.execute("DROP TABLE t;")
 ---
+- rowcount: 1
 ...
 --
 -- gh-3653: Dissallow bindings for DDL
 --
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT);")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT);")
 ---
+- rowcount: 1
 ...
 space_id = box.space.T1.id
 ---
 ...
-box.sql.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
+box.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
 ---
-- error: bindings are not allowed in DDL
+- error: 'Error during execution of VDBE byte-code: bindings are not allowed in DDL'
 ...
 tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;]]}}
 ---
@@ -407,6 +495,7 @@ box.space._trigger:insert(tuple)
 ---
 - error: bindings are not allowed in DDL
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/triggers.test.lua b/test/sql/triggers.test.lua
index b22b4f9..8bd6633 100644
--- a/test/sql/triggers.test.lua
+++ b/test/sql/triggers.test.lua
@@ -1,7 +1,7 @@
 env = require('test_run')
 test_run = env.new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Get invariant part of the tuple; name and opts don't change.
  function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.opts}) end return r end
@@ -10,9 +10,9 @@ box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
 -- gh-3273: Move Triggers to server
 --
 
-box.sql.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
-box.sql.execute("CREATE TABLE t2(x INTEGER PRIMARY KEY);")
-box.sql.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
+box.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t2(x INTEGER PRIMARY KEY);")
+box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
 immutable_part(box.space._trigger:select())
 
 space_id = box.space._space.index["name"]:get('T1').id
@@ -28,19 +28,19 @@ tuple = {"T2T", box.space.T1.id + 1, {sql = "CREATE TRIGGER t2t AFTER INSERT ON
 box.space._trigger:insert(tuple)
 immutable_part(box.space._trigger:select())
 
-box.sql.execute("DROP TABLE T1;")
+box.execute("DROP TABLE T1;")
 immutable_part(box.space._trigger:select())
 
-box.sql.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
-box.sql.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
+box.execute("CREATE TABLE t1(x INTEGER PRIMARY KEY);")
+box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(1); END; ]])
 immutable_part(box.space._trigger:select())
 
 space_id = box.space._space.index["name"]:get('T1').id
 
 -- Test, didn't trigger t1t degrade.
-box.sql.execute("INSERT INTO t1 VALUES(1);")
-box.sql.execute("SELECT * FROM t2;")
-box.sql.execute("DELETE FROM t2;")
+box.execute("INSERT INTO t1 VALUES(1);")
+box.execute("SELECT * FROM t2;")
+box.execute("DELETE FROM t2;")
 
 
 -- Test triggers.
@@ -49,38 +49,38 @@ _ = box.space._trigger:insert(tuple)
 tuple = {"T3T", space_id, {sql = "CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3); END;"}}
 _ = box.space._trigger:insert(tuple)
 immutable_part(box.space._trigger:select())
-box.sql.execute("INSERT INTO t1 VALUES(2);")
-box.sql.execute("SELECT * FROM t2;")
-box.sql.execute("DELETE FROM t2;")
+box.execute("INSERT INTO t1 VALUES(2);")
+box.execute("SELECT * FROM t2;")
+box.execute("DELETE FROM t2;")
 
 -- Test t1t after t2t and t3t drop.
-box.sql.execute("DROP TRIGGER T2T;")
+box.execute("DROP TRIGGER T2T;")
 _ = box.space._trigger:delete("T3T")
 immutable_part(box.space._trigger:select())
-box.sql.execute("INSERT INTO t1 VALUES(3);")
-box.sql.execute("SELECT * FROM t2;")
-box.sql.execute("DELETE FROM t2;")
+box.execute("INSERT INTO t1 VALUES(3);")
+box.execute("SELECT * FROM t2;")
+box.execute("DELETE FROM t2;")
 
 -- Insert new SQL t2t and t3t.
-box.sql.execute([[CREATE TRIGGER t2t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(2); END; ]])
-box.sql.execute([[CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3); END; ]])
+box.execute([[CREATE TRIGGER t2t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(2); END; ]])
+box.execute([[CREATE TRIGGER t3t AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(3); END; ]])
 immutable_part(box.space._trigger:select())
-box.sql.execute("INSERT INTO t1 VALUES(4);")
-box.sql.execute("SELECT * FROM t2;")
+box.execute("INSERT INTO t1 VALUES(4);")
+box.execute("SELECT * FROM t2;")
 
 -- Clean up.
-box.sql.execute("DROP TABLE t1;")
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t2;")
 immutable_part(box.space._trigger:select())
 
 -- Test target tables restricts.
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
 space_id = box.space.T1.id
 
 tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on t1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
 box.space._trigger:insert(tuple)
 
-box.sql.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
+box.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
 space_id = box.space.V1.id
 
 tuple = {"V1T", space_id, {sql = [[create trigger v1t before update on v1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
@@ -93,75 +93,75 @@ space_id =  box.space._sql_stat1.id
 tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on "_sql_stat1" for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
 box.space._trigger:insert(tuple)
 
-box.sql.execute("DROP VIEW V1;")
-box.sql.execute("DROP TABLE T1;")
+box.execute("DROP VIEW V1;")
+box.execute("DROP TABLE T1;")
 
 --
 -- gh-3531: Assertion with trigger and two storage engines
 --
 -- Case 1: Src 'vinyl' table; Dst 'memtx' table
-box.sql.execute("PRAGMA sql_default_engine ('vinyl');")
-box.sql.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
-box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
-box.sql.execute("PRAGMA sql_default_engine('memtx');")
-box.sql.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
-box.sql.execute("INSERT INTO m VALUES (0, '0');")
-box.sql.execute("INSERT INTO n VALUES (0, '',null);")
-box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';")
+box.execute("PRAGMA sql_default_engine ('vinyl');")
+box.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
+box.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
+box.execute("PRAGMA sql_default_engine('memtx');")
+box.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
+box.execute("INSERT INTO m VALUES (0, '0');")
+box.execute("INSERT INTO n VALUES (0, '',null);")
+box.execute("UPDATE m SET s1 = 'The Rain In Spain';")
 
 -- ANALYZE operates with _sql_stat{1,4} tables should work
-box.sql.execute("ANALYZE m;")
-box.sql.execute("DROP TABLE m;")
-box.sql.execute("DROP TABLE n;")
+box.execute("ANALYZE m;")
+box.execute("DROP TABLE m;")
+box.execute("DROP TABLE n;")
 
 
 -- Case 2: Src 'memtx' table; Dst 'vinyl' table
-box.sql.execute("PRAGMA sql_default_engine ('memtx');")
-box.sql.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
-box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
-box.sql.execute("PRAGMA sql_default_engine('vinyl');")
-box.sql.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
-box.sql.execute("INSERT INTO m VALUES (0, '0');")
-box.sql.execute("INSERT INTO n VALUES (0, '',null);")
-box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';")
+box.execute("PRAGMA sql_default_engine ('memtx');")
+box.execute("CREATE TABLE m (s0 INT PRIMARY KEY, s1 TEXT UNIQUE);")
+box.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = 'now'; END;")
+box.execute("PRAGMA sql_default_engine('vinyl');")
+box.execute("CREATE TABLE n (s0 INT PRIMARY KEY, s1 TEXT UNIQUE, s2 REAL);")
+box.execute("INSERT INTO m VALUES (0, '0');")
+box.execute("INSERT INTO n VALUES (0, '',null);")
+box.execute("UPDATE m SET s1 = 'The Rain In Spain';")
 
 -- ANALYZE operates with _sql_stat{1,4} tables should work
-box.sql.execute("ANALYZE n;")
-box.sql.execute("DROP TABLE m;")
-box.sql.execute("DROP TABLE n;")
+box.execute("ANALYZE n;")
+box.execute("DROP TABLE m;")
+box.execute("DROP TABLE n;")
 
 -- Test SQL Transaction with LUA
-box.sql.execute("PRAGMA sql_default_engine ('memtx');")
-box.sql.execute("CREATE TABLE test (id INT PRIMARY KEY)")
-box.sql.execute("PRAGMA sql_default_engine='vinyl'")
-box.sql.execute("CREATE TABLE test2 (id INT PRIMARY KEY)")
-box.sql.execute("INSERT INTO test2 VALUES (2)")
-box.sql.execute("START TRANSACTION")
-box.sql.execute("INSERT INTO test VALUES (1)")
-box.sql.execute("SELECT * FROM test2")
-box.sql.execute("ROLLBACK;")
-box.sql.execute("DROP TABLE test;")
-box.sql.execute("DROP TABLE test2;")
+box.execute("PRAGMA sql_default_engine ('memtx');")
+box.execute("CREATE TABLE test (id INT PRIMARY KEY)")
+box.execute("PRAGMA sql_default_engine='vinyl'")
+box.execute("CREATE TABLE test2 (id INT PRIMARY KEY)")
+box.execute("INSERT INTO test2 VALUES (2)")
+box.execute("START TRANSACTION")
+box.execute("INSERT INTO test VALUES (1)")
+box.execute("SELECT * FROM test2")
+box.execute("ROLLBACK;")
+box.execute("DROP TABLE test;")
+box.execute("DROP TABLE test2;")
 
 --
 -- gh-3536: Some triggers cause error messages and/or half-finished updates
 --
-box.sql.execute("CREATE TABLE t (s1 INT, s2 INT, s3 INT, s4 INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW v AS SELECT s1, s2 FROM t;")
-box.sql.execute("CREATE TRIGGER tv INSTEAD OF UPDATE ON v BEGIN UPDATE t SET s3 = new.s1 WHERE s1 = old.s1; END;")
-box.sql.execute("INSERT INTO t VALUES (1,1,1,1);")
-box.sql.execute("UPDATE v SET s2 = s1 + 1;")
-box.sql.execute("UPDATE v SET s1 = s1 + 5;")
-box.sql.execute("SELECT * FROM t;")
-box.sql.execute("DROP VIEW v;")
-box.sql.execute("DROP TABLE t;")
+box.execute("CREATE TABLE t (s1 INT, s2 INT, s3 INT, s4 INT PRIMARY KEY);")
+box.execute("CREATE VIEW v AS SELECT s1, s2 FROM t;")
+box.execute("CREATE TRIGGER tv INSTEAD OF UPDATE ON v BEGIN UPDATE t SET s3 = new.s1 WHERE s1 = old.s1; END;")
+box.execute("INSERT INTO t VALUES (1,1,1,1);")
+box.execute("UPDATE v SET s2 = s1 + 1;")
+box.execute("UPDATE v SET s1 = s1 + 5;")
+box.execute("SELECT * FROM t;")
+box.execute("DROP VIEW v;")
+box.execute("DROP TABLE t;")
 
 --
 -- gh-3653: Dissallow bindings for DDL
 --
-box.sql.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT);")
+box.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT);")
 space_id = box.space.T1.id
-box.sql.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
+box.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
 tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;]]}}
 box.space._trigger:insert(tuple)
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
diff --git a/test/sql/types.result b/test/sql/types.result
index dd9011b..a0a1ba9 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -6,33 +6,34 @@ test_run = env.new()
 ...
 -- gh-3018: typeless columns are prohibited.
 --
-box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (id PRIMARY KEY);")
 ---
 - error: Keyword 'PRIMARY' is reserved. Please use double quotes if 'PRIMARY' is an
     identifier.
 ...
-box.sql.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
 ---
 - error: Syntax error near ','
 ...
-box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
 ---
 - error: Keyword 'PRIMARY' is reserved. Please use double quotes if 'PRIMARY' is an
     identifier.
 ...
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
 ---
 - error: Syntax error near ')'
 ...
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
 ---
 - error: Keyword 'UNIQUE' is reserved. Please use double quotes if 'UNIQUE' is an
     identifier.
 ...
 -- gh-3104: real type is stored in space format.
 --
-box.sql.execute("CREATE TABLE t1 (id TEXT PRIMARY KEY, a REAL, b INT, c TEXT, d SCALAR);")
+box.execute("CREATE TABLE t1 (id TEXT PRIMARY KEY, a REAL, b INT, c TEXT, d SCALAR);")
 ---
+- rowcount: 1
 ...
 box.space.T1:format()
 ---
@@ -42,8 +43,9 @@ box.space.T1:format()
   {'type': 'string', 'nullable_action': 'none', 'name': 'C', 'is_nullable': true},
   {'type': 'scalar', 'nullable_action': 'none', 'name': 'D', 'is_nullable': true}]
 ...
-box.sql.execute("CREATE VIEW v1 AS SELECT b + a, b - a FROM t1;")
+box.execute("CREATE VIEW v1 AS SELECT b + a, b - a FROM t1;")
 ---
+- rowcount: 1
 ...
 box.space.V1:format()
 ---
@@ -52,17 +54,21 @@ box.space.V1:format()
 ...
 -- gh-2494: index's part also features correct declared type.
 --
-box.sql.execute("CREATE INDEX i1 ON t1 (a);")
+box.execute("CREATE INDEX i1 ON t1 (a);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i2 ON t1 (b);")
+box.execute("CREATE INDEX i2 ON t1 (b);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i3 ON t1 (c);")
+box.execute("CREATE INDEX i3 ON t1 (c);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE INDEX i4 ON t1 (id, c, b, a, d);")
+box.execute("CREATE INDEX i4 ON t1 (id, c, b, a, d);")
 ---
+- rowcount: 1
 ...
 box.space.T1.index.I1.parts
 ---
@@ -100,11 +106,13 @@ box.space.T1.index.I4.parts
     is_nullable: true
     fieldno: 5
 ...
-box.sql.execute("DROP VIEW v1;")
+box.execute("DROP VIEW v1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP TABLE t1;")
 ---
+- rowcount: 1
 ...
 -- gh-3906: data of type BOOL is displayed as should
 -- during SQL SELECT.
@@ -126,9 +134,15 @@ sp:insert({2, false})
 ---
 - [2, false]
 ...
-box.sql.execute("SELECT * FROM test")
+box.execute("SELECT * FROM test")
 ---
-- - [1, 1]
+- metadata:
+  - name: ID
+    type: UNKNOWN
+  - name: A
+    type: BOOLEAN
+  rows:
+  - [1, 1]
   - [2, 0]
 ...
 sp:drop()
@@ -136,83 +150,99 @@ sp:drop()
 ...
 -- gh-3544: concatenation operator accepts only TEXT and BLOB.
 --
-box.sql.execute("SELECT 'abc' || 1;")
+box.execute("SELECT 'abc' || 1;")
 ---
 - error: 'Inconsistent types: expected TEXT or BLOB got INTEGER'
 ...
-box.sql.execute("SELECT 'abc' || 1.123;")
+box.execute("SELECT 'abc' || 1.123;")
 ---
 - error: 'Inconsistent types: expected TEXT or BLOB got REAL'
 ...
-box.sql.execute("SELECT 1 || 'abc';")
+box.execute("SELECT 1 || 'abc';")
 ---
 - error: 'Inconsistent types: expected TEXT or BLOB got INTEGER'
 ...
-box.sql.execute("SELECT 1.123 || 'abc';")
+box.execute("SELECT 1.123 || 'abc';")
 ---
 - error: 'Inconsistent types: expected TEXT or BLOB got REAL'
 ...
-box.sql.execute("SELECt 'a' || 'b' || 1;")
+box.execute("SELECt 'a' || 'b' || 1;")
 ---
 - error: 'Inconsistent types: expected TEXT or BLOB got INTEGER'
 ...
 -- What is more, they must be of the same type.
 --
-box.sql.execute("SELECT 'abc' || randomblob(5);")
+box.execute("SELECT 'abc' || randomblob(5);")
 ---
 - error: 'Inconsistent types: expected TEXT got BLOB'
 ...
-box.sql.execute("SELECT randomblob(5) || 'x';")
+box.execute("SELECT randomblob(5) || 'x';")
 ---
 - error: 'Inconsistent types: expected BLOB got TEXT'
 ...
 -- Result of BLOBs concatenation must be BLOB.
 --
-box.sql.execute("VALUES (TYPEOF(randomblob(5) || zeroblob(5)));")
+box.execute("VALUES (TYPEOF(randomblob(5) || zeroblob(5)));")
 ---
-- - ['blob']
+- metadata:
+  - name: column1
+    type: TEXT
+  rows:
+  - ['blob']
 ...
 -- gh-3954: LIKE accepts only arguments of type TEXT and NULLs.
 --
-box.sql.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (randomblob(5));")
+box.execute("INSERT INTO t1 VALUES (randomblob(5));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
+box.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
 ---
 - error: 'Inconsistent types: expected TEXT got BLOB'
 ...
-box.sql.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
+box.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
 ---
 - error: 'Inconsistent types: expected TEXT got BLOB'
 ...
-box.sql.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
+box.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
 ---
 - error: 'Inconsistent types: expected TEXT got BLOB'
 ...
-box.sql.execute("SELECT s LIKE NULL FROM t1;")
+box.execute("SELECT s LIKE NULL FROM t1;")
 ---
-- - [null]
+- metadata:
+  - name: s LIKE NULL
+    type: INTEGER
+  rows:
+  - [null]
 ...
-box.sql.execute("DELETE FROM t1;")
+box.execute("DELETE FROM t1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES (1);")
+box.execute("INSERT INTO t1 VALUES (1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("SELECT * FROM t1 WHERE s LIKE 'int';")
+box.execute("SELECT * FROM t1 WHERE s LIKE 'int';")
 ---
 - error: 'Inconsistent types: expected TEXT got INTEGER'
 ...
-box.sql.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;")
+box.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;")
 ---
 - error: 'Inconsistent types: expected TEXT got INTEGER'
 ...
-box.sql.execute("SELECT NULL LIKE s FROM t1;")
+box.execute("SELECT NULL LIKE s FROM t1;")
 ---
-- - [null]
+- metadata:
+  - name: NULL LIKE s
+    type: INTEGER
+  rows:
+  - [null]
 ...
 box.space.T1:drop()
 ---
diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua
index bb8bf7d..dab5872 100644
--- a/test/sql/types.test.lua
+++ b/test/sql/types.test.lua
@@ -3,32 +3,32 @@ test_run = env.new()
 
 -- gh-3018: typeless columns are prohibited.
 --
-box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY);")
-box.sql.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
-box.sql.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
-box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
+box.execute("CREATE TABLE t1 (id PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (a, id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t1 (id PRIMARY KEY, a INT);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a);")
+box.execute("CREATE TABLE t1 (id INT PRIMARY KEY, a INT, b UNIQUE);")
 
 -- gh-3104: real type is stored in space format.
 --
-box.sql.execute("CREATE TABLE t1 (id TEXT PRIMARY KEY, a REAL, b INT, c TEXT, d SCALAR);")
+box.execute("CREATE TABLE t1 (id TEXT PRIMARY KEY, a REAL, b INT, c TEXT, d SCALAR);")
 box.space.T1:format()
-box.sql.execute("CREATE VIEW v1 AS SELECT b + a, b - a FROM t1;")
+box.execute("CREATE VIEW v1 AS SELECT b + a, b - a FROM t1;")
 box.space.V1:format()
 
 -- gh-2494: index's part also features correct declared type.
 --
-box.sql.execute("CREATE INDEX i1 ON t1 (a);")
-box.sql.execute("CREATE INDEX i2 ON t1 (b);")
-box.sql.execute("CREATE INDEX i3 ON t1 (c);")
-box.sql.execute("CREATE INDEX i4 ON t1 (id, c, b, a, d);")
+box.execute("CREATE INDEX i1 ON t1 (a);")
+box.execute("CREATE INDEX i2 ON t1 (b);")
+box.execute("CREATE INDEX i3 ON t1 (c);")
+box.execute("CREATE INDEX i4 ON t1 (id, c, b, a, d);")
 box.space.T1.index.I1.parts
 box.space.T1.index.I2.parts
 box.space.T1.index.I3.parts
 box.space.T1.index.I4.parts
 
-box.sql.execute("DROP VIEW v1;")
-box.sql.execute("DROP TABLE t1;")
+box.execute("DROP VIEW v1;")
+box.execute("DROP TABLE t1;")
 
 -- gh-3906: data of type BOOL is displayed as should
 -- during SQL SELECT.
@@ -38,35 +38,35 @@ sp = box.schema.space.create("TEST", { format = format } )
 i = sp:create_index('primary', {parts = {1, 'unsigned' }})
 sp:insert({1, true})
 sp:insert({2, false})
-box.sql.execute("SELECT * FROM test")
+box.execute("SELECT * FROM test")
 sp:drop()
 
 -- gh-3544: concatenation operator accepts only TEXT and BLOB.
 --
-box.sql.execute("SELECT 'abc' || 1;")
-box.sql.execute("SELECT 'abc' || 1.123;")
-box.sql.execute("SELECT 1 || 'abc';")
-box.sql.execute("SELECT 1.123 || 'abc';")
-box.sql.execute("SELECt 'a' || 'b' || 1;")
+box.execute("SELECT 'abc' || 1;")
+box.execute("SELECT 'abc' || 1.123;")
+box.execute("SELECT 1 || 'abc';")
+box.execute("SELECT 1.123 || 'abc';")
+box.execute("SELECt 'a' || 'b' || 1;")
 -- What is more, they must be of the same type.
 --
-box.sql.execute("SELECT 'abc' || randomblob(5);")
-box.sql.execute("SELECT randomblob(5) || 'x';")
+box.execute("SELECT 'abc' || randomblob(5);")
+box.execute("SELECT randomblob(5) || 'x';")
 -- Result of BLOBs concatenation must be BLOB.
 --
-box.sql.execute("VALUES (TYPEOF(randomblob(5) || zeroblob(5)));")
+box.execute("VALUES (TYPEOF(randomblob(5) || zeroblob(5)));")
 
 -- gh-3954: LIKE accepts only arguments of type TEXT and NULLs.
 --
-box.sql.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
-box.sql.execute("INSERT INTO t1 VALUES (randomblob(5));")
-box.sql.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
-box.sql.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
-box.sql.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
-box.sql.execute("SELECT s LIKE NULL FROM t1;")
-box.sql.execute("DELETE FROM t1;")
-box.sql.execute("INSERT INTO t1 VALUES (1);")
-box.sql.execute("SELECT * FROM t1 WHERE s LIKE 'int';")
-box.sql.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;")
-box.sql.execute("SELECT NULL LIKE s FROM t1;")
+box.execute("CREATE TABLE t1 (s SCALAR PRIMARY KEY);")
+box.execute("INSERT INTO t1 VALUES (randomblob(5));")
+box.execute("SELECT * FROM t1 WHERE s LIKE 'blob';")
+box.execute("SELECT * FROM t1 WHERE 'blob' LIKE s;")
+box.execute("SELECT * FROM t1 WHERE 'blob' LIKE x'0000';")
+box.execute("SELECT s LIKE NULL FROM t1;")
+box.execute("DELETE FROM t1;")
+box.execute("INSERT INTO t1 VALUES (1);")
+box.execute("SELECT * FROM t1 WHERE s LIKE 'int';")
+box.execute("SELECT * FROM t1 WHERE 'int' LIKE 4;")
+box.execute("SELECT NULL LIKE s FROM t1;")
 box.space.T1:drop()
diff --git a/test/sql/update-with-nested-select.result b/test/sql/update-with-nested-select.result
index e75fe5d..0a93243 100644
--- a/test/sql/update-with-nested-select.result
+++ b/test/sql/update-with-nested-select.result
@@ -4,36 +4,46 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- box.cfg()
 -- create space
-box.sql.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);");
+box.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);");
 ---
+- rowcount: 1
 ...
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);");
+box.execute("INSERT INTO t1 VALUES(1,4,6);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);");
+box.execute("INSERT INTO t1 VALUES(2,5,7);");
 ---
+- rowcount: 1
 ...
 -- Both entries must be updated
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 ---
+- rowcount: 2
 ...
 -- Check
-box.sql.execute("SELECT e FROM t1");
+box.execute("SELECT e FROM t1");
 ---
-- - [7]
+- metadata:
+  - name: E
+    type: INTEGER
+  rows:
+  - [7]
   - [8]
 ...
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 ---
+- rowcount: 1
 ...
 -- Debug
 -- require("console").start()
diff --git a/test/sql/update-with-nested-select.test.lua b/test/sql/update-with-nested-select.test.lua
index 8e508b1..88424fc 100644
--- a/test/sql/update-with-nested-select.test.lua
+++ b/test/sql/update-with-nested-select.test.lua
@@ -1,27 +1,27 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- box.cfg()
 
 -- create space
-box.sql.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);");
+box.execute("CREATE TABLE t1(a integer primary key, b INT UNIQUE, e INT);");
 
 -- Debug
--- box.sql.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
+-- box.execute("PRAGMA vdbe_debug=ON ; INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 
 -- Seed entries
-box.sql.execute("INSERT INTO t1 VALUES(1,4,6);");
-box.sql.execute("INSERT INTO t1 VALUES(2,5,7);");
+box.execute("INSERT INTO t1 VALUES(1,4,6);");
+box.execute("INSERT INTO t1 VALUES(2,5,7);");
 
 -- Both entries must be updated
-box.sql.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
+box.execute("UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);");
 
 -- Check
-box.sql.execute("SELECT e FROM t1");
+box.execute("SELECT e FROM t1");
 
 -- Cleanup
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 
 -- Debug
 -- require("console").start()
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index 02ab9b4..9c999f1 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 work_dir = 'sql/upgrade/1.10/'
 ---
@@ -66,17 +67,21 @@ box.space._index:get({box.space._space.index['name']:get('T1').id, 0})
 - [512, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]]
 ...
 -- test system tables functionality
-box.sql.execute("CREATE TABLE t(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t(x INTEGER PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE t_out(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t_out(x INTEGER PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER t1t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(1); END;")
+box.execute("CREATE TRIGGER t1t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(1); END;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TRIGGER t2t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(2); END;")
+box.execute("CREATE TRIGGER t2t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(2); END;")
 ---
+- rowcount: 1
 ...
 box.space._space.index['name']:get('T')
 ---
@@ -120,8 +125,9 @@ assert(t1t.space_id == box.space.T.id)
 ---
 - true
 ...
-box.sql.execute("INSERT INTO T VALUES(1);")
+box.execute("INSERT INTO T VALUES(1);")
 ---
+- rowcount: 1
 ...
 box.space.T:select()
 ---
@@ -132,19 +138,29 @@ box.space.T_OUT:select()
 - - [1]
   - [2]
 ...
-box.sql.execute("SELECT * FROM T")
+box.execute("SELECT * FROM T")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("SELECT * FROM T")
+box.execute("SELECT * FROM T")
 ---
-- - [1]
+- metadata:
+  - name: X
+    type: INTEGER
+  rows:
+  - [1]
 ...
-box.sql.execute("DROP TABLE T;")
+box.execute("DROP TABLE T;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE T_OUT;")
+box.execute("DROP TABLE T_OUT;")
 ---
+- rowcount: 1
 ...
 test_run:switch('default')
 ---
diff --git a/test/sql/upgrade.test.lua b/test/sql/upgrade.test.lua
index cd4dd3c..036c1a6 100644
--- a/test/sql/upgrade.test.lua
+++ b/test/sql/upgrade.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 work_dir = 'sql/upgrade/1.10/'
 test_run:cmd('create server upgrade with script="sql/upgrade/upgrade.lua", workdir="' .. work_dir .. '"')
@@ -24,10 +24,10 @@ box.space._space.index['name']:get('T1')
 box.space._index:get({box.space._space.index['name']:get('T1').id, 0})
 
 -- test system tables functionality
-box.sql.execute("CREATE TABLE t(x INTEGER PRIMARY KEY);")
-box.sql.execute("CREATE TABLE t_out(x INTEGER PRIMARY KEY);")
-box.sql.execute("CREATE TRIGGER t1t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(1); END;")
-box.sql.execute("CREATE TRIGGER t2t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(2); END;")
+box.execute("CREATE TABLE t(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TABLE t_out(x INTEGER PRIMARY KEY);")
+box.execute("CREATE TRIGGER t1t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(1); END;")
+box.execute("CREATE TRIGGER t2t AFTER INSERT ON t BEGIN INSERT INTO t_out VALUES(2); END;")
 box.space._space.index['name']:get('T')
 box.space._space.index['name']:get('T_OUT')
 t1t = box.space._trigger:get('T1T')
@@ -39,15 +39,15 @@ t2t.opts
 assert(t1t.space_id == t2t.space_id)
 assert(t1t.space_id == box.space.T.id)
 
-box.sql.execute("INSERT INTO T VALUES(1);")
+box.execute("INSERT INTO T VALUES(1);")
 box.space.T:select()
 box.space.T_OUT:select()
-box.sql.execute("SELECT * FROM T")
-box.sql.execute("SELECT * FROM T")
+box.execute("SELECT * FROM T")
+box.execute("SELECT * FROM T")
 
 
-box.sql.execute("DROP TABLE T;")
-box.sql.execute("DROP TABLE T_OUT;")
+box.execute("DROP TABLE T;")
+box.execute("DROP TABLE T_OUT;")
 
 
 test_run:switch('default')
diff --git a/test/sql/view.result b/test/sql/view.result
index e99a9bd..b7efb84 100644
--- a/test/sql/view.result
+++ b/test/sql/view.result
@@ -4,20 +4,23 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 -- Verify that constraints on 'view' option are working.
 -- box.cfg()
 -- Create space and view.
-box.sql.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
+box.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
+box.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
 ---
+- rowcount: 1
 ...
 -- View can't have any indexes.
-box.sql.execute("CREATE INDEX i1 on v1(a);");
+box.execute("CREATE INDEX i1 on v1(a);");
 ---
 - error: views can not be indexed
 ...
@@ -73,7 +76,7 @@ box.schema.create_space('view', {view = true})
 - error: Illegal parameters, unexpected option 'view'
 ...
 -- Space referenced by a view can't be renamed.
-box.sql.execute("ALTER TABLE t1 RENAME TO new_name;")
+box.execute("ALTER TABLE t1 RENAME TO new_name;")
 ---
 - error: 'Can''t modify space ''T1'': can not rename space which is referenced by
     view'
@@ -114,30 +117,36 @@ sp = box.space._space:replace(raw_sp);
 - error: 'Failed to execute SQL statement: SELECT 1;'
 ...
 -- Can't drop space via Lua if at least one view refers to it.
-box.sql.execute('CREATE TABLE t2(id INT PRIMARY KEY);');
+box.execute('CREATE TABLE t2(id INT PRIMARY KEY);');
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE VIEW v2 AS SELECT * FROM t2;');
+box.execute('CREATE VIEW v2 AS SELECT * FROM t2;');
 ---
+- rowcount: 1
 ...
 box.space.T2:drop();
 ---
 - error: 'Can''t drop space ''T2'': other views depend on this space'
 ...
-box.sql.execute('DROP VIEW v2;');
+box.execute('DROP VIEW v2;');
 ---
+- rowcount: 1
 ...
-box.sql.execute('DROP TABLE t2;');
+box.execute('DROP TABLE t2;');
 ---
+- rowcount: 1
 ...
 -- Check that alter transfers reference counter.
-box.sql.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY);");
+box.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY);");
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v2 AS SELECT * FROM t2;");
+box.execute("CREATE VIEW v2 AS SELECT * FROM t2;");
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2;");
+box.execute("DROP TABLE t2;");
 ---
 - error: 'Can''t drop space ''T2'': other views depend on this space'
 ...
@@ -147,99 +156,122 @@ sp = box.space._space:get{box.space.T2.id};
 sp = box.space._space:replace(sp);
 ---
 ...
-box.sql.execute("DROP TABLE t2;");
+box.execute("DROP TABLE t2;");
 ---
 - error: 'Can''t drop space ''T2'': other views depend on this space'
 ...
-box.sql.execute("DROP VIEW v2;");
+box.execute("DROP VIEW v2;");
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t2;");
+box.execute("DROP TABLE t2;");
 ---
+- rowcount: 1
 ...
 -- gh-3849: failed to create VIEW in form of AS VALUES (const);
 --
-box.sql.execute("CREATE VIEW cv AS VALUES(1);")
+box.execute("CREATE VIEW cv AS VALUES(1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW cv1 AS VALUES('k', 1);")
+box.execute("CREATE VIEW cv1 AS VALUES('k', 1);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW cv2 AS VALUES((VALUES((SELECT 1))));")
+box.execute("CREATE VIEW cv2 AS VALUES((VALUES((SELECT 1))));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW cv3 AS VALUES(1+2, 1+2);")
+box.execute("CREATE VIEW cv3 AS VALUES(1+2, 1+2);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP VIEW cv;")
+box.execute("DROP VIEW cv;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP VIEW cv1;")
+box.execute("DROP VIEW cv1;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP VIEW cv2;")
+box.execute("DROP VIEW cv2;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP VIEW cv3;")
+box.execute("DROP VIEW cv3;")
 ---
+- rowcount: 1
 ...
 -- gh-3815: AS VALUES syntax didn't incerement VIEW reference
 -- counter. Moreover, tables within sub-select were not accounted
 -- as well.
 --
-box.sql.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW bv (wombat) AS VALUES ((SELECT 'k' FROM b));")
+box.execute("CREATE VIEW bv (wombat) AS VALUES ((SELECT 'k' FROM b));")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE b;")
+box.execute("DROP TABLE b;")
 ---
 - error: 'Can''t drop space ''B'': other views depend on this space'
 ...
-box.sql.execute("DROP VIEW bv;")
+box.execute("DROP VIEW bv;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE b;")
+box.execute("DROP TABLE b;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW bcv AS SELECT * FROM b WHERE s1 IN (SELECT * FROM c);")
+box.execute("CREATE VIEW bcv AS SELECT * FROM b WHERE s1 IN (SELECT * FROM c);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE c;")
+box.execute("DROP TABLE c;")
 ---
 - error: 'Can''t drop space ''C'': other views depend on this space'
 ...
-box.sql.execute("DROP VIEW bcv;")
+box.execute("DROP VIEW bcv;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE c;")
+box.execute("DROP TABLE c;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW bcv(x, y) AS VALUES((SELECT 'k' FROM b), (VALUES((SELECT 1 FROM b WHERE s1 IN (VALUES((SELECT 1 + c.s1 FROM c)))))))")
+box.execute("CREATE VIEW bcv(x, y) AS VALUES((SELECT 'k' FROM b), (VALUES((SELECT 1 FROM b WHERE s1 IN (VALUES((SELECT 1 + c.s1 FROM c)))))))")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE c;")
+box.execute("DROP TABLE c;")
 ---
 - error: 'Can''t drop space ''C'': other views depend on this space'
 ...
 box.space.BCV:drop()
 ---
 ...
-box.sql.execute("DROP TABLE c;")
+box.execute("DROP TABLE c;")
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE b;")
+box.execute("DROP TABLE b;")
 ---
+- rowcount: 1
 ...
 -- gh-3814: make sure that recovery of view processed without
 -- unexpected errors.
@@ -248,20 +280,25 @@ box.snapshot()
 ---
 - ok
 ...
-box.sql.execute("CREATE TABLE t2 (id INT PRIMARY KEY);")
+box.execute("CREATE TABLE t2 (id INT PRIMARY KEY);")
 ---
+- rowcount: 1
 ...
-box.sql.execute("CREATE VIEW v2 AS SELECT * FROM t2;")
+box.execute("CREATE VIEW v2 AS SELECT * FROM t2;")
 ---
+- rowcount: 1
 ...
 test_run:cmd('restart server default')
-box.sql.execute("DROP TABLE t2;")
+box.execute("DROP TABLE t2;")
 ---
 - error: 'Can''t drop space ''T2'': other views depend on this space'
 ...
-box.sql.execute("SELECT * FROM v2;")
+box.execute("SELECT * FROM v2;")
 ---
-- []
+- metadata:
+  - name: ID
+    type: INTEGER
+  rows: []
 ...
 box.space.V2:drop()
 ---
@@ -270,9 +307,11 @@ box.space.T2:drop()
 ---
 ...
 -- Cleanup
-box.sql.execute("DROP VIEW v1;");
+box.execute("DROP VIEW v1;");
 ---
+- rowcount: 1
 ...
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP TABLE t1;");
 ---
+- rowcount: 1
 ...
diff --git a/test/sql/view.test.lua b/test/sql/view.test.lua
index 592d788..0008056 100644
--- a/test/sql/view.test.lua
+++ b/test/sql/view.test.lua
@@ -1,17 +1,17 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 
 -- Verify that constraints on 'view' option are working.
 
 -- box.cfg()
 
 -- Create space and view.
-box.sql.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
-box.sql.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
+box.execute("CREATE TABLE t1(a INT, b INT, PRIMARY KEY(a, b));");
+box.execute("CREATE VIEW v1 AS SELECT a+b FROM t1;");
 
 -- View can't have any indexes.
-box.sql.execute("CREATE INDEX i1 on v1(a);");
+box.execute("CREATE INDEX i1 on v1(a);");
 v1 = box.space.V1;
 v1:create_index('primary', {parts = {1, 'string'}})
 v1:create_index('secondary', {parts = {1, 'string'}})
@@ -35,7 +35,7 @@ box.space._space:replace(v1);
 box.schema.create_space('view', {view = true})
 
 -- Space referenced by a view can't be renamed.
-box.sql.execute("ALTER TABLE t1 RENAME TO new_name;")
+box.execute("ALTER TABLE t1 RENAME TO new_name;")
 
 -- View can be created via straight insertion into _space.
 sp = box.schema.create_space('test');
@@ -53,70 +53,70 @@ raw_sp[6].sql = 'SELECT 1;';
 sp = box.space._space:replace(raw_sp);
 
 -- Can't drop space via Lua if at least one view refers to it.
-box.sql.execute('CREATE TABLE t2(id INT PRIMARY KEY);');
-box.sql.execute('CREATE VIEW v2 AS SELECT * FROM t2;');
+box.execute('CREATE TABLE t2(id INT PRIMARY KEY);');
+box.execute('CREATE VIEW v2 AS SELECT * FROM t2;');
 box.space.T2:drop();
-box.sql.execute('DROP VIEW v2;');
-box.sql.execute('DROP TABLE t2;');
+box.execute('DROP VIEW v2;');
+box.execute('DROP TABLE t2;');
 
 -- Check that alter transfers reference counter.
-box.sql.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY);");
-box.sql.execute("CREATE VIEW v2 AS SELECT * FROM t2;");
-box.sql.execute("DROP TABLE t2;");
+box.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY);");
+box.execute("CREATE VIEW v2 AS SELECT * FROM t2;");
+box.execute("DROP TABLE t2;");
 sp = box.space._space:get{box.space.T2.id};
 sp = box.space._space:replace(sp);
-box.sql.execute("DROP TABLE t2;");
-box.sql.execute("DROP VIEW v2;");
-box.sql.execute("DROP TABLE t2;");
+box.execute("DROP TABLE t2;");
+box.execute("DROP VIEW v2;");
+box.execute("DROP TABLE t2;");
 
 -- gh-3849: failed to create VIEW in form of AS VALUES (const);
 --
-box.sql.execute("CREATE VIEW cv AS VALUES(1);")
-box.sql.execute("CREATE VIEW cv1 AS VALUES('k', 1);")
-box.sql.execute("CREATE VIEW cv2 AS VALUES((VALUES((SELECT 1))));")
-box.sql.execute("CREATE VIEW cv3 AS VALUES(1+2, 1+2);")
-box.sql.execute("DROP VIEW cv;")
-box.sql.execute("DROP VIEW cv1;")
-box.sql.execute("DROP VIEW cv2;")
-box.sql.execute("DROP VIEW cv3;")
+box.execute("CREATE VIEW cv AS VALUES(1);")
+box.execute("CREATE VIEW cv1 AS VALUES('k', 1);")
+box.execute("CREATE VIEW cv2 AS VALUES((VALUES((SELECT 1))));")
+box.execute("CREATE VIEW cv3 AS VALUES(1+2, 1+2);")
+box.execute("DROP VIEW cv;")
+box.execute("DROP VIEW cv1;")
+box.execute("DROP VIEW cv2;")
+box.execute("DROP VIEW cv3;")
 
 -- gh-3815: AS VALUES syntax didn't incerement VIEW reference
 -- counter. Moreover, tables within sub-select were not accounted
 -- as well.
 --
-box.sql.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW bv (wombat) AS VALUES ((SELECT 'k' FROM b));")
-box.sql.execute("DROP TABLE b;")
-box.sql.execute("DROP VIEW bv;")
-box.sql.execute("DROP TABLE b;")
-
-box.sql.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
-box.sql.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW bcv AS SELECT * FROM b WHERE s1 IN (SELECT * FROM c);")
-box.sql.execute("DROP TABLE c;")
-box.sql.execute("DROP VIEW bcv;")
-box.sql.execute("DROP TABLE c;")
-
-box.sql.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW bcv(x, y) AS VALUES((SELECT 'k' FROM b), (VALUES((SELECT 1 FROM b WHERE s1 IN (VALUES((SELECT 1 + c.s1 FROM c)))))))")
-box.sql.execute("DROP TABLE c;")
+box.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
+box.execute("CREATE VIEW bv (wombat) AS VALUES ((SELECT 'k' FROM b));")
+box.execute("DROP TABLE b;")
+box.execute("DROP VIEW bv;")
+box.execute("DROP TABLE b;")
+
+box.execute("CREATE TABLE b (s1 INT PRIMARY KEY);")
+box.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
+box.execute("CREATE VIEW bcv AS SELECT * FROM b WHERE s1 IN (SELECT * FROM c);")
+box.execute("DROP TABLE c;")
+box.execute("DROP VIEW bcv;")
+box.execute("DROP TABLE c;")
+
+box.execute("CREATE TABLE c (s1 INT PRIMARY KEY);")
+box.execute("CREATE VIEW bcv(x, y) AS VALUES((SELECT 'k' FROM b), (VALUES((SELECT 1 FROM b WHERE s1 IN (VALUES((SELECT 1 + c.s1 FROM c)))))))")
+box.execute("DROP TABLE c;")
 box.space.BCV:drop()
-box.sql.execute("DROP TABLE c;")
-box.sql.execute("DROP TABLE b;")
+box.execute("DROP TABLE c;")
+box.execute("DROP TABLE b;")
 
 -- gh-3814: make sure that recovery of view processed without
 -- unexpected errors.
 --
 box.snapshot()
-box.sql.execute("CREATE TABLE t2 (id INT PRIMARY KEY);")
-box.sql.execute("CREATE VIEW v2 AS SELECT * FROM t2;")
+box.execute("CREATE TABLE t2 (id INT PRIMARY KEY);")
+box.execute("CREATE VIEW v2 AS SELECT * FROM t2;")
 test_run:cmd('restart server default')
 
-box.sql.execute("DROP TABLE t2;")
-box.sql.execute("SELECT * FROM v2;")
+box.execute("DROP TABLE t2;")
+box.execute("SELECT * FROM v2;")
 box.space.V2:drop()
 box.space.T2:drop()
 
 -- Cleanup
-box.sql.execute("DROP VIEW v1;");
-box.sql.execute("DROP TABLE t1;");
+box.execute("DROP VIEW v1;");
+box.execute("DROP TABLE t1;");
diff --git a/test/sql/view_delayed_wal.result b/test/sql/view_delayed_wal.result
index 2e1047a..ff0c70f 100644
--- a/test/sql/view_delayed_wal.result
+++ b/test/sql/view_delayed_wal.result
@@ -4,8 +4,9 @@ test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
 ---
 ...
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
+- rowcount: 0
 ...
 fiber = require('fiber')
 ---
@@ -15,10 +16,11 @@ fiber = require('fiber')
 -- it is impossible to create view on dropped (but not written
 -- into WAL) space.
 --
-box.sql.execute('CREATE TABLE t1(id INT PRIMARY KEY)')
+box.execute('CREATE TABLE t1(id INT PRIMARY KEY)')
 ---
+- rowcount: 1
 ...
-function create_view() box.sql.execute('CREATE VIEW v1 AS SELECT * FROM t1') end
+function create_view() box.execute('CREATE VIEW v1 AS SELECT * FROM t1') end
 ---
 ...
 function drop_index_t1() box.space._index:delete{box.space.T1.id, 0} end
@@ -60,11 +62,13 @@ box.space.V1
 -- dropping view, since view reference counter of space to be
 -- dropped is checked before firing on_commit trigger.
 --
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY)')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY)')
 ---
+- rowcount: 1
 ...
-box.sql.execute('CREATE VIEW view2 AS SELECT * FROM t2')
+box.execute('CREATE VIEW view2 AS SELECT * FROM t2')
 ---
+- rowcount: 1
 ...
 function drop_view() box.space._space:delete{box.space.VIEW2.id} end
 ---
diff --git a/test/sql/view_delayed_wal.test.lua b/test/sql/view_delayed_wal.test.lua
index 62ab7d7..8e73b03 100644
--- a/test/sql/view_delayed_wal.test.lua
+++ b/test/sql/view_delayed_wal.test.lua
@@ -1,6 +1,6 @@
 test_run = require('test_run').new()
 engine = test_run:get_cfg('engine')
-box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
 fiber = require('fiber')
 
 -- View reference counters are incremented before firing
@@ -8,8 +8,8 @@ fiber = require('fiber')
 -- it is impossible to create view on dropped (but not written
 -- into WAL) space.
 --
-box.sql.execute('CREATE TABLE t1(id INT PRIMARY KEY)')
-function create_view() box.sql.execute('CREATE VIEW v1 AS SELECT * FROM t1') end
+box.execute('CREATE TABLE t1(id INT PRIMARY KEY)')
+function create_view() box.execute('CREATE VIEW v1 AS SELECT * FROM t1') end
 function drop_index_t1() box.space._index:delete{box.space.T1.id, 0} end
 function drop_space_t1() box.space._space:delete{box.space.T1.id} end
 box.error.injection.set("ERRINJ_WAL_DELAY", true)
@@ -26,8 +26,8 @@ box.space.V1
 -- dropping view, since view reference counter of space to be
 -- dropped is checked before firing on_commit trigger.
 --
-box.sql.execute('CREATE TABLE t2 (id INT PRIMARY KEY)')
-box.sql.execute('CREATE VIEW view2 AS SELECT * FROM t2')
+box.execute('CREATE TABLE t2 (id INT PRIMARY KEY)')
+box.execute('CREATE VIEW view2 AS SELECT * FROM t2')
 
 function drop_view() box.space._space:delete{box.space.VIEW2.id} end
 function drop_index_t2() box.space._index:delete{box.space.T2.id, 0} end
diff --git a/test/sql/vinyl-opts.result b/test/sql/vinyl-opts.result
index 4e6c4bc..19769f8 100644
--- a/test/sql/vinyl-opts.result
+++ b/test/sql/vinyl-opts.result
@@ -13,11 +13,13 @@ test_run:cmd("switch test")
 ---
 - true
 ...
-box.sql.execute('pragma sql_default_engine= \'vinyl\'')
+box.execute('pragma sql_default_engine= \'vinyl\'')
 ---
+- rowcount: 0
 ...
-box.sql.execute('CREATE TABLE v1 (id INT PRIMARY KEY, b INT);')
+box.execute('CREATE TABLE v1 (id INT PRIMARY KEY, b INT);')
 ---
+- rowcount: 1
 ...
 box.space.V1.index[0].options
 ---
@@ -27,8 +29,9 @@ box.space.V1.index[0].options
   bloom_fpr: 0.1
   range_size: 536870912
 ...
-box.sql.execute('CREATE INDEX i1 ON v1(b);')
+box.execute('CREATE INDEX i1 ON v1(b);')
 ---
+- rowcount: 1
 ...
 box.space.V1.index[1].options
 ---
diff --git a/test/sql/vinyl-opts.test.lua b/test/sql/vinyl-opts.test.lua
index 843693b..4460724 100644
--- a/test/sql/vinyl-opts.test.lua
+++ b/test/sql/vinyl-opts.test.lua
@@ -3,11 +3,11 @@ test_run:cmd("create server test with script='sql/vinyl-opts-cfg.lua'")
 test_run:cmd("start server test")
 test_run:cmd("switch test")
 
-box.sql.execute('pragma sql_default_engine= \'vinyl\'')
-box.sql.execute('CREATE TABLE v1 (id INT PRIMARY KEY, b INT);')
+box.execute('pragma sql_default_engine= \'vinyl\'')
+box.execute('CREATE TABLE v1 (id INT PRIMARY KEY, b INT);')
 box.space.V1.index[0].options
 
-box.sql.execute('CREATE INDEX i1 ON v1(b);')
+box.execute('CREATE INDEX i1 ON v1(b);')
 box.space.V1.index[1].options
 
 box.space.V1:drop()
-- 
2.7.4

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 1/7] sql: add column name to SQL change counter
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
@ 2019-03-22 15:42   ` Konstantin Osipov
  2019-03-25 19:34     ` Mergen Imeev
  2019-03-29 12:00   ` Kirill Yukhin
  1 sibling, 1 reply; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:42 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:

The patch is OK to push.

> +cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
> +---
> +- metadata:
> +  - name: rows inserted
> +    type: INTEGER
> +  rows:
> +  - [3]
> +...
> +cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
> +---
> +- metadata:
> +  - name: rows inserted

Why rows inserted, not rows replaced?

> +    type: INTEGER
> +  rows:
> +  - [4]
> +...
> +cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
> +---
> +- metadata:
> +  - name: rows updated
> +    type: INTEGER
> +  rows:
> +  - [0]
> +...

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
@ 2019-03-22 15:45   ` Konstantin Osipov
  2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-29 12:01   ` Kirill Yukhin
  2 siblings, 0 replies; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:45 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> Currently, functions sql_execute() and sql_prepare_and_execute()
> set the ER_SQL_EXECUTE code for all errors that occur during the
> execution of a SQL command. This is considered incorrect because
> some of these errors may have their own error code.
> 
> In addition, all errors that do not have an error code are VDBE
> errors due to issue #3965, so it makes sense to set the error
> code ER_VDBE_EXECUTE for all errors without an error code.

LGTM.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma
@ 2019-03-22 15:46   ` Konstantin Osipov
  2019-03-25 19:39     ` Mergen Imeev
  2019-03-29 12:02   ` Kirill Yukhin
  1 sibling, 1 reply; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:46 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> Due to removing of box.sql.execute() it makes sense to remove
> box.sql.debug() and move SQL info to box.info.

This looks like statistics, not info, so should be moved to
box.stat.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma
@ 2019-03-22 15:53   ` Konstantin Osipov
  2019-03-29 19:26     ` Vladislav Shpilevoy
  2019-03-26 21:48   ` Vladislav Shpilevoy
  1 sibling, 1 reply; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:53 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
>  	case LUA_TCDATA:
>  	{
> -		uint32_t ctypeid = 0;
> -		void *cdata = luaL_checkcdata(L, index, &ctypeid);
> +		GCcdata *cd = cdataV(L->base + index - 1);
> +		void *cdata = (void *)cdataptr(cd);
> +

What is the reason for this change?


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 5/7] iproto: create port_sql
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql imeevma
@ 2019-03-22 15:55   ` Konstantin Osipov
  0 siblings, 0 replies; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:55 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> This patch creates port_sql implementation for the port. This will
> allow us to dump sql responses to obuf or to Lua. Also this patch
> defines methods dump_msgpack() and destroy() of port_sql.

LGTM.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 6/7] sql: create box.execute()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 6/7] sql: create box.execute() imeevma
@ 2019-03-22 15:57   ` Konstantin Osipov
  0 siblings, 0 replies; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-22 15:57 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

* imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> This patch creates the method dump_lua() for port_sql and uses it
> in the new function box.execute(). The function box.execute()
> replaces box.sql.execute() in the next patch.

Shoudln't the implementation of these functions be moved to
box/lua?

port_tuple_dump_lua is implemented there.

>  #include "box/box.h"
>  #include "box/txn.h"
>  #include "box/vclock.h"
> +#include "box/execute.h"
Circular dependency.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 1/7] sql: add column name to SQL change counter
  2019-03-22 15:42   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-25 19:34     ` Mergen Imeev
  0 siblings, 0 replies; 36+ messages in thread
From: Mergen Imeev @ 2019-03-25 19:34 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, kostja

Hi! Thank you for review. My answer, diff and new patch below.
Actually, I am not sure that this pragma will be useful after
box.sql.execute() will be deleted because INSERT, UPDATE, REPLACE
and DELETE returns rowcount after execution. This pragma do the
same thing, but only for INSERT, UPDATE and REPLACE. I think it
should work for DELETE, but it doesn't. The only difference is
the way the data shown: if pragma is on then the data returned as
rows with metadata, otherwise the data returned as info.


On Fri, Mar 22, 2019 at 06:42:00PM +0300, Konstantin Osipov wrote:
> * imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> 
> The patch is OK to push.
> 
> > +cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
> > +---
> > +- metadata:
> > +  - name: rows inserted
> > +    type: INTEGER
> > +  rows:
> > +  - [3]
> > +...
> > +cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
> > +---
> > +- metadata:
> > +  - name: rows inserted
> 
> Why rows inserted, not rows replaced?
>
Fixed. 
> > +    type: INTEGER
> > +  rows:
> > +  - [4]
> > +...
> > +cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
> > +---
> > +- metadata:
> > +  - name: rows updated
> > +    type: INTEGER
> > +  rows:
> > +  - [0]
> > +...
> 
> -- 
> Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
> http://tarantool.io - www.twitter.com/kostja_osipov
> 


Diff:

diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 654b282..1c6b7cf 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -786,8 +786,12 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	    pParse->triggered_space == NULL) {
 		sqlVdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
 		sqlVdbeSetNumCols(v, 1);
-		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows inserted",
-				      SQL_STATIC);
+		const char *column_name;
+		if (on_error == ON_CONFLICT_ACTION_REPLACE)
+			column_name = "rows replaced";
+		else
+			column_name = "rows inserted";
+		sqlVdbeSetColName(v, 0, COLNAME_NAME, column_name, SQL_STATIC);
 		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
 				  SQL_STATIC);
 	}
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index c700a80..0944f61 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -960,7 +960,7 @@ cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
 cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
 ---
 - metadata:
-  - name: rows inserted
+  - name: rows replaced
     type: INTEGER
   rows:
   - [4]


New patch:

commit c830c5bd283daa2a7f34b7e6a4a3ffccef28ec52
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sat Mar 16 17:20:00 2019 +0300

    sql: add column name to SQL change counter
    
    Currently, if the count_changes pragma is enabled, the INSERT,
    REPLACE and UPDATE statements will return the number of changes at
    execution time. This patch sets an INTEGER type for this result.
    
    Follow up #3832
    Needed from #3505

diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index f4d0334..eb1c8aa 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -422,6 +422,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		sqlVdbeSetNumCols(v, 1);
 		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows deleted",
 				      SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  delete_from_cleanup:
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6f7f020..1c6b7cf 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -786,8 +786,14 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	    pParse->triggered_space == NULL) {
 		sqlVdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
 		sqlVdbeSetNumCols(v, 1);
-		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows inserted",
-				      SQL_STATIC);
+		const char *column_name;
+		if (on_error == ON_CONFLICT_ACTION_REPLACE)
+			column_name = "rows replaced";
+		else
+			column_name = "rows inserted";
+		sqlVdbeSetColName(v, 0, COLNAME_NAME, column_name, SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  insert_cleanup:
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 05ceeb4..82de09c 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -530,6 +530,8 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		sqlVdbeSetNumCols(v, 1);
 		sqlVdbeSetColName(v, 0, COLNAME_NAME, "rows updated",
 				      SQL_STATIC);
+		sqlVdbeSetColName(v, 0, COLNAME_DECLTYPE, "INTEGER",
+				  SQL_STATIC);
 	}
 
  update_cleanup:
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 938aea9..0944f61 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -942,6 +942,37 @@ res.metadata
   - name: detail
     type: TEXT
 ...
+-- When pragma count_changes is on, statements INSERT, REPLACE and
+-- UPDATE returns number of changed columns. Make sure that this
+-- result has a column type.
+cn:execute("PRAGMA count_changes = 1;")
+---
+- rowcount: 0
+...
+cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
+---
+- metadata:
+  - name: rows inserted
+    type: INTEGER
+  rows:
+  - [3]
+...
+cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
+---
+- metadata:
+  - name: rows replaced
+    type: INTEGER
+  rows:
+  - [4]
+...
+cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
+---
+- metadata:
+  - name: rows updated
+    type: INTEGER
+  rows:
+  - [0]
+...
 cn:close()
 ---
 ...
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua
index fbdc5a2..3b36cc3 100644
--- a/test/sql/iproto.test.lua
+++ b/test/sql/iproto.test.lua
@@ -289,6 +289,14 @@ res.metadata
 res = cn:execute("EXPLAIN QUERY PLAN SELECT COUNT(*) FROM t1")
 res.metadata
 
+-- When pragma count_changes is on, statements INSERT, REPLACE and
+-- UPDATE returns number of changed columns. Make sure that this
+-- result has a column type.
+cn:execute("PRAGMA count_changes = 1;")
+cn:execute("INSERT INTO t1 VALUES (1), (2), (3);")
+cn:execute("REPLACE INTO t1 VALUES (2), (3), (4), (5);")
+cn:execute("UPDATE t1 SET id = id + 100 WHERE id > 10;")
+
 cn:close()
 box.sql.execute('DROP TABLE t1')
 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-22 15:46   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-25 19:39     ` Mergen Imeev
  2019-03-26 21:48       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 36+ messages in thread
From: Mergen Imeev @ 2019-03-25 19:39 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, kostja

Hi! Thank you for review! My answer, diff and new patch below.

On Fri, Mar 22, 2019 at 06:46:07PM +0300, Konstantin Osipov wrote:
> * imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
> > Due to removing of box.sql.execute() it makes sense to remove
> > box.sql.debug() and move SQL info to box.info.
> 
> This looks like statistics, not info, so should be moved to
> box.stat.
> 
Fixed, moved to box.stat.
> 
> -- 
> Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
> http://tarantool.io - www.twitter.com/kostja_osipov
> 


Diff:

diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 707cd28..76b5646 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -45,7 +45,6 @@
 #include "box/gc.h"
 #include "box/engine.h"
 #include "box/vinyl.h"
-#include "box/sql.h"
 #include "main.h"
 #include "version.h"
 #include "box/box.h"
@@ -202,15 +201,6 @@ lbox_info_replication(struct lua_State *L)
 }
 
 static int
-lbox_info_sql_call(struct lua_State *L)
-{
-	struct info_handler info;
-	luaT_info_handler_create(&info, L);
-	sql_debug_info(&info);
-	return 1;
-}
-
-static int
 lbox_info_id(struct lua_State *L)
 {
 	/*
@@ -507,7 +497,6 @@ static const struct luaL_Reg lbox_info_dynamic_meta[] = {
 	{"memory", lbox_info_memory},
 	{"gc", lbox_info_gc},
 	{"vinyl", lbox_info_vinyl},
-	{"sql", lbox_info_sql_call},
 	{NULL, NULL}
 };
 
diff --git a/src/box/lua/stat.c b/src/box/lua/stat.c
index 1015253..ce0e9e9 100644
--- a/src/box/lua/stat.c
+++ b/src/box/lua/stat.c
@@ -42,6 +42,7 @@
 #include "box/iproto.h"
 #include "box/engine.h"
 #include "box/vinyl.h"
+#include "box/sql.h"
 #include "info/info.h"
 #include "lua/info.h"
 #include "lua/utils.h"
@@ -160,6 +161,15 @@ lbox_stat_net_call(struct lua_State *L)
 	return 1;
 }
 
+static int
+lbox_stat_sql(struct lua_State *L)
+{
+	struct info_handler info;
+	luaT_info_handler_create(&info, L);
+	sql_debug_info(&info);
+	return 1;
+}
+
 static const struct luaL_Reg lbox_stat_meta [] = {
 	{"__index", lbox_stat_index},
 	{"__call",  lbox_stat_call},
@@ -179,6 +189,7 @@ box_lua_stat_init(struct lua_State *L)
 	static const struct luaL_Reg statlib [] = {
 		{"vinyl", lbox_stat_vinyl},
 		{"reset", lbox_stat_reset},
+		{"sql", lbox_stat_sql},
 		{NULL, NULL}
 	};
 
diff --git a/test/box/info.result b/test/box/info.result
index 26377b1..3384631 100644
--- a/test/box/info.result
+++ b/test/box/info.result
@@ -83,7 +83,6 @@ t
   - replication
   - ro
   - signature
-  - sql
   - status
   - uptime
   - uuid
diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index 79583af..1663e39 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -51,10 +51,10 @@ test:do_test(
 -- is done.  Then it appends the names of the table and index used.
 --
 local function queryplan(sql)
-    local sqlite_sort_count = box.info.sql.sql_sort_count
+    local sqlite_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
     local x = "nosort"
-    if box.info.sql.sql_sort_count - sqlite_sort_count then
+    if box.stat.sql().sql_sort_count - sqlite_sort_count then
         x = "sort"
     end
     table.insert(data,x)
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 57fbca6..bb8a498 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -5,9 +5,9 @@ test:plan(39)
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.info.sql.sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.info.sql.sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end
diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index 63c5d9b..05e2824 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -326,9 +326,9 @@ function test.do_select_tests(self, label, tests)
 end
 
 function test.sf_execsql(self, sql)
-    local old = box.info.sql.sql_search_count
+    local old = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    local new = box.info.sql.sql_search_count - old
+    local new = box.stat.sql().sql_search_count - old
 
     return {new, r}
 end
diff --git a/test/sql-tap/minmax2.test.lua b/test/sql-tap/minmax2.test.lua
index e75709a..0e0f0d0 100755
--- a/test/sql-tap/minmax2.test.lua
+++ b/test/sql-tap/minmax2.test.lua
@@ -59,7 +59,7 @@ test:do_execsql_test(
 test:do_test(
     "minmax2-1.1",
     function()
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.1>
@@ -70,13 +70,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.2",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.3",
     function()
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.3>
@@ -87,14 +87,14 @@ test:do_test(
 test:do_test(
     "minmax2-1.4",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.5",
     function()
         test:execsql "CREATE INDEX t1i1 ON t1(x DESC)"
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.5>
@@ -105,13 +105,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.6",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_test(
     "minmax2-1.7",
     function()
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.7>
@@ -122,13 +122,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.8",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-1.9",
     function()
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(y) FROM t1"
     end, {
         -- <minmax2-1.9>
@@ -139,7 +139,7 @@ test:do_test(
 test:do_test(
     "minmax2-1.10",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
@@ -149,7 +149,7 @@ test:do_test(
             CREATE TABLE t2(a INTEGER PRIMARY KEY, b INT );
             INSERT INTO t2 SELECT x, y FROM t1;
         ]]
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(a) FROM t2"
     end, {
         -- <minmax2-2.0>
@@ -160,13 +160,13 @@ test:do_test(
 test:do_test(
     "minmax2-2.1",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-2.2",
     function()
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-2.2>
@@ -177,7 +177,7 @@ test:do_test(
 test:do_test(
     "minmax2-2.3",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -186,7 +186,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-3.0>
@@ -197,7 +197,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.1",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -206,7 +206,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.info.sql.sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql " SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) "
 
 
@@ -220,7 +220,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.3",
     function()
-        return box.info.sql.sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_execsql_test(
diff --git a/test/sql-tap/minmax3.test.lua b/test/sql-tap/minmax3.test.lua
index 8cefd63..f1d9efc 100755
--- a/test/sql-tap/minmax3.test.lua
+++ b/test/sql-tap/minmax3.test.lua
@@ -24,9 +24,9 @@ test:plan(47)
 --
 
 local function count(sql)
-    local sql_search_count = box.info.sql.sql_search_count
+    local sql_search_count = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    table.insert(r, box.info.sql.sql_search_count - sql_search_count)
+    table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
     return r
 end
 
diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua
index 831221c..4116ca9 100755
--- a/test/sql-tap/where2.test.lua
+++ b/test/sql-tap/where2.test.lua
@@ -62,10 +62,10 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.info.sql.sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    print("lol "..(box.info.sql.sql_sort_count - sql_sort_count))
-    table.insert(r, box.info.sql.sql_sort_count - sql_sort_count)
+    print("lol "..(box.stat.sql().sql_sort_count - sql_sort_count))
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
@@ -78,9 +78,9 @@ end
 --
 local function cksort(sql)
     local sort = "nosort"
-    local sql_sort_count = box.info.sql.sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.info.sql.sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
             sort = 'sort'
     end
     table.insert(data, sort)
@@ -97,9 +97,9 @@ end
 --
 local function queryplan(sql)
     local sort = "nosort"
-    local sql_sort_count = box.info.sql.sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.info.sql.sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
         sort = 'sort'
     end
     table.insert(data, sort)
diff --git a/test/sql-tap/whereA.test.lua b/test/sql-tap/whereA.test.lua
index 34d70ef..2c8cb45 100755
--- a/test/sql-tap/whereA.test.lua
+++ b/test/sql-tap/whereA.test.lua
@@ -193,9 +193,9 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.info.sql.sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    table.insert(r, box.info.sql.sql_sort_count - sql_sort_count)
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
diff --git a/test/sql-tap/whereD.test.lua b/test/sql-tap/whereD.test.lua
index 0973208..14dc8d3 100755
--- a/test/sql-tap/whereD.test.lua
+++ b/test/sql-tap/whereD.test.lua
@@ -234,10 +234,10 @@ local function do_searchcount_test(tn, sql, res)
     test:do_test(
         tn,
         function()
-            local sql_search_count = box.info.sql.sql_search_count
+            local sql_search_count = box.stat.sql().sql_search_count
             local r = test:execsql(sql)
             table.insert(r, "search")
-            table.insert(r, box.info.sql.sql_search_count - sql_search_count)
+            table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
             return r
         end,
         res)
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index df6b0ec..48f7377 100755
--- a/test/sql-tap/with2.test.lua
+++ b/test/sql-tap/with2.test.lua
@@ -389,9 +389,9 @@ genstmt(255), {
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.info.sql.sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.info.sql.sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end


New patch:

commit 1e21fc73be5df524d500723bd3e47066475ee6b9
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Thu Jan 31 14:09:51 2019 +0300

    sql: remove box.sql.debug()
    
    Due to removing of box.sql.execute() it makes sense to remove
    box.sql.debug() and move SQL statistics to box.stat.
    
    Part or #3505

diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index ee20faa..cd6e75c 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -110,21 +110,11 @@ sqlerror:
 	return lua_error(L);
 }
 
-static int
-lua_sql_debug(struct lua_State *L)
-{
-	struct info_handler info;
-	luaT_info_handler_create(&info, L);
-	sql_debug_info(&info);
-	return 1;
-}
-
 void
 box_lua_sql_init(struct lua_State *L)
 {
 	static const struct luaL_Reg module_funcs [] = {
 		{"execute", lua_sql_execute},
-		{"debug", lua_sql_debug},
 		{NULL, NULL}
 	};
 
diff --git a/src/box/lua/stat.c b/src/box/lua/stat.c
index 1015253..ce0e9e9 100644
--- a/src/box/lua/stat.c
+++ b/src/box/lua/stat.c
@@ -42,6 +42,7 @@
 #include "box/iproto.h"
 #include "box/engine.h"
 #include "box/vinyl.h"
+#include "box/sql.h"
 #include "info/info.h"
 #include "lua/info.h"
 #include "lua/utils.h"
@@ -160,6 +161,15 @@ lbox_stat_net_call(struct lua_State *L)
 	return 1;
 }
 
+static int
+lbox_stat_sql(struct lua_State *L)
+{
+	struct info_handler info;
+	luaT_info_handler_create(&info, L);
+	sql_debug_info(&info);
+	return 1;
+}
+
 static const struct luaL_Reg lbox_stat_meta [] = {
 	{"__index", lbox_stat_index},
 	{"__call",  lbox_stat_call},
@@ -179,6 +189,7 @@ box_lua_stat_init(struct lua_State *L)
 	static const struct luaL_Reg statlib [] = {
 		{"vinyl", lbox_stat_vinyl},
 		{"reset", lbox_stat_reset},
+		{"sql", lbox_stat_sql},
 		{NULL, NULL}
 	};
 
diff --git a/test/sql-tap/analyze3.test.lua b/test/sql-tap/analyze3.test.lua
index 1396287..dcbea1d 100755
--- a/test/sql-tap/analyze3.test.lua
+++ b/test/sql-tap/analyze3.test.lua
@@ -48,15 +48,6 @@ testprefix = "analyze3"
 --               have been fixed.
 --
 
-local function eqp(sql)
-    return test:execsql("EXPLAIN QUERY PLAN"..sql)
-end
-
-local function sf_execsql(sql, db)
-    r = test:execsql(sql)
-    return {box.sql.debug().sql_search_count, r}
-end
-
 ---------------------------------------------------------------------------
 --
 -- analyze3-1.1.1: 
diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index d56de4e..1663e39 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -51,10 +51,10 @@ test:do_test(
 -- is done.  Then it appends the names of the table and index used.
 --
 local function queryplan(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sqlite_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
     local x = "nosort"
-    if box.sql.debug().sql_sort_count - sql_sort_count then
+    if box.stat.sql().sql_sort_count - sqlite_sort_count then
         x = "sort"
     end
     table.insert(data,x)
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 80a2a2d..bb8a498 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -5,9 +5,9 @@ test:plan(39)
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end
diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index 8aac64c..05e2824 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -326,9 +326,9 @@ function test.do_select_tests(self, label, tests)
 end
 
 function test.sf_execsql(self, sql)
-    local old = box.sql.debug().sql_search_count
+    local old = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    local new = box.sql.debug().sql_search_count - old
+    local new = box.stat.sql().sql_search_count - old
 
     return {new, r}
 end
diff --git a/test/sql-tap/minmax2.test.lua b/test/sql-tap/minmax2.test.lua
index a6f840d..0e0f0d0 100755
--- a/test/sql-tap/minmax2.test.lua
+++ b/test/sql-tap/minmax2.test.lua
@@ -59,7 +59,7 @@ test:do_execsql_test(
 test:do_test(
     "minmax2-1.1",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.1>
@@ -70,13 +70,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.2",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.3",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.3>
@@ -87,14 +87,14 @@ test:do_test(
 test:do_test(
     "minmax2-1.4",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.5",
     function()
         test:execsql "CREATE INDEX t1i1 ON t1(x DESC)"
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.5>
@@ -105,13 +105,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.6",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_test(
     "minmax2-1.7",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.7>
@@ -122,13 +122,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.8",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-1.9",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(y) FROM t1"
     end, {
         -- <minmax2-1.9>
@@ -139,7 +139,7 @@ test:do_test(
 test:do_test(
     "minmax2-1.10",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
@@ -149,7 +149,7 @@ test:do_test(
             CREATE TABLE t2(a INTEGER PRIMARY KEY, b INT );
             INSERT INTO t2 SELECT x, y FROM t1;
         ]]
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(a) FROM t2"
     end, {
         -- <minmax2-2.0>
@@ -160,13 +160,13 @@ test:do_test(
 test:do_test(
     "minmax2-2.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-2.2",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-2.2>
@@ -177,7 +177,7 @@ test:do_test(
 test:do_test(
     "minmax2-2.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -186,7 +186,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-3.0>
@@ -197,7 +197,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -206,7 +206,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql " SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) "
 
 
@@ -220,7 +220,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_execsql_test(
diff --git a/test/sql-tap/minmax3.test.lua b/test/sql-tap/minmax3.test.lua
index 3707575..f1d9efc 100755
--- a/test/sql-tap/minmax3.test.lua
+++ b/test/sql-tap/minmax3.test.lua
@@ -24,9 +24,9 @@ test:plan(47)
 --
 
 local function count(sql)
-    local sql_search_count = box.sql.debug().sql_search_count
+    local sql_search_count = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+    table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
     return r
 end
 
diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua
index 8eaf405..4116ca9 100755
--- a/test/sql-tap/where2.test.lua
+++ b/test/sql-tap/where2.test.lua
@@ -62,10 +62,10 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    print("lol "..(box.sql.debug().sql_sort_count - sql_sort_count))
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    print("lol "..(box.stat.sql().sql_sort_count - sql_sort_count))
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
@@ -78,9 +78,9 @@ end
 --
 local function cksort(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
             sort = 'sort'
     end
     table.insert(data, sort)
@@ -97,9 +97,9 @@ end
 --
 local function queryplan(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
         sort = 'sort'
     end
     table.insert(data, sort)
diff --git a/test/sql-tap/whereA.test.lua b/test/sql-tap/whereA.test.lua
index b4a0f0a..2c8cb45 100755
--- a/test/sql-tap/whereA.test.lua
+++ b/test/sql-tap/whereA.test.lua
@@ -193,9 +193,9 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
diff --git a/test/sql-tap/whereD.test.lua b/test/sql-tap/whereD.test.lua
index b103f3c..14dc8d3 100755
--- a/test/sql-tap/whereD.test.lua
+++ b/test/sql-tap/whereD.test.lua
@@ -234,10 +234,10 @@ local function do_searchcount_test(tn, sql, res)
     test:do_test(
         tn,
         function()
-            local sql_search_count = box.sql.debug().sql_search_count
+            local sql_search_count = box.stat.sql().sql_search_count
             local r = test:execsql(sql)
             table.insert(r, "search")
-            table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+            table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
             return r
         end,
         res)
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index ca3f00e..48f7377 100755
--- a/test/sql-tap/with2.test.lua
+++ b/test/sql-tap/with2.test.lua
@@ -389,9 +389,9 @@ genstmt(255), {
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 7/7] sql: remove box.sql.execute()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute() imeevma
@ 2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-28 20:13     ` Mergen Imeev
  0 siblings, 1 reply; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-26 21:48 UTC (permalink / raw)
  To: tarantool-patches, imeevma

Thanks for the patch! See 5 comments below.

> diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
> index 6c9a820..5530b2c 100644
> --- a/src/box/lua/load_cfg.lua
> +++ b/src/box/lua/load_cfg.lua
> @@ -505,17 +505,14 @@ end
>  box.cfg = locked(load_cfg)
>  
>  --
> --- This makes possible do box.sql.execute without calling box.cfg
> +-- This makes possible do box.execute without calling box.cfg
>  -- manually. The load_cfg call overwrites following table and

1. There are no metatable anymore as I see. At least an explicit.
Do you really call load_cfg on every single box.execute() call?
If you do - please, do not. It is too slow. Use metatables.

>  -- metatable.
>  --
> -box.sql = {}
> -setmetatable(box.sql, {
> -    __index = function(table, index)
> -        load_cfg()
> -        return box.sql[index]
> -    end,
> -})
> +function box.execute(...)
> +    load_cfg()
> +    return box.execute(...)
> +end
>  
>  -- gh-810:
>  -- hack luajit default cpath
> diff --git a/test/sql-tap/gh2140-trans.test.lua b/test/sql-tap/gh2140-trans.test.lua
> index fe978d1..3843c97 100755
> --- a/test/sql-tap/gh2140-trans.test.lua
> +++ b/test/sql-tap/gh2140-trans.test.lua
> @@ -28,8 +28,8 @@ test:do_execsql_test('rollback1_check',
>  for _, verb in ipairs({'ROLLBACK', 'ABORT'}) do
> -    box.sql.execute('DELETE FROM t2')
> -    answer = "Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
> +    box.execute('DELETE FROM t2')
> +    answer = "/Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'/"

2. Why are '/' added?

>      test:do_catchsql_test('insert1_'..verb,
>                            [[START TRANSACTION;
>                              INSERT INTO t2 VALUES (20, 2, 2);
> diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
> index 63c5d9b..cb77bc4 100644
> --- a/test/sql-tap/lua/sqltester.lua
> +++ b/test/sql-tap/lua/sqltester.lua
> @@ -403,7 +415,16 @@ test.do_eqp_test = function (self, label, sql, result)
>      test:do_test(
>          label,
>          function()
> -            return execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
> +            local result = execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
> +            local res = {}
> +            for k,v in pairs(result) do
> +                res[k] = {}
> +                res[k][1] = v[1]
> +                res[k][2] = v[2]
> +                res[k][3] = v[3]
> +                res[k][4] = tostring(v[4])

3. What is happening here?

> +            end
> +            return res
>          end,
>          result)
>  end
> diff --git a/test/sql-tap/orderby9.test.lua b/test/sql-tap/orderby9.test.lua
> index 191c21b..13f9ec6 100755
> --- a/test/sql-tap/orderby9.test.lua
> +++ b/test/sql-tap/orderby9.test.lua
> @@ -41,6 +41,7 @@ test:do_test(
>          -- separately for the result set and the ORDER BY clause, then the output
>          -- order will be random.
>          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY 1;")
> +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
>          local l2 = table.deepcopy(l1)
>          table.sort(l1)
>          return test.is_deeply_regex(l1, l2)
> @@ -50,6 +51,7 @@ test:do_test(
>      1.1,
>      function()
>          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY random();")
> +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end

4. And in these two hunks?

>          local l2 = table.deepcopy(l1)
>          table.sort(l1)
>          return test.is_deeply_regex(l1, l2)
> diff --git a/test/sql/min-on-index.result b/test/sql/min-on-index.result
> deleted file mode 100644
> index 1b2aadf..0000000
> --- a/test/sql/min-on-index.result
> +++ /dev/null

5. Why removed?

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
  2019-03-22 15:45   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-27 11:43     ` Konstantin Osipov
  2019-03-28 17:46     ` Mergen Imeev
  2019-03-29 12:01   ` Kirill Yukhin
  2 siblings, 2 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-26 21:48 UTC (permalink / raw)
  To: tarantool-patches, imeevma

Hi! Thanks for the patch!

On 22/03/2019 13:50, imeevma@tarantool.org wrote:
> Currently, functions sql_execute() and sql_prepare_and_execute()
> set the ER_SQL_EXECUTE code for all errors that occur during the
> execution of a SQL command. This is considered incorrect because
> some of these errors may have their own error code.
> 
> In addition, all errors that do not have an error code are VDBE
> errors due to issue #3965, so it makes sense to set the error
> code ER_VDBE_EXECUTE for all errors without an error code.
> 
> Part of #3505
> ---
>  src/box/errcode.h                            |  1 +
>  src/box/execute.c                            | 14 ++++++++--
>  test/box/misc.result                         |  1 +
>  test/sql/collation.result                    |  2 +-
>  test/sql/gh-2362-select-access-rights.result | 12 +++------
>  test/sql/iproto.result                       | 39 ++++++++++++----------------
>  6 files changed, 35 insertions(+), 34 deletions(-)
> 
> diff --git a/src/box/execute.c b/src/box/execute.c
> index 7c77df2..5810086 100644
> --- a/src/box/execute.c
> +++ b/src/box/execute.c
> @@ -541,7 +546,12 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
>  		return -1;
>  	}
>  	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
> -		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
> +		if (db->errCode != SQL_TARANTOOL_ERROR) {
> +			const char *err = (char *)sql_value_text(db->pErr);
> +			if (err == NULL)
> +				err = sqlErrStr(db->errCode);
> +			diag_set(ClientError, ER_VDBE_EXECUTE, err);

Prepare has nothing to do with Vdbe except allocation. Error
says "Error during execution of VDBE", but VDBE is not executed
here. Also, not sure if 'VDBE' should be showed to a user. He is
likely not to be aware about what it is.

In my opinion, ER_VDBE_EXECUTE should be replaced with ER_SQL_EXECUTE,
and the message should be "Error during execution of SQL bytecode: ...".

> +		}
>  		return -1;
>  	}
>  	assert(stmt != NULL);

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma
  2019-03-22 15:53   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-28 17:54     ` Mergen Imeev
  1 sibling, 1 reply; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-26 21:48 UTC (permalink / raw)
  To: tarantool-patches, imeevma

Thanks for the patch!

>> 2. Looking at lua_field_inspect_table I found that if a table has __serialize
>> metamethod, it is called without a protection (utils.c:409). __serialize is an
>> arbitrary unprotected user code, that can throw an error deliberately. What are
>> we gonna do with that? Personally I've already faced with some user code, throwing
>> an error from __serialize, deliberately. On not critical errors not meaning panic.
>>
>> As a solution we could 1) do not care, again; 2) finally accept the fact that
>> wrap into a pcall was not so bad and use it; 3) use lua_pcall in that
>> particular place. Please, consult Kostja, what does he choose.
> 
> I checked it this way:
> 
> diff --git a/src/lua/utils.c b/src/lua/utils.c
> index bbfc549..eae3b5f 100644
> --- a/src/lua/utils.c
> +++ b/src/lua/utils.c
> @@ -398,6 +398,20 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
>  	lua_settop(L, top); /* remove temporary objects */
>  }
>  
> +/**
> + * Check that the field LUAL_SERIALIZE of the metatable is
> + * available.
> + *
> + * @param L Lua State.
> + */
> +static int
> +get_metafield_serialize(struct lua_State *L)
> +{
> +	int idx = *(int *)lua_topointer(L, -1);
> +	luaL_getmetafield(L, idx, LUAL_SERIALIZE);
> +	return 0;
> +}
> +
>  static int
>  lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
>  			int idx, struct luaL_field *field)
> @@ -408,6 +422,9 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
>  	uint32_t max = 0;
>  
>  	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
> +	if (!cfg->encode_load_metatables &&
> +	    luaT_cpcall(L, get_metafield_serialize, &idx) != 0)
> +		return -1;
>  	if (!cfg->encode_load_metatables ||
>  	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
>  		goto skip;
> 

Unfortunately, it was not the place I told about. You
wrapped just a fetch of __serialize function, but not its
call. It is still unsafe on line 435. In other words it was:

    1 func = obj.__serialize
    2 func(obj)

You made it:

    1 func = pcall(function() obj.__serialize end)
    2 func(obj)

The second line is still unsafe. And much more unsafe than
the first one.

But it is a good catch too, because a user could define
such an __index for his metatable, that it throws an error.
You should wrap both __serialize fetch and call into a single
lua_cpcall.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-25 19:39     ` Mergen Imeev
@ 2019-03-26 21:48       ` Vladislav Shpilevoy
  2019-03-28 17:48         ` Mergen Imeev
  0 siblings, 1 reply; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-26 21:48 UTC (permalink / raw)
  To: tarantool-patches, Mergen Imeev; +Cc: kostja

Thanks for the patch! See 3 comments below.

> commit 1e21fc73be5df524d500723bd3e47066475ee6b9
> Author: Mergen Imeev <imeevma@gmail.com>
> Date:   Thu Jan 31 14:09:51 2019 +0300
> 
>     sql: remove box.sql.debug()
>     
>     Due to removing of box.sql.execute() it makes sense to remove

1. 'removing' -> 'removal'.

>     box.sql.debug() and move SQL statistics to box.stat.

2. For a foreign reader it is unknown that box.sql.execute() removal
== box.sql removal. Probably, it should be said explicitly, that
we want to remove the whole box.sql.

>     
>     Part or #3505
> 
> diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
> index d56de4e..1663e39 100755
> --- a/test/sql-tap/between.test.lua
> +++ b/test/sql-tap/between.test.lua
> @@ -51,10 +51,10 @@ test:do_test(
>  -- is done.  Then it appends the names of the table and index used.
>  --
>  local function queryplan(sql)
> -    local sql_sort_count = box.sql.debug().sql_sort_count
> +    local sqlite_sort_count = box.stat.sql().sql_sort_count

3. 'sqlite' -> 'sql'

>      local data = test:execsql(sql)
>      local x = "nosort"
> -    if box.sql.debug().sql_sort_count - sql_sort_count then
> +    if box.stat.sql().sql_sort_count - sqlite_sort_count then
>          x = "sort"
>      end
>      table.insert(data,x)

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-26 21:48   ` Vladislav Shpilevoy
@ 2019-03-27 11:43     ` Konstantin Osipov
  2019-03-28 17:46     ` Mergen Imeev
  1 sibling, 0 replies; 36+ messages in thread
From: Konstantin Osipov @ 2019-03-27 11:43 UTC (permalink / raw)
  To: tarantool-patches; +Cc: imeevma

* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [19/03/27 09:35]:
> In my opinion, ER_VDBE_EXECUTE should be replaced with ER_SQL_EXECUTE,
> and the message should be "Error during execution of SQL bytecode: ...".

I'm OK with this.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-26 21:48   ` Vladislav Shpilevoy
  2019-03-27 11:43     ` Konstantin Osipov
@ 2019-03-28 17:46     ` Mergen Imeev
  1 sibling, 0 replies; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 17:46 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Hi! Thank you for review. My answer, diffs and new patch nelow.
There will be two diffs - one is diff for this patch and one for
the last one in the patch-set.

On Wed, Mar 27, 2019 at 12:48:27AM +0300, Vladislav Shpilevoy wrote:
> Hi! Thanks for the patch!
> 
> > diff --git a/src/box/execute.c b/src/box/execute.c
> > index 7c77df2..5810086 100644
> > --- a/src/box/execute.c
> > +++ b/src/box/execute.c
> > @@ -541,7 +546,12 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
> >  		return -1;
> >  	}
> >  	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
> > -		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
> > +		if (db->errCode != SQL_TARANTOOL_ERROR) {
> > +			const char *err = (char *)sql_value_text(db->pErr);
> > +			if (err == NULL)
> > +				err = sqlErrStr(db->errCode);
> > +			diag_set(ClientError, ER_VDBE_EXECUTE, err);
> 
> Prepare has nothing to do with Vdbe except allocation. Error
> says "Error during execution of VDBE", but VDBE is not executed
> here. Also, not sure if 'VDBE' should be showed to a user. He is
> likely not to be aware about what it is.
> 
> In my opinion, ER_VDBE_EXECUTE should be replaced with ER_SQL_EXECUTE,
> and the message should be "Error during execution of SQL bytecode: ...".
> 
Fixed. Changed to errcode that was used before - ER_SQL_EXECUTE.


Diff from previous version of this patch:

diff --git a/src/box/errcode.h b/src/box/errcode.h
index e39a89e..7764aa3 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -240,7 +240,6 @@ struct errcode_record {
 	/*185 */_(ER_SQL_UNKNOWN_TOKEN,		"Syntax error: unrecognized token: '%.*s'") \
 	/*186 */_(ER_SQL_PARSER_GENERIC,	"%s") \
 	/*187 */_(ER_SQL_ANALYZE_ARGUMENT,	"ANALYZE statement argument %s is not a base table") \
-	/*188 */_(ER_VDBE_EXECUTE,		"Error during execution of VDBE byte-code: %s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/execute.c b/src/box/execute.c
index 5810086..d45fb12 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -527,7 +527,7 @@ sql_execute(sql *db, struct sql_stmt *stmt, struct port *port,
 			const char *err = (char *)sql_value_text(db->pErr);
 			if (err == NULL)
 				err = sqlErrStr(db->errCode);
-			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+			diag_set(ClientError, ER_SQL_EXECUTE, err);
 		}
 		return -1;
 	}
@@ -550,7 +550,7 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
 			const char *err = (char *)sql_value_text(db->pErr);
 			if (err == NULL)
 				err = sqlErrStr(db->errCode);
-			diag_set(ClientError, ER_VDBE_EXECUTE, err);
+			diag_set(ClientError, ER_SQL_EXECUTE, err);
 		}
 		return -1;
 	}
diff --git a/test/box/misc.result b/test/box/misc.result
index 4f1116e..c350bbd 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -516,7 +516,6 @@ t;
   185: box.error.SQL_UNKNOWN_TOKEN
   186: box.error.SQL_PARSER_GENERIC
   187: box.error.SQL_ANALYZE_ARGUMENT
-  188: box.error.VDBE_EXECUTE
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index 2738ed6..269fbaf 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -651,8 +651,8 @@ future1:wait_result()
 future2:wait_result()
 ---
 - null
-- 'Error during execution of VDBE byte-code: Duplicate key exists in unique index
-  ''pk_unnamed_TEST_1'' in space ''TEST'''
+- 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_TEST_1''
+  in space ''TEST'''
 ...
 future3:wait_result()
 ---


Diff from last version of the patch "Remove box.sql.execute()":

diff --git a/test/sql-tap/alter.test.lua b/test/sql-tap/alter.test.lua
index 2331cd0..97039e2 100755
--- a/test/sql-tap/alter.test.lua
+++ b/test/sql-tap/alter.test.lua
@@ -359,7 +359,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 1, 3);
     ]], {
         -- <alter-7.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter-7.2>
     })
 
@@ -369,7 +369,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 2, 2);
     ]], {
         -- <alter-7.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter-7.3>
     })
 
@@ -435,7 +435,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(4, 5, 3);
     ]], {
         -- <alter-7.9>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter-7.9>
     })
 
@@ -490,7 +490,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(6, 5, 10);
     ]], {
         -- <alter-7.14>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter-7.14>
     })
 
diff --git a/test/sql-tap/alter2.test.lua b/test/sql-tap/alter2.test.lua
index 612dd74..d714a4b 100755
--- a/test/sql-tap/alter2.test.lua
+++ b/test/sql-tap/alter2.test.lua
@@ -24,7 +24,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(2, 3, 2);
     ]], {
         -- <alter2-1.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-1.2>
     })
 
@@ -45,7 +45,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(2, 3, 2);
     ]], {
         -- <alter2-1.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-1.4>
     })
 
@@ -83,7 +83,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(4, 2, 1);
     ]], {
         -- <alter2-1.6>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-1.6>
     })
 
@@ -143,7 +143,7 @@ test:do_catchsql_test(
         INSERT INTO parent VALUES(1, 2, 3);
     ]], {
         -- <alter2-2.1>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-2.1>
     })
 
@@ -154,7 +154,7 @@ test:do_catchsql_test(
         INSERT INTO child VALUES(2, 1, 1);
     ]], {
         -- <alter2-2.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-2.2>
     })
 
@@ -165,7 +165,7 @@ test:do_catchsql_test(
         INSERT INTO parent VALUES(3, 4, 2);
     ]], {
         -- <alter2-2.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </alter2-2.3>
     })
 
diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
index bdc0053..36c243f 100755
--- a/test/sql-tap/autoinc.test.lua
+++ b/test/sql-tap/autoinc.test.lua
@@ -531,7 +531,7 @@ test:do_catchsql_test(
         INSERT INTO t6 VALUES(NULL,1);
     ]], {
         -- <autoinc-6.2>
-        1, "Error during execution of VDBE byte-code: Sequence 'T6' has overflowed"
+        1, "Failed to execute SQL statement: Sequence 'T6' has overflowed"
         -- </autoinc-6.2>
     })
 
@@ -810,7 +810,7 @@ test:do_catchsql_test(
         INSERT INTO t1 SELECT s2, s2 FROM t1;
     ]], {
         -- <autoinc-gh-3670>
-        1, "Error during execution of VDBE byte-code: datatype mismatch"
+        1, "Failed to execute SQL statement: datatype mismatch"
         -- </autoinc-gh-3670>
     })
 
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index c7ef1a1..d5760b0 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -55,7 +55,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(6,7, 2);
     ]], {
         -- <check-1.3>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-1.3>
     })
 
@@ -75,7 +75,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(4,3, 2);
     ]], {
         -- <check-1.5>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-1.5>
     })
 
@@ -147,7 +147,7 @@ test:do_catchsql_test(
         UPDATE t1 SET x=7 WHERE x==2
     ]], {
         -- <check-1.12>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-1.12>
     })
 
@@ -167,7 +167,7 @@ test:do_catchsql_test(
         UPDATE t1 SET x=5 WHERE x==2
     ]], {
         -- <check-1.14>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-1.14>
     })
 
@@ -246,7 +246,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(3, 1.1, NULL, NULL);
     ]], {
         -- <check-2.4>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: ONE"
+        1, "Failed to execute SQL statement: CHECK constraint failed: ONE"
         -- </check-2.4>
     })
 
@@ -256,7 +256,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(4, NULL, 5, NULL);
     ]], {
         -- <check-2.5>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: TWO"
+        1, "Failed to execute SQL statement: CHECK constraint failed: TWO"
         -- </check-2.5>
     })
 
@@ -266,7 +266,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(5, NULL, NULL, 3.14159);
     ]], {
         -- <check-2.6>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: THREE"
+        1, "Failed to execute SQL statement: CHECK constraint failed: THREE"
         -- </check-2.6>
     })
 
@@ -413,7 +413,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(111,222,333);
     ]], {
         -- <check-3.9>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T3"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T3"
         -- </check-3.9>
     })
 
@@ -484,7 +484,7 @@ test:do_catchsql_test(
         UPDATE t4 SET x=0, y=1;
     ]], {
         -- <check-4.6>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T4"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T4"
         -- </check-4.6>
     })
 
@@ -504,7 +504,7 @@ test:do_catchsql_test(
         UPDATE t4 SET x=0, y=2;
     ]], {
         -- <check-4.9>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T4"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T4"
         -- </check-4.9>
     })
 
@@ -581,7 +581,7 @@ test:do_catchsql_test(
         UPDATE OR FAIL t1 SET x=7-x, y=y+1;
     ]], {
         -- <check-6.5>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-6.5>
     })
 
@@ -603,7 +603,7 @@ test:do_catchsql_test(
         INSERT OR ROLLBACK INTO t1 VALUES(8,40.0, 10);
     ]], {
         -- <check-6.7>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-6.7>
     })
 
@@ -613,7 +613,7 @@ test:do_catchsql_test(
         COMMIT;
     ]], {
         -- <check-6.8>
-        1, "Error during execution of VDBE byte-code: cannot commit - no transaction is active"
+        1, "Failed to execute SQL statement: cannot commit - no transaction is active"
         -- </check-6.8>
     })
 
@@ -636,7 +636,7 @@ test:do_catchsql_test(
         REPLACE INTO t1 VALUES(6,7, 11);
     ]], {
         -- <check-6.12>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T1"
         -- </check-6.12>
     })
 
@@ -700,7 +700,7 @@ test:do_catchsql_test(
     7.3,
     " INSERT INTO t6 VALUES(11) ", {
         -- <7.3>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T6"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T6"
         -- </7.3>
     })
 
diff --git a/test/sql-tap/fkey1.test.lua b/test/sql-tap/fkey1.test.lua
index f7c473f..7000ec0 100755
--- a/test/sql-tap/fkey1.test.lua
+++ b/test/sql-tap/fkey1.test.lua
@@ -158,7 +158,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO t11 VALUES (2, 3);
     ]], {
         -- <fkey1-5.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey1-5.2>
     })
 
@@ -194,7 +194,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3');
     ]], {
         -- <fkey1-5.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey1-5.5>
     })
 
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index 525ef2a..09d55be 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -26,7 +26,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,d) VALUES(1, 3);
     ]], {
         -- <fkey2-1.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.2>
     })
 
@@ -56,7 +56,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,d) VALUES(2, 4);
     ]], {
         -- <fkey2-1.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.5>
     })
 
@@ -77,7 +77,7 @@ test:do_catchsql_test(
         UPDATE t2 SET c = 2 WHERE d = 4;
     ]], {
         -- <fkey2-1.7>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.7>
     })
 
@@ -98,7 +98,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE a = 1;
     ]], {
         -- <fkey2-1.9>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.9>
     })
 
@@ -109,7 +109,7 @@ test:do_catchsql_test(
         UPDATE t1 SET a = 2;
     ]], {
         -- <fkey2-1.10>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.10>
     })
 
@@ -131,7 +131,7 @@ test:do_catchsql_test(
         INSERT INTO t4(c,d) values (1,3);
     ]], {
         -- <fkey2-1.12>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.12>
     })
 
@@ -161,7 +161,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (1,3);
     ]], {
         -- <fkey2-1.15>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.15>
     })
 
@@ -191,7 +191,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (2,4);
     ]], {
         -- <fkey2-1.18>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.18>
     })
 
@@ -201,7 +201,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) values (6,4);
     ]], {
         -- <fkey2-1.19>
-        1,"Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1,"Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.19>
     })
 
@@ -241,7 +241,7 @@ test:do_catchsql_test(
         DELETE FROM t7 WHERE b = 1;
     ]], {
         -- <fkey2-1.23>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.23>
     })
 
@@ -251,7 +251,7 @@ test:do_catchsql_test(
         UPDATE t7 SET b = 2;
     ]], {
         -- <fkey2-1.24>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.24>
     })
 
@@ -271,7 +271,7 @@ test:do_catchsql_test(
         INSERT INTO t8(c,d) VALUES(666, 54644);
     ]], {
         -- <fkey2-1.26>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.26>
     })
 
@@ -281,7 +281,7 @@ test:do_catchsql_test(
         UPDATE t7 SET b = 5;
     ]], {
         -- <fkey2-1.27>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-1.27>
     })
 
@@ -333,7 +333,7 @@ test:do_catchsql_test(
         DELETE FROM i;
     ]], {
         -- <fkey2-2.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-2.2>
     })
 
@@ -362,7 +362,7 @@ test:do_catchsql_test(
         UPDATE ab SET a = 5;
     ]], {
         -- <fkey2-3.2>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: EF"
+        1, "Failed to execute SQL statement: CHECK constraint failed: EF"
         -- </fkey2-3.2>
     })
 
@@ -382,7 +382,7 @@ test:do_catchsql_test(
         UPDATE ab SET a = 5;
     ]], {
         -- <fkey2-3.4>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: EF"
+        1, "Failed to execute SQL statement: CHECK constraint failed: EF"
         -- </fkey2-3.4>
     })
 
@@ -403,7 +403,7 @@ test:do_catchsql_test(
         DELETE FROM ab;
     ]], {
         -- <fkey2-3.6>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-3.6>
     })
 
@@ -540,7 +540,7 @@ test:do_catchsql_test(
         INSERT INTO t2(c,b) VALUES(1, 'A');
     ]], {
         -- <fkey2-5.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-5.2>
     })
 
@@ -562,7 +562,7 @@ test:do_catchsql_test(
         UPDATE t2 SET c = 3;
     ]], {
         -- <fkey2-5.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-5.4>
     })
 
@@ -572,7 +572,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE a = 2;
     ]], {
         -- <fkey2-5.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-5.5>
     })
 
@@ -591,7 +591,7 @@ test:do_catchsql_test(
         UPDATE t1 SET a = 3;
     ]], {
         -- <fkey2-5.7>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-5.7>
     })
 
@@ -656,7 +656,7 @@ test:do_catchsql_test(
         DELETE FROM t1;
     ]], {
         -- <fkey2-6.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-6.5>
     })
 
@@ -808,7 +808,7 @@ test:do_catchsql_test(
       UPDATE t1 SET b = 'five' WHERE b = 'two';
     ]], {
         -- <fkey2-9.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.2>
     })
 
@@ -818,7 +818,7 @@ test:do_catchsql_test(
         DELETE FROM t1 WHERE b = 'two';
     ]], {
         -- <fkey2-9.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.3>
     })
 
@@ -828,7 +828,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES('five');
     ]], {
         -- <fkey2-9.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.4>
     })
 
@@ -885,7 +885,7 @@ test:do_catchsql_test(
         DELETE FROM t1;
     ]], {
         -- <fkey2-9.8>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.8>
     })
 
@@ -942,7 +942,7 @@ test:do_catchsql_test(
         INSERT INTO down(c39, c38) VALUES('yes', 'no');
     ]], {
         -- <fkey2-9.12>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.12>
     })
 
@@ -954,7 +954,7 @@ test:do_catchsql_test(
         DELETE FROM up WHERE c34 = 'yes';
     ]], {
         -- <fkey2-9.13>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-9.13>
     })
 
@@ -1007,7 +1007,7 @@ test:do_execsql_test(
 --         INSERT INTO t3 VALUES(1, 2, 3);
 --     ]], {
 --         -- <fkey2-10.2>
---         1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+--         1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
 --         -- </fkey2-10.2>
 --     })
 
@@ -1027,7 +1027,7 @@ test:do_execsql_test(
 --         UPDATE t4 SET b = 5;
 --     ]], {
 --         -- <fkey2-10.4>
---         1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+--         1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
 --         -- </fkey2-10.4>
 --     })
 
@@ -1183,7 +1183,7 @@ test:do_catchsql_test(
         UPDATE self SET b = 15;
     ]], {
         -- <fkey2-11.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.2>
     })
 
@@ -1193,7 +1193,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15;
     ]], {
         -- <fkey2-11.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.3>
     })
 
@@ -1203,7 +1203,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15, b = 16;
     ]], {
         -- <fkey2-11.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.4>
     })
 
@@ -1231,7 +1231,7 @@ test:do_catchsql_test(
         INSERT INTO self(a,b) VALUES(20, 21);
     ]], {
         -- <fkey2-11.7>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.7>
     })
 
@@ -1253,7 +1253,7 @@ test:do_catchsql_test(
         UPDATE self SET b = 15;
     ]], {
         -- <fkey2-11.9>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.9>
     })
 
@@ -1263,7 +1263,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15;
     ]], {
         -- <fkey2-11.10>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.10>
     })
 
@@ -1273,7 +1273,7 @@ test:do_catchsql_test(
         UPDATE self SET a = 15, b = 16;
     ]], {
         -- <fkey2-11.11>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.11>
     })
 
@@ -1301,7 +1301,7 @@ test:do_catchsql_test(
         INSERT INTO self(a,b) VALUES(20, 21);
     ]], {
         -- <fkey2-11.14>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-11.14>
     })
 
@@ -1328,7 +1328,7 @@ test:do_catchsql_test(
         DELETE FROM tdd08;
     ]], {
         -- <fkey2-12.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-12.2>
     })
 
@@ -1348,7 +1348,7 @@ test:do_catchsql_test(
         INSERT INTO tdd08_b(w,x,y) VALUES(400,500,300);
     ]], {
         -- <fkey2-12.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-12.4>
     })
 
@@ -1358,7 +1358,7 @@ test:do_catchsql_test(
         UPDATE tdd08_b SET x=x+1;
     ]], {
         -- <fkey2-12.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-12.5>
     })
 
@@ -1368,7 +1368,7 @@ test:do_catchsql_test(
         UPDATE tdd08 SET a=a+1;
     ]], {
         -- <fkey2-12.6>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-12.6>
     })
 
@@ -1394,7 +1394,7 @@ test:do_catchsql_test(
         UPDATE tce71 set b = 201 where a = 100;
     ]], {
         -- <fkey2-13.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-13.2>
     })
 
@@ -1404,7 +1404,7 @@ test:do_catchsql_test(
         UPDATE tce71 set a = 101 where a = 100;
     ]], {
         -- <fkey2-13.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-13.3>
     })
 
@@ -1429,7 +1429,7 @@ test:do_catchsql_test(
         UPDATE tce73 set b = 201 where a = 100;
     ]], {
         -- <fkey2-14.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-14.2>
     })
 
@@ -1439,7 +1439,7 @@ test:do_catchsql_test(
         UPDATE tce71 set a = 101 where a = 100;
     ]], {
         -- <fkey2-14.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey2-14.3>
     })
 
diff --git a/test/sql-tap/fkey3.test.lua b/test/sql-tap/fkey3.test.lua
index c66b5e0..900ac32 100755
--- a/test/sql-tap/fkey3.test.lua
+++ b/test/sql-tap/fkey3.test.lua
@@ -122,7 +122,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(2, 2, 5, 2);
     ]], {
         -- <fkey3-3.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.2>
     })
 
@@ -132,7 +132,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(2, 3, 5, 2);
     ]], {
         -- <fkey3-3.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.3>
     })
 
@@ -151,7 +151,7 @@ test:do_catchsql_test(
         INSERT INTO t4 VALUES(2, 1);
     ]], {
         -- <fkey3-3.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.5>
     })
 
@@ -175,7 +175,7 @@ test:do_catchsql_test(
         INSERT INTO t6(a,b,c,d) VALUES(4, 'a', 65, 'a');
     ]], {
         -- <fkey3-3.7>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.7>
     })
 
@@ -197,7 +197,7 @@ test:do_catchsql_test(
         UPDATE t6 SET c = 1, d = 'a' WHERE a = 100;
     ]], {
         -- <fkey3-3.9>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.9>
     })
 
@@ -219,7 +219,7 @@ test:do_catchsql_test(
         INSERT INTO t7 VALUES('x', 450, 'x', 4);
     ]], {
         -- <fkey3-3.11>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.11>
     })
 
@@ -229,7 +229,7 @@ test:do_catchsql_test(
         INSERT INTO t7 VALUES('x', 450, 'x', 451);
     ]], {
         -- <fkey3-3.12>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-3.12>
     })
 
@@ -253,7 +253,7 @@ test:do_catchsql_test(
         UPDATE t8 SET d = 2;
     ]], {
         -- <fkey3-6.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-6.2>
     })
 
@@ -282,7 +282,7 @@ test:do_catchsql_test(
         UPDATE TestTable SET parent_id=1000 WHERE id=2;
     ]], {
         -- <fkey3-6.4>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey3-6.4>
     })
 
diff --git a/test/sql-tap/fkey4.test.lua b/test/sql-tap/fkey4.test.lua
index 3a03cd5..ea77278 100755
--- a/test/sql-tap/fkey4.test.lua
+++ b/test/sql-tap/fkey4.test.lua
@@ -30,7 +30,7 @@ test:do_catchsql_test(
         DELETE FROM p1 WHERE a = 2;
     ]], {
         -- <fkey8-1.2>
-        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: C1.B"
+        1, "Failed to execute SQL statement: NOT NULL constraint failed: C1.B"
         -- </fkey8-1.2>
     })
 
@@ -83,7 +83,7 @@ test:do_catchsql_test(
         DELETE FROM p1 WHERE a = 2;
     ]], {
         -- <fkey8-1.5>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey8-1.5>
     })
 
@@ -159,7 +159,7 @@ test:do_catchsql_test(
         UPDATE OR IGNORE p1 SET a = 4 WHERE a = 2;
     ]], {
         -- <fkey8-1.9>
-        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: C1.B"
+        1, "Failed to execute SQL statement: NOT NULL constraint failed: C1.B"
         -- </fkey8-1.9>
     })
 
@@ -203,7 +203,7 @@ test:do_catchsql_test(
         INSERT OR REPLACE INTO p1 VALUES(2, 'two');
     ]], {
         -- <fkey8-2.2>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey8-2.2>
     })
 
@@ -278,7 +278,7 @@ test:do_catchsql_test(
         DELETE FROM p3 WHERE a=1;
     ]], {
         -- <fkey8-4.3>
-        1, "Error during execution of VDBE byte-code: FOREIGN KEY constraint failed"
+        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
         -- </fkey8-4.3>
     })
 
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index a7fff6e..f6f1eb1 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -1598,7 +1598,7 @@ test:do_catchsql_test(
         SELECT sum(x) - ((1<<62)*2.0+1) from t6;
     ]], {
         -- <func-18.12>
-        1, "Error during execution of VDBE byte-code: integer overflow"
+        1, "Failed to execute SQL statement: integer overflow"
         -- </func-18.12>
     })
 
@@ -1653,7 +1653,7 @@ test:do_catchsql_test(
             SELECT 10 AS x);
     ]], {
         -- <func-18.15>
-        1, "Error during execution of VDBE byte-code: integer overflow"
+        1, "Failed to execute SQL statement: integer overflow"
         -- </func-18.15>
     })
 
@@ -1665,7 +1665,7 @@ test:do_catchsql_test(
             SELECT -10 AS x);
     ]], {
         -- <func-18.18>
-        1, "Error during execution of VDBE byte-code: integer overflow"
+        1, "Failed to execute SQL statement: integer overflow"
         -- </func-18.18>
     })
 
@@ -1730,7 +1730,7 @@ test:do_catchsql_test(
         SELECT abs(-9223372036854775807-1);
     ]], {
         -- <func-18.32>
-        1, "Error during execution of VDBE byte-code: integer overflow"
+        1, "Failed to execute SQL statement: integer overflow"
         -- </func-18.32>
     })
 
@@ -1752,7 +1752,7 @@ test:do_catchsql_test(
         SELECT 'abc' MATCH 'xyz';
     ]], {
         -- <func-19.2>
-        1, "Error during execution of VDBE byte-code: unable to use function MATCH in the requested context"
+        1, "Failed to execute SQL statement: unable to use function MATCH in the requested context"
         -- </func-19.2>
     })
 
@@ -1762,7 +1762,7 @@ test:do_catchsql_test(
         SELECT 'abc' NOT MATCH 'xyz';
     ]], {
         -- <func-19.3>
-        1, "Error during execution of VDBE byte-code: unable to use function MATCH in the requested context"
+        1, "Failed to execute SQL statement: unable to use function MATCH in the requested context"
         -- </func-19.3>
     })
 
diff --git a/test/sql-tap/gh-2931-savepoints.test.lua b/test/sql-tap/gh-2931-savepoints.test.lua
index 85dc7a1..acb8bd4 100755
--- a/test/sql-tap/gh-2931-savepoints.test.lua
+++ b/test/sql-tap/gh-2931-savepoints.test.lua
@@ -38,7 +38,7 @@ local testcases = {
 		{0,{1,1}}},
 	{"5",
 		[[rollback to savepoint s1_2;]],
-		{1, "Error during execution of VDBE byte-code: no such savepoint: S1_2"}},
+		{1, "Failed to execute SQL statement: no such savepoint: S1_2"}},
 	{"6",
 		[[insert into t1 values(2);
 		select * from t1 union all select * from t2;]],
@@ -80,13 +80,13 @@ local testcases = {
 		{0,{1,2,10,11,1,2,4,10,11}}},
 	{"14",
 		[[insert into t1 values(4);]],
-		{1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
+		{1,"Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
 	{"15",
 		[[select * from t1 union all select * from t2;]],
 		{0,{1,2,10,11,1,2,4,10,11}}},
 	{"16",
 		[[insert or rollback into t1 values(4);]],
-		{1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
+		{1,"Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}},
 	{"17",  -- should work as transaction is rolled back
 		[[insert or rollback into t1 values(4);
 		select * from t1 union all select * from t2;]],
diff --git a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
index 0e0a003..dec3cce 100755
--- a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
+++ b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
@@ -142,17 +142,17 @@ for i, tested_string in ipairs(invalid_testcases) do
     local test_name = prefix .. "2." .. tostring(i)
     local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Failed to execute SQL statement: LIKE pattern can only contain UTF-8 characters"})
 
     test_name = prefix .. "3." .. tostring(i)
     test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Failed to execute SQL statement: LIKE pattern can only contain UTF-8 characters"})
 
     test_name = prefix .. "4." .. tostring(i)
     test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"
     test:do_catchsql_test(test_name, test_itself,
-                          {1, "Error during execution of VDBE byte-code: LIKE pattern can only contain UTF-8 characters"})
+                          {1, "Failed to execute SQL statement: LIKE pattern can only contain UTF-8 characters"})
 
     -- Just skipping if row value predicand contains invalid character.
 
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 2369be7..859a6ff 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -139,7 +139,7 @@ test:do_catchsql_xfer_test(
         INSERT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.9>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.9>
     }, {
         exp_xfer_count = 0
@@ -210,7 +210,7 @@ test:do_catchsql_xfer_test(
             INSERT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.13>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.13>
     }, {
         exp_xfer_count = 0
@@ -245,7 +245,7 @@ test:do_catchsql_xfer_test(
             INSERT OR ABORT INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.20>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.20>
     }, {
         exp_xfer_count = 1
@@ -313,7 +313,7 @@ test:do_catchsql_xfer_test(
             INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.24>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.24>
     }, {
         exp_xfer_count = 0
@@ -381,7 +381,7 @@ test:do_catchsql_xfer_test(
             INSERT OR FAIL INTO t2 SELECT * FROM t1;
     ]], {
         -- <xfer-optimization-1.28>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"
         -- <xfer-optimization-1.28>
     }, {
         exp_xfer_count = 1
diff --git a/test/sql-tap/gh2259-in-stmt-trans.test.lua b/test/sql-tap/gh2259-in-stmt-trans.test.lua
index 1ac0d53..36f5e90 100755
--- a/test/sql-tap/gh2259-in-stmt-trans.test.lua
+++ b/test/sql-tap/gh2259-in-stmt-trans.test.lua
@@ -18,7 +18,7 @@ for _, prefix in pairs({"BEFORE", "AFTER"}) do
 
     test:do_catchsql_test(prefix..'_insert1',
                           'INSERT INTO t1 VALUES(1, 2)',
-                          {1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1,"Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     test:do_execsql_test(prefix..'_insert1_check1',
                          'SELECT *  FROM t1',
@@ -34,7 +34,7 @@ for _, prefix in pairs({"BEFORE", "AFTER"}) do
 
     test:do_catchsql_test(prefix..'_update1',
                           'UPDATE t1 SET s1=1',
-                          {1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1,"Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     test:do_execsql_test(prefix..'_update1_check1',
                          'SELECT *  FROM t1',
@@ -52,7 +52,7 @@ for _, prefix in pairs({"BEFORE", "AFTER"}) do
 
     test:do_catchsql_test(prefix..'delete1',
                           'DELETE FROM t1;',
-                          {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                          {1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 
     -- Nothing should be inserted due to abort
     test:do_execsql_test('delete1_check1',
@@ -69,7 +69,7 @@ end
 -- Check multi-insert
 test:do_catchsql_test('insert2',
                       'INSERT INTO t1 VALUES (5, 6), (6, 7)',
-                      {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
+                      {1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_2' in space 'T2'"})
 test:do_execsql_test('insert2_check',
                      'SELECT * FROM t1;',
                      {3, 3})
diff --git a/test/sql-tap/gh2964-abort.test.lua b/test/sql-tap/gh2964-abort.test.lua
index 82aa3e4..fce691a 100755
--- a/test/sql-tap/gh2964-abort.test.lua
+++ b/test/sql-tap/gh2964-abort.test.lua
@@ -12,7 +12,7 @@ test:do_catchsql_test(
     test_prefix.."1.0.2",
     "CREATE TABLE t2 (a int primary key);")
 
-local insert_err = {1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}
+local insert_err = {1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T2_1' in space 'T2'"}
 local data = {
 --id|TRIG TYPE|INSERT TYPE|insert error|commit error| result
  {1, "AFTER", "or abort",   insert_err, {0},          {1,1,2}},
diff --git a/test/sql-tap/intpkey.test.lua b/test/sql-tap/intpkey.test.lua
index 3ec3771..b2e8084 100755
--- a/test/sql-tap/intpkey.test.lua
+++ b/test/sql-tap/intpkey.test.lua
@@ -96,7 +96,7 @@ test:do_catchsql_test(
         INSERT INTO t1 VALUES(5,'second','entry');
     ]], {
         -- <intpkey-1.6>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </intpkey-1.6>
     })
 
diff --git a/test/sql-tap/misc1.test.lua b/test/sql-tap/misc1.test.lua
index 55c0c13..abd2be5 100755
--- a/test/sql-tap/misc1.test.lua
+++ b/test/sql-tap/misc1.test.lua
@@ -383,7 +383,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(1,2,4);
     ]], {
         -- <misc1-7.4>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T5_1' in space 'T5'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T5_1' in space 'T5'"
         -- </misc1-7.4>
     })
 
diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
index 4470bce..5b793c0 100755
--- a/test/sql-tap/table.test.lua
+++ b/test/sql-tap/table.test.lua
@@ -722,7 +722,7 @@ test:do_catchsql_test(
         INSERT INTO t6 VALUES(NULL);
     ]], {
         -- <table-10.1>
-        1, "Error during execution of VDBE byte-code: NOT NULL constraint failed: T6.A"
+        1, "Failed to execute SQL statement: NOT NULL constraint failed: T6.A"
         -- </table-10.1>
     })
 
@@ -1211,7 +1211,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, 2, 2);
     ]], {
         -- <table-21.2>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T21_1' in space 'T21'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T21_1' in space 'T21'"
         -- </table-21.2>
     })
 
@@ -1221,7 +1221,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, -1, 1);
     ]], {
         -- <table-21.3>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T21"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T21"
         -- </table-21.3>
     })
 
@@ -1231,7 +1231,7 @@ test:do_catchsql_test(
         INSERT INTO T21 VALUES(1, 1, -1);
     ]], {
         -- <table-21.4>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: T21"
+        1, "Failed to execute SQL statement: CHECK constraint failed: T21"
         -- </table-21.4>
     })
 
@@ -1283,7 +1283,7 @@ test:do_catchsql_test(
         INSERT INTO T22 VALUES(2, 1, 1);
     ]], {
         -- <table-22.3>
-        1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_ONE_2' in space 'T22'"
+        1,"Failed to execute SQL statement: Duplicate key exists in unique index 'unique_ONE_2' in space 'T22'"
         -- </table-22.3>
     })
 
@@ -1308,7 +1308,7 @@ test:do_catchsql_test(
         INSERT INTO T24 VALUES(2, 1, 1);
     ]], {
         -- <table-22.5>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_TWO_2' in space 'T24'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'unique_TWO_2' in space 'T24'"
         -- </table-22.5>
     })
 
@@ -1362,7 +1362,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(11);
     ]], {
         -- <table-22.9>
-        1,"Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T28_1' in space 'T28'"
+        1,"Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T28_1' in space 'T28'"
         -- </table-22.9>
     })
 
@@ -1372,7 +1372,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(0);
     ]], {
         -- <table-22.10>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: CHECK1"
+        1, "Failed to execute SQL statement: CHECK constraint failed: CHECK1"
         -- </table-22.10>
     })
 
@@ -1382,7 +1382,7 @@ test:do_catchsql_test(
         INSERT INTO T28 VALUES(9);
     ]], {
         -- <table-22.11>
-        1, "Error during execution of VDBE byte-code: CHECK constraint failed: CHECK2"
+        1, "Failed to execute SQL statement: CHECK constraint failed: CHECK2"
         -- </table-22.11>
     })
 
diff --git a/test/sql-tap/tkt-4a03edc4c8.test.lua b/test/sql-tap/tkt-4a03edc4c8.test.lua
index 703bf74..0f44d4f 100755
--- a/test/sql-tap/tkt-4a03edc4c8.test.lua
+++ b/test/sql-tap/tkt-4a03edc4c8.test.lua
@@ -38,7 +38,7 @@ test:do_test(
         ]]
     end, {
         -- <tkt-4a03ed-1.1>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </tkt-4a03ed-1.1>
     })
 
diff --git a/test/sql-tap/trigger1.test.lua b/test/sql-tap/trigger1.test.lua
index bf1857f..a7dafd4 100755
--- a/test/sql-tap/trigger1.test.lua
+++ b/test/sql-tap/trigger1.test.lua
@@ -251,7 +251,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.12>
-        1, "Error during execution of VDBE byte-code: SQL error: cannot create INSTEAD OF trigger on space: T1"
+        1, "Failed to execute SQL statement: SQL error: cannot create INSTEAD OF trigger on space: T1"
         -- </trigger1-1.12>
     })
 
@@ -265,7 +265,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.13>
-        1, "Error during execution of VDBE byte-code: SQL error: cannot create BEFORE trigger on view: V1"
+        1, "Failed to execute SQL statement: SQL error: cannot create BEFORE trigger on view: V1"
         -- </trigger1-1.13>
     })
 
@@ -280,7 +280,7 @@ test:do_catchsql_test(
         end;
     ]], {
         -- <trigger1-1.14>
-        1, "Error during execution of VDBE byte-code: SQL error: cannot create AFTER trigger on view: V1"
+        1, "Failed to execute SQL statement: SQL error: cannot create AFTER trigger on view: V1"
         -- </trigger1-1.14>
     })
 
@@ -495,7 +495,7 @@ test:do_catchsql_test(
         DELETE FROM t2
     ]], {
         -- <trigger1-6.3>
-        1, "Error during execution of VDBE byte-code: deletes are not permitted"
+        1, "Failed to execute SQL statement: deletes are not permitted"
         -- </trigger1-6.3>
     })
 
@@ -857,7 +857,7 @@ test:do_catchsql_test(
             INSERT INTO t16 values(1);
           END;
    ]], {
-        1, [[Error during execution of VDBE byte-code: Space _trigger does not support multi-statement transactions]]
+        1, [[Failed to execute SQL statement: Space _trigger does not support multi-statement transactions]]
 })
 
 test:execsql [[
@@ -870,7 +870,7 @@ test:do_catchsql_test(
         START TRANSACTION;
           DROP TRIGGER t16err3;
    ]], {
-        1, [[Error during execution of VDBE byte-code: Space _trigger does not support multi-statement transactions]]
+        1, [[Failed to execute SQL statement: Space _trigger does not support multi-statement transactions]]
 })
 -- MUST_WORK_TEST
 -- #-------------------------------------------------------------------------
diff --git a/test/sql-tap/triggerC.test.lua b/test/sql-tap/triggerC.test.lua
index 76558f1..c050fb2 100755
--- a/test/sql-tap/triggerC.test.lua
+++ b/test/sql-tap/triggerC.test.lua
@@ -165,7 +165,7 @@ test:do_test(
         return test:catchsql " DELETE FROM t4 "
     end, {
         -- <triggerC-1.9>
-        1, "Error during execution of VDBE byte-code: delete is not supported"
+        1, "Failed to execute SQL statement: delete is not supported"
         -- </triggerC-1.9>
     })
 
@@ -199,7 +199,7 @@ test:do_catchsql_test(
         UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1
     ]], {
         -- <triggerC-1.12>
-        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
+        1, "Failed to execute SQL statement: too many levels of trigger recursion"
         -- </triggerC-1.12>
     })
 
@@ -285,7 +285,7 @@ tests =   { {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
 
             {[[ CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN
                   INSERT INTO t2 VALUES(new.a - 1);
-                END;]], {1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"}},
+                END;]], {1, "Failed to execute SQL statement: too many levels of trigger recursion"}},
 
             {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
                   INSERT OR IGNORE INTO t2 VALUES(new.a);
@@ -293,7 +293,7 @@ tests =   { {[[ CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN
 
             {[[  CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN
                    INSERT OR IGNORE INTO t2 VALUES(new.a);
-                 END;]], {1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"}}}
+                 END;]], {1, "Failed to execute SQL statement: too many levels of trigger recursion"}}}
 
 for n, v in ipairs(tests) do
     test:do_test(
@@ -382,7 +382,7 @@ test:do_catchsql_test(
         INSERT INTO t3 VALUES(0,0)
     ]], {
         -- <triggerC-3.1.2>
-        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
+        1, "Failed to execute SQL statement: too many levels of trigger recursion"
         -- </triggerC-3.1.2>
     })
 
@@ -411,7 +411,7 @@ test:do_catchsql_test(
         INSERT INTO t3b VALUES(1);
     ]], {
         -- <triggerC-3.1.3>
-        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
+        1, "Failed to execute SQL statement: too many levels of trigger recursion"
         -- </triggerC-3.1.3>
     })
 
@@ -908,7 +908,7 @@ test:do_catchsql_test(
         UPDATE t12 SET a=a+1, b=b+1;
     ]], {
         -- <triggerC-13.2>
-        1, "Error during execution of VDBE byte-code: too many levels of trigger recursion"
+        1, "Failed to execute SQL statement: too many levels of trigger recursion"
         -- </triggerC-13.2>
     })
 
diff --git a/test/sql-tap/unique.test.lua b/test/sql-tap/unique.test.lua
index 927c419..7ec7651 100755
--- a/test/sql-tap/unique.test.lua
+++ b/test/sql-tap/unique.test.lua
@@ -70,7 +70,7 @@ test:do_catchsql_test(
         INSERT INTO t1(a,b,c) VALUES(1,3,4)
     ]], {
         -- <unique-1.3>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'pk_unnamed_T1_1' in space 'T1'"
         -- </unique-1.3>
     })
 
@@ -91,7 +91,7 @@ test:do_catchsql_test(
         INSERT INTO t1(a,b,c) VALUES(3,2,4)
     ]], {
         -- <unique-1.5>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
         -- </unique-1.5>
     })
 
@@ -167,7 +167,7 @@ test:do_catchsql_test(
         INSERT INTO t2 VALUES(3, 1,5);
     ]], {
         -- <unique-2.3>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'I2' in space 'T2'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'I2' in space 'T2'"
         -- </unique-2.3>
     })
 
@@ -287,7 +287,7 @@ test:do_catchsql_test(
         SELECT a,b,c,d FROM t3 ORDER BY a,b,c,d;
     ]], {
         -- <unique-3.4>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T3_2' in space 'T3'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'unique_unnamed_T3_2' in space 'T3'"
         -- </unique-3.4>
     })
 
@@ -444,7 +444,7 @@ test:do_catchsql_test(
         INSERT INTO t5 VALUES(2, 1,2,3,4,5,6);
     ]], {
         -- <unique-5.2>
-        1, "Error during execution of VDBE byte-code: Duplicate key exists in unique index 'unique_unnamed_T5_2' in space 'T5'"
+        1, "Failed to execute SQL statement: Duplicate key exists in unique index 'unique_unnamed_T5_2' in space 'T5'"
         -- </unique-5.2>
     })
 
diff --git a/test/sql/collation.result b/test/sql/collation.result
index cc1b1a9..397f653 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -426,7 +426,7 @@ box.execute("INSERT INTO t1 VALUES (1,'a');")
 -- Should fail.
 box.execute("UPDATE t0 SET s1 = 'A';")
 ---
-- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
+- error: 'Failed to execute SQL statement: FOREIGN KEY constraint failed'
 ...
 box.execute("SELECT * FROM t1;")
 ---
@@ -676,8 +676,8 @@ box.execute("INSERT INTO t3b VALUES ('A');")
 ...
 box.execute("INSERT INTO t3b VALUES ('a');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3B_1'' in space ''T3B'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3B_1''
+    in space ''T3B'''
 ...
 box.execute("SELECT * FROM t3b;")
 ---
@@ -741,8 +741,8 @@ box.execute("INSERT INTO t3c VALUES ('A');")
 ...
 box.execute("INSERT INTO t3c VALUES ('a');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3C_1'' in space ''T3C'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3C_1''
+    in space ''T3C'''
 ...
 box.execute("SELECT * FROM t3c;")
 ---
@@ -806,8 +806,8 @@ box.execute("INSERT INTO t3d VALUES ('A');")
 ...
 box.execute("INSERT INTO t3d VALUES ('a');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3D_1'' in space ''T3D'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3D_1''
+    in space ''T3D'''
 ...
 box.execute("SELECT * FROM t3d;")
 ---
@@ -837,8 +837,8 @@ box.execute("INSERT INTO t3e VALUES ('a');")
 ...
 box.execute("INSERT INTO t3e VALUES ('A');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3E_1'' in space ''T3E'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3E_1''
+    in space ''T3E'''
 ...
 box.execute("SELECT * FROM t3e;")
 ---
@@ -901,8 +901,8 @@ box.execute("INSERT INTO t3f VALUES ('A');")
 ...
 box.execute("INSERT INTO t3f VALUES ('a');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3F_1'' in space ''T3F'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3F_1''
+    in space ''T3F'''
 ...
 box.execute("SELECT * FROM t3f;")
 ---
@@ -932,8 +932,8 @@ box.execute("INSERT INTO t3g VALUES ('a');")
 ...
 box.execute("INSERT INTO t3g VALUES ('A');")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_T3G_1'' in space ''T3G'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_T3G_1''
+    in space ''T3G'''
 ...
 box.execute("SELECT * FROM t3g;")
 ---
diff --git a/test/sql/errinj.result b/test/sql/errinj.result
index 4b26560..63fa9d9 100644
--- a/test/sql/errinj.result
+++ b/test/sql/errinj.result
@@ -189,7 +189,7 @@ box.error.injection.set("ERRINJ_WAL_IO", true)
 ...
 box.execute("CREATE TRIGGER t1t INSERT ON t1 BEGIN INSERT INTO t2 VALUES (1, 1); END;")
 ---
-- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
+- error: 'Failed to execute SQL statement: Failed to write to disk'
 ...
 box.execute("CREATE INDEX t1a ON t1(a);")
 ---
@@ -257,7 +257,7 @@ box.error.injection.set("ERRINJ_WAL_IO", true)
 ...
 box.execute("DROP TRIGGER t1t;")
 ---
-- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
+- error: 'Failed to execute SQL statement: Failed to write to disk'
 ...
 box.error.injection.set("ERRINJ_WAL_IO", false)
 ---
@@ -334,7 +334,7 @@ errinj.set("ERRINJ_WAL_IO", false)
 ...
 box.execute("INSERT INTO t3 VALUES (1, 2, 2);")
 ---
-- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
+- error: 'Failed to execute SQL statement: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", true)
 ---
@@ -366,7 +366,7 @@ box.execute("ALTER TABLE t3 ADD CONSTRAINT fk1 FOREIGN KEY (b) REFERENCES t3;")
 ...
 box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 ---
-- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
+- error: 'Failed to execute SQL statement: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", true)
 ---
@@ -374,11 +374,11 @@ errinj.set("ERRINJ_WAL_IO", true)
 ...
 box.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;")
 ---
-- error: 'Error during execution of VDBE byte-code: Failed to write to disk'
+- error: 'Failed to execute SQL statement: Failed to write to disk'
 ...
 box.execute("INSERT INTO t3 VALUES(1, 1, 3);")
 ---
-- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
+- error: 'Failed to execute SQL statement: FOREIGN KEY constraint failed'
 ...
 errinj.set("ERRINJ_WAL_IO", false)
 ---
diff --git a/test/sql/foreign-keys.result b/test/sql/foreign-keys.result
index 293492e..f0ff70d 100644
--- a/test/sql/foreign-keys.result
+++ b/test/sql/foreign-keys.result
@@ -181,8 +181,8 @@ t = box.space._fk_constraint:insert(t)
 --
 box.execute("DROP INDEX i1 on t1;")
 ---
-- error: 'Error during execution of VDBE byte-code: Can''t modify space ''T1'': can
-    not drop a referenced index'
+- error: 'Failed to execute SQL statement: Can''t modify space ''T1'': can not drop
+    a referenced index'
 ...
 -- Referenced index can't be altered as well, if alter leads to
 -- rebuild of index (e.g. index still can be renamed).
diff --git a/test/sql/gh-2981-check-autoinc.result b/test/sql/gh-2981-check-autoinc.result
index 00bfe91..ee5ab4c 100644
--- a/test/sql/gh-2981-check-autoinc.result
+++ b/test/sql/gh-2981-check-autoinc.result
@@ -29,7 +29,7 @@ box.execute("insert into t1 values (18, null);")
 ...
 box.execute("insert into t1(s2) values (null);")
 ---
-- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T1'
+- error: 'Failed to execute SQL statement: CHECK constraint failed: T1'
 ...
 box.execute("insert into t2 values (18, null);")
 ---
@@ -37,7 +37,7 @@ box.execute("insert into t2 values (18, null);")
 ...
 box.execute("insert into t2(s2) values (null);")
 ---
-- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T2'
+- error: 'Failed to execute SQL statement: CHECK constraint failed: T2'
 ...
 box.execute("insert into t2 values (24, null);")
 ---
@@ -45,7 +45,7 @@ box.execute("insert into t2 values (24, null);")
 ...
 box.execute("insert into t2(s2) values (null);")
 ---
-- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T2'
+- error: 'Failed to execute SQL statement: CHECK constraint failed: T2'
 ...
 box.execute("insert into t3 values (9, null)")
 ---
@@ -53,7 +53,7 @@ box.execute("insert into t3 values (9, null)")
 ...
 box.execute("insert into t3(s2) values (null)")
 ---
-- error: 'Error during execution of VDBE byte-code: CHECK constraint failed: T3'
+- error: 'Failed to execute SQL statement: CHECK constraint failed: T3'
 ...
 box.execute("DROP TABLE t1")
 ---
diff --git a/test/sql/insert-unique.result b/test/sql/insert-unique.result
index a18f9ff..75354de 100644
--- a/test/sql/insert-unique.result
+++ b/test/sql/insert-unique.result
@@ -28,14 +28,14 @@ box.execute("INSERT INTO zoobar VALUES (111, 222, 'c3', 444)")
 -- PK must be unique
 box.execute("INSERT INTO zoobar VALUES (112, 222, 'c3', 444)")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_ZOOBAR_1'' in space ''ZOOBAR'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_ZOOBAR_1''
+    in space ''ZOOBAR'''
 ...
 -- Unique index must be respected
 box.execute("INSERT INTO zoobar VALUES (111, 223, 'c3', 444)")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''ZOOBAR2'' in space ''ZOOBAR'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''ZOOBAR2''
+    in space ''ZOOBAR'''
 ...
 -- Cleanup
 box.execute("DROP INDEX zoobar2 ON zoobar")
diff --git a/test/sql/persistency.result b/test/sql/persistency.result
index 9e5994b..8ba42cb 100644
--- a/test/sql/persistency.result
+++ b/test/sql/persistency.result
@@ -31,8 +31,8 @@ box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 ...
 box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_FOOBAR_1'' in space ''FOOBAR'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_FOOBAR_1''
+    in space ''FOOBAR'''
 ...
 -- simple select
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
@@ -416,8 +416,8 @@ box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
 -- prove barfoo2 still exists
 box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_BARFOO_1'' in space ''BARFOO'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_BARFOO_1''
+    in space ''BARFOO'''
 ...
 box.execute("SELECT * FROM barfoo")
 ---
diff --git a/test/sql/row-count.result b/test/sql/row-count.result
index fe48430..657f03a 100644
--- a/test/sql/row-count.result
+++ b/test/sql/row-count.result
@@ -272,8 +272,7 @@ box.execute("SELECT ROW_COUNT();")
 ...
 box.execute("COMMIT;")
 ---
-- error: 'Error during execution of VDBE byte-code: cannot commit - no transaction
-    is active'
+- error: 'Failed to execute SQL statement: cannot commit - no transaction is active'
 ...
 box.execute("SELECT ROW_COUNT();")
 ---
diff --git a/test/sql/savepoints.result b/test/sql/savepoints.result
index d378172..3cebc65 100644
--- a/test/sql/savepoints.result
+++ b/test/sql/savepoints.result
@@ -67,7 +67,7 @@ end;
 ...
 release_sv_fail();
 ---
-- error: 'Error during execution of VDBE byte-code: no such savepoint: T1'
+- error: 'Failed to execute SQL statement: no such savepoint: T1'
 ...
 box.commit();
 ---
diff --git a/test/sql/transition.result b/test/sql/transition.result
index b348730..19212c5 100644
--- a/test/sql/transition.result
+++ b/test/sql/transition.result
@@ -28,8 +28,8 @@ box.execute("INSERT INTO foobar VALUES (1000, 'foobar')")
 ...
 box.execute("INSERT INTO foobar VALUES (1, 'duplicate')")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_FOOBAR_1'' in space ''FOOBAR'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_FOOBAR_1''
+    in space ''FOOBAR'''
 ...
 -- simple select
 box.execute("SELECT bar, foo, 42, 'awesome' FROM foobar")
@@ -312,8 +312,8 @@ box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 -- prove barfoo2 was created
 box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
 ---
-- error: 'Error during execution of VDBE byte-code: Duplicate key exists in unique
-    index ''pk_unnamed_BARFOO_1'' in space ''BARFOO'''
+- error: 'Failed to execute SQL statement: Duplicate key exists in unique index ''pk_unnamed_BARFOO_1''
+    in space ''BARFOO'''
 ...
 box.execute("SELECT foo, bar FROM barfoo")
 ---
diff --git a/test/sql/transitive-transactions.result b/test/sql/transitive-transactions.result
index 82e0406..72b19d2 100644
--- a/test/sql/transitive-transactions.result
+++ b/test/sql/transitive-transactions.result
@@ -100,7 +100,7 @@ end;
 ...
 fk_defer();
 ---
-- error: 'Error during execution of VDBE byte-code: FOREIGN KEY constraint failed'
+- error: 'Failed to execute SQL statement: FOREIGN KEY constraint failed'
 ...
 box.space.CHILD:select();
 ---
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 3f0f388..9ec932f 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -486,7 +486,7 @@ space_id = box.space.T1.id
 ...
 box.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;")
 ---
-- error: 'Error during execution of VDBE byte-code: bindings are not allowed in DDL'
+- error: 'Failed to execute SQL statement: bindings are not allowed in DDL'
 ...
 tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END;]]}}
 ---



New patch:

commit 64bd59449df2a45be02670249a69e80cd87ef3b0
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Sat Mar 16 14:15:22 2019 +0300

    sql: fix error code for SQL errors in execute.c
    
    Currently, functions sql_execute() and sql_prepare_and_execute()
    set the ER_SQL_EXECUTE code for all errors that occur during the
    execution of a SQL command. This is considered incorrect because
    some of these errors may have their own error code. After this
    patch, only errors without an error code will have the error code
    ER_SQL_EXECUTE.
    
    Part of #3505

diff --git a/src/box/execute.c b/src/box/execute.c
index 7c77df2..d45fb12 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -523,7 +523,12 @@ sql_execute(sql *db, struct sql_stmt *stmt, struct port *port,
 		assert(rc != SQL_ROW && rc != SQL_OK);
 	}
 	if (rc != SQL_DONE) {
-		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_SQL_EXECUTE, err);
+		}
 		return -1;
 	}
 	return 0;
@@ -541,7 +546,12 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
 		return -1;
 	}
 	if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) {
-		diag_set(ClientError, ER_SQL_EXECUTE, sql_errmsg(db));
+		if (db->errCode != SQL_TARANTOOL_ERROR) {
+			const char *err = (char *)sql_value_text(db->pErr);
+			if (err == NULL)
+				err = sqlErrStr(db->errCode);
+			diag_set(ClientError, ER_SQL_EXECUTE, err);
+		}
 		return -1;
 	}
 	assert(stmt != NULL);
diff --git a/test/sql/collation.result b/test/sql/collation.result
index 90a9f72..ac8cff2 100644
--- a/test/sql/collation.result
+++ b/test/sql/collation.result
@@ -102,7 +102,7 @@ cn = remote.connect(box.cfg.listen)
 ...
 cn:execute('select 1 limit ? collate not_exist', {1})
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''COLLATE'''
+- error: Syntax error near 'COLLATE'
 ...
 cn:close()
 ---
diff --git a/test/sql/gh-2362-select-access-rights.result b/test/sql/gh-2362-select-access-rights.result
index 0e5b9bf..8f1ecfa 100644
--- a/test/sql/gh-2362-select-access-rights.result
+++ b/test/sql/gh-2362-select-access-rights.result
@@ -46,8 +46,7 @@ c = nb.connect(box.cfg.listen)
 ...
 c:execute("SELECT * FROM t1;")
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.schema.user.grant('guest','read', 'space', 'T2')
 ---
@@ -57,8 +56,7 @@ c = nb.connect(box.cfg.listen)
 ...
 c:execute('SELECT * FROM t1, t2 WHERE t1.s1 = t2.s1')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.sql.execute("CREATE VIEW v AS SELECT * FROM t1")
 ---
@@ -71,8 +69,7 @@ v = nb.connect(box.cfg.listen)
 ...
 c:execute('SELECT * FROM v')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 box.sql.execute('CREATE TABLE t3 (s1 INT PRIMARY KEY, fk INT, FOREIGN KEY (fk) REFERENCES t1(s2))')
 ---
@@ -85,8 +82,7 @@ v = nb.connect(box.cfg.listen)
 ...
 c:execute('INSERT INTO t3 VALUES (1, 1)')
 ---
-- error: 'Failed to execute SQL statement: Read access to space ''T1'' is denied for
-    user ''guest'''
+- error: Read access to space 'T1' is denied for user 'guest'
 ...
 -- Cleanup
 box.schema.user.revoke('guest','read','space', 'V')
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index afddf05..33e845c 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -92,11 +92,11 @@ cn:execute('delete from test where a = 12')
 -- SQL errors.
 cn:execute('insert into not_existing_table values ("kek")')
 ---
-- error: 'Failed to execute SQL statement: Space ''NOT_EXISTING_TABLE'' does not exist'
+- error: Space 'NOT_EXISTING_TABLE' does not exist
 ...
 cn:execute('insert qwerty gjsdjq  q  qwd qmq;; q;qwd;')
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''qwerty'''
+- error: Syntax error near 'qwerty'
 ...
 -- Empty result.
 cn:execute('select id as identifier from test where a = 5;')
@@ -109,7 +109,7 @@ cn:execute('select id as identifier from test where a = 5;')
 -- netbox API errors.
 cn:execute(100)
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''100'''
+- error: Syntax error near '100'
 ...
 cn:execute('select 1', nil, {dry_run = true})
 ---
@@ -118,11 +118,11 @@ cn:execute('select 1', nil, {dry_run = true})
 -- Empty request.
 cn:execute('')
 ---
-- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
+- error: Failed to execute an empty SQL statement
 ...
 cn:execute('   ;')
 ---
-- error: 'Failed to execute SQL statement: Failed to execute an empty SQL statement'
+- error: Failed to execute an empty SQL statement
 ...
 --
 -- gh-3467: allow only positive integers under limit clause.
@@ -154,18 +154,15 @@ cn:execute('select * from test limit ?', {2})
 ...
 cn:execute('select * from test limit ?', {-2})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit ?', {2.7})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit ?', {'Hello'})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    LIMIT clause'
+- error: Only positive integers are allowed in the LIMIT clause
 ...
 cn:execute('select * from test limit 1 offset ?', {2})
 ---
@@ -181,18 +178,15 @@ cn:execute('select * from test limit 1 offset ?', {2})
 ...
 cn:execute('select * from test limit 1 offset ?', {-2})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 cn:execute('select * from test limit 1 offset ?', {2.7})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 cn:execute('select * from test limit 1 offset ?', {'Hello'})
 ---
-- error: 'Failed to execute SQL statement: Only positive integers are allowed in the
-    OFFSET clause'
+- error: Only positive integers are allowed in the OFFSET clause
 ...
 --
 -- Parameters binding.
@@ -363,7 +357,7 @@ sql = 'select '..string.rep('?, ', box.schema.SQL_BIND_PARAMETER_MAX)..'?'
 ...
 cn:execute(sql)
 ---
-- error: 'Failed to execute SQL statement: SQL bind parameter limit reached: 65000'
+- error: 'SQL bind parameter limit reached: 65000'
 ...
 -- Try too many parameter values.
 sql = 'select ?'
@@ -567,12 +561,11 @@ cn:execute('drop table if exists test3')
 --
 cn:execute('select ?1, ?2, ?3', {1, 2, 3})
 ---
-- error: 'Failed to execute SQL statement: Syntax error near ''?1'''
+- error: Syntax error near '?1'
 ...
 cn:execute('select $name, $name2', {1, 2})
 ---
-- error: 'Failed to execute SQL statement: Index of binding slots must start from
-    1'
+- error: Index of binding slots must start from 1
 ...
 parameters = {}
 ---

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-26 21:48       ` Vladislav Shpilevoy
@ 2019-03-28 17:48         ` Mergen Imeev
  2019-03-28 18:01           ` Vladislav Shpilevoy
  0 siblings, 1 reply; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 17:48 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches, kostja

Hi! Thank you for review. My answers, diff and new patch below.

On Wed, Mar 27, 2019 at 12:48:39AM +0300, Vladislav Shpilevoy wrote:
> Thanks for the patch! See 3 comments below.
> 
> > commit 1e21fc73be5df524d500723bd3e47066475ee6b9
> > Author: Mergen Imeev <imeevma@gmail.com>
> > Date:   Thu Jan 31 14:09:51 2019 +0300
> > 
> >     sql: remove box.sql.debug()
> >     
> >     Due to removing of box.sql.execute() it makes sense to remove
> 
> 1. 'removing' -> 'removal'.
> 
Fixed.

> >     box.sql.debug() and move SQL statistics to box.stat.
> 
> 2. For a foreign reader it is unknown that box.sql.execute() removal
> == box.sql removal. Probably, it should be said explicitly, that
> we want to remove the whole box.sql.
> 
Fixed.

> >     
> >     Part or #3505
> > 
> > diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
> > index d56de4e..1663e39 100755
> > --- a/test/sql-tap/between.test.lua
> > +++ b/test/sql-tap/between.test.lua
> > @@ -51,10 +51,10 @@ test:do_test(
> >  -- is done.  Then it appends the names of the table and index used.
> >  --
> >  local function queryplan(sql)
> > -    local sql_sort_count = box.sql.debug().sql_sort_count
> > +    local sqlite_sort_count = box.stat.sql().sql_sort_count
> 
> 3. 'sqlite' -> 'sql'
> 
Fixed.


Diff:

diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index 1663e39..cd1aba1 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -51,10 +51,10 @@ test:do_test(
 -- is done.  Then it appends the names of the table and index used.
 --
 local function queryplan(sql)
-    local sqlite_sort_count = box.stat.sql().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
     local x = "nosort"
-    if box.stat.sql().sql_sort_count - sqlite_sort_count then
+    if box.stat.sql().sql_sort_count - sql_sort_count then
         x = "sort"
     end
     table.insert(data,x)



New patch:

commit 9ccee3a74a213ec1cc2e32e870d118dd772f4a6f
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Thu Jan 31 14:09:51 2019 +0300

    sql: remove box.sql.debug()
    
    Due to removal of box.sql.execute(), it makes sense to completely
    remove box.sql. This patch moves the SQL statistics to box.stat
    and removes box.sql.debug().
    
    Part or #3505

diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
index ee20faa..cd6e75c 100644
--- a/src/box/lua/sql.c
+++ b/src/box/lua/sql.c
@@ -110,21 +110,11 @@ sqlerror:
 	return lua_error(L);
 }
 
-static int
-lua_sql_debug(struct lua_State *L)
-{
-	struct info_handler info;
-	luaT_info_handler_create(&info, L);
-	sql_debug_info(&info);
-	return 1;
-}
-
 void
 box_lua_sql_init(struct lua_State *L)
 {
 	static const struct luaL_Reg module_funcs [] = {
 		{"execute", lua_sql_execute},
-		{"debug", lua_sql_debug},
 		{NULL, NULL}
 	};
 
diff --git a/src/box/lua/stat.c b/src/box/lua/stat.c
index f6b2812..18b81a1 100644
--- a/src/box/lua/stat.c
+++ b/src/box/lua/stat.c
@@ -42,6 +42,7 @@
 #include "box/iproto.h"
 #include "box/engine.h"
 #include "box/vinyl.h"
+#include "box/sql.h"
 #include "info/info.h"
 #include "lua/info.h"
 #include "lua/utils.h"
@@ -187,6 +188,15 @@ lbox_stat_net_call(struct lua_State *L)
 	return 1;
 }
 
+static int
+lbox_stat_sql(struct lua_State *L)
+{
+	struct info_handler info;
+	luaT_info_handler_create(&info, L);
+	sql_debug_info(&info);
+	return 1;
+}
+
 static const struct luaL_Reg lbox_stat_meta [] = {
 	{"__index", lbox_stat_index},
 	{"__call",  lbox_stat_call},
@@ -206,6 +216,7 @@ box_lua_stat_init(struct lua_State *L)
 	static const struct luaL_Reg statlib [] = {
 		{"vinyl", lbox_stat_vinyl},
 		{"reset", lbox_stat_reset},
+		{"sql", lbox_stat_sql},
 		{NULL, NULL}
 	};
 
diff --git a/test/sql-tap/analyze3.test.lua b/test/sql-tap/analyze3.test.lua
index 1396287..dcbea1d 100755
--- a/test/sql-tap/analyze3.test.lua
+++ b/test/sql-tap/analyze3.test.lua
@@ -48,15 +48,6 @@ testprefix = "analyze3"
 --               have been fixed.
 --
 
-local function eqp(sql)
-    return test:execsql("EXPLAIN QUERY PLAN"..sql)
-end
-
-local function sf_execsql(sql, db)
-    r = test:execsql(sql)
-    return {box.sql.debug().sql_search_count, r}
-end
-
 ---------------------------------------------------------------------------
 --
 -- analyze3-1.1.1: 
diff --git a/test/sql-tap/between.test.lua b/test/sql-tap/between.test.lua
index d56de4e..cd1aba1 100755
--- a/test/sql-tap/between.test.lua
+++ b/test/sql-tap/between.test.lua
@@ -51,10 +51,10 @@ test:do_test(
 -- is done.  Then it appends the names of the table and index used.
 --
 local function queryplan(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
     local x = "nosort"
-    if box.sql.debug().sql_sort_count - sql_sort_count then
+    if box.stat.sql().sql_sort_count - sql_sort_count then
         x = "sort"
     end
     table.insert(data,x)
diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
index 80a2a2d..bb8a498 100755
--- a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua
@@ -5,9 +5,9 @@ test:plan(39)
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end
diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index 8aac64c..05e2824 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -326,9 +326,9 @@ function test.do_select_tests(self, label, tests)
 end
 
 function test.sf_execsql(self, sql)
-    local old = box.sql.debug().sql_search_count
+    local old = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    local new = box.sql.debug().sql_search_count - old
+    local new = box.stat.sql().sql_search_count - old
 
     return {new, r}
 end
diff --git a/test/sql-tap/minmax2.test.lua b/test/sql-tap/minmax2.test.lua
index a6f840d..0e0f0d0 100755
--- a/test/sql-tap/minmax2.test.lua
+++ b/test/sql-tap/minmax2.test.lua
@@ -59,7 +59,7 @@ test:do_execsql_test(
 test:do_test(
     "minmax2-1.1",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.1>
@@ -70,13 +70,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.2",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.3",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.3>
@@ -87,14 +87,14 @@ test:do_test(
 test:do_test(
     "minmax2-1.4",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
     "minmax2-1.5",
     function()
         test:execsql "CREATE INDEX t1i1 ON t1(x DESC)"
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(x) FROM t1"
     end, {
         -- <minmax2-1.5>
@@ -105,13 +105,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.6",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_test(
     "minmax2-1.7",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(x) FROM t1"
     end, {
         -- <minmax2-1.7>
@@ -122,13 +122,13 @@ test:do_test(
 test:do_test(
     "minmax2-1.8",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-1.9",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(y) FROM t1"
     end, {
         -- <minmax2-1.9>
@@ -139,7 +139,7 @@ test:do_test(
 test:do_test(
     "minmax2-1.10",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 19)
 
 test:do_test(
@@ -149,7 +149,7 @@ test:do_test(
             CREATE TABLE t2(a INTEGER PRIMARY KEY, b INT );
             INSERT INTO t2 SELECT x, y FROM t1;
         ]]
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT min(a) FROM t2"
     end, {
         -- <minmax2-2.0>
@@ -160,13 +160,13 @@ test:do_test(
 test:do_test(
     "minmax2-2.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
     "minmax2-2.2",
     function()
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-2.2>
@@ -177,7 +177,7 @@ test:do_test(
 test:do_test(
     "minmax2-2.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -186,7 +186,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql "SELECT max(a) FROM t2"
     end, {
         -- <minmax2-3.0>
@@ -197,7 +197,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.1",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 0)
 
 test:do_test(
@@ -206,7 +206,7 @@ test:do_test(
         test:execsql "INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)"
 
 
-        sql_search_count = box.sql.debug().sql_search_count
+        sql_search_count = box.stat.sql().sql_search_count
         return test:execsql " SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) "
 
 
@@ -220,7 +220,7 @@ test:do_test(
 test:do_test(
     "minmax2-3.3",
     function()
-        return box.sql.debug().sql_search_count - sql_search_count
+        return box.stat.sql().sql_search_count - sql_search_count
     end, 1)
 
 test:do_execsql_test(
diff --git a/test/sql-tap/minmax3.test.lua b/test/sql-tap/minmax3.test.lua
index 3707575..f1d9efc 100755
--- a/test/sql-tap/minmax3.test.lua
+++ b/test/sql-tap/minmax3.test.lua
@@ -24,9 +24,9 @@ test:plan(47)
 --
 
 local function count(sql)
-    local sql_search_count = box.sql.debug().sql_search_count
+    local sql_search_count = box.stat.sql().sql_search_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+    table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
     return r
 end
 
diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua
index 8eaf405..4116ca9 100755
--- a/test/sql-tap/where2.test.lua
+++ b/test/sql-tap/where2.test.lua
@@ -62,10 +62,10 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    print("lol "..(box.sql.debug().sql_sort_count - sql_sort_count))
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    print("lol "..(box.stat.sql().sql_sort_count - sql_sort_count))
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
@@ -78,9 +78,9 @@ end
 --
 local function cksort(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
             sort = 'sort'
     end
     table.insert(data, sort)
@@ -97,9 +97,9 @@ end
 --
 local function queryplan(sql)
     local sort = "nosort"
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local data = test:execsql(sql)
-    if sql_sort_count < box.sql.debug().sql_sort_count then
+    if sql_sort_count < box.stat.sql().sql_sort_count then
         sort = 'sort'
     end
     table.insert(data, sort)
diff --git a/test/sql-tap/whereA.test.lua b/test/sql-tap/whereA.test.lua
index b4a0f0a..2c8cb45 100755
--- a/test/sql-tap/whereA.test.lua
+++ b/test/sql-tap/whereA.test.lua
@@ -193,9 +193,9 @@ test:do_test(
 -- Do an SQL statement.  Append the search count to the end of the result.
 --
 local function count(sql)
-    local sql_sort_count = box.sql.debug().sql_sort_count
+    local sql_sort_count = box.stat.sql().sql_sort_count
     local r = test:execsql(sql)
-    table.insert(r, box.sql.debug().sql_sort_count - sql_sort_count)
+    table.insert(r, box.stat.sql().sql_sort_count - sql_sort_count)
     return r
 end
 
diff --git a/test/sql-tap/whereD.test.lua b/test/sql-tap/whereD.test.lua
index b103f3c..14dc8d3 100755
--- a/test/sql-tap/whereD.test.lua
+++ b/test/sql-tap/whereD.test.lua
@@ -234,10 +234,10 @@ local function do_searchcount_test(tn, sql, res)
     test:do_test(
         tn,
         function()
-            local sql_search_count = box.sql.debug().sql_search_count
+            local sql_search_count = box.stat.sql().sql_search_count
             local r = test:execsql(sql)
             table.insert(r, "search")
-            table.insert(r, box.sql.debug().sql_search_count - sql_search_count)
+            table.insert(r, box.stat.sql().sql_search_count - sql_search_count)
             return r
         end,
         res)
diff --git a/test/sql-tap/with2.test.lua b/test/sql-tap/with2.test.lua
index ca3f00e..48f7377 100755
--- a/test/sql-tap/with2.test.lua
+++ b/test/sql-tap/with2.test.lua
@@ -389,9 +389,9 @@ genstmt(255), {
 local function do_xfer_test(test, test_func, test_name, func, exp, opts)
     local opts = opts or {}
     local exp_xfer_count = opts.exp_xfer_count
-    local before = box.sql.debug().sql_xfer_count
+    local before = box.stat.sql().sql_xfer_count
     test_func(test, test_name, func, exp)
-    local after = box.sql.debug().sql_xfer_count
+    local after = box.stat.sql().sql_xfer_count
     test:is(after - before, exp_xfer_count,
                    test_name .. '-xfer-count')
 end

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-26 21:48   ` Vladislav Shpilevoy
@ 2019-03-28 17:54     ` Mergen Imeev
  2019-03-28 18:40       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 17:54 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Hi! Thank you for review. I created a new function, but it turned
out to be quite complex. Sorry, I have not found a simpler
solution. My answer, diff and new patch below.

On Wed, Mar 27, 2019 at 12:48:32AM +0300, Vladislav Shpilevoy wrote:
> Thanks for the patch!
> 
> >> 2. Looking at lua_field_inspect_table I found that if a table has __serialize
> >> metamethod, it is called without a protection (utils.c:409). __serialize is an
> >> arbitrary unprotected user code, that can throw an error deliberately. What are
> >> we gonna do with that? Personally I've already faced with some user code, throwing
> >> an error from __serialize, deliberately. On not critical errors not meaning panic.
> >>
> >> As a solution we could 1) do not care, again; 2) finally accept the fact that
> >> wrap into a pcall was not so bad and use it; 3) use lua_pcall in that
> >> particular place. Please, consult Kostja, what does he choose.
> > 
> > I checked it this way:
> > 
> > diff --git a/src/lua/utils.c b/src/lua/utils.c
> > index bbfc549..eae3b5f 100644
> > --- a/src/lua/utils.c
> > +++ b/src/lua/utils.c
> > @@ -398,6 +398,20 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
> >  	lua_settop(L, top); /* remove temporary objects */
> >  }
> >  
> > +/**
> > + * Check that the field LUAL_SERIALIZE of the metatable is
> > + * available.
> > + *
> > + * @param L Lua State.
> > + */
> > +static int
> > +get_metafield_serialize(struct lua_State *L)
> > +{
> > +	int idx = *(int *)lua_topointer(L, -1);
> > +	luaL_getmetafield(L, idx, LUAL_SERIALIZE);
> > +	return 0;
> > +}
> > +
> >  static int
> >  lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
> >  			int idx, struct luaL_field *field)
> > @@ -408,6 +422,9 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
> >  	uint32_t max = 0;
> >  
> >  	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
> > +	if (!cfg->encode_load_metatables &&
> > +	    luaT_cpcall(L, get_metafield_serialize, &idx) != 0)
> > +		return -1;
> >  	if (!cfg->encode_load_metatables ||
> >  	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
> >  		goto skip;
> > 
> 
> Unfortunately, it was not the place I told about. You
> wrapped just a fetch of __serialize function, but not its
> call. It is still unsafe on line 435. In other words it was:
> 
>     1 func = obj.__serialize
>     2 func(obj)
> 
> You made it:
> 
>     1 func = pcall(function() obj.__serialize end)
>     2 func(obj)
> 
> The second line is still unsafe. And much more unsafe than
> the first one.
> 
> But it is a good catch too, because a user could define
> such an __index for his metatable, that it throws an error.
> You should wrap both __serialize fetch and call into a single
> lua_cpcall.
Fixed.


Diff:

diff --git a/src/lua/utils.c b/src/lua/utils.c
index eae3b5f..6150e84 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -400,72 +400,110 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 
 /**
  * Check that the field LUAL_SERIALIZE of the metatable is
- * available.
+ * available. If so, create field using it.
+ *
+ * This function returns two values on stack. On top of the stack
+ * is boolean that shows if error happened. On the next position
+ * can be boolean or result of execution of function from the
+ * metatable. If it is boolean, that if its value is false than
+ * there were no metatables.It its value is true than there was
+ * string in that field of metatable.
  *
  * @param L Lua State.
  */
 static int
 get_metafield_serialize(struct lua_State *L)
 {
-	int idx = *(int *)lua_topointer(L, -1);
-	luaL_getmetafield(L, idx, LUAL_SERIALIZE);
-	return 0;
-}
-
-static int
-lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
-			int idx, struct luaL_field *field)
-{
-	assert(lua_type(L, idx) == LUA_TTABLE);
-	const char *type;
-	uint32_t size = 0;
-	uint32_t max = 0;
-
-	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
-	if (!cfg->encode_load_metatables &&
-	    luaT_cpcall(L, get_metafield_serialize, &idx) != 0)
-		return -1;
-	if (!cfg->encode_load_metatables ||
-	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
-		goto skip;
+	if (luaL_getmetafield(L, 1, LUAL_SERIALIZE) == 0) {
+		lua_pushboolean(L, false);
+		lua_pushboolean(L, false);
+		return 2;
+	}
+	bool is_error = false;
+	struct luaL_serializer *cfg =
+		(struct luaL_serializer *)lua_touserdata(L, 2);
+	struct luaL_field *field =
+		(struct luaL_field *)lua_touserdata(L, 3);
 
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
-		lua_pushvalue(L, idx);
+		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
-		/* replace obj with the unpacked value */
-		lua_replace(L, idx);
-		return luaL_tofield(L, cfg, idx, field);
-	} else if (!lua_isstring(L, -1)) {
+		is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+		lua_pushboolean(L, is_error);
+		return 2;
+	}
+
+	if (!lua_isstring(L, -1)) {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		return -1;
+		lua_pushboolean(L, true);
+		return 2;
 	}
 
-	type = lua_tostring(L, -1);
+	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
 	    strcmp(type, "sequence") == 0) {
 		field->type = MP_ARRAY; /* Override type */
-		field->size = luaL_arrlen(L, idx);
+		field->size = luaL_arrlen(L, 1);
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-
-		return 0;
+		lua_pushboolean(L, true);
+		lua_pushboolean(L, false);
+		return 2;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
-		field->size = luaL_maplen(L, idx);
+		field->size = luaL_maplen(L, 1);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-		return 0;
+		lua_pushboolean(L, true);
+		lua_pushboolean(L, false);
+		return 2;
 	} else {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		return -1;
+		lua_pushboolean(L, true);
+		return 2;
+	}
+}
+
+static int
+lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
+			int idx, struct luaL_field *field)
+{
+	assert(lua_type(L, idx) == LUA_TTABLE);
+	uint32_t size = 0;
+	uint32_t max = 0;
+
+	if (cfg->encode_load_metatables) {
+		lua_pushcfunction(L, get_metafield_serialize);
+		lua_pushvalue(L, idx);
+		lua_pushlightuserdata(L, cfg);
+		lua_pushlightuserdata(L, field);
+		if (lua_pcall(L, 3, 2, 0) != 0) {
+			diag_set(LuajitError, lua_tostring(L, -1));
+			return -1;
+		}
+		bool is_error = lua_toboolean(L, -1);
+		bool is_bool = lua_isboolean(L, -2);
+		if (is_error) {
+			lua_pop(L, 2);
+			return -1;
+		}
+		if (is_bool) {
+			bool is_metatable_available = lua_toboolean(L, -2);
+			lua_pop(L, 2);
+			if (is_metatable_available)
+				return 0;
+		} else {
+			lua_pop(L, 1);
+			lua_replace(L, idx);
+			return 0;
+		}
 	}
 
-skip:
 	field->type = MP_ARRAY;
 
 	/* Calculate size and check that table can represent an array */


New patch:

commit 0c9ec5f6d4c935c3811600ca166727afd5005e7a
Author: Mergen Imeev <imeevma@gmail.com>
Date:   Fri Jan 11 21:16:12 2019 +0300

    lua: remove exceptions from function luaL_tofield()
    
    Before this commit, the luaL_tofield() function threw a Lua
    exception when an error occurred. This behavior has been changed
    in this patch. Now it sets diag_set() and returns -1 in case of
    an error.
    
    Needed for #3505

diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 52939ae..2049ee5 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -164,7 +164,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 		 */
 		for (int i = 1; i <= nrets; ++i) {
 			struct luaL_field field;
-			luaL_tofield(L, cfg, i, &field);
+			if (luaL_tofield(L, cfg, i, &field) < 0)
+				return luaT_error(L);
 			struct tuple *tuple;
 			if (field.type == MP_EXT &&
 			    (tuple = luaT_istuple(L, i)) != NULL) {
@@ -192,7 +193,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	 * Inspect the first result
 	 */
 	struct luaL_field root;
-	luaL_tofield(L, cfg, 1, &root);
+	if (luaL_tofield(L, cfg, 1, &root) < 0)
+		return luaT_error(L);
 	struct tuple *tuple;
 	if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) {
 		/* `return box.tuple()` */
@@ -221,7 +223,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	for (uint32_t t = 1; t <= root.size; t++) {
 		lua_rawgeti(L, 1, t);
 		struct luaL_field field;
-		luaL_tofield(L, cfg, -1, &field);
+		if (luaL_tofield(L, cfg, -1, &field) < 0)
+			return luaT_error(L);
 		if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) {
 			tuple_to_mpstream(tuple, stream);
 		} else if (field.type != MP_ARRAY) {
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 60c1a39..1903ee8 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -229,7 +229,8 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
 		return tuple_to_mpstream(tuple, stream);
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, index, &field);
+	if (luaL_tofield(L, cfg, index, &field) < 0)
+		luaT_error(L);
 	if (field.type == MP_ARRAY) {
 		lua_pushvalue(L, index);
 		luamp_encode_r(L, cfg, stream, &field, 0);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index b470060..1b1874e 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -149,10 +149,12 @@ restart: /* used by MP_EXT */
 		lua_pushnil(L);  /* first key */
 		while (lua_next(L, top) != 0) {
 			lua_pushvalue(L, -2); /* push a copy of key to top */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop a copy of key */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop value */
 		}
@@ -168,7 +170,8 @@ restart: /* used by MP_EXT */
 		mpstream_encode_array(stream, size);
 		for (uint32_t i = 0; i < size; i++) {
 			lua_rawgeti(L, top, i + 1);
-			luaL_tofield(L, cfg, top + 1, field);
+			if (luaL_tofield(L, cfg, top + 1, field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1);
 		}
@@ -203,7 +206,8 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
 	}
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, lua_gettop(L), &field);
+	if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0)
+		return luaT_error(L);
 	enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0);
 
 	if (!on_top) {
diff --git a/src/lua/utils.c b/src/lua/utils.c
index a418b95..6150e84 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -392,61 +392,118 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 		lua_pcall(L, 1, 1, 0);
 		/* replace obj with the unpacked value */
 		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
+		if (luaL_tofield(L, cfg, idx, field) < 0)
+			luaT_error(L);
 	} /* else ignore lua_gettable exceptions */
 	lua_settop(L, top); /* remove temporary objects */
 }
 
-static void
-lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
-			int idx, struct luaL_field *field)
+/**
+ * Check that the field LUAL_SERIALIZE of the metatable is
+ * available. If so, create field using it.
+ *
+ * This function returns two values on stack. On top of the stack
+ * is boolean that shows if error happened. On the next position
+ * can be boolean or result of execution of function from the
+ * metatable. If it is boolean, that if its value is false than
+ * there were no metatables.It its value is true than there was
+ * string in that field of metatable.
+ *
+ * @param L Lua State.
+ */
+static int
+get_metafield_serialize(struct lua_State *L)
 {
-	assert(lua_type(L, idx) == LUA_TTABLE);
-	const char *type;
-	uint32_t size = 0;
-	uint32_t max = 0;
-
-	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
-	if (!cfg->encode_load_metatables ||
-	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
-		goto skip;
+	if (luaL_getmetafield(L, 1, LUAL_SERIALIZE) == 0) {
+		lua_pushboolean(L, false);
+		lua_pushboolean(L, false);
+		return 2;
+	}
+	bool is_error = false;
+	struct luaL_serializer *cfg =
+		(struct luaL_serializer *)lua_touserdata(L, 2);
+	struct luaL_field *field =
+		(struct luaL_field *)lua_touserdata(L, 3);
 
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
-		lua_pushvalue(L, idx);
+		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
-		/* replace obj with the unpacked value */
-		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
-		return;
-	} else if (!lua_isstring(L, -1)) {
-		luaL_error(L, "invalid " LUAL_SERIALIZE  " value");
+		is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+		lua_pushboolean(L, is_error);
+		return 2;
+	}
+
+	if (!lua_isstring(L, -1)) {
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		lua_pushboolean(L, true);
+		return 2;
 	}
 
-	type = lua_tostring(L, -1);
+	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
 	    strcmp(type, "sequence") == 0) {
 		field->type = MP_ARRAY; /* Override type */
-		field->size = luaL_arrlen(L, idx);
+		field->size = luaL_arrlen(L, 1);
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-
-		return;
+		lua_pushboolean(L, true);
+		lua_pushboolean(L, false);
+		return 2;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
-		field->size = luaL_maplen(L, idx);
+		field->size = luaL_maplen(L, 1);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-		return;
+		lua_pushboolean(L, true);
+		lua_pushboolean(L, false);
+		return 2;
 	} else {
-		luaL_error(L, "invalid " LUAL_SERIALIZE "  value");
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		lua_pushboolean(L, true);
+		return 2;
+	}
+}
+
+static int
+lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
+			int idx, struct luaL_field *field)
+{
+	assert(lua_type(L, idx) == LUA_TTABLE);
+	uint32_t size = 0;
+	uint32_t max = 0;
+
+	if (cfg->encode_load_metatables) {
+		lua_pushcfunction(L, get_metafield_serialize);
+		lua_pushvalue(L, idx);
+		lua_pushlightuserdata(L, cfg);
+		lua_pushlightuserdata(L, field);
+		if (lua_pcall(L, 3, 2, 0) != 0) {
+			diag_set(LuajitError, lua_tostring(L, -1));
+			return -1;
+		}
+		bool is_error = lua_toboolean(L, -1);
+		bool is_bool = lua_isboolean(L, -2);
+		if (is_error) {
+			lua_pop(L, 2);
+			return -1;
+		}
+		if (is_bool) {
+			bool is_metatable_available = lua_toboolean(L, -2);
+			lua_pop(L, 2);
+			if (is_metatable_available)
+				return 0;
+		} else {
+			lua_pop(L, 1);
+			lua_replace(L, idx);
+			return 0;
+		}
 	}
 
-skip:
 	field->type = MP_ARRAY;
 
 	/* Calculate size and check that table can represent an array */
@@ -465,7 +522,7 @@ skip:
 			}
 			field->type = MP_MAP;
 			field->size = size;
-			return;
+			return 0;
 		}
 		if (k > max)
 			max = k;
@@ -475,15 +532,18 @@ skip:
 	if (cfg->encode_sparse_ratio > 0 &&
 	    max > size * (uint32_t)cfg->encode_sparse_ratio &&
 	    max > (uint32_t)cfg->encode_sparse_safe) {
-		if (!cfg->encode_sparse_convert)
-			luaL_error(L, "excessively sparse array");
+		if (!cfg->encode_sparse_convert) {
+			diag_set(LuajitError, "excessively sparse array");
+			return -1;
+		}
 		field->type = MP_MAP;
 		field->size = size;
-		return;
+		return 0;
 	}
 
 	assert(field->type == MP_ARRAY);
 	field->size = max;
+	return 0;
 }
 
 static void
@@ -496,12 +556,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 	lua_call(L, 1, 1);
 	lua_replace(L, idx);
 	lua_settop(L, top);
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 }
 
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
-		 struct luaL_field *field)
+	     struct luaL_field *field)
 {
 	if (index < 0)
 		index = lua_gettop(L) + index + 1;
@@ -510,10 +571,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	double intpart;
 	size_t size;
 
-#define CHECK_NUMBER(x) ({\
-	if (!isfinite(x) && !cfg->encode_invalid_numbers) {		\
-		if (!cfg->encode_invalid_as_nil)				\
-			luaL_error(L, "number must not be NaN or Inf");		\
+#define CHECK_NUMBER(x) ({							\
+	if (!isfinite(x) && !cfg->encode_invalid_numbers) {			\
+		if (!cfg->encode_invalid_as_nil) {				\
+			diag_set(LuajitError, "number must not be NaN or Inf");	\
+			return -1;						\
+		}								\
 		field->type = MP_NIL;						\
 	}})
 
@@ -534,94 +597,93 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 			field->dval = num;
 			CHECK_NUMBER(num);
 		}
-		return;
+		return 0;
 	case LUA_TCDATA:
 	{
-		uint32_t ctypeid = 0;
-		void *cdata = luaL_checkcdata(L, index, &ctypeid);
+		GCcdata *cd = cdataV(L->base + index - 1);
+		void *cdata = (void *)cdataptr(cd);
+
 		int64_t ival;
-		switch (ctypeid) {
+		switch (cd->ctypeid) {
 		case CTID_BOOL:
 			field->type = MP_BOOL;
 			field->bval = *(bool*) cdata;
-			return;
+			return 0;
 		case CTID_CCHAR:
 		case CTID_INT8:
 			ival = *(int8_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT16:
 			ival = *(int16_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT32:
 			ival = *(int32_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT64:
 			ival = *(int64_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_UINT8:
 			field->type = MP_UINT;
 			field->ival = *(uint8_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT16:
 			field->type = MP_UINT;
 			field->ival = *(uint16_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT32:
 			field->type = MP_UINT;
 			field->ival = *(uint32_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT64:
 			field->type = MP_UINT;
 			field->ival = *(uint64_t *) cdata;
-			return;
+			return 0;
 		case CTID_FLOAT:
 			field->type = MP_FLOAT;
 			field->fval = *(float *) cdata;
 			CHECK_NUMBER(field->fval);
-			return;
+			return 0;
 		case CTID_DOUBLE:
 			field->type = MP_DOUBLE;
 			field->dval = *(double *) cdata;
 			CHECK_NUMBER(field->dval);
-			return;
+			return 0;
 		case CTID_P_CVOID:
 		case CTID_P_VOID:
 			if (*(void **) cdata == NULL) {
 				field->type = MP_NIL;
-				return;
+				return 0;
 			}
 			/* Fall through */
 		default:
 			field->type = MP_EXT;
-			return;
 		}
-		return;
+		return 0;
 	}
 	case LUA_TBOOLEAN:
 		field->type = MP_BOOL;
 		field->bval = lua_toboolean(L, index);
-		return;
+		return 0;
 	case LUA_TNIL:
 		field->type = MP_NIL;
-		return;
+		return 0;
 	case LUA_TSTRING:
 		field->sval.data = lua_tolstring(L, index, &size);
 		field->sval.len = (uint32_t) size;
 		field->type = MP_STR;
-		return;
+		return 0;
 	case LUA_TTABLE:
 	{
 		field->compact = false;
-		lua_field_inspect_table(L, cfg, index, field);
-		return;
+		return lua_field_inspect_table(L, cfg, index, field);
 	}
 	case LUA_TLIGHTUSERDATA:
 	case LUA_TUSERDATA:
@@ -629,14 +691,14 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 		field->sval.len = 0;
 		if (lua_touserdata(L, index) == NULL) {
 			field->type = MP_NIL;
-			return;
+			return 0;
 		}
 		/* Fall through */
 	default:
 		field->type = MP_EXT;
-		return;
 	}
 #undef CHECK_NUMBER
+	return 0;
 }
 
 void
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 96311b7..df4d50e 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -311,8 +311,11 @@ struct luaL_field {
  * @param cfg configuration
  * @param index stack index
  * @param field conversion result
+ *
+ * @retval  0 Success.
+ * @retval -1 Error.
  */
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	     struct luaL_field *field);
 
@@ -352,7 +355,8 @@ static inline void
 luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 		struct luaL_field *field)
 {
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 	if (field->type != MP_EXT)
 		return;
 	luaL_convertfield(L, cfg, idx, field);

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-28 17:48         ` Mergen Imeev
@ 2019-03-28 18:01           ` Vladislav Shpilevoy
  0 siblings, 0 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-28 18:01 UTC (permalink / raw)
  To: tarantool-patches, Mergen Imeev, Kirill Yukhin; +Cc: kostja

First 3 commits LGTM. I propose to push them as soon as
possible so as to save them from taint. And because I am
not sure about how much time next patches will take.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-28 17:54     ` Mergen Imeev
@ 2019-03-28 18:40       ` Vladislav Shpilevoy
  2019-03-28 19:56         ` Mergen Imeev
  0 siblings, 1 reply; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-28 18:40 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

Thanks for the fixes! I've pushed my review fixes on the
branch, and dropped at the end of the email. But I've not
tested them, so I propose you to do that, if you agree
with the concept.

=========================================================

diff --git a/src/lua/utils.c b/src/lua/utils.c
index 6150e84ff..38245a979 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -399,47 +399,58 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 }
 
 /**
- * Check that the field LUAL_SERIALIZE of the metatable is
- * available. If so, create field using it.
- *
- * This function returns two values on stack. On top of the stack
- * is boolean that shows if error happened. On the next position
- * can be boolean or result of execution of function from the
- * metatable. If it is boolean, that if its value is false than
- * there were no metatables.It its value is true than there was
- * string in that field of metatable.
- *
- * @param L Lua State.
+ * A helper structure to simplify safe call of __serialize method.
+ * It passes some arguments into the serializer called via pcall,
+ * and carries out some results.
+ */
+struct lua_serialize_status {
+	/**
+	 * True if an attempt to call __serialize has failed. A
+	 * diag message is set.
+	 */
+	bool is_error;
+	/**
+	 * True, if __serialize exists. Otherwise an ordinary
+	 * default serialization is used.
+	 */
+	bool is_serialize_used;
+	/** Parameters, passed originally to luaL_tofield. */
+	struct luaL_serializer *cfg;
+	struct luaL_field *field;
+};
+
+/**
+ * Call __serialize method of a table object if the former exists.
+ * The function expects 2 values pushed onto the Lua stack: a
+ * value to serialize, and a pointer at a struct
+ * lua_serialize_status object. If __serialize exists and is
+ * correct, then one value is pushed as a result of serialization.
+ * Otherwise is_error and is_serialize_used attributes of status
+ * should be checked.
  */
 static int
-get_metafield_serialize(struct lua_State *L)
+lua_field_try_serialize(struct lua_State *L)
 {
-	if (luaL_getmetafield(L, 1, LUAL_SERIALIZE) == 0) {
-		lua_pushboolean(L, false);
-		lua_pushboolean(L, false);
-		return 2;
-	}
-	bool is_error = false;
-	struct luaL_serializer *cfg =
-		(struct luaL_serializer *)lua_touserdata(L, 2);
-	struct luaL_field *field =
-		(struct luaL_field *)lua_touserdata(L, 3);
-
+	struct lua_serialize_status *s =
+		(struct lua_serialize_status *) lua_touserdata(L, 2);
+	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
+	s->is_error = false;
+	if (! s->is_serialize_used)
+		return 0;
+	struct luaL_serializer *cfg = s->cfg;
+	struct luaL_field *field = s->field;
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
 		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
-		is_error = (luaL_tofield(L, cfg, -1, field) != 0);
-		lua_pushboolean(L, is_error);
-		return 2;
+		s->is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+		return s->is_error ? 0 : 1;
 	}
-
 	if (!lua_isstring(L, -1)) {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		lua_pushboolean(L, true);
-		return 2;
+		s->is_error = true;
+		return 0;
 	}
-
 	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
 	    strcmp(type, "sequence") == 0) {
@@ -449,9 +460,7 @@ get_metafield_serialize(struct lua_State *L)
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-		lua_pushboolean(L, true);
-		lua_pushboolean(L, false);
-		return 2;
+		return 1;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
 		field->size = luaL_maplen(L, 1);
@@ -459,13 +468,11 @@ get_metafield_serialize(struct lua_State *L)
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 		lua_pop(L, 1); /* type */
-		lua_pushboolean(L, true);
-		lua_pushboolean(L, false);
-		return 2;
+		return 1;
 	} else {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		lua_pushboolean(L, true);
-		return 2;
+		s->is_error = true;
+		return 0;
 	}
 }
 
@@ -478,27 +485,19 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 	uint32_t max = 0;
 
 	if (cfg->encode_load_metatables) {
-		lua_pushcfunction(L, get_metafield_serialize);
+		struct lua_serialize_status s;
+		s.cfg = cfg;
+		s.field = field;
+		lua_pushcfunction(L, lua_field_try_serialize);
 		lua_pushvalue(L, idx);
-		lua_pushlightuserdata(L, cfg);
-		lua_pushlightuserdata(L, field);
-		if (lua_pcall(L, 3, 2, 0) != 0) {
+		lua_pushlightuserdata(L, &s);
+		if (lua_pcall(L, 2, 1, 0) != 0) {
 			diag_set(LuajitError, lua_tostring(L, -1));
 			return -1;
 		}
-		bool is_error = lua_toboolean(L, -1);
-		bool is_bool = lua_isboolean(L, -2);
-		if (is_error) {
-			lua_pop(L, 2);
+		if (s.is_error)
 			return -1;
-		}
-		if (is_bool) {
-			bool is_metatable_available = lua_toboolean(L, -2);
-			lua_pop(L, 2);
-			if (is_metatable_available)
-				return 0;
-		} else {
-			lua_pop(L, 1);
+		if (s.is_serialize_used) {
 			lua_replace(L, idx);
 			return 0;
 		}

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-28 18:40       ` Vladislav Shpilevoy
@ 2019-03-28 19:56         ` Mergen Imeev
  2019-03-28 21:41           ` Mergen Imeev
  2019-03-29 21:06           ` Vladislav Shpilevoy
  0 siblings, 2 replies; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 19:56 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Thank you for fixes! I checked and added some more fixes:
1) I added new field is_value_returned for struct
lua_serialize_status.
2) I changed all "return 0" to "return 1" because we should return
exactly one result since we call this function through pcall.
3) I pushed a new value on stack when serialization is not used.
I think it should work even without this value, since we will
discard it, but I’m not 100% sure of that.

Diff between commits and diff of whole patch below. I haven't
squashed these two review fixes.

On Thu, Mar 28, 2019 at 09:40:00PM +0300, Vladislav Shpilevoy wrote:
> Thanks for the fixes! I've pushed my review fixes on the
> branch, and dropped at the end of the email. But I've not
> tested them, so I propose you to do that, if you agree
> with the concept.
> 
> =========================================================
> 

Diff between commits:

diff --git a/src/lua/utils.c b/src/lua/utils.c
index 38245a9..e3b12d8 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -414,6 +414,10 @@ struct lua_serialize_status {
 	 * default serialization is used.
 	 */
 	bool is_serialize_used;
+	/**
+	 * True, if value should be returned after serialization.
+	 */
+	bool is_value_returned;
 	/** Parameters, passed originally to luaL_tofield. */
 	struct luaL_serializer *cfg;
 	struct luaL_field *field;
@@ -435,8 +439,11 @@ lua_field_try_serialize(struct lua_State *L)
 		(struct lua_serialize_status *) lua_touserdata(L, 2);
 	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
 	s->is_error = false;
-	if (! s->is_serialize_used)
-		return 0;
+	s->is_value_returned = false;
+	if (! s->is_serialize_used) {
+		lua_pushvalue(L, 1);
+		return 1;
+	}
 	struct luaL_serializer *cfg = s->cfg;
 	struct luaL_field *field = s->field;
 	if (lua_isfunction(L, -1)) {
@@ -444,12 +451,13 @@ lua_field_try_serialize(struct lua_State *L)
 		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
 		s->is_error = (luaL_tofield(L, cfg, -1, field) != 0);
-		return s->is_error ? 0 : 1;
+		s->is_value_returned = true;
+		return 1;
 	}
 	if (!lua_isstring(L, -1)) {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
 		s->is_error = true;
-		return 0;
+		return 1;
 	}
 	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
@@ -459,21 +467,17 @@ lua_field_try_serialize(struct lua_State *L)
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-		return 1;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
 		field->size = luaL_maplen(L, 1);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-		return 1;
 	} else {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
 		s->is_error = true;
-		return 0;
 	}
+	return 1;
 }
 
 static int
@@ -497,10 +501,12 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 		}
 		if (s.is_error)
 			return -1;
-		if (s.is_serialize_used) {
+		if (s.is_serialize_used && s.is_value_returned)
 			lua_replace(L, idx);
+		else
+			lua_pop(L, 1);
+		if (s.is_serialize_used)
 			return 0;
-		}
 	}
 
 	field->type = MP_ARRAY;




Diff of whole patch:

diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 52939ae..2049ee5 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -164,7 +164,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 		 */
 		for (int i = 1; i <= nrets; ++i) {
 			struct luaL_field field;
-			luaL_tofield(L, cfg, i, &field);
+			if (luaL_tofield(L, cfg, i, &field) < 0)
+				return luaT_error(L);
 			struct tuple *tuple;
 			if (field.type == MP_EXT &&
 			    (tuple = luaT_istuple(L, i)) != NULL) {
@@ -192,7 +193,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	 * Inspect the first result
 	 */
 	struct luaL_field root;
-	luaL_tofield(L, cfg, 1, &root);
+	if (luaL_tofield(L, cfg, 1, &root) < 0)
+		return luaT_error(L);
 	struct tuple *tuple;
 	if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) {
 		/* `return box.tuple()` */
@@ -221,7 +223,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	for (uint32_t t = 1; t <= root.size; t++) {
 		lua_rawgeti(L, 1, t);
 		struct luaL_field field;
-		luaL_tofield(L, cfg, -1, &field);
+		if (luaL_tofield(L, cfg, -1, &field) < 0)
+			return luaT_error(L);
 		if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) {
 			tuple_to_mpstream(tuple, stream);
 		} else if (field.type != MP_ARRAY) {
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 60c1a39..1903ee8 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -229,7 +229,8 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
 		return tuple_to_mpstream(tuple, stream);
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, index, &field);
+	if (luaL_tofield(L, cfg, index, &field) < 0)
+		luaT_error(L);
 	if (field.type == MP_ARRAY) {
 		lua_pushvalue(L, index);
 		luamp_encode_r(L, cfg, stream, &field, 0);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index b470060..1b1874e 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -149,10 +149,12 @@ restart: /* used by MP_EXT */
 		lua_pushnil(L);  /* first key */
 		while (lua_next(L, top) != 0) {
 			lua_pushvalue(L, -2); /* push a copy of key to top */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop a copy of key */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop value */
 		}
@@ -168,7 +170,8 @@ restart: /* used by MP_EXT */
 		mpstream_encode_array(stream, size);
 		for (uint32_t i = 0; i < size; i++) {
 			lua_rawgeti(L, top, i + 1);
-			luaL_tofield(L, cfg, top + 1, field);
+			if (luaL_tofield(L, cfg, top + 1, field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1);
 		}
@@ -203,7 +206,8 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
 	}
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, lua_gettop(L), &field);
+	if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0)
+		return luaT_error(L);
 	enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0);
 
 	if (!on_top) {
diff --git a/src/lua/utils.c b/src/lua/utils.c
index a418b95..e3b12d8 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -392,61 +392,123 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 		lua_pcall(L, 1, 1, 0);
 		/* replace obj with the unpacked value */
 		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
+		if (luaL_tofield(L, cfg, idx, field) < 0)
+			luaT_error(L);
 	} /* else ignore lua_gettable exceptions */
 	lua_settop(L, top); /* remove temporary objects */
 }
 
-static void
-lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
-			int idx, struct luaL_field *field)
-{
-	assert(lua_type(L, idx) == LUA_TTABLE);
-	const char *type;
-	uint32_t size = 0;
-	uint32_t max = 0;
-
-	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
-	if (!cfg->encode_load_metatables ||
-	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
-		goto skip;
+/**
+ * A helper structure to simplify safe call of __serialize method.
+ * It passes some arguments into the serializer called via pcall,
+ * and carries out some results.
+ */
+struct lua_serialize_status {
+	/**
+	 * True if an attempt to call __serialize has failed. A
+	 * diag message is set.
+	 */
+	bool is_error;
+	/**
+	 * True, if __serialize exists. Otherwise an ordinary
+	 * default serialization is used.
+	 */
+	bool is_serialize_used;
+	/**
+	 * True, if value should be returned after serialization.
+	 */
+	bool is_value_returned;
+	/** Parameters, passed originally to luaL_tofield. */
+	struct luaL_serializer *cfg;
+	struct luaL_field *field;
+};
 
+/**
+ * Call __serialize method of a table object if the former exists.
+ * The function expects 2 values pushed onto the Lua stack: a
+ * value to serialize, and a pointer at a struct
+ * lua_serialize_status object. If __serialize exists and is
+ * correct, then one value is pushed as a result of serialization.
+ * Otherwise is_error and is_serialize_used attributes of status
+ * should be checked.
+ */
+static int
+lua_field_try_serialize(struct lua_State *L)
+{
+	struct lua_serialize_status *s =
+		(struct lua_serialize_status *) lua_touserdata(L, 2);
+	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
+	s->is_error = false;
+	s->is_value_returned = false;
+	if (! s->is_serialize_used) {
+		lua_pushvalue(L, 1);
+		return 1;
+	}
+	struct luaL_serializer *cfg = s->cfg;
+	struct luaL_field *field = s->field;
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
-		lua_pushvalue(L, idx);
+		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
-		/* replace obj with the unpacked value */
-		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
-		return;
-	} else if (!lua_isstring(L, -1)) {
-		luaL_error(L, "invalid " LUAL_SERIALIZE  " value");
+		s->is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+		s->is_value_returned = true;
+		return 1;
 	}
-
-	type = lua_tostring(L, -1);
+	if (!lua_isstring(L, -1)) {
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		s->is_error = true;
+		return 1;
+	}
+	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
 	    strcmp(type, "sequence") == 0) {
 		field->type = MP_ARRAY; /* Override type */
-		field->size = luaL_arrlen(L, idx);
+		field->size = luaL_arrlen(L, 1);
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-
-		return;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
-		field->size = luaL_maplen(L, idx);
+		field->size = luaL_maplen(L, 1);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-		return;
 	} else {
-		luaL_error(L, "invalid " LUAL_SERIALIZE "  value");
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		s->is_error = true;
+	}
+	return 1;
+}
+
+static int
+lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
+			int idx, struct luaL_field *field)
+{
+	assert(lua_type(L, idx) == LUA_TTABLE);
+	uint32_t size = 0;
+	uint32_t max = 0;
+
+	if (cfg->encode_load_metatables) {
+		struct lua_serialize_status s;
+		s.cfg = cfg;
+		s.field = field;
+		lua_pushcfunction(L, lua_field_try_serialize);
+		lua_pushvalue(L, idx);
+		lua_pushlightuserdata(L, &s);
+		if (lua_pcall(L, 2, 1, 0) != 0) {
+			diag_set(LuajitError, lua_tostring(L, -1));
+			return -1;
+		}
+		if (s.is_error)
+			return -1;
+		if (s.is_serialize_used && s.is_value_returned)
+			lua_replace(L, idx);
+		else
+			lua_pop(L, 1);
+		if (s.is_serialize_used)
+			return 0;
 	}
 
-skip:
 	field->type = MP_ARRAY;
 
 	/* Calculate size and check that table can represent an array */
@@ -465,7 +527,7 @@ skip:
 			}
 			field->type = MP_MAP;
 			field->size = size;
-			return;
+			return 0;
 		}
 		if (k > max)
 			max = k;
@@ -475,15 +537,18 @@ skip:
 	if (cfg->encode_sparse_ratio > 0 &&
 	    max > size * (uint32_t)cfg->encode_sparse_ratio &&
 	    max > (uint32_t)cfg->encode_sparse_safe) {
-		if (!cfg->encode_sparse_convert)
-			luaL_error(L, "excessively sparse array");
+		if (!cfg->encode_sparse_convert) {
+			diag_set(LuajitError, "excessively sparse array");
+			return -1;
+		}
 		field->type = MP_MAP;
 		field->size = size;
-		return;
+		return 0;
 	}
 
 	assert(field->type == MP_ARRAY);
 	field->size = max;
+	return 0;
 }
 
 static void
@@ -496,12 +561,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 	lua_call(L, 1, 1);
 	lua_replace(L, idx);
 	lua_settop(L, top);
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 }
 
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
-		 struct luaL_field *field)
+	     struct luaL_field *field)
 {
 	if (index < 0)
 		index = lua_gettop(L) + index + 1;
@@ -510,10 +576,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	double intpart;
 	size_t size;
 
-#define CHECK_NUMBER(x) ({\
-	if (!isfinite(x) && !cfg->encode_invalid_numbers) {		\
-		if (!cfg->encode_invalid_as_nil)				\
-			luaL_error(L, "number must not be NaN or Inf");		\
+#define CHECK_NUMBER(x) ({							\
+	if (!isfinite(x) && !cfg->encode_invalid_numbers) {			\
+		if (!cfg->encode_invalid_as_nil) {				\
+			diag_set(LuajitError, "number must not be NaN or Inf");	\
+			return -1;						\
+		}								\
 		field->type = MP_NIL;						\
 	}})
 
@@ -534,94 +602,93 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 			field->dval = num;
 			CHECK_NUMBER(num);
 		}
-		return;
+		return 0;
 	case LUA_TCDATA:
 	{
-		uint32_t ctypeid = 0;
-		void *cdata = luaL_checkcdata(L, index, &ctypeid);
+		GCcdata *cd = cdataV(L->base + index - 1);
+		void *cdata = (void *)cdataptr(cd);
+
 		int64_t ival;
-		switch (ctypeid) {
+		switch (cd->ctypeid) {
 		case CTID_BOOL:
 			field->type = MP_BOOL;
 			field->bval = *(bool*) cdata;
-			return;
+			return 0;
 		case CTID_CCHAR:
 		case CTID_INT8:
 			ival = *(int8_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT16:
 			ival = *(int16_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT32:
 			ival = *(int32_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT64:
 			ival = *(int64_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_UINT8:
 			field->type = MP_UINT;
 			field->ival = *(uint8_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT16:
 			field->type = MP_UINT;
 			field->ival = *(uint16_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT32:
 			field->type = MP_UINT;
 			field->ival = *(uint32_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT64:
 			field->type = MP_UINT;
 			field->ival = *(uint64_t *) cdata;
-			return;
+			return 0;
 		case CTID_FLOAT:
 			field->type = MP_FLOAT;
 			field->fval = *(float *) cdata;
 			CHECK_NUMBER(field->fval);
-			return;
+			return 0;
 		case CTID_DOUBLE:
 			field->type = MP_DOUBLE;
 			field->dval = *(double *) cdata;
 			CHECK_NUMBER(field->dval);
-			return;
+			return 0;
 		case CTID_P_CVOID:
 		case CTID_P_VOID:
 			if (*(void **) cdata == NULL) {
 				field->type = MP_NIL;
-				return;
+				return 0;
 			}
 			/* Fall through */
 		default:
 			field->type = MP_EXT;
-			return;
 		}
-		return;
+		return 0;
 	}
 	case LUA_TBOOLEAN:
 		field->type = MP_BOOL;
 		field->bval = lua_toboolean(L, index);
-		return;
+		return 0;
 	case LUA_TNIL:
 		field->type = MP_NIL;
-		return;
+		return 0;
 	case LUA_TSTRING:
 		field->sval.data = lua_tolstring(L, index, &size);
 		field->sval.len = (uint32_t) size;
 		field->type = MP_STR;
-		return;
+		return 0;
 	case LUA_TTABLE:
 	{
 		field->compact = false;
-		lua_field_inspect_table(L, cfg, index, field);
-		return;
+		return lua_field_inspect_table(L, cfg, index, field);
 	}
 	case LUA_TLIGHTUSERDATA:
 	case LUA_TUSERDATA:
@@ -629,14 +696,14 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 		field->sval.len = 0;
 		if (lua_touserdata(L, index) == NULL) {
 			field->type = MP_NIL;
-			return;
+			return 0;
 		}
 		/* Fall through */
 	default:
 		field->type = MP_EXT;
-		return;
 	}
 #undef CHECK_NUMBER
+	return 0;
 }
 
 void
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 96311b7..df4d50e 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -311,8 +311,11 @@ struct luaL_field {
  * @param cfg configuration
  * @param index stack index
  * @param field conversion result
+ *
+ * @retval  0 Success.
+ * @retval -1 Error.
  */
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	     struct luaL_field *field);
 
@@ -352,7 +355,8 @@ static inline void
 luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 		struct luaL_field *field)
 {
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 	if (field->type != MP_EXT)
 		return;
 	luaL_convertfield(L, cfg, idx, field);

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 7/7] sql: remove box.sql.execute()
  2019-03-26 21:48   ` [tarantool-patches] " Vladislav Shpilevoy
@ 2019-03-28 20:13     ` Mergen Imeev
  2019-03-29 21:06       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 20:13 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Hi! Thank you for review. My answers, diff and new patch below.

On Wed, Mar 27, 2019 at 12:48:17AM +0300, Vladislav Shpilevoy wrote:
> Thanks for the patch! See 5 comments below.
> 
> > diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
> > index 6c9a820..5530b2c 100644
> > --- a/src/box/lua/load_cfg.lua
> > +++ b/src/box/lua/load_cfg.lua
> > @@ -505,17 +505,14 @@ end
> >  box.cfg = locked(load_cfg)
> >  
> >  --
> > --- This makes possible do box.sql.execute without calling box.cfg
> > +-- This makes possible do box.execute without calling box.cfg
> >  -- manually. The load_cfg call overwrites following table and
> 
> 1. There are no metatable anymore as I see. At least an explicit.
> Do you really call load_cfg on every single box.execute() call?
> If you do - please, do not. It is too slow. Use metatables.
> 
After load_cfg() is executed, this version of box.execute() will
be replaced by a new one, and load_cfg() will no longer be
executed. At the same time, if load_cfg() does not replace this
box.execute() with a new one, Tarantool may hang. Not sure that
metatables can fix this problem. Should I use metatables or it is
enough to fix comment? This issue should be automatically fixed
after feature "load cfg after anything from box is called".

> >  -- metatable.
> >  --
> > -box.sql = {}
> > -setmetatable(box.sql, {
> > -    __index = function(table, index)
> > -        load_cfg()
> > -        return box.sql[index]
> > -    end,
> > -})
> > +function box.execute(...)
> > +    load_cfg()
> > +    return box.execute(...)
> > +end
> >  
> >  -- gh-810:
> >  -- hack luajit default cpath
> > diff --git a/test/sql-tap/gh2140-trans.test.lua b/test/sql-tap/gh2140-trans.test.lua
> > index fe978d1..3843c97 100755
> > --- a/test/sql-tap/gh2140-trans.test.lua
> > +++ b/test/sql-tap/gh2140-trans.test.lua
> > @@ -28,8 +28,8 @@ test:do_execsql_test('rollback1_check',
> >  for _, verb in ipairs({'ROLLBACK', 'ABORT'}) do
> > -    box.sql.execute('DELETE FROM t2')
> > -    answer = "Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'"
> > +    box.execute('DELETE FROM t2')
> > +    answer = "/Duplicate key exists in unique index 'unique_unnamed_T1_2' in space 'T1'/"
> 
> 2. Why are '/' added?
> 
This is something like regular expression. I did this because two
errors appears this test:
"Duplicate key exists ..."
and
"Failed to execute SQL statement: Duplicate key exists ..."

This way I can catch both of them.

> >      test:do_catchsql_test('insert1_'..verb,
> >                            [[START TRANSACTION;
> >                              INSERT INTO t2 VALUES (20, 2, 2);
> > diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
> > index 63c5d9b..cb77bc4 100644
> > --- a/test/sql-tap/lua/sqltester.lua
> > +++ b/test/sql-tap/lua/sqltester.lua
> > @@ -403,7 +415,16 @@ test.do_eqp_test = function (self, label, sql, result)
> >      test:do_test(
> >          label,
> >          function()
> > -            return execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
> > +            local result = execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
> > +            local res = {}
> > +            for k,v in pairs(result) do
> > +                res[k] = {}
> > +                res[k][1] = v[1]
> > +                res[k][2] = v[2]
> > +                res[k][3] = v[3]
> > +                res[k][4] = tostring(v[4])
> 
> 3. What is happening here?
> 
Fixed:

diff --git a/test/sql-tap/lua/sqltester.lua b/test/sql-tap/lua/sqltester.lua
index af93baa..4450f5a 100644
--- a/test/sql-tap/lua/sqltester.lua
+++ b/test/sql-tap/lua/sqltester.lua
@@ -416,15 +416,10 @@ test.do_eqp_test = function (self, label, sql, result)
         label,
         function()
             local result = execsql_one_by_one("EXPLAIN QUERY PLAN "..sql)
-            local res = {}
             for k,v in pairs(result) do
-                res[k] = {}
-                res[k][1] = v[1]
-                res[k][2] = v[2]
-                res[k][3] = v[3]
-                res[k][4] = tostring(v[4])
+                result[k] = v:totable()
             end
-            return res
+            return result
         end,
         result)
 end


> > +            end
> > +            return res
> >          end,
> >          result)
> >  end
> > diff --git a/test/sql-tap/orderby9.test.lua b/test/sql-tap/orderby9.test.lua
> > index 191c21b..13f9ec6 100755
> > --- a/test/sql-tap/orderby9.test.lua
> > +++ b/test/sql-tap/orderby9.test.lua
> > @@ -41,6 +41,7 @@ test:do_test(
> >          -- separately for the result set and the ORDER BY clause, then the output
> >          -- order will be random.
> >          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY 1;")
> > +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
> >          local l2 = table.deepcopy(l1)
> >          table.sort(l1)
> >          return test.is_deeply_regex(l1, l2)
> > @@ -50,6 +51,7 @@ test:do_test(
> >      1.1,
> >      function()
> >          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY random();")
> > +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
> 
> 4. And in these two hunks?
> 
I did this because values returned by test:execsql weren't
numbers. They were cdata. Due to this they were sorted not
the way it was expected.

> >          local l2 = table.deepcopy(l1)
> >          table.sort(l1)
> >          return test.is_deeply_regex(l1, l2)
> > diff --git a/test/sql/min-on-index.result b/test/sql/min-on-index.result
> > deleted file mode 100644
> > index 1b2aadf..0000000
> > --- a/test/sql/min-on-index.result
> > +++ /dev/null
> 
> 5. Why removed?
There is no such test, only result-file left. I thought that it
isn't needed anymore.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-28 19:56         ` Mergen Imeev
@ 2019-03-28 21:41           ` Mergen Imeev
  2019-03-29 21:06           ` Vladislav Shpilevoy
  1 sibling, 0 replies; 36+ messages in thread
From: Mergen Imeev @ 2019-03-28 21:41 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Added check for number of returned values after pcall. Diff below.

On Thu, Mar 28, 2019 at 10:56:32PM +0300, Mergen Imeev wrote:
> Thank you for fixes! I checked and added some more fixes:
> 1) I added new field is_value_returned for struct
> lua_serialize_status.
> 2) I changed all "return 0" to "return 1" because we should return
> exactly one result since we call this function through pcall.
> 3) I pushed a new value on stack when serialization is not used.
> I think it should work even without this value, since we will
> discard it, but I’m not 100% sure of that.
> 
> Diff between commits and diff of whole patch below. I haven't
> squashed these two review fixes.
> 
> On Thu, Mar 28, 2019 at 09:40:00PM +0300, Vladislav Shpilevoy wrote:
> > Thanks for the fixes! I've pushed my review fixes on the
> > branch, and dropped at the end of the email. But I've not
> > tested them, so I propose you to do that, if you agree
> > with the concept.
> > 
> > =========================================================
> > 


Diff:

diff --git a/src/lua/utils.c b/src/lua/utils.c
index e3b12d8..1612c5a 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -440,10 +440,8 @@ lua_field_try_serialize(struct lua_State *L)
 	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
 	s->is_error = false;
 	s->is_value_returned = false;
-	if (! s->is_serialize_used) {
-		lua_pushvalue(L, 1);
-		return 1;
-	}
+	if (! s->is_serialize_used)
+		return 0;
 	struct luaL_serializer *cfg = s->cfg;
 	struct luaL_field *field = s->field;
 	if (lua_isfunction(L, -1)) {
@@ -457,7 +455,8 @@ lua_field_try_serialize(struct lua_State *L)
 	if (!lua_isstring(L, -1)) {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
 		s->is_error = true;
-		return 1;
+		lua_pop(L, 1);
+		return 0;
 	}
 	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
@@ -477,7 +476,8 @@ lua_field_try_serialize(struct lua_State *L)
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
 		s->is_error = true;
 	}
-	return 1;
+	lua_pop(L, 1);
+	return 0;
 }
 
 static int
@@ -489,6 +489,7 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 	uint32_t max = 0;
 
 	if (cfg->encode_load_metatables) {
+		int top = lua_gettop(L);
 		struct lua_serialize_status s;
 		s.cfg = cfg;
 		s.field = field;
@@ -501,6 +502,8 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 		}
 		if (s.is_error)
 			return -1;
+		assert(lua_gettop(L) == top + 1);
+		(void)top;
 		if (s.is_serialize_used && s.is_value_returned)
 			lua_replace(L, idx);
 		else



New patch:

diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 52939ae..2049ee5 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -164,7 +164,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 		 */
 		for (int i = 1; i <= nrets; ++i) {
 			struct luaL_field field;
-			luaL_tofield(L, cfg, i, &field);
+			if (luaL_tofield(L, cfg, i, &field) < 0)
+				return luaT_error(L);
 			struct tuple *tuple;
 			if (field.type == MP_EXT &&
 			    (tuple = luaT_istuple(L, i)) != NULL) {
@@ -192,7 +193,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	 * Inspect the first result
 	 */
 	struct luaL_field root;
-	luaL_tofield(L, cfg, 1, &root);
+	if (luaL_tofield(L, cfg, 1, &root) < 0)
+		return luaT_error(L);
 	struct tuple *tuple;
 	if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) {
 		/* `return box.tuple()` */
@@ -221,7 +223,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
 	for (uint32_t t = 1; t <= root.size; t++) {
 		lua_rawgeti(L, 1, t);
 		struct luaL_field field;
-		luaL_tofield(L, cfg, -1, &field);
+		if (luaL_tofield(L, cfg, -1, &field) < 0)
+			return luaT_error(L);
 		if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) {
 			tuple_to_mpstream(tuple, stream);
 		} else if (field.type != MP_ARRAY) {
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 60c1a39..1903ee8 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -229,7 +229,8 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
 		return tuple_to_mpstream(tuple, stream);
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, index, &field);
+	if (luaL_tofield(L, cfg, index, &field) < 0)
+		luaT_error(L);
 	if (field.type == MP_ARRAY) {
 		lua_pushvalue(L, index);
 		luamp_encode_r(L, cfg, stream, &field, 0);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index b470060..1b1874e 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -149,10 +149,12 @@ restart: /* used by MP_EXT */
 		lua_pushnil(L);  /* first key */
 		while (lua_next(L, top) != 0) {
 			lua_pushvalue(L, -2); /* push a copy of key to top */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop a copy of key */
-			luaL_tofield(L, cfg, lua_gettop(L), field);
+			if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1); /* pop value */
 		}
@@ -168,7 +170,8 @@ restart: /* used by MP_EXT */
 		mpstream_encode_array(stream, size);
 		for (uint32_t i = 0; i < size; i++) {
 			lua_rawgeti(L, top, i + 1);
-			luaL_tofield(L, cfg, top + 1, field);
+			if (luaL_tofield(L, cfg, top + 1, field) < 0)
+				return luaT_error(L);
 			luamp_encode_r(L, cfg, stream, field, level + 1);
 			lua_pop(L, 1);
 		}
@@ -203,7 +206,8 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
 	}
 
 	struct luaL_field field;
-	luaL_tofield(L, cfg, lua_gettop(L), &field);
+	if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0)
+		return luaT_error(L);
 	enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0);
 
 	if (!on_top) {
diff --git a/src/lua/utils.c b/src/lua/utils.c
index a418b95..1612c5a 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -392,61 +392,126 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 		lua_pcall(L, 1, 1, 0);
 		/* replace obj with the unpacked value */
 		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
+		if (luaL_tofield(L, cfg, idx, field) < 0)
+			luaT_error(L);
 	} /* else ignore lua_gettable exceptions */
 	lua_settop(L, top); /* remove temporary objects */
 }
 
-static void
-lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
-			int idx, struct luaL_field *field)
-{
-	assert(lua_type(L, idx) == LUA_TTABLE);
-	const char *type;
-	uint32_t size = 0;
-	uint32_t max = 0;
-
-	/* Try to get field LUAL_SERIALIZER_TYPE from metatable */
-	if (!cfg->encode_load_metatables ||
-	    !luaL_getmetafield(L, idx, LUAL_SERIALIZE))
-		goto skip;
+/**
+ * A helper structure to simplify safe call of __serialize method.
+ * It passes some arguments into the serializer called via pcall,
+ * and carries out some results.
+ */
+struct lua_serialize_status {
+	/**
+	 * True if an attempt to call __serialize has failed. A
+	 * diag message is set.
+	 */
+	bool is_error;
+	/**
+	 * True, if __serialize exists. Otherwise an ordinary
+	 * default serialization is used.
+	 */
+	bool is_serialize_used;
+	/**
+	 * True, if value should be returned after serialization.
+	 */
+	bool is_value_returned;
+	/** Parameters, passed originally to luaL_tofield. */
+	struct luaL_serializer *cfg;
+	struct luaL_field *field;
+};
 
+/**
+ * Call __serialize method of a table object if the former exists.
+ * The function expects 2 values pushed onto the Lua stack: a
+ * value to serialize, and a pointer at a struct
+ * lua_serialize_status object. If __serialize exists and is
+ * correct, then one value is pushed as a result of serialization.
+ * Otherwise is_error and is_serialize_used attributes of status
+ * should be checked.
+ */
+static int
+lua_field_try_serialize(struct lua_State *L)
+{
+	struct lua_serialize_status *s =
+		(struct lua_serialize_status *) lua_touserdata(L, 2);
+	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
+	s->is_error = false;
+	s->is_value_returned = false;
+	if (! s->is_serialize_used)
+		return 0;
+	struct luaL_serializer *cfg = s->cfg;
+	struct luaL_field *field = s->field;
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
-		lua_pushvalue(L, idx);
+		lua_pushvalue(L, 1);
 		lua_call(L, 1, 1);
-		/* replace obj with the unpacked value */
-		lua_replace(L, idx);
-		luaL_tofield(L, cfg, idx, field);
-		return;
-	} else if (!lua_isstring(L, -1)) {
-		luaL_error(L, "invalid " LUAL_SERIALIZE  " value");
+		s->is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+		s->is_value_returned = true;
+		return 1;
 	}
-
-	type = lua_tostring(L, -1);
+	if (!lua_isstring(L, -1)) {
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		s->is_error = true;
+		lua_pop(L, 1);
+		return 0;
+	}
+	const char *type = lua_tostring(L, -1);
 	if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
 	    strcmp(type, "sequence") == 0) {
 		field->type = MP_ARRAY; /* Override type */
-		field->size = luaL_arrlen(L, idx);
+		field->size = luaL_arrlen(L, 1);
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-
-		return;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
-		field->size = luaL_maplen(L, idx);
+		field->size = luaL_maplen(L, 1);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
-		lua_pop(L, 1); /* type */
-		return;
 	} else {
-		luaL_error(L, "invalid " LUAL_SERIALIZE "  value");
+		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+		s->is_error = true;
+	}
+	lua_pop(L, 1);
+	return 0;
+}
+
+static int
+lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
+			int idx, struct luaL_field *field)
+{
+	assert(lua_type(L, idx) == LUA_TTABLE);
+	uint32_t size = 0;
+	uint32_t max = 0;
+
+	if (cfg->encode_load_metatables) {
+		int top = lua_gettop(L);
+		struct lua_serialize_status s;
+		s.cfg = cfg;
+		s.field = field;
+		lua_pushcfunction(L, lua_field_try_serialize);
+		lua_pushvalue(L, idx);
+		lua_pushlightuserdata(L, &s);
+		if (lua_pcall(L, 2, 1, 0) != 0) {
+			diag_set(LuajitError, lua_tostring(L, -1));
+			return -1;
+		}
+		if (s.is_error)
+			return -1;
+		assert(lua_gettop(L) == top + 1);
+		(void)top;
+		if (s.is_serialize_used && s.is_value_returned)
+			lua_replace(L, idx);
+		else
+			lua_pop(L, 1);
+		if (s.is_serialize_used)
+			return 0;
 	}
 
-skip:
 	field->type = MP_ARRAY;
 
 	/* Calculate size and check that table can represent an array */
@@ -465,7 +530,7 @@ skip:
 			}
 			field->type = MP_MAP;
 			field->size = size;
-			return;
+			return 0;
 		}
 		if (k > max)
 			max = k;
@@ -475,15 +540,18 @@ skip:
 	if (cfg->encode_sparse_ratio > 0 &&
 	    max > size * (uint32_t)cfg->encode_sparse_ratio &&
 	    max > (uint32_t)cfg->encode_sparse_safe) {
-		if (!cfg->encode_sparse_convert)
-			luaL_error(L, "excessively sparse array");
+		if (!cfg->encode_sparse_convert) {
+			diag_set(LuajitError, "excessively sparse array");
+			return -1;
+		}
 		field->type = MP_MAP;
 		field->size = size;
-		return;
+		return 0;
 	}
 
 	assert(field->type == MP_ARRAY);
 	field->size = max;
+	return 0;
 }
 
 static void
@@ -496,12 +564,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 	lua_call(L, 1, 1);
 	lua_replace(L, idx);
 	lua_settop(L, top);
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 }
 
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
-		 struct luaL_field *field)
+	     struct luaL_field *field)
 {
 	if (index < 0)
 		index = lua_gettop(L) + index + 1;
@@ -510,10 +579,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	double intpart;
 	size_t size;
 
-#define CHECK_NUMBER(x) ({\
-	if (!isfinite(x) && !cfg->encode_invalid_numbers) {		\
-		if (!cfg->encode_invalid_as_nil)				\
-			luaL_error(L, "number must not be NaN or Inf");		\
+#define CHECK_NUMBER(x) ({							\
+	if (!isfinite(x) && !cfg->encode_invalid_numbers) {			\
+		if (!cfg->encode_invalid_as_nil) {				\
+			diag_set(LuajitError, "number must not be NaN or Inf");	\
+			return -1;						\
+		}								\
 		field->type = MP_NIL;						\
 	}})
 
@@ -534,94 +605,93 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 			field->dval = num;
 			CHECK_NUMBER(num);
 		}
-		return;
+		return 0;
 	case LUA_TCDATA:
 	{
-		uint32_t ctypeid = 0;
-		void *cdata = luaL_checkcdata(L, index, &ctypeid);
+		GCcdata *cd = cdataV(L->base + index - 1);
+		void *cdata = (void *)cdataptr(cd);
+
 		int64_t ival;
-		switch (ctypeid) {
+		switch (cd->ctypeid) {
 		case CTID_BOOL:
 			field->type = MP_BOOL;
 			field->bval = *(bool*) cdata;
-			return;
+			return 0;
 		case CTID_CCHAR:
 		case CTID_INT8:
 			ival = *(int8_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT16:
 			ival = *(int16_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT32:
 			ival = *(int32_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_INT64:
 			ival = *(int64_t *) cdata;
 			field->type = (ival >= 0) ? MP_UINT : MP_INT;
 			field->ival = ival;
-			return;
+			return 0;
 		case CTID_UINT8:
 			field->type = MP_UINT;
 			field->ival = *(uint8_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT16:
 			field->type = MP_UINT;
 			field->ival = *(uint16_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT32:
 			field->type = MP_UINT;
 			field->ival = *(uint32_t *) cdata;
-			return;
+			return 0;
 		case CTID_UINT64:
 			field->type = MP_UINT;
 			field->ival = *(uint64_t *) cdata;
-			return;
+			return 0;
 		case CTID_FLOAT:
 			field->type = MP_FLOAT;
 			field->fval = *(float *) cdata;
 			CHECK_NUMBER(field->fval);
-			return;
+			return 0;
 		case CTID_DOUBLE:
 			field->type = MP_DOUBLE;
 			field->dval = *(double *) cdata;
 			CHECK_NUMBER(field->dval);
-			return;
+			return 0;
 		case CTID_P_CVOID:
 		case CTID_P_VOID:
 			if (*(void **) cdata == NULL) {
 				field->type = MP_NIL;
-				return;
+				return 0;
 			}
 			/* Fall through */
 		default:
 			field->type = MP_EXT;
-			return;
 		}
-		return;
+		return 0;
 	}
 	case LUA_TBOOLEAN:
 		field->type = MP_BOOL;
 		field->bval = lua_toboolean(L, index);
-		return;
+		return 0;
 	case LUA_TNIL:
 		field->type = MP_NIL;
-		return;
+		return 0;
 	case LUA_TSTRING:
 		field->sval.data = lua_tolstring(L, index, &size);
 		field->sval.len = (uint32_t) size;
 		field->type = MP_STR;
-		return;
+		return 0;
 	case LUA_TTABLE:
 	{
 		field->compact = false;
-		lua_field_inspect_table(L, cfg, index, field);
-		return;
+		return lua_field_inspect_table(L, cfg, index, field);
 	}
 	case LUA_TLIGHTUSERDATA:
 	case LUA_TUSERDATA:
@@ -629,14 +699,14 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 		field->sval.len = 0;
 		if (lua_touserdata(L, index) == NULL) {
 			field->type = MP_NIL;
-			return;
+			return 0;
 		}
 		/* Fall through */
 	default:
 		field->type = MP_EXT;
-		return;
 	}
 #undef CHECK_NUMBER
+	return 0;
 }
 
 void
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 96311b7..df4d50e 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -311,8 +311,11 @@ struct luaL_field {
  * @param cfg configuration
  * @param index stack index
  * @param field conversion result
+ *
+ * @retval  0 Success.
+ * @retval -1 Error.
  */
-void
+int
 luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
 	     struct luaL_field *field);
 
@@ -352,7 +355,8 @@ static inline void
 luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
 		struct luaL_field *field)
 {
-	luaL_tofield(L, cfg, idx, field);
+	if (luaL_tofield(L, cfg, idx, field) < 0)
+		luaT_error(L);
 	if (field->type != MP_EXT)
 		return;
 	luaL_convertfield(L, cfg, idx, field);

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 1/7] sql: add column name to SQL change counter
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
  2019-03-22 15:42   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-29 12:00   ` Kirill Yukhin
  1 sibling, 0 replies; 36+ messages in thread
From: Kirill Yukhin @ 2019-03-29 12:00 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

Hello,

On 22 Mar 13:50, imeevma@tarantool.org wrote:
> Currently, if the count_changes pragma is enabled, the INSERT,
> REPLACE and UPDATE statements will return the number of changes at
> execution time. This patch sets an INTEGER type for this result.
> 
> Follow up #3832
> Needed from #3505

I've checked your patch into master.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
  2019-03-22 15:45   ` [tarantool-patches] " Konstantin Osipov
  2019-03-26 21:48   ` Vladislav Shpilevoy
@ 2019-03-29 12:01   ` Kirill Yukhin
  2 siblings, 0 replies; 36+ messages in thread
From: Kirill Yukhin @ 2019-03-29 12:01 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

Hello,

On 22 Mar 13:50, imeevma@tarantool.org wrote:
> Currently, functions sql_execute() and sql_prepare_and_execute()
> set the ER_SQL_EXECUTE code for all errors that occur during the
> execution of a SQL command. This is considered incorrect because
> some of these errors may have their own error code.
> 
> In addition, all errors that do not have an error code are VDBE
> errors due to issue #3965, so it makes sense to set the error
> code ER_VDBE_EXECUTE for all errors without an error code.
> 
> Part of #3505
I've checked your patch into master.

--
Reagrds, Kirill Yukhin

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 3/7] sql: remove box.sql.debug()
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma
  2019-03-22 15:46   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-29 12:02   ` Kirill Yukhin
  1 sibling, 0 replies; 36+ messages in thread
From: Kirill Yukhin @ 2019-03-29 12:02 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

Hello,

On 22 Mar 13:50, imeevma@tarantool.org wrote:
> Due to removing of box.sql.execute() it makes sense to remove
> box.sql.debug() and move SQL info to box.info.
> 
> Part or #3505

I've checked your patch into master.

--
Regards, Kirill Yukhin

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-22 15:53   ` [tarantool-patches] " Konstantin Osipov
@ 2019-03-29 19:26     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-29 19:26 UTC (permalink / raw)
  To: tarantool-patches, Konstantin Osipov



On 22/03/2019 18:53, Konstantin Osipov wrote:
> * imeevma@tarantool.org <imeevma@tarantool.org> [19/03/22 13:57]:
>>  	case LUA_TCDATA:
>>  	{
>> -		uint32_t ctypeid = 0;
>> -		void *cdata = luaL_checkcdata(L, index, &ctypeid);
>> +		GCcdata *cd = cdataV(L->base + index - 1);
>> +		void *cdata = (void *)cdataptr(cd);
>> +
> 
> What is the reason for this change?

Because luaL_checkcdata throws. So here it is partially inlined.

> 
> 
> -- 
> Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
> http://tarantool.io - www.twitter.com/kostja_osipov
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield()
  2019-03-28 19:56         ` Mergen Imeev
  2019-03-28 21:41           ` Mergen Imeev
@ 2019-03-29 21:06           ` Vladislav Shpilevoy
  1 sibling, 0 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-29 21:06 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

Thanks for the fixes!

I've failed to find a simpler solution, so I kept
is_value_returned. But I refactored inspect_table()
a bit taking into account that if is_value_returned
can be set only together with is_serialize_used. Also
I wrote more descriptive comments. See on the branch.
I squashed all our fixes into your main commit.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 7/7] sql: remove box.sql.execute()
  2019-03-28 20:13     ` Mergen Imeev
@ 2019-03-29 21:06       ` Vladislav Shpilevoy
  0 siblings, 0 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-29 21:06 UTC (permalink / raw)
  To: Mergen Imeev; +Cc: tarantool-patches

Thanks for the fixes!

On 28/03/2019 23:13, Mergen Imeev wrote:
> Hi! Thank you for review. My answers, diff and new patch below.
> 
> On Wed, Mar 27, 2019 at 12:48:17AM +0300, Vladislav Shpilevoy wrote:
>> Thanks for the patch! See 5 comments below.
>>
>>> diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
>>> index 6c9a820..5530b2c 100644
>>> --- a/src/box/lua/load_cfg.lua
>>> +++ b/src/box/lua/load_cfg.lua
>>> @@ -505,17 +505,14 @@ end
>>>  box.cfg = locked(load_cfg)
>>>  
>>>  --
>>> --- This makes possible do box.sql.execute without calling box.cfg
>>> +-- This makes possible do box.execute without calling box.cfg
>>>  -- manually. The load_cfg call overwrites following table and
>>
>> 1. There are no metatable anymore as I see. At least an explicit.
>> Do you really call load_cfg on every single box.execute() call?
>> If you do - please, do not. It is too slow. Use metatables.
>>
> After load_cfg() is executed, this version of box.execute() will
> be replaced by a new one, and load_cfg() will no longer be
> executed. At the same time, if load_cfg() does not replace this
> box.execute() with a new one, Tarantool may hang. Not sure that
> metatables can fix this problem. Should I use metatables or it is
> enough to fix comment? This issue should be automatically fixed
> after feature "load cfg after anything from box is called".

Sorry, now I see that in the previous patch box.execute replaces
any other 'box.execute' versions on load_cfg() invocation. It is
ok.

> 
>>> diff --git a/test/sql-tap/orderby9.test.lua b/test/sql-tap/orderby9.test.lua
>>> index 191c21b..13f9ec6 100755
>>> --- a/test/sql-tap/orderby9.test.lua
>>> +++ b/test/sql-tap/orderby9.test.lua
>>> @@ -41,6 +41,7 @@ test:do_test(
>>>          -- separately for the result set and the ORDER BY clause, then the output
>>>          -- order will be random.
>>>          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY 1;")
>>> +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
>>>          local l2 = table.deepcopy(l1)
>>>          table.sort(l1)
>>>          return test.is_deeply_regex(l1, l2)
>>> @@ -50,6 +51,7 @@ test:do_test(
>>>      1.1,
>>>      function()
>>>          local l1 = test:execsql("SELECT random() AS y FROM t1 ORDER BY random();")
>>> +        for k,_ in pairs(l1) do l1[k] = tonumber(l1[k]) end
>>
>> 4. And in these two hunks?
>>
> I did this because values returned by test:execsql weren't
> numbers. They were cdata. Due to this they were sorted not
> the way it was expected.

Apparently looks like a bug: numbers should be compared correctly.
I confirmed that via a simple test case and opened a bug:
https://github.com/tarantool/tarantool/issues/4089

In your test I added a comment:

        -- Big random() numbers are cdata, but cdata numbers can
        -- not be compared nor sorted correctly.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [tarantool-patches] Re: [PATCH v9 0/7] sql: remove box.sql.execute
  2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
                   ` (6 preceding siblings ...)
  2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute() imeevma
@ 2019-03-29 21:07 ` Vladislav Shpilevoy
  7 siblings, 0 replies; 36+ messages in thread
From: Vladislav Shpilevoy @ 2019-03-29 21:07 UTC (permalink / raw)
  To: imeevma, Kirill Yukhin; +Cc: tarantool-patches

I forced pushed all of my micro-fixes, and the patchset LGTM now.

Please, do not forget to re-pull the branch before push into the
master, because it was forcefully updated.

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2019-03-29 21:07 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma
2019-03-22 15:42   ` [tarantool-patches] " Konstantin Osipov
2019-03-25 19:34     ` Mergen Imeev
2019-03-29 12:00   ` Kirill Yukhin
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma
2019-03-22 15:45   ` [tarantool-patches] " Konstantin Osipov
2019-03-26 21:48   ` Vladislav Shpilevoy
2019-03-27 11:43     ` Konstantin Osipov
2019-03-28 17:46     ` Mergen Imeev
2019-03-29 12:01   ` Kirill Yukhin
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma
2019-03-22 15:46   ` [tarantool-patches] " Konstantin Osipov
2019-03-25 19:39     ` Mergen Imeev
2019-03-26 21:48       ` Vladislav Shpilevoy
2019-03-28 17:48         ` Mergen Imeev
2019-03-28 18:01           ` Vladislav Shpilevoy
2019-03-29 12:02   ` Kirill Yukhin
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma
2019-03-22 15:53   ` [tarantool-patches] " Konstantin Osipov
2019-03-29 19:26     ` Vladislav Shpilevoy
2019-03-26 21:48   ` Vladislav Shpilevoy
2019-03-28 17:54     ` Mergen Imeev
2019-03-28 18:40       ` Vladislav Shpilevoy
2019-03-28 19:56         ` Mergen Imeev
2019-03-28 21:41           ` Mergen Imeev
2019-03-29 21:06           ` Vladislav Shpilevoy
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql imeevma
2019-03-22 15:55   ` [tarantool-patches] " Konstantin Osipov
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 6/7] sql: create box.execute() imeevma
2019-03-22 15:57   ` [tarantool-patches] " Konstantin Osipov
2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute() imeevma
2019-03-26 21:48   ` [tarantool-patches] " Vladislav Shpilevoy
2019-03-28 20:13     ` Mergen Imeev
2019-03-29 21:06       ` Vladislav Shpilevoy
2019-03-29 21:07 ` [tarantool-patches] Re: [PATCH v9 0/7] sql: remove box.sql.execute Vladislav Shpilevoy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox