diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 6d5ad2a2729c7d96aa5f580c0af2161d58047e1e..648b7170eeda306ffd7597904cb132fa2e23e0b0 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -4136,11 +4136,18 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) sqlVdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)coll, P4_COLLSEQ); } - int op = func->def->language == - FUNC_LANGUAGE_SQL_BUILTIN ? - OP_BuiltinFunction0 : OP_Function; - sqlVdbeAddOp4(v, op, constMask, r1, target, - (char *)func, P4_FUNC); + if (func->def->language == FUNC_LANGUAGE_SQL_BUILTIN) { + sqlVdbeAddOp4(v, OP_BuiltinFunction0, constMask, + r1, target, (char *)func, + P4_FUNC); + } else { + sqlVdbeAddOp4(v, OP_FunctionByName, constMask, + r1, target, + sqlDbStrNDup(pParse->db, + func->def->name, + func->def->name_len), + P4_DYNAMIC); + } sqlVdbeChangeP5(v, (u8) nFarg); if (nFarg && constMask == 0) { sqlReleaseTempRange(pParse, r1, nFarg); diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index f50198281f2bc61e84a34821f9ec929992bf7b1e..9495ada37f60ca63ff26bfab6b96627466788e68 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1828,7 +1828,7 @@ case OP_BuiltinFunction: { break; } -/* Opcode: Function * P2 P3 P4 P5 +/* Opcode: FunctionByName * P2 P3 P4 P5 * Synopsis: r[P3]=func(r[P2@P5]) * * Invoke a user function (P4 is a pointer to a function object @@ -1836,8 +1836,13 @@ case OP_BuiltinFunction: { * register P2 and successors. The result of the function is * stored in register P3. */ -case OP_Function: { - struct func *func = pOp->p4.func; +case OP_FunctionByName: { + assert(pOp->p4type == P4_DYNAMIC); + struct func *func = func_by_name(pOp->p4.z, strlen(pOp->p4.z)); + if (unlikely(func == NULL)) { + diag_set(ClientError, ER_NO_SUCH_FUNCTION, pOp->p4.z); + goto abort_due_to_error; + } int argc = pOp->p5; struct Mem *argv = &aMem[pOp->p2]; struct port args, ret; diff --git a/test/sql/checks.result b/test/sql/checks.result index 6d584c30137ada68449d9b726b6c9f3e0da571cc..a952b2b70b951364a69e6cb8945643204a934a1c 100644 --- a/test/sql/checks.result +++ b/test/sql/checks.result @@ -839,6 +839,31 @@ box.execute("DROP TABLE test;") --- - row_count: 1 ... +-- +-- gh-4176: Can't recover if check constraint involves function. +-- +function MYFUNC(x) return x < 10 end +--- +... +box.schema.func.create('MYFUNC', {param_list = {'integer'}, returns = 'integer', is_deterministic = true, exports = {'LUA', 'SQL'}}) +--- +... +box.execute("CREATE TABLE t6(a INT CHECK (myfunc(a)) primary key);"); +--- +- row_count: 1 +... +box.func.MYFUNC:drop() +--- +... +box.execute("INSERT INTO t6 VALUES(11);"); +--- +- null +- Function 'MYFUNC' does not exist +... +box.execute("DROP TABLE t6") +--- +- row_count: 1 +... test_run:cmd("clear filter") --- - true diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua index f8dc5d3638b94e8d46101399fdb346166a8dfdfc..4d338235361ba5509e6478c7585f69c939e6b54a 100644 --- a/test/sql/checks.test.lua +++ b/test/sql/checks.test.lua @@ -272,4 +272,14 @@ assert(box.space.TEST.ck_constraint.some_ck.is_enabled == true) box.space.TEST:insert({12}) box.execute("DROP TABLE test;") +-- +-- gh-4176: Can't recover if check constraint involves function. +-- +function MYFUNC(x) return x < 10 end +box.schema.func.create('MYFUNC', {param_list = {'integer'}, returns = 'integer', is_deterministic = true, exports = {'LUA', 'SQL'}}) +box.execute("CREATE TABLE t6(a INT CHECK (myfunc(a)) primary key);"); +box.func.MYFUNC:drop() +box.execute("INSERT INTO t6 VALUES(11);"); +box.execute("DROP TABLE t6") + test_run:cmd("clear filter")