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