void luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg, struct mpstream *stream, int index) { struct tuple *tuple = luaT_istuple(L, index); if (tuple != NULL) { return tuple_to_mpstream(tuple, stream); } else if (luamp_encode(L, cfg, stream, index) != MP_ARRAY) { diag_set(ClientError, ER_TUPLE_NOT_ARRAY); luaT_error(L); } }
static int execute_lua_eval(lua_State *L) { struct lua_function_ctx *ctx = (struct lua_function_ctx *) lua_topointer(L, 1); struct request *request = ctx->request; struct obuf *out = ctx->out; struct obuf_svp *svp = &ctx->svp; lua_settop(L, 0); /* clear the stack to simplify the logic below */ /* Compile expression */ const char *expr = request->key; uint32_t expr_len = mp_decode_strl(&expr); if (luaL_loadbuffer(L, expr, expr_len, "=eval")) { diag_set(LuajitError, lua_tostring(L, -1)); lbox_error(L); } /* Unpack arguments */ const char *args = request->tuple; uint32_t arg_count = mp_decode_array(&args); luaL_checkstack(L, arg_count, "eval: out of stack"); for (uint32_t i = 0; i < arg_count; i++) { luamp_decode(L, luaL_msgpack_default, &args); } /* Call compiled code */ lua_call(L, arg_count, LUA_MULTRET); /* Send results of the called procedure to the client. */ if (iproto_prepare_select(out, svp) != 0) diag_raise(); ctx->out_is_dirty = true; struct mpstream stream; mpstream_init(&stream, out, obuf_reserve_cb, obuf_alloc_cb, luamp_error, L); int nrets = lua_gettop(L); for (int k = 1; k <= nrets; ++k) { luamp_encode(L, luaL_msgpack_default, &stream, k); } mpstream_flush(&stream); iproto_reply_select(out, svp, request->header->sync, nrets); return 0; }
struct tuple * luaT_tuple_new(struct lua_State *L, int idx, box_tuple_format_t *format) { if (idx != 0 && !lua_istable(L, idx) && !luaT_istuple(L, idx)) { diag_set(IllegalParams, "A tuple or a table expected, got %s", lua_typename(L, lua_type(L, idx))); return NULL; } struct ibuf *buf = tarantool_lua_ibuf; ibuf_reset(buf); struct mpstream stream; mpstream_init(&stream, buf, ibuf_reserve_cb, ibuf_alloc_cb, luamp_error, L); if (idx == 0) { /* * Create the tuple from lua stack * objects. */ int argc = lua_gettop(L); mpstream_encode_array(&stream, argc); for (int k = 1; k <= argc; ++k) { luamp_encode(L, luaL_msgpack_default, &stream, k); } } else { /* Create the tuple from a Lua table. */ luamp_encode_tuple(L, &tuple_serializer, &stream, idx); } mpstream_flush(&stream); struct tuple *tuple = box_tuple_new(format, buf->buf, buf->buf + ibuf_used(buf)); if (tuple == NULL) return NULL; ibuf_reinit(tarantool_lua_ibuf); return tuple; }
/** * Tuple transforming function. * * Remove the fields designated by 'offset' and 'len' from an tuple, * and replace them with the elements of supplied data fields, * if any. * * Function returns newly allocated tuple. * It does not change any parent tuple data. */ static int lbox_tuple_transform(struct lua_State *L) { struct tuple *tuple = lua_checktuple(L, 1); int argc = lua_gettop(L); if (argc < 3) luaL_error(L, "tuple.transform(): bad arguments"); lua_Integer offset = lua_tointeger(L, 2); /* Can be negative and can be > INT_MAX */ lua_Integer len = lua_tointeger(L, 3); lua_Integer field_count = box_tuple_field_count(tuple); /* validate offset and len */ if (offset == 0) { luaL_error(L, "tuple.transform(): offset is out of bound"); } else if (offset < 0) { if (-offset > field_count) luaL_error(L, "tuple.transform(): offset is out of bound"); offset += field_count + 1; } else if (offset > field_count) { offset = field_count + 1; } if (len < 0) luaL_error(L, "tuple.transform(): len is negative"); if (len > field_count + 1 - offset) len = field_count + 1 - offset; assert(offset + len <= field_count + 1); /* * Calculate the number of operations and length of UPDATE expression */ uint32_t op_cnt = 0; if (offset < field_count + 1 && len > 0) op_cnt++; if (argc > 3) op_cnt += argc - 3; if (op_cnt == 0) { /* tuple_update() does not accept an empty operation list. */ luaT_pushtuple(L, tuple); return 1; } struct ibuf *buf = tarantool_lua_ibuf; ibuf_reset(buf); struct mpstream stream; mpstream_init(&stream, buf, ibuf_reserve_cb, ibuf_alloc_cb, luamp_error, L); /* * Prepare UPDATE expression */ mpstream_encode_array(&stream, op_cnt); if (len > 0) { mpstream_encode_array(&stream, 3); mpstream_encode_str(&stream, "#"); mpstream_encode_uint(&stream, offset); mpstream_encode_uint(&stream, len); } for (int i = argc ; i > 3; i--) { mpstream_encode_array(&stream, 3); mpstream_encode_str(&stream, "!"); mpstream_encode_uint(&stream, offset); luamp_encode(L, luaL_msgpack_default, &stream, i); } mpstream_flush(&stream); uint32_t new_size = 0, bsize; const char *old_data = tuple_data_range(tuple, &bsize); struct region *region = &fiber()->gc; size_t used = region_used(region); struct tuple *new_tuple = NULL; /* * Can't use box_tuple_update() since transform must reset * the tuple format to default. The new tuple most likely * won't coerce into the original space format, so we have * to use the default one with no restrictions on field * count or types. */ const char *new_data = tuple_update_execute(region_aligned_alloc_cb, region, buf->buf, buf->buf + ibuf_used(buf), old_data, old_data + bsize, &new_size, 1, NULL); if (new_data != NULL) new_tuple = tuple_new(box_tuple_format_default(), new_data, new_data + new_size); region_truncate(region, used); if (new_tuple == NULL) luaT_error(L); luaT_pushtuple(L, new_tuple); ibuf_reset(buf); return 1; }
/* * Encode CALL result. * Please read gh-291 carefully before "fixing" this code. */ static inline uint32_t luamp_encode_call(lua_State *L, struct luaL_serializer *cfg, struct mpstream *stream) { int nrets = lua_gettop(L); if (nrets == 0) { return 0; } else if (nrets > 1) { /* * Multireturn: * `return 1, box.tuple.new(...), array, 3, ...` */ for (int i = 1; i <= nrets; ++i) { struct luaL_field field; luaL_tofield(L, cfg, i, &field); struct tuple *tuple; if (field.type == MP_EXT && (tuple = lua_istuple(L, i)) != NULL) { /* `return ..., box.tuple.new(...), ...` */ tuple_to_mpstream(tuple, stream); } else if (field.type != MP_ARRAY) { /* * `return ..., scalar, ... => * ..., { scalar }, ...` */ lua_pushvalue(L, i); luamp_encode_array(cfg, stream, 1); luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); } else { /* `return ..., array, ...` */ luamp_encode(L, cfg, stream, i); } } return nrets; } assert(nrets == 1); /* * Inspect the first result */ struct luaL_field root; luaL_tofield(L, cfg, 1, &root); struct tuple *tuple; if (root.type == MP_EXT && (tuple = lua_istuple(L, 1)) != NULL) { /* `return box.tuple()` */ tuple_to_mpstream(tuple, stream); return 1; } else if (root.type != MP_ARRAY) { /* * `return scalar` * `return map` */ luamp_encode_array(cfg, stream, 1); assert(lua_gettop(L) == 1); luamp_encode_r(L, cfg, stream, &root, 0); return 1; } assert(root.type == MP_ARRAY); if (root.size == 0) { /* `return {}` => `{ box.tuple() }` */ luamp_encode_array(cfg, stream, 0); return 1; } /* `return { tuple, scalar, tuple }` */ assert(root.type == MP_ARRAY && root.size > 0); for (uint32_t t = 1; t <= root.size; t++) { lua_rawgeti(L, 1, t); struct luaL_field field; luaL_tofield(L, cfg, -1, &field); if (field.type == MP_EXT && (tuple = lua_istuple(L, -1))) { tuple_to_mpstream(tuple, stream); } else if (field.type != MP_ARRAY) { /* The first member of root table is not tuple/array */ if (t == 1) { /* * `return { scalar, ... } => * box.tuple.new(scalar, ...)` */ luamp_encode_array(cfg, stream, root.size); /* * Encode the first field of tuple using * existing information from luaL_tofield */ luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); assert(lua_gettop(L) == 1); /* Encode remaining fields as usual */ for (uint32_t f = 2; f <= root.size; f++) { lua_rawgeti(L, 1, f); luamp_encode(L, cfg, stream, -1); lua_pop(L, 1); } return 1; } /* * `return { tuple/array, ..., scalar, ... } => * { tuple/array, ..., { scalar }, ... }` */ luamp_encode_array(cfg, stream, 1); luamp_encode_r(L, cfg, stream, &field, 0); } else { /* `return { tuple/array, ..., tuple/array, ... }` */ luamp_encode_r(L, cfg, stream, &field, 0); } lua_pop(L, 1); assert(lua_gettop(L) == 1); } return root.size; }