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")