From d882d9f4483341645f477bae185d1a30dacc4cdf Mon Sep 17 00:00:00 2001
From: Mergen Imeev <imeevma@tarantool.org>
Date: Tue, 16 Aug 2022 15:56:29 +0300
Subject: [PATCH] sql: introduce new parsing rule

This commit introduces a new parse rule for compiling an unresolved
single expression. This simplifies the current implementation of
sql_expr_compile() and is useful for generating SQL expressions that can
be used in the core check constraint. This rule is for internal use
only.

Part of #6986

NO_DOC=will be added later
NO_TEST=refactoring
NO_CHANGELOG=will be added later
---
 extra/mkkeywordhash.c  |  2 +-
 src/box/sql.h          |  8 --------
 src/box/sql/parse.y    | 21 +++++++++++++++++----
 src/box/sql/select.c   | 16 ----------------
 src/box/sql/sqlInt.h   |  2 ++
 src/box/sql/tokenize.c |  7 ++++---
 6 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index fd1cc83f3d..6d961d9de7 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 fe7cb2f51f..bf8d7b7c5b 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 40297cd03a..2046dc19ed 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 3ae7a709bd..a4b0c3c72e 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 a629024557..4b95045d79 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 5b6eef4d97..8b5cac26d0 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;
 	}
-- 
GitLab