diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index fd1cc83f3d24c783200b3f061b89d10e427908ea..6d961d9de7b98acd584a64ebaa9b4f1f05b9ba2a 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -210,7 +210,7 @@ static Keyword aKeywordTable[] = {
   { "ENABLE",                 "TK_ENABLE",      false },
   { "FETCH",                  "TK_STANDARD",    true  },
   { "FLOAT",                  "TK_STANDARD",    true  },
-  { "FUNCTION",               "TK_STANDARD",    true  },
+  { "FUNCTION",               "TK_FUNCTION_KW", true  },
   { "GET",                    "TK_STANDARD",    true  },
   { "GRANT",                  "TK_STANDARD",    true  },
   { "INT",                    "TK_INT",         true  },
diff --git a/src/box/sql.h b/src/box/sql.h
index fe7cb2f51f1edc8a78ff18259eceeea0a75f7af1..bf8d7b7c5b03f1d849e5aa14f5f24723a6ba4f03 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -157,14 +157,6 @@ sql_trigger_name(struct sql_trigger *trigger);
 uint32_t
 sql_trigger_space_id(struct sql_trigger *trigger);
 
-/**
- * Store duplicate of a parsed expression into @a parser.
- * @param parser Parser context.
- * @param select Select to extract from.
- */
-void
-sql_expr_extract_select(struct Parse *parser, struct Select *select);
-
 /**
  * Given space_id and field number, return default value
  * for the field.
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 40297cd03a99072c44232e76db51e655eb6e9e9a..2046dc19ed23ae9743defd1f27e426302589bfda 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -458,10 +458,13 @@ cmd ::= createkw(X) VIEW ifnotexists(E) nm(Y) eidlist_opt(C)
 //
 cmd ::= select(X).  {
   SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
-  if(!pParse->parse_only)
-          sqlSelect(pParse, X, &dest);
-  else
-          sql_expr_extract_select(pParse, X);
+  if(pParse->parse_only) {
+    diag_set(ClientError, ER_SQL_PARSER_GENERIC,
+             "Failed to parse SQL expression");
+    pParse->is_aborted = true;
+    return;
+  }
+  sqlSelect(pParse, X, &dest);
   sql_select_delete(pParse->db, X);
 }
 
@@ -1611,6 +1614,16 @@ cmd ::= PRAGMA nm(X) LP nm(Y) RP.         {
 cmd ::= PRAGMA nm(X) LP nm(Z) DOT nm(Y) RP.  {
     sqlPragma(pParse,&X,&Y,&Z);
 }
+cmd ::= FUNCTION_KW(T) expr(E). {
+  if (!pParse->is_expr) {
+    diag_set(ClientError, ER_SQL_SYNTAX_NEAR_TOKEN, pParse->line_count, T.n,
+             T.z);
+    pParse->is_aborted = true;
+    return;
+  }
+  pParse->parsed_ast_type = AST_TYPE_EXPR;
+  pParse->parsed_ast.expr = sqlExprDup(pParse->db, E.pExpr, 0);
+}
 //////////////////////////// The CREATE TRIGGER command /////////////////////
 
 cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 3ae7a709bda48a7f20852f8fd0d2d4f2e40f0a4b..a4b0c3c72e3f0f194b26c76bd5cde8ef89059c08 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -6714,22 +6714,6 @@ sqlSelect(Parse * pParse,		/* The parser context */
 	return rc;
 }
 
-void
-sql_expr_extract_select(struct Parse *parser, struct Select *select)
-{
-	struct ExprList *expr_list = select->pEList;
-	assert(expr_list->nExpr == 1);
-	parser->parsed_ast_type = AST_TYPE_EXPR;
-	/*
-	 * Extract a copy of parsed expression.
-	 * We cannot use EXPRDUP_REDUCE flag in sqlExprDup call
-	 * because some compiled Expr (like Checks expressions)
-	 * may require further resolve with sqlResolveExprNames.
-	 */
-	parser->parsed_ast.expr =
-		sqlExprDup(parser->db, expr_list->a->pExpr, 0);
-}
-
 struct sql_context *
 sql_context_new(struct func *func, struct coll *coll)
 {
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index a629024557b90d90d2ad62a1e7ba71124c8065c2..4b95045d791ccfed079c4b90a7f37fc09522e313 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2090,6 +2090,8 @@ struct Parse {
 	bool initiateTTrans;	/* Initiate Tarantool transaction */
 	/** If set - do not emit byte code at all, just parse.  */
 	bool parse_only;
+	/** If true, then parsed_ast_type should be EXPR after parsing. */
+	bool is_expr;
 	/** Type of parsed_ast member. */
 	enum ast_type parsed_ast_type;
 	/** SQL options which were used to compile this VDBE. */
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 5b6eef4d97b392092f2b08b871e1bc598001b83c..8b5cac26d02212c6dab3c76a52cda5336e19eaae 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -570,7 +570,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 struct Expr *
 sql_expr_compile(sql *db, const char *expr, int expr_len)
 {
-	const char *outer = "SELECT ";
+	const char *outer = "FUNCTION ";
 	int len = strlen(outer) + expr_len;
 
 	struct Parse parser;
@@ -582,6 +582,7 @@ sql_expr_compile(sql *db, const char *expr, int expr_len)
 	 */
 	parser.line_pos -= strlen(outer);
 	parser.parse_only = true;
+	parser.is_expr = true;
 
 	struct Expr *expression = NULL;
 	char *stmt = (char *)region_alloc(&parser.region, len + 1);
@@ -591,8 +592,8 @@ sql_expr_compile(sql *db, const char *expr, int expr_len)
 	}
 	snprintf(stmt, len + 1, "%s%.*s", outer, expr_len, expr);
 
-	if (sqlRunParser(&parser, stmt) == 0 &&
-	    parser.parsed_ast_type == AST_TYPE_EXPR) {
+	if (sqlRunParser(&parser, stmt) == 0) {
+		assert(parser.parsed_ast_type == AST_TYPE_EXPR);
 		expression = parser.parsed_ast.expr;
 		parser.parsed_ast.expr = NULL;
 	}