From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 06D0946970E for ; Wed, 18 Dec 2019 21:27:16 +0300 (MSK) From: Nikita Pettik Date: Wed, 18 Dec 2019 21:27:12 +0300 Message-Id: <6ff37ee003c8278c1676e72277c4033dae3e0871.1576692754.git.korablev@tarantool.org> Subject: [Tarantool-patches] [PATCH] sql: allow nil to be returned from UDF List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@dev.tarantool.org 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