[tarantool-patches] Re: [PATCH v1 1/3] sql: errors for UDFs returning too many values

Kirill Shcherbatov kshcherbatov at tarantool.org
Tue Oct 8 11:38:40 MSK 2019


This patch introduces handling the situation when UDF returns
too many values. Previously Tarantool used to silently use
the first value returned. Now an error is raised.

Moreover a test coverage is improved also for the situation when
no value is returned.

Needed for #4387
---
 src/box/sql/func.c          | 16 +++++++++-------
 test/box/function1.c        | 19 +++++++++++++++++++
 test/box/function1.result   | 33 +++++++++++++++++++++++++++++++++
 test/box/function1.test.lua | 12 ++++++++++++
 4 files changed, 73 insertions(+), 7 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 60efd0d9a..551613908 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -242,9 +242,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t *size)
 	struct port_lua *port = (struct port_lua *) base;
 	struct lua_State *L = port->L;
 	int argc = lua_gettop(L);
-	if (argc == 0) {
+	if (argc == 0 || argc > 1) {
 		diag_set(ClientError, ER_SQL_EXECUTE,
-			 "No value was passed from Lua");
+			 argc == 0 ? "No value was passed from Lua" :
+				     "Too many values were returned from LUA");
 		return NULL;
 	}
 	*size = argc;
@@ -288,9 +289,10 @@ port_tuple_get_vdbemem(struct port *base, uint32_t *size)
 {
 	struct port_tuple *port = (struct port_tuple *)base;
 	*size = port->size;
-	if (*size == 0) {
+	if (*size == 0 || *size > 1) {
 		diag_set(ClientError, ER_SQL_EXECUTE,
-			 "No value was passed from C");
+			 *size == 0 ? "No value was passed from C" :
+				      "Too many values were returned from C");
 		return NULL;
 	}
 	struct region *region = &fiber()->gc;
@@ -321,10 +323,10 @@ port_tuple_get_vdbemem(struct port *base, uint32_t *size)
 			sqlVdbeMemSetDouble(&val[i], mp_decode_double(&data));
 			break;
 		case MP_INT:
-			mem_set_i64(val, mp_decode_int(&data));
+			mem_set_i64(&val[i], mp_decode_int(&data));
 			break;
 		case MP_UINT:
-			mem_set_u64(val, mp_decode_uint(&data));
+			mem_set_u64(&val[i], mp_decode_uint(&data));
 			break;
 		case MP_STR:
 			str = mp_decode_str(&data, &len);
@@ -333,7 +335,7 @@ port_tuple_get_vdbemem(struct port *base, uint32_t *size)
 				goto error;
 			break;
 		case MP_NIL:
-			sqlVdbeMemSetNull(val);
+			sqlVdbeMemSetNull(&val[i]);
 			break;
 		default:
 			diag_set(ClientError, ER_SQL_EXECUTE,
diff --git a/test/box/function1.c b/test/box/function1.c
index ee5a422b5..b0d983e2b 100644
--- a/test/box/function1.c
+++ b/test/box/function1.c
@@ -12,6 +12,25 @@ function1(box_function_ctx_t *ctx, const char *args, const char *args_end)
 	return 0;
 }
 
+int
+multireturn(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	char tuple_buf[512];
+	char *d = tuple_buf;
+	d = mp_encode_array(d, 1);
+	d = mp_encode_uint(d, 1);
+	assert(d <= tuple_buf + sizeof(tuple_buf));
+
+	box_tuple_format_t *fmt = box_tuple_format_default();
+	box_tuple_t *tuple_a = box_tuple_new(fmt, tuple_buf, d);
+	if (tuple_a == NULL)
+		return -1;
+	int rc = box_return_tuple(ctx, tuple_a);
+	if (rc != 0)
+		return rc;
+	return box_return_tuple(ctx, tuple_a);
+}
+
 int
 args(box_function_ctx_t *ctx, const char *args, const char *args_end)
 {
diff --git a/test/box/function1.result b/test/box/function1.result
index a41ca4e3c..546f83369 100644
--- a/test/box/function1.result
+++ b/test/box/function1.result
@@ -757,6 +757,39 @@ box.schema.func.drop('secret_leak')
 box.schema.func.drop('secret')
 ---
 ...
+-- UDF corner cases for SQL: no value returned, too many values returned
+box.execute("SELECT LUA('a = 1 + 1')")
+---
+- null
+- 'Failed to execute SQL statement: No value was passed from Lua'
+...
+box.execute("SELECT LUA('return 1, 2')")
+---
+- null
+- 'Failed to execute SQL statement: Too many values were returned from LUA'
+...
+box.schema.func.create('function1', {language = "C", exports = {'LUA', 'SQL'}})
+---
+...
+box.execute("SELECT \"function1\"()")
+---
+- null
+- 'Failed to execute SQL statement: No value was passed from C'
+...
+box.schema.func.drop("function1")
+---
+...
+box.schema.func.create('function1.multireturn', {language = "C", exports = {'LUA', 'SQL'}})
+---
+...
+box.execute("SELECT \"function1.multireturn\"()")
+---
+- null
+- 'Failed to execute SQL statement: Too many values were returned from C'
+...
+box.schema.func.drop("function1.multireturn")
+---
+...
 --
 -- gh-4182: Introduce persistent Lua functions.
 --
diff --git a/test/box/function1.test.lua b/test/box/function1.test.lua
index e576cbb6f..b1841f3ad 100644
--- a/test/box/function1.test.lua
+++ b/test/box/function1.test.lua
@@ -256,6 +256,18 @@ box.schema.user.revoke('guest', 'execute', 'function', 'secret_leak')
 box.schema.func.drop('secret_leak')
 box.schema.func.drop('secret')
 
+-- UDF corner cases for SQL: no value returned, too many values returned
+box.execute("SELECT LUA('a = 1 + 1')")
+box.execute("SELECT LUA('return 1, 2')")
+
+box.schema.func.create('function1', {language = "C", exports = {'LUA', 'SQL'}})
+box.execute("SELECT \"function1\"()")
+box.schema.func.drop("function1")
+
+box.schema.func.create('function1.multireturn', {language = "C", exports = {'LUA', 'SQL'}})
+box.execute("SELECT \"function1.multireturn\"()")
+box.schema.func.drop("function1.multireturn")
+
 --
 -- gh-4182: Introduce persistent Lua functions.
 --
-- 
2.23.0





More information about the Tarantool-patches mailing list