<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p>Hi! Thank you for review and fixes! I squashed you fixes and done<br>
some changes:<br>
- Luastream created. Some methods were moved from Lua<br>
implementation for vstream to luastream.<br>
- Binding now saves some data in allocated memory as they can be<br>
removed from memory after being poped from Lua stack.<br>
- Lua implementation for vstream was moved to luastream.c<br>
<br>
New patch and some answers below.<br>
<br>
</p>
<div class="moz-cite-prefix">On 11/23/18 12:49 AM, Vladislav
Shpilevoy wrote:<br>
</div>
<blockquote type="cite"
cite="mid:2f7e9873-8558-b7f6-2fd0-6ec01815ae1f@tarantool.org">Thanks
for the fixes! See my 5 comments below, fix <br>
at the end of the email and on the branch. <br>
<br>
Also note, that I did not run tests. So before squashing <br>
please, check the tests. <br>
<br>
<blockquote type="cite">diff --git a/src/box/execute.h
b/src/box/execute.h <br>
index 5a11a8a..8ee0a89 100644 <br>
--- a/src/box/execute.h <br>
+++ b/src/box/execute.h <br>
@@ -131,6 +131,21 @@ xrow_decode_sql(const struct xrow_header
*row, struct sql_request *request, <br>
struct region *region); <br>
/** <br>
+ * Parse Lua table of SQL parameters and store a result <br>
+ * into the @request->bind, bind_count. <br>
+ * @param L Lua stack to get data from. <br>
+ * @param request Request to save decoded parameters. <br>
+ * @param idx Position of table with parameters on Lua stack. <br>
+ * @param region Allocator. <br>
+ * <br>
+ * @retval 0 Success. <br>
+ * @retval -1 Client or memory error. <br>
+ */ <br>
+int <br>
+lua_sql_bind_list_decode(struct lua_State *L, struct
sql_request *request, <br>
+ int idx, struct region *region); <br>
</blockquote>
1. You do not need to pass region here. Get fiber()->gc inside
this <br>
function. In original implementation it is passed because back in
<br>
those days we hoped to remove fiber()->gc, but now we decided
not to <br>
do it. <br>
<br>
Also, it leaks now. You allocate sql_bind parameters on region,
but <br>
never truncate it. Iproto has the same bug, but you should not <br>
duplicate it. <br>
<br>
But you can not truncate it as well, since sql_prepare_and_execute
<br>
could save some transactional data onto it. And this bug iproto
does <br>
not have since it uses region of iproto thread. <br>
<br>
I think, here you should use malloc. <br>
</blockquote>
After discussion it was decided that we will use region for now.<br>
region will be cleared in sqlite3_finalize(). Comment added.<br>
<blockquote type="cite"
cite="mid:2f7e9873-8558-b7f6-2fd0-6ec01815ae1f@tarantool.org"> <br>
<blockquote type="cite"> lua_pushnil(L); <br>
lua_next(L, lua_gettop(L) - 1); <br>
struct luaL_field field_name; <br>
lua_pushvalue(L, -2); <br>
luaL_tofield(L, luaL_msgpack_default, -1,
&field_name); <br>
lua_pop(L, 1); <br>
assert(field_name.type == MP_STR); <br>
luaL_tofield(L, luaL_msgpack_default, -1, field); <br>
lua_pop(L, 1); <br>
bind->name = field_name.sval.data; <br>
bind->name_len = field_name.sval.len; <br>
</blockquote>
<br>
2. Please, do not use luaL_tofield for each scalar value. Here <br>
it is much simpler to just use lua_tostring or something. <br>
</blockquote>
Fixed.<br>
<blockquote type="cite"
cite="mid:2f7e9873-8558-b7f6-2fd0-6ec01815ae1f@tarantool.org"> <br>
<blockquote type="cite"> for (uint32_t i = 0; i <
bind_count; ++i) { <br>
struct luaL_field field; <br>
lua_rawgeti(L, idx, i + 1); <br>
luaL_tofield(L, luaL_msgpack_default, -1, &field); <br>
if (lua_sql_bind_decode(L, &bind[i], i, &field)
!= 0) { <br>
region_truncate(region, used); <br>
return -1; <br>
} <br>
lua_pop(L, 1); <br>
} <br>
</blockquote>
<br>
3. Why not to do lua_rawgeti and luaL_tofield inside
lua_sql_bind_decode? <br>
</blockquote>
Fixed. Moved them to lua_sql_bind_decode().
<blockquote type="cite"
cite="mid:2f7e9873-8558-b7f6-2fd0-6ec01815ae1f@tarantool.org"> <br>
4. Lua vstream should not be implemented in sql.c. It is a
separate file <br>
luastream.c or something. You should have separate luastream
implementation <br>
without any vtabs and lua_vstream_vtab specially for vstream. Just
like <br>
it is done now for mpstream. <br>
</blockquote>
Fixed. Added struct luastream in new file luastream.c<br>
<blockquote type="cite"
cite="mid:2f7e9873-8558-b7f6-2fd0-6ec01815ae1f@tarantool.org"> <br>
5. API of creating mp_vstream and lua_vstream is asymmetrical: to
create <br>
mp_vstream you call two functions to initialize mpstream and then
vtab, <br>
but to create lua_vstream you call one function. I think, it
should be <br>
fixed automatically once you've fixed the previous comment. <br>
</blockquote>
Fixed.<br>
<p><b>New version:</b></p>
<p>commit cbd68868b8abf60beede0fcbdd144ca99fa49d15<br>
Author: Mergen Imeev <a class="moz-txt-link-rfc2396E"
href="mailto:imeevma@gmail.com"><imeevma@gmail.com></a><br>
Date: Thu Nov 15 18:55:29 2018 +0300<br>
<br>
lua: create vstream implementation for Lua<br>
<br>
Thas patch creates vstream implementation for Lua and function<br>
box.sql.new_execute() that uses this implementation. Also it<br>
creates parameters binding for SQL statements executed through<br>
box.<br>
<br>
Part of #3505<br>
Closes #3401<br>
<br>
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt<br>
index d127647..9f5b2b9 100644<br>
--- a/src/box/CMakeLists.txt<br>
+++ b/src/box/CMakeLists.txt<br>
@@ -138,6 +138,7 @@ add_library(box STATIC<br>
lua/session.c<br>
lua/net_box.c<br>
lua/xlog.c<br>
+ lua/luastream.c<br>
lua/sql.c<br>
${bin_sources})<br>
<br>
diff --git a/src/box/execute.c b/src/box/execute.c<br>
index 0fee5c1..efe4e79 100644<br>
--- a/src/box/execute.c<br>
+++ b/src/box/execute.c<br>
@@ -43,6 +43,8 @@<br>
#include "tuple.h"<br>
#include "sql/vdbe.h"<br>
#include "vstream.h"<br>
+#include "lua/utils.h"<br>
+#include "lua/msgpack.h"<br>
<br>
const char *sql_type_strs[] = {<br>
NULL,<br>
@@ -299,6 +301,195 @@ error:<br>
}<br>
<br>
/**<br>
+ * Decode a single bind column from Lua stack.<br>
+ *<br>
+ * @param L Lua stack.<br>
+ * @param[out] bind Bind to decode to.<br>
+ * @param idx Position of table with bind columns on Lua stack.<br>
+ * @param i Ordinal bind number.<br>
+ *<br>
+ * @retval 0 Success.<br>
+ * @retval -1 Memory or client error.<br>
+ */<br>
+static inline int<br>
+lua_sql_bind_decode(struct lua_State *L, struct sql_bind *bind,
int idx, int i)<br>
+{<br>
+ struct luaL_field field;<br>
+ char *buf;<br>
+ lua_rawgeti(L, idx, i + 1);<br>
+ luaL_tofield(L, luaL_msgpack_default, -1, &field);<br>
+ bind->pos = i + 1;<br>
+ if (field.type == MP_MAP) {<br>
+ /*<br>
+ * A named parameter is an MP_MAP with<br>
+ * one key - {'name': value}.<br>
+ * Report parse error otherwise.<br>
+ */<br>
+ if (field.size != 1) {<br>
+ diag_set(ClientError, ER_ILLEGAL_PARAMS, "SQL bind "\<br>
+ "parameter should be {'name': value}");<br>
+ return -1;<br>
+ }<br>
+ /*<br>
+ * Get key and value of the only map element to<br>
+ * lua stack.<br>
+ */<br>
+ lua_pushnil(L);<br>
+ lua_next(L, lua_gettop(L) - 1);<br>
+ /* At first we should deal with the value. */<br>
+ luaL_tofield(L, luaL_msgpack_default, -1, &field);<br>
+ lua_pop(L, 1);<br>
+ /* Now key is on the top of Lua stack. */<br>
+ size_t name_len = 0;<br>
+ bind->name = luaL_checklstring(L, -1, &name_len);<br>
+ if (bind->name == NULL) {<br>
+ diag_set(ClientError, ER_ILLEGAL_PARAMS, "SQL bind "\<br>
+ "parameter should be {'name': value}");<br>
+ return -1;<br>
+ }<br>
+ /*<br>
+ * Name should be saved in allocated memory as it<br>
+ * will be poped from Lua stack.<br>
+ */<br>
+ buf = region_alloc(&fiber()->gc, name_len + 1);<br>
+ if (buf == NULL) {<br>
+ diag_set(OutOfMemory, name_len + 1, "region_alloc",<br>
+ "buf");<br>
+ return -1;<br>
+ }<br>
+ memcpy(buf, bind->name, name_len + 1);<br>
+ bind->name = buf;<br>
+ bind->name_len = name_len;<br>
+ lua_pop(L, 1);<br>
+ } else {<br>
+ bind->name = NULL;<br>
+ bind->name_len = 0;<br>
+ }<br>
+ switch (field.type) {<br>
+ case MP_UINT: {<br>
+ bind->i64 = field.ival;<br>
+ bind->type = SQLITE_INTEGER;<br>
+ bind->bytes = sizeof(bind->i64);<br>
+ break;<br>
+ }<br>
+ case MP_INT:<br>
+ bind->i64 = field.ival;<br>
+ bind->type = SQLITE_INTEGER;<br>
+ bind->bytes = sizeof(bind->i64);<br>
+ break;<br>
+ case MP_STR:<br>
+ /*<br>
+ * Data should be saved in allocated memory as it<br>
+ * will be poped from Lua stack.<br>
+ */<br>
+ buf = region_alloc(&fiber()->gc, field.sval.len +
1);<br>
+ if (buf == NULL) {<br>
+ diag_set(OutOfMemory, field.sval.len + 1,<br>
+ "region_alloc", "buf");<br>
+ return -1;<br>
+ }<br>
+ memcpy(buf, field.sval.data, field.sval.len + 1);<br>
+ bind->s = buf;<br>
+ bind->type = SQLITE_TEXT;<br>
+ bind->bytes = field.sval.len;<br>
+ break;<br>
+ case MP_DOUBLE:<br>
+ bind->d = field.dval;<br>
+ bind->type = SQLITE_FLOAT;<br>
+ bind->bytes = sizeof(bind->d);<br>
+ break;<br>
+ case MP_FLOAT:<br>
+ bind->d = field.dval;<br>
+ bind->type = SQLITE_FLOAT;<br>
+ bind->bytes = sizeof(bind->d);<br>
+ break;<br>
+ case MP_NIL:<br>
+ bind->type = SQLITE_NULL;<br>
+ bind->bytes = 1;<br>
+ break;<br>
+ case MP_BOOL:<br>
+ /* SQLite doesn't support boolean. Use int instead. */<br>
+ bind->i64 = field.bval ? 1 : 0;<br>
+ bind->type = SQLITE_INTEGER;<br>
+ bind->bytes = sizeof(bind->i64);<br>
+ break;<br>
+ case MP_BIN:<br>
+ bind->s = mp_decode_bin(&field.sval.data,
&bind->bytes);<br>
+ bind->type = SQLITE_BLOB;<br>
+ break;<br>
+ case MP_EXT:<br>
+ /*<br>
+ * Data should be saved in allocated memory as it<br>
+ * will be poped from Lua stack.<br>
+ */<br>
+ buf = region_alloc(&fiber()->gc, sizeof(field));<br>
+ if (buf == NULL) {<br>
+ diag_set(OutOfMemory, sizeof(field), "region_alloc",<br>
+ "buf");<br>
+ return -1;<br>
+ }<br>
+ memcpy(buf, &field, sizeof(field));<br>
+ bind->s = buf;<br>
+ bind->bytes = sizeof(field);<br>
+ bind->type = SQLITE_BLOB;<br>
+ break;<br>
+ case MP_ARRAY:<br>
+ diag_set(ClientError, ER_SQL_BIND_TYPE, "ARRAY",<br>
+ sql_bind_name(bind));<br>
+ return -1;<br>
+ case MP_MAP:<br>
+ diag_set(ClientError, ER_SQL_BIND_TYPE, "MAP",<br>
+ sql_bind_name(bind));<br>
+ return -1;<br>
+ default:<br>
+ unreachable();<br>
+ }<br>
+ lua_pop(L, 1);<br>
+ return 0;<br>
+}<br>
+<br>
+int<br>
+lua_sql_bind_list_decode(struct lua_State *L, struct sql_request
*request,<br>
+ int idx)<br>
+{<br>
+ assert(request != NULL);<br>
+ if (! lua_istable(L, idx)) {<br>
+ diag_set(ClientError, ER_INVALID_MSGPACK, "SQL parameter
list");<br>
+ return -1;<br>
+ }<br>
+ uint32_t bind_count = lua_objlen(L, idx);<br>
+ if (bind_count == 0)<br>
+ return 0;<br>
+ if (bind_count > SQL_BIND_PARAMETER_MAX) {<br>
+ diag_set(ClientError, ER_SQL_BIND_PARAMETER_MAX,<br>
+ (int) bind_count);<br>
+ return -1;<br>
+ }<br>
+ struct region *region = &fiber()->gc;<br>
+ uint32_t used = region_used(region);<br>
+ size_t size = sizeof(struct sql_bind) * bind_count;<br>
+ /*<br>
+ * Memory allocated here will be freed in<br>
+ * sqlite3_finalize() or in txn_commit()/txn_rollback() if<br>
+ * there is an active transaction.<br>
+ */<br>
+ struct sql_bind *bind = (struct sql_bind *)
region_alloc(region, size);<br>
+ if (bind == NULL) {<br>
+ diag_set(OutOfMemory, size, "region_alloc", "bind");<br>
+ return -1;<br>
+ }<br>
+ for (uint32_t i = 0; i < bind_count; ++i) {<br>
+ if (lua_sql_bind_decode(L, &bind[i], idx, i) != 0) {<br>
+ region_truncate(region, used);<br>
+ return -1;<br>
+ }<br>
+ }<br>
+ request->bind_count = bind_count;<br>
+ request->bind = bind;<br>
+ return 0;<br>
+}<br>
+<br>
+/**<br>
* Serialize a single column of a result set row.<br>
* @param stmt Prepared and started statement. At least one<br>
* sqlite3_step must be called.<br>
diff --git a/src/box/execute.h b/src/box/execute.h<br>
index 56b7339..e186340 100644<br>
--- a/src/box/execute.h<br>
+++ b/src/box/execute.h<br>
@@ -142,6 +142,20 @@ xrow_decode_sql(const struct xrow_header
*row, struct sql_request *request,<br>
struct region *region);<br>
<br>
/**<br>
+ * Parse Lua table of SQL parameters and store a result<br>
+ * into the @request->bind, bind_count.<br>
+ * @param L Lua stack to get data from.<br>
+ * @param request Request to save decoded parameters.<br>
+ * @param idx Position of table with parameters on Lua stack.<br>
+ *<br>
+ * @retval 0 Success.<br>
+ * @retval -1 Client or memory error.<br>
+ */<br>
+int<br>
+lua_sql_bind_list_decode(struct lua_State *L, struct sql_request
*request,<br>
+ int idx);<br>
+<br>
+/**<br>
* Prepare and execute an SQL statement.<br>
* @param request IProto request.<br>
* @param[out] response Response to store result.<br>
diff --git a/src/box/lua/luastream.c b/src/box/lua/luastream.c<br>
new file mode 100644<br>
index 0000000..79a5246<br>
--- /dev/null<br>
+++ b/src/box/lua/luastream.c<br>
@@ -0,0 +1,151 @@<br>
+/*<br>
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS
file.<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or<br>
+ * without modification, are permitted provided that the
following<br>
+ * conditions are met:<br>
+ *<br>
+ * 1. Redistributions of source code must retain the above<br>
+ * copyright notice, this list of conditions and the<br>
+ * following disclaimer.<br>
+ *<br>
+ * 2. Redistributions in binary form must reproduce the above<br>
+ * copyright notice, this list of conditions and the following<br>
+ * disclaimer in the documentation and/or other materials<br>
+ * provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS
IS'' AND<br>
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED<br>
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR<br>
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL<br>
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT,<br>
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL<br>
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR<br>
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF<br>
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT<br>
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF<br>
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF<br>
+ * SUCH DAMAGE.<br>
+ */<br>
+<br>
+#include "lua/utils.h"<br>
+#include "box/execute.h"<br>
+#include "box/vstream.h"<br>
+<br>
+struct luastream {<br>
+ struct lua_State *L;<br>
+};<br>
+<br>
+void<br>
+luastream_init(struct luastream *stream, struct lua_State *L)<br>
+{<br>
+ struct luastream *luastream = (struct luastream *)stream;<br>
+ luastream->L = L;<br>
+}<br>
+<br>
+void<br>
+luastream_encode_array(struct luastream *stream, uint32_t size)<br>
+{<br>
+ lua_createtable(stream->L, size, 0);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_map(struct luastream *stream, uint32_t size)<br>
+{<br>
+ lua_createtable(stream->L, size, 0);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_uint(struct luastream *stream, uint64_t num)<br>
+{<br>
+ luaL_pushuint64(stream->L, num);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_int(struct luastream *stream, int64_t num)<br>
+{<br>
+ luaL_pushint64(stream->L, num);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_float(struct luastream *stream, float num)<br>
+{<br>
+ lua_pushnumber(stream->L, num);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_double(struct luastream *stream, double num)<br>
+{<br>
+ lua_pushnumber(stream->L, num);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_strn(struct luastream *stream, const char *str,
uint32_t len)<br>
+{<br>
+ lua_pushlstring(stream->L, str, len);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_nil(struct luastream *stream)<br>
+{<br>
+ lua_pushnil(stream->L);<br>
+}<br>
+<br>
+void<br>
+luastream_encode_bool(struct luastream *stream, bool val)<br>
+{<br>
+ lua_pushboolean(stream->L, val);<br>
+}<br>
+<br>
+int<br>
+lua_vstream_encode_port(struct vstream *stream, struct port
*port)<br>
+{<br>
+ port_dump_lua(port, ((struct luastream *)stream)->L);<br>
+ return 0;<br>
+}<br>
+<br>
+void<br>
+lua_vstream_encode_enum(struct vstream *stream, int64_t num,
const char *str)<br>
+{<br>
+ (void)num;<br>
+ lua_pushlstring(((struct luastream *)stream)->L, str,
strlen(str));<br>
+}<br>
+<br>
+void<br>
+lua_vstream_encode_map_commit(struct vstream *stream)<br>
+{<br>
+ size_t length;<br>
+ const char *key = lua_tolstring(((struct luastream
*)stream)->L, -2,<br>
+ &length);<br>
+ lua_setfield(((struct luastream *)stream)->L, -3, key);<br>
+ lua_pop(((struct luastream *)stream)->L, 1);<br>
+}<br>
+<br>
+void<br>
+lua_vstream_encode_array_commit(struct vstream *stream, uint32_t
id)<br>
+{<br>
+ lua_rawseti(((struct luastream *)stream)->L, -2, id + 1);<br>
+}<br>
+<br>
+const struct vstream_vtab lua_vstream_vtab = {<br>
+ /** encode_array = */ (encode_array_f)luastream_encode_array,<br>
+ /** encode_map = */ (encode_map_f)luastream_encode_map,<br>
+ /** encode_uint = */ (encode_uint_f)luastream_encode_uint,<br>
+ /** encode_int = */ (encode_int_f)luastream_encode_int,<br>
+ /** encode_float = */ (encode_float_f)luastream_encode_float,<br>
+ /** encode_double = */
(encode_double_f)luastream_encode_double,<br>
+ /** encode_strn = */ (encode_strn_f)luastream_encode_strn,<br>
+ /** encode_nil = */ (encode_nil_f)luastream_encode_nil,<br>
+ /** encode_bool = */ (encode_bool_f)luastream_encode_bool,<br>
+ /** encode_enum = */ lua_vstream_encode_enum,<br>
+ /** encode_port = */ lua_vstream_encode_port,<br>
+ /** encode_array_commit = */ lua_vstream_encode_array_commit,<br>
+ /** encode_map_commit = */ lua_vstream_encode_map_commit,<br>
+};<br>
+<br>
+void<br>
+lua_vstream_init_vtab(struct vstream *stream)<br>
+{<br>
+ stream->vtab = &lua_vstream_vtab;<br>
+}<br>
diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c<br>
index 17e2694..7567afc 100644<br>
--- a/src/box/lua/sql.c<br>
+++ b/src/box/lua/sql.c<br>
@@ -6,6 +6,8 @@<br>
#include "box/info.h"<br>
#include "lua/utils.h"<br>
#include "info.h"<br>
+#include "box/execute.h"<br>
+#include "box/vstream.h"<br>
<br>
static void<br>
lua_push_column_names(struct lua_State *L, struct sqlite3_stmt
*stmt)<br>
@@ -111,6 +113,39 @@ sqlerror:<br>
}<br>
<br>
static int<br>
+lbox_execute(struct lua_State *L)<br>
+{<br>
+ struct sqlite3 *db = sql_get();<br>
+ if (db == NULL)<br>
+ return luaL_error(L, "not ready");<br>
+<br>
+ size_t length;<br>
+ const char *sql = lua_tolstring(L, 1, &length);<br>
+ if (sql == NULL)<br>
+ return luaL_error(L, "usage: box.execute(sqlstring)");<br>
+<br>
+ struct sql_request request = {};<br>
+ request.sql_text = sql;<br>
+ request.sql_text_len = length;<br>
+ if (lua_gettop(L) == 2 && lua_sql_bind_list_decode(L,
&request, 2) != 0)<br>
+ return luaT_error(L);<br>
+ struct sql_response response = {.is_info_flattened = true};<br>
+ if (sql_prepare_and_execute(&request, &response,
&fiber()->gc) != 0)<br>
+ return luaT_error(L);<br>
+<br>
+ int keys;<br>
+ struct vstream vstream;<br>
+ luastream_init((struct luastream *)&vstream, L);<br>
+ lua_vstream_init_vtab(&vstream);<br>
+ lua_newtable(L);<br>
+ if (sql_response_dump(&response, &keys, &vstream)
!= 0) {<br>
+ lua_pop(L, 1);<br>
+ return luaT_error(L);<br>
+ }<br>
+ return 1;<br>
+}<br>
+<br>
+static int<br>
lua_sql_debug(struct lua_State *L)<br>
{<br>
struct info_handler info;<br>
@@ -124,6 +159,7 @@ box_lua_sqlite_init(struct lua_State *L)<br>
{<br>
static const struct luaL_Reg module_funcs [] = {<br>
{"execute", lua_sql_execute},<br>
+ {"new_execute", lbox_execute},<br>
{"debug", lua_sql_debug},<br>
{NULL, NULL}<br>
};<br>
diff --git a/src/box/lua/sql.h b/src/box/lua/sql.h<br>
index 65ff6d5..a83a5b8 100644<br>
--- a/src/box/lua/sql.h<br>
+++ b/src/box/lua/sql.h<br>
@@ -36,9 +36,13 @@ extern "C" {<br>
#endif<br>
<br>
struct lua_State;<br>
+struct luastream;<br>
<br>
void box_lua_sqlite_init(struct lua_State *L);<br>
<br>
+void<br>
+luastream_init(struct luastream *stream, struct lua_State *L);<br>
+<br>
#ifdef __cplusplus<br>
}<br>
#endif<br>
diff --git a/src/box/vstream.h b/src/box/vstream.h<br>
index 01a5212..b846b68 100644<br>
--- a/src/box/vstream.h<br>
+++ b/src/box/vstream.h<br>
@@ -71,7 +71,10 @@ struct vstream_vtab {<br>
};<br>
<br>
struct vstream {<br>
- /** Here struct mpstream lives under the hood. */<br>
+ /**<br>
+ * Here struct mpstream or struct luastream lives under<br>
+ * the hood.<br>
+ */<br>
char inheritance_padding[64];<br>
/** Virtual function table. */<br>
const struct vstream_vtab *vtab;<br>
@@ -80,6 +83,9 @@ struct vstream {<br>
void<br>
mp_vstream_init_vtab(struct vstream *vstream);<br>
<br>
+void<br>
+lua_vstream_init_vtab(struct vstream *vstream);<br>
+<br>
static inline void<br>
vstream_encode_array(struct vstream *stream, uint32_t size)<br>
{<br>
<br>
</p>
</body>
</html>