<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>