diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 20464e854c4ec5097d1f66ddaa55fc113e3d45a8..ba763724ab3fb05d34109ff264b7ad520fe29ec8 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -44,7 +44,7 @@ add_library(box alter.cc schema.cc session.cc - port.c + port.cc request.cc txn.cc box.cc diff --git a/src/box/box.cc b/src/box/box.cc index 647a899ecbeb4be9a5e71ce3d446a22139f0f383..55e273a4ddd7551a1d4e3a63904d6b2812930bee 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -378,6 +378,7 @@ box_free(void) user_cache_free(); schema_free(); tuple_free(); + port_free(); recovery_exit(recovery); recovery = NULL; engine_shutdown(); @@ -464,6 +465,7 @@ box_init(void) cfg_getd("wal_dir_rescan_delay")); title("hot_standby", NULL); + port_init(); iproto_init(); box_set_listen(cfg_gets("listen")); diff --git a/src/box/func.cc b/src/box/func.cc index 21708cb6a0b2945c188ae2e63b0a4714ac43a6d7..73f00a346f9f97d6ea5e97cf23c4da1c054db906 100644 --- a/src/box/func.cc +++ b/src/box/func.cc @@ -90,16 +90,16 @@ func_load(struct func *func) * Extract package name from function name. * E.g. name = foo.bar.baz, function = baz, package = foo.bar */ + const char *sym; const char *package = func->def.name; - const char *package_end = NULL; - const char *sym = package; - while ((sym = strchr(sym, '.'))) - package_end = sym; - if (package_end == NULL) { + const char *package_end = strrchr(package, '.'); + if (package_end != NULL) { + /* module.submodule.function => module.submodule, function */ + sym = package_end + 1; + } else { + /* package == function => function, function */ sym = package; package_end = package + strlen(package); - } else { - sym = package_end + 1; } /* First argument of searchpath: name */ diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index b10d1246cf04f9f93720e327bfd77fa495da8686..947237c767b0eb2a054078b8d9c7cd9bd35429d1 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -221,53 +221,8 @@ lbox_request_create(struct request *request, } } -static void -port_ffi_add_tuple(struct port *port, struct tuple *tuple) -{ - struct port_ffi *port_ffi = (struct port_ffi *) port; - if (port_ffi->size >= port_ffi->capacity) { - uint32_t capacity = (port_ffi->capacity > 0) ? - 2 * port_ffi->capacity : 1024; - struct tuple **ret = (struct tuple **) - realloc(port_ffi->ret, sizeof(*ret) * capacity); - assert(ret != NULL); - port_ffi->ret = ret; - port_ffi->capacity = capacity; - } - tuple_ref(tuple); - port_ffi->ret[port_ffi->size++] = tuple; -} - -struct port_vtab port_ffi_vtab = { - port_ffi_add_tuple, - null_port_eof, -}; - -void -port_ffi_create(struct port_ffi *port) -{ - memset(port, 0, sizeof(*port)); - port->vtab = &port_ffi_vtab; -} - -static inline void -port_ffi_clear(struct port_ffi *port) -{ - for (uint32_t i = 0; i < port->size; i++) { - tuple_unref(port->ret[i]); - port->ret[i] = NULL; - } - port->size = 0; -} - -void -port_ffi_destroy(struct port_ffi *port) -{ - free(port->ret); -} - int -boxffi_select(struct port_ffi *port, uint32_t space_id, uint32_t index_id, +boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id, int iterator, uint32_t offset, uint32_t limit, const char *key, const char *key_end) { @@ -281,21 +236,10 @@ boxffi_select(struct port_ffi *port, uint32_t space_id, uint32_t index_id, request.key = key; request.key_end = key_end; - /* - * A single instance of port_ffi object is used - * for all selects, reset it. - */ - port->size = 0; try { - box_process(&request, (struct port *) port); + box_process(&request, port); return 0; } catch (Exception *e) { - /* - * The tuples will be not blessed and garbage - * collected, unreference them here, to avoid - * a leak. - */ - port_ffi_clear(port); /* will be hanled by box.error() in Lua */ return -1; } @@ -571,28 +515,60 @@ lua_isarray(struct lua_State *L, int i) return index_starts_at_1; } +static inline void +execute_c_call(struct func *func, struct request *request, struct obuf *out) +{ + assert(func != NULL && func->def.language == FUNC_LANGUAGE_C); + if (func->func == NULL) + func_load(func); + + const char *name = request->key; + uint32_t name_len = mp_decode_strl(&name); + SetuidGuard setuid(name, name_len, PRIV_X, func); + + struct port_buf port_buf; + port_buf_create(&port_buf); + auto guard = make_scoped_guard([&]{ + port_buf_destroy(&port_buf); + }); + + func->func(request, &port_buf.base); + + if (in_txn()) { + say_warn("a transaction is active at CALL return"); + txn_rollback(); + } + + struct obuf_svp svp = iproto_prepare_select(out); + try { + for (struct port_buf_entry *entry = port_buf.first; + entry != NULL; entry = entry->next) { + tuple_to_obuf(entry->tuple, out); + } + iproto_reply_select(out, &svp, request->header->sync, + port_buf.size); + } catch (Exception *e) { + obuf_rollback_to_svp(out, &svp); + txn_rollback(); + /* Let all well-behaved exceptions pass through. */ + throw; + } +} + /** * Invoke a Lua stored procedure from the binary protocol * (implementation of 'CALL' command code). */ static inline void -execute_call(lua_State *L, struct request *request, struct obuf *out) +execute_lua_call(lua_State *L, struct func *func, struct request *request, + struct obuf *out) { const char *name = request->key; uint32_t name_len = mp_decode_strl(&name); - int oc = 0; /* how many objects are on stack after box_lua_find */ - struct func *func = func_by_name(name, name_len); - /* - * func == NULL means that perhaps the user has a global - * "EXECUTE" privilege, so no specific grant to a function. - */ - if (func == NULL || func->def.language == FUNC_LANGUAGE_LUA) { - /* Try to find a function by name in Lua */ - oc = box_lua_find(L, name, name + name_len); - } else if (func->func == NULL) { - func_load(func); - } + int oc = 0; /* how many objects are on stack after box_lua_find */ + /* Try to find a function by name in Lua */ + oc = box_lua_find(L, name, name + name_len); /** * Check access to the function and optionally change * execution time user id (set user id). Sic: the order @@ -604,20 +580,18 @@ execute_call(lua_State *L, struct request *request, struct obuf *out) /* Push the rest of args (a tuple). */ const char *args = request->tuple; - if (func == NULL || func->def.language == FUNC_LANGUAGE_LUA) { - uint32_t arg_count = mp_decode_array(&args); - luaL_checkstack(L, arg_count, "call: out of stack"); + uint32_t arg_count = mp_decode_array(&args); + luaL_checkstack(L, arg_count, "call: out of stack"); - for (uint32_t i = 0; i < arg_count; i++) { - luamp_decode(L, luaL_msgpack_default, &args); - } - lua_call(L, arg_count + oc - 1, LUA_MULTRET); - } else { - struct port_lua port; - port_lua_create(&port, L); + for (uint32_t i = 0; i < arg_count; i++) + luamp_decode(L, luaL_msgpack_default, &args); + lua_call(L, arg_count + oc - 1, LUA_MULTRET); - func->func(request, (struct port *) &port); + if (in_txn()) { + say_warn("a transaction is active at CALL return"); + txn_rollback(); } + /** * Add all elements from Lua stack to iproto. * @@ -688,15 +662,23 @@ execute_call(lua_State *L, struct request *request, struct obuf *out) void box_lua_call(struct request *request, struct obuf *out) { + const char *name = request->key; + uint32_t name_len = mp_decode_strl(&name); + + struct func *func = func_by_name(name, name_len); + if (func != NULL && func->def.language == FUNC_LANGUAGE_C) + return execute_c_call(func, request, out); + + /* + * func == NULL means that perhaps the user has a global + * "EXECUTE" privilege, so no specific grant to a function. + */ + assert(func == NULL || func->def.language == FUNC_LANGUAGE_LUA); lua_State *L = NULL; try { L = lua_newthread(tarantool_L); LuarefGuard coro_ref(tarantool_L); - execute_call(L, request, out); - if (in_txn()) { - say_warn("a transaction is active at CALL return"); - txn_rollback(); - } + execute_lua_call(L, func, request, out); } catch (Exception *e) { txn_rollback(); /* Let all well-behaved exceptions pass through. */ diff --git a/src/box/lua/call.h b/src/box/lua/call.h index f578e6a54c0cb5051161689033c2c5c8f67921f2..35b53cce5660ced6926ea76efdaac6ec6637aa6e 100644 --- a/src/box/lua/call.h +++ b/src/box/lua/call.h @@ -45,22 +45,9 @@ void box_lua_eval(struct request *request, struct obuf *out); extern "C" { -struct port_ffi -{ - struct port_vtab *vtab; - uint32_t size; - uint32_t capacity; - struct tuple **ret; -}; - -void -port_ffi_create(struct port_ffi *port); - -void -port_ffi_destroy(struct port_ffi *port); int -boxffi_select(struct port_ffi *port, uint32_t space_id, uint32_t index_id, +boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id, int iterator, uint32_t offset, uint32_t limit, const char *key, const char *key_end); diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index a02d49643c65b9eea2f4c041f90a0a76589e5ee1..0e4fe51352a1c8c2fb6e4b8a0efae218d4e04c55 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -42,22 +42,35 @@ ffi.cdef[[ struct tuple * boxffi_iterator_next(struct iterator *itr); - struct port; - struct port_ffi + struct port { struct port_vtab *vtab; - uint32_t size; - uint32_t capacity; - struct tuple **ret; + }; + + struct port_buf_entry { + struct port_buf_entry *next; + struct tuple *tuple; + }; + + struct port_buf { + struct port base; + size_t size; + struct port_buf_entry *first; + struct port_buf_entry *last; + struct port_buf_entry first_entry; }; void - port_ffi_create(struct port_ffi *port); + port_buf_create(struct port_buf *port_buf); + + void + port_buf_destroy(struct port_buf *port_buf); + void - port_ffi_destroy(struct port_ffi *port); + port_buf_transfer(struct port_buf *port_buf); int - boxffi_select(struct port_ffi *port, uint32_t space_id, uint32_t index_id, + boxffi_select(struct port_buf *port, uint32_t space_id, uint32_t index_id, int iterator, uint32_t offset, uint32_t limit, const char *key, const char *key_end); void password_prepare(const char *password, int len, @@ -552,9 +565,8 @@ local iterator_cdata_gc = function(iterator) end -- global struct port instance to use by select()/get() -local port = ffi.new('struct port_ffi') -builtin.port_ffi_create(port) -ffi.gc(port, builtin.port_ffi_destroy) +local port_buf = ffi.new('struct port_buf') +local port_buf_entry_t = ffi.typeof('struct port_buf_entry') -- Helper function for nicer error messages -- in some cases when space object is misused @@ -740,16 +752,23 @@ function box.schema.space.bless(space) local key, key_end = msgpackffi.encode_tuple(key) local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end) - if builtin.boxffi_select(port, index.space_id, + builtin.port_buf_create(port_buf) + if builtin.boxffi_select(port_buf, index.space_id, index.id, iterator, offset, limit, key, key_end) ~=0 then + builtin.port_buf_destroy(port_buf); return box.error() end local ret = {} - for i=0,port.size - 1,1 do + local entry = port_buf.first + local i = 1 + while entry ~= nil do -- tuple.bless must never fail - ret[i + 1] = box.tuple.bless(port.ret[i]) + ret[i] = box.tuple.bless(entry.tuple) + entry = entry.next + i = i + 1 end + builtin.port_buf_transfer(port_buf); return ret end diff --git a/src/box/port.c b/src/box/port.c deleted file mode 100644 index 9eed9679704592c59c09bff202ae5567e2b2fcca..0000000000000000000000000000000000000000 --- a/src/box/port.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 "port.h" - -void -null_port_eof(struct port *port __attribute__((unused))) -{ -} - -static void -null_port_add_tuple(struct port *port __attribute__((unused)), - struct tuple *tuple __attribute__((unused))) -{ -} - -static struct port_vtab null_port_vtab = { - null_port_add_tuple, - null_port_eof, -}; - -struct port null_port = { - /* .vtab = */ &null_port_vtab, -}; - diff --git a/src/box/port.cc b/src/box/port.cc new file mode 100644 index 0000000000000000000000000000000000000000..5598e59b1c879e78f6d0622f144106c39d043b95 --- /dev/null +++ b/src/box/port.cc @@ -0,0 +1,138 @@ +/* + * 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 "port.h" +#include "tuple.h" +#include <lib/small/slab_cache.h> +#include <lib/small/mempool.h> +#include <fiber.h> + +void +null_port_eof(struct port *port __attribute__((unused))) +{ +} + +static void +null_port_add_tuple(struct port *port __attribute__((unused)), + struct tuple *tuple __attribute__((unused))) +{ +} + +static struct port_vtab null_port_vtab = { + null_port_add_tuple, + null_port_eof, +}; + +struct port null_port = { + /* .vtab = */ &null_port_vtab, +}; + +static struct mempool port_buf_entry_pool; + +static void +port_buf_add_tuple(struct port *port, struct tuple *tuple) +{ + struct port_buf *port_buf = (struct port_buf *) port; + struct port_buf_entry *e; + if (port_buf->size == 0) { + tuple_ref(tuple); /* throws */ + e = &port_buf->first_entry; + port_buf->first = port_buf->last = e; + } else { + e = (struct port_buf_entry *) + mempool_alloc(&port_buf_entry_pool); /* throws */ + try { + tuple_ref(tuple); /* throws */ + } catch (Exception *) { + mempool_free(&port_buf_entry_pool, e); + throw; + } + port_buf->last->next = e; + port_buf->last = e; + } + e->tuple = tuple; + e->next = NULL; + ++port_buf->size; +} + +static struct port_vtab port_buf_vtab = { + port_buf_add_tuple, + null_port_eof, +}; + +void +port_buf_create(struct port_buf *port_buf) +{ + port_buf->base.vtab = &port_buf_vtab; + port_buf->size = 0; + port_buf->first = NULL; + port_buf->last = NULL; +} + +void +port_buf_destroy(struct port_buf *port_buf) +{ + struct port_buf_entry *e = port_buf->first; + if (e == NULL) + return; + tuple_unref(e->tuple); + e = e->next; + while (e != NULL) { + struct port_buf_entry *cur = e; + e = e->next; + tuple_unref(cur->tuple); + mempool_free(&port_buf_entry_pool, cur); + } +} + +void +port_buf_transfer(struct port_buf *port_buf) +{ + struct port_buf_entry *e = port_buf->first; + if (e == NULL) + return; + e = e->next; + while (e != NULL) { + struct port_buf_entry *cur = e; + e = e->next; + mempool_free(&port_buf_entry_pool, cur); + } +} + +void +port_init(void) +{ + mempool_create(&port_buf_entry_pool, &cord()->slabc, + sizeof(struct port_buf_entry)); +} + +void +port_free(void) +{ + mempool_destroy(&port_buf_entry_pool); +} diff --git a/src/box/port.h b/src/box/port.h index e839114876127ea8ea9796e112d7e741c884d4ca..9bfa361c47a75e1bd98cdfa8724b438d1ea94055 100644 --- a/src/box/port.h +++ b/src/box/port.h @@ -29,6 +29,7 @@ * SUCH DAMAGE. */ #include "trivia/util.h" +#include "lib/salad/rlist.h" #if defined(__cplusplus) extern "C" { @@ -91,6 +92,40 @@ extern struct port null_port; void null_port_eof(struct port *port __attribute__((unused))); +struct port_buf_entry { + struct port_buf_entry *next; + struct tuple *tuple; +}; + +struct port_buf { + struct port base; + size_t size; + struct port_buf_entry *first; + struct port_buf_entry *last; + struct port_buf_entry first_entry; +}; + +void +port_buf_create(struct port_buf *port_buf); + +/** + * Unref all tuples and free allocated memory + */ +void +port_buf_destroy(struct port_buf *port_buf); + +/** + * Like port_buf_destroy() but doesn't do tuple_unref() + */ +void +port_buf_transfer(struct port_buf *port_buf); + +void +port_init(void); + +void +port_free(void); + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/ffisyms.cc b/src/ffisyms.cc index f2d3fbe3162236af028effbc9d1dccd2e36b9a05..e2edf632796fe4fa2b31233c38473535927b7897 100644 --- a/src/ffisyms.cc +++ b/src/ffisyms.cc @@ -7,6 +7,7 @@ #include <box/lua/tuple.h> #include <box/lua/call.h> #include <box/sophia_engine.h> +#include <box/port.h> #include <lua/init.h> #include "main.h" #include "lua/bsdsocket.h" @@ -43,8 +44,6 @@ void *ffi_symbols[] = { (void *) boxffi_index_iterator, (void *) boxffi_tuple_update, (void *) boxffi_iterator_next, - (void *) port_ffi_create, - (void *) port_ffi_destroy, (void *) boxffi_select, (void *) password_prepare, (void *) tarantool_error_message, @@ -71,4 +70,7 @@ void *ffi_symbols[] = { (void *) ibuf_create, (void *) ibuf_destroy, (void *) ibuf_reserve_nothrow_slow, + (void *) port_buf_create, + (void *) port_buf_destroy, + (void *) port_buf_transfer }; diff --git a/test/app/function1.c b/test/app/function1.c index b73e500823a27fd7398fd08b153020198ea630da..d0308c662bfe1eb1780a4eafffe0dbc36c2b0216 100644 --- a/test/app/function1.c +++ b/test/app/function1.c @@ -1,8 +1,18 @@ #include "tarantool.h" +#include <stdio.h> int function1(struct request *request, struct port *port) { say_info("-- function1 - called --"); + printf("ok - function1\n"); + return 0; +} + +int +test(struct request *request, struct port *port) +{ + say_info("-- test - called --"); + printf("ok - test\n"); return 0; } diff --git a/test/app/function1.result b/test/app/function1.result index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..208183413473d241189f187817cf0d9f79201ca6 100644 --- a/test/app/function1.result +++ b/test/app/function1.result @@ -0,0 +1,2 @@ +ok - function1 +ok - test diff --git a/test/app/function1.test.lua b/test/app/function1.test.lua index f84ff151a4e5bda83b1701ce0c1a095adbb2e412..f63a02f9ae43c26ccd05db7170439295a649c5f0 100755 --- a/test/app/function1.test.lua +++ b/test/app/function1.test.lua @@ -15,8 +15,11 @@ net = require('net.box') box.schema.func.create('function1', {language = "C"}) box.schema.user.grant('guest', 'execute', 'function', 'function1') +box.schema.func.create('function1.test', {language = "C"}) +box.schema.user.grant('guest', 'execute', 'function', 'function1.test') c = net:new(os.getenv("LISTEN")) c:call('function1') +c:call('function1.test') os.exit(0)