[Tarantool-patches] [PATCH] sql: allow nil to be returned from UDF

Nikita Pettik korablev at tarantool.org
Wed Dec 18 21:27:12 MSK 2019


Any user defined function features assumed type of returned value
(if it is not set explicitly during UDF creation, then it is ANY).
After function's invocation in SQL, type of returned value is checked to
be compatible with mentioned type of returned value specified in
function's definition. It is done by means of
field_mp_plain_type_is_compatible(). This functions accepts
'is_nullable' arguments which indicates whether value can be nullable or
not. For some reason 'is_nullable' is set to 'false' in our particular
case. Hence, nils can't be returned from UDF for SCALAR types.

Since there's no reason why nils can't be returned from UDFs,
let's fix this unfortunate bug.
---
Branch: https://github.com/tarantool/tarantool/tree/np/fix-sql-func-ret-nullability

 src/box/sql/vdbe.c         |  2 +-
 test/sql-tap/func.test.lua | 36 ++++++++++++++++++------------------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 6ebc5f027..932b9db4e 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -1871,7 +1871,7 @@ case OP_FunctionByName: {
 	if (mem == NULL)
 		goto abort_due_to_error;
 	enum mp_type type = sql_value_type((sql_value *)pOut);
-	if (!field_mp_plain_type_is_compatible(returns, type, false)) {
+	if (!field_mp_plain_type_is_compatible(returns, type, true)) {
 		diag_set(ClientError, ER_FUNC_INVALID_RETURN_TYPE, pOp->p4.z,
 			 field_type_strs[returns], mp_type_strs[type]);
 		goto abort_due_to_error;
diff --git a/test/sql-tap/func.test.lua b/test/sql-tap/func.test.lua
index 4574ddfeb..30d545d82 100755
--- a/test/sql-tap/func.test.lua
+++ b/test/sql-tap/func.test.lua
@@ -2963,9 +2963,9 @@ test:do_catchsql_test("func-77.3", "SELECT TOSTRING(1);", {1, "Function 'TOSTRIN
 test:do_catchsql_test("func-77.4", "SELECT TOSTRING(-1);", {1, "Function 'TOSTRING' returned value of invalid type: expected string got integer"})
 test:do_catchsql_test("func-77.5", "SELECT TOSTRING(-1.1);", {1, "Function 'TOSTRING' returned value of invalid type: expected string got double"})
 test:do_catchsql_test("func-77.6", "SELECT TOSTRING(TRUE);", {1, "Function 'TOSTRING' returned value of invalid type: expected string got boolean"})
-test:do_catchsql_test("func-77.7", "SELECT TOSTRING(NULL);", {1, "Function 'TOSTRING' returned value of invalid type: expected string got nil"})
-test:do_catchsql_test("func-77.8", "SELECT TOSTRING(LUA('return nil'));", {1, "Function 'TOSTRING' returned value of invalid type: expected string got nil"})
-test:do_catchsql_test("func-77.9", "SELECT TOSTRING(LUA('return box.NULL'));", {1, "Function 'TOSTRING' returned value of invalid type: expected string got nil"})
+test:do_execsql_test("func-77.7", "SELECT TOSTRING(NULL);", {""})
+test:do_execsql_test("func-77.8", "SELECT TOSTRING(LUA('return nil'));", {""})
+test:do_execsql_test("func-77.9", "SELECT TOSTRING(LUA('return box.NULL'));", {""})
 test:do_catchsql_test("func-77.10", "SELECT TOSTRING(LUA('return ffi.new(\"unsigned\", 666)'));", {1, "Function 'TOSTRING' returned value of invalid type: expected string got unsigned"})
 test:do_catchsql_test("func-77.11", "SELECT TOSTRING(LUA('return ffi.new(\"int\", -666)'));", {1, "Function 'TOSTRING' returned value of invalid type: expected string got integer"})
 test:do_catchsql_test("func-77.12", "SELECT TOSTRING(LUA('return ffi.new(\"double\", -666.666)'));", {1, "Function 'TOSTRING' returned value of invalid type: expected string got double"})
@@ -2983,9 +2983,9 @@ test:do_execsql_test("func-78.3", "SELECT TOUNSIGNED(1);", {1})
 test:do_catchsql_test("func-78.4", "SELECT TOUNSIGNED(-1);", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got integer"})
 test:do_catchsql_test("func-78.5", "SELECT TOUNSIGNED(-1.1);", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got double"})
 test:do_catchsql_test("func-78.6", "SELECT TOUNSIGNED(TRUE);", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got boolean"})
-test:do_catchsql_test("func-78.7", "SELECT TOUNSIGNED(NULL);", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got nil"})
-test:do_catchsql_test("func-78.8", "SELECT TOUNSIGNED(LUA('return nil'));", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got nil"})
-test:do_catchsql_test("func-78.9", "SELECT TOUNSIGNED(LUA('return box.NULL'));", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got nil"})
+test:do_execsql_test("func-78.7", "SELECT TOUNSIGNED(NULL);", {""})
+test:do_execsql_test("func-78.8", "SELECT TOUNSIGNED(LUA('return nil'));", {""})
+test:do_execsql_test("func-78.9", "SELECT TOUNSIGNED(LUA('return box.NULL'));", {""})
 test:do_execsql_test("func-78.10", "SELECT TOUNSIGNED(LUA('return ffi.new(\"unsigned\", 666)'));", {666})
 test:do_catchsql_test("func-78.11", "SELECT TOUNSIGNED(LUA('return ffi.new(\"int\", -666)'));", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got integer"})
 test:do_catchsql_test("func-78.12", "SELECT TOUNSIGNED(LUA('return ffi.new(\"double\", -666.666)'));", {1, "Function 'TOUNSIGNED' returned value of invalid type: expected unsigned got double"})
@@ -3003,9 +3003,9 @@ test:do_execsql_test("func-79.3", "SELECT TOINTEGER(1);", {1})
 test:do_execsql_test("func-79.4", "SELECT TOINTEGER(-1);", {-1})
 test:do_catchsql_test("func-79.5", "SELECT TOINTEGER(-1.1);", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got double"})
 test:do_catchsql_test("func-79.6", "SELECT TOINTEGER(TRUE);", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got boolean"})
-test:do_catchsql_test("func-79.7", "SELECT TOINTEGER(NULL);", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got nil"})
-test:do_catchsql_test("func-79.8", "SELECT TOINTEGER(LUA('return nil'));", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got nil"})
-test:do_catchsql_test("func-79.9", "SELECT TOINTEGER(LUA('return box.NULL'));", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got nil"})
+test:do_execsql_test("func-79.7", "SELECT TOINTEGER(NULL);", {""})
+test:do_execsql_test("func-79.8", "SELECT TOINTEGER(LUA('return nil'));", {""})
+test:do_execsql_test("func-79.9", "SELECT TOINTEGER(LUA('return box.NULL'));", {""})
 test:do_execsql_test("func-79.10", "SELECT TOINTEGER(LUA('return ffi.new(\"unsigned\", 666)'));", {666})
 test:do_execsql_test("func-79.11", "SELECT TOINTEGER(LUA('return ffi.new(\"int\", -666)'));", {-666})
 test:do_catchsql_test("func-79.12", "SELECT TOINTEGER(LUA('return ffi.new(\"double\", -666.666)'));", {1, "Function 'TOINTEGER' returned value of invalid type: expected integer got double"})
@@ -3023,9 +3023,9 @@ test:do_execsql_test("func-80.3", "SELECT TONUMBER(1);", {1})
 test:do_execsql_test("func-80.4", "SELECT TONUMBER(-1);", {-1})
 test:do_execsql_test("func-80.5", "SELECT TONUMBER(-1.1);", {-1.1})
 test:do_catchsql_test("func-80.6", "SELECT TONUMBER(TRUE);", {1, "Function 'TONUMBER' returned value of invalid type: expected number got boolean"})
-test:do_catchsql_test("func-80.7", "SELECT TONUMBER(NULL);", {1, "Function 'TONUMBER' returned value of invalid type: expected number got nil"})
-test:do_catchsql_test("func-80.8", "SELECT TONUMBER(LUA('return nil'));", {1, "Function 'TONUMBER' returned value of invalid type: expected number got nil"})
-test:do_catchsql_test("func-80.9", "SELECT TONUMBER(LUA('return box.NULL'));", {1, "Function 'TONUMBER' returned value of invalid type: expected number got nil"})
+test:do_execsql_test("func-80.7", "SELECT TONUMBER(NULL);", {""})
+test:do_execsql_test("func-80.8", "SELECT TONUMBER(LUA('return nil'));", {""})
+test:do_execsql_test("func-80.9", "SELECT TONUMBER(LUA('return box.NULL'));", {""})
 test:do_execsql_test("func-80.10", "SELECT TONUMBER(LUA('return ffi.new(\"unsigned\", 666)'));", {666})
 test:do_execsql_test("func-80.11", "SELECT TONUMBER(LUA('return ffi.new(\"int\", -666)'));", {-666})
 test:do_execsql_test("func-80.12", "SELECT TONUMBER(LUA('return ffi.new(\"double\", -666.666)'));", {-666.666})
@@ -3043,9 +3043,9 @@ test:do_catchsql_test("func-81.3", "SELECT TOBOOLEAN(1);", {1, "Function 'TOBOOL
 test:do_catchsql_test("func-81.4", "SELECT TOBOOLEAN(-1);", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got integer"})
 test:do_catchsql_test("func-81.5", "SELECT TOBOOLEAN(-1.1);", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got double"})
 test:do_execsql_test("func-81.6", "SELECT TOBOOLEAN(TRUE);", {true})
-test:do_catchsql_test("func-81.7", "SELECT TOBOOLEAN(NULL);", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got nil"})
-test:do_catchsql_test("func-81.8", "SELECT TOBOOLEAN(LUA('return nil'));", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got nil"})
-test:do_catchsql_test("func-81.9", "SELECT TOBOOLEAN(LUA('return box.NULL'));", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got nil"})
+test:do_execsql_test("func-81.7", "SELECT TOBOOLEAN(NULL);", {""})
+test:do_execsql_test("func-81.8", "SELECT TOBOOLEAN(LUA('return nil'));", {""})
+test:do_execsql_test("func-81.9", "SELECT TOBOOLEAN(LUA('return box.NULL'));", {""})
 test:do_catchsql_test("func-81.10", "SELECT TOBOOLEAN(LUA('return ffi.new(\"unsigned\", 666)'));", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got unsigned"})
 test:do_catchsql_test("func-81.11", "SELECT TOBOOLEAN(LUA('return ffi.new(\"int\", -666)'));", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got integer"})
 test:do_catchsql_test("func-81.12", "SELECT TOBOOLEAN(LUA('return ffi.new(\"double\", -666.666)'));", {1, "Function 'TOBOOLEAN' returned value of invalid type: expected boolean got double"})
@@ -3063,9 +3063,9 @@ test:do_execsql_test("func-82.3", "SELECT TOSCALAR(1);", {1})
 test:do_execsql_test("func-82.4", "SELECT TOSCALAR(-1);", {-1})
 test:do_execsql_test("func-82.5", "SELECT TOSCALAR(-1.1);", {-1.1})
 test:do_execsql_test("func-82.6", "SELECT TOSCALAR(TRUE);", {true})
-test:do_catchsql_test("func-82.7", "SELECT TOSCALAR(NULL);", {1, "Function 'TOSCALAR' returned value of invalid type: expected scalar got nil"})
-test:do_catchsql_test("func-82.8", "SELECT TOSCALAR(LUA('return nil'));", {1, "Function 'TOSCALAR' returned value of invalid type: expected scalar got nil"})
-test:do_catchsql_test("func-82.9", "SELECT TOSCALAR(LUA('return box.NULL'));", {1, "Function 'TOSCALAR' returned value of invalid type: expected scalar got nil"})
+test:do_execsql_test("func-82.7", "SELECT TOSCALAR(NULL);", {""})
+test:do_execsql_test("func-82.8", "SELECT TOSCALAR(LUA('return nil'));", {""})
+test:do_execsql_test("func-82.9", "SELECT TOSCALAR(LUA('return box.NULL'));", {""})
 test:do_execsql_test("func-82.10", "SELECT TOSCALAR(LUA('return ffi.new(\"unsigned\", 666)'));", {666})
 test:do_execsql_test("func-82.11", "SELECT TOSCALAR(LUA('return ffi.new(\"int\", -666)'));", {-666})
 test:do_execsql_test("func-82.12", "SELECT TOSCALAR(LUA('return ffi.new(\"double\", -666.666)'));", {-666.666})
-- 
2.15.1



More information about the Tarantool-patches mailing list