diff --git a/lib/ast.ml b/lib/ast.ml index 5d3859f..e5dc4a1 100644 --- a/lib/ast.ml +++ b/lib/ast.ml @@ -1,12 +1,18 @@ -type query = Query of select_stmt -and select_stmt = - | Select of column list * table list option * filter option +type query = + (*| Select of column list * table list option * filter option*) + | Select of column list * table_expression + | CreateSchema of string + | CreateTable of table + | DropSchema of string + | DropTable of table and column = | Asterisk (* | Column of string *) | Column of expression * as_clause option and as_clause = | As of string +and table_expression = + | TableExpression of table list option * filter option * group option and table = | Table of string | Join of table * join_type * table * condition option @@ -30,8 +36,8 @@ and predicate = | NotBetween of predicand * predicand | In of predicand list | NotIn of predicand list - | Like of string - | NotLike of string + | Like of predicand + | NotLike of predicand and operator = | Equals | NotEquals @@ -39,8 +45,9 @@ and operator = | GreaterThan | LessEquals | GreaterEquals -and filter = - | Filter of condition +and filter = condition +and group = + | Group of quantifier option * expression list option and aggregate = | Aggregate of func * filter option and func = @@ -62,31 +69,41 @@ and expression = let rec pp_query fmt ast = match ast with - | Query(s) -> Format.fprintf fmt "%s" (pp_select s) - -and pp_select s = - match s with - | Select(cols, _, _) -> String.cat "SELECT " (pp_columns cols) + | Select(cols, table_exp) -> Format.fprintf fmt "%s" (String.cat "SELECT " (pp_columns cols) ^ pp_table_expression table_exp) + | _ -> failwith "not yet supported" and pp_columns cols = match cols with | [] -> "" | [col] -> pp_column col - | col::l -> pp_column col ^ ";" ^ pp_columns l + | col::l -> pp_column col ^ ", " ^ pp_columns l and pp_column col = match col with - | Column(Ref(name),_) -> name - | Column(StringLiteral(name),_) -> "'"^name^"'" - | Column(DateLiteral(name),_) -> "'"^name^"'" + | Column(exp,_) -> pp_expression exp | Asterisk -> "*" - | _ -> failwith "Pretty parsing of Column not supported" + +and pp_expression exp = + match exp with + | Ref(name) -> name + | StringLiteral(name) -> "'"^name^"'" + | DateLiteral(name) -> "'"^name^"'" + | TimeLiteral(name) -> "'"^name^"'" + +and pp_table_expression table_exp = + match table_exp with + | TableExpression(tables, filter, _) -> pp_tables tables ^ pp_filter filter and pp_tables tables = + let rec aux t = + match t with + | [] -> "" + | table::[] -> pp_table table + | table::l -> pp_table table ^ ", " ^ aux l + in match tables with - | [] -> "" - | [table] -> pp_table table - | table::l -> pp_table table ^ ";" ^ pp_tables l + | None -> "" + | Some(t) -> " FROM " ^ aux t and pp_table table = match table with @@ -103,3 +120,32 @@ and pp_join_type j = | Union -> "union" | Natural -> "natural" +and pp_filter filter = + let rec aux f = + match f with + | Condition(exp, pred) -> pp_expression exp ^ " " ^ pp_predicate pred + | And(c1, c2) -> aux c1 ^ " AND " ^ aux c2 + | Or(c1, c2) -> aux c1 ^ " OR " ^ aux c2 + | Not(c) -> "NOT" ^ aux c + in + match filter with + | None -> "" + | Some(f) -> " WHERE " ^ aux f + +and pp_predicate pred = + match pred with + | Comparison(op, exp) -> pp_operator op ^ pp_expression exp + | Between(exp1, exp2) -> "BETWEEN " ^ pp_expression exp1 ^ " AND " ^pp_expression exp2 + | NotBetween(exp1, exp2) -> "NOT BETWEEN " ^ pp_expression exp1 ^ " AND " ^pp_expression exp2 + | Like(exp) -> "LIKE " ^ pp_expression exp + | NotLike(exp) -> " NOT LIKE " ^ pp_expression exp + | _ -> failwith "Predicate not supported" + +and pp_operator op = + match op with + | Equals -> "=" + | NotEquals -> "!=" + | LessThan -> "<" + | GreaterThan -> ">" + | LessEquals -> "<=" + | GreaterEquals -> ">=" diff --git a/parser/parser.mly b/parser/parser.mly index 7127122..d9eca54 100644 --- a/parser/parser.mly +++ b/parser/parser.mly @@ -31,7 +31,11 @@ open Ast %% main: - | query_specification EOF { Query($1) } + | query_specification EOF { $1 } + | schema_definition EOF { $1 } + | table_definition EOF { $1 } + | drop_schema_statement EOF { $1 } + | drop_table_statement EOF { $1 } (* 5.2 TOKEN / SEPARATOR *) @@ -146,7 +150,7 @@ table_name : | IDENT { Table($1) } schema_name: - | IDENT {} + | IDENT { $1 } (****************************) @@ -169,7 +173,7 @@ parenthesized_value_expression: nonparenthesized_value_expression_primary: | unsigned_value_specification { $1 } | column_reference { Ref($1) } - (*| set_function_specification { Ref("function") } *) + | set_function_specification { Ref("function") } (***************************) @@ -279,14 +283,14 @@ truth_value: boolean_primary : | predicate { $1 } - (*| boolean_predicand {}*) + | boolean_predicand { $1 } boolean_predicand: - | parenthesized_boolean_value_expression {} - | nonparenthesized_value_expression_primary {} + | parenthesized_boolean_value_expression { $1 } +(* | nonparenthesized_value_expression_primary { $1 }*) parenthesized_boolean_value_expression: - | LEFT_PAREN boolean_value_expression RIGHT_PAREN {} + | LEFT_PAREN boolean_value_expression RIGHT_PAREN { $2 } (****************************) @@ -322,9 +326,15 @@ row_value_special_case : (* 7.4 TABLE EXPRESSION *) table_expression: - | { None } - | from_clause { Some($1) } - | from_clause where_clause { Some($1) } + | { TableExpression(None, None, None) } + | from_clause { TableExpression(Some($1), None, None) } + | from_clause where_clause { TableExpression(Some($1), Some($2), None) } + | from_clause where_clause group_by_clause { TableExpression(Some($1), Some($2), Some($3)) } +(* | from_clause where_clause group_by_clause having_clause { Some($1) } + | from_clause group_by_clause { Some($1) } + | from_clause group_by_clause having_clause { Some($1) } + | from_clause having_clause { Some($1) } +*) (************************) @@ -369,8 +379,8 @@ cross_join: | table_reference CROSS JOIN table_primary { Join($1, Cross, $4, None) } qualified_join: - | table_reference JOIN table_reference join_specification { Join($1, Left, $3, $4) } - | table_reference join_type JOIN table_reference join_specification { Join($1, $2, $4, $5) } + | table_reference JOIN table_reference join_specification { Join($1, Left, $3, Some($4)) } + | table_reference join_type JOIN table_reference join_specification { Join($1, $2, $4, Some($5)) } natural_join: | table_reference NATURAL JOIN table_primary { Join($1, Natural, $4, None) } @@ -384,7 +394,7 @@ join_specification: (* | named_columns_join {} *) join_condition: - | ON search_condition { Some($2) } + | ON search_condition { $2 } join_type: | INNER { Inner } @@ -416,23 +426,27 @@ where_clause : (* 7.9 GROUP BY CLAUSE *) group_by_clause: - | GROUP BY grouping_element_list {} - | GROUP BY set_quantifier grouping_element_list {} + | GROUP BY grouping_element_list { Group(None, Some($3)) } + | GROUP BY set_quantifier grouping_element_list { Group(Some($3), Some($4)) } grouping_element_list : - | grouping_element {} - | grouping_element_list COMMA grouping_element_list {} + | grouping_element { [$1] } + | grouping_element_list COMMA grouping_element { $3::$1 } grouping_element: - | ordinary_grouping_set {} + | ordinary_grouping_set { $1 } ordinary_grouping_set : - | grouping_column_reference {} + | grouping_column_reference { $1 } + (*| LEFT_PAREN grouping_column_reference_list RIGHT_PAREN { $2 }*) grouping_column_reference: - | column_reference {} + | column_reference { Ref($1) } (*| column_reference collate_clause {}*) - +(* +grouping_column_reference_list : + | grouping_column_reference { [ +*) (***********************) (* 7.10 HAVING CLAUSE *) @@ -449,8 +463,8 @@ having_clause : (* 7.12 QUERY SPECIFICATION *) query_specification : - | SELECT select_list table_expression { Select($2, $3, None) } - | SELECT set_quantifier select_list table_expression { Select($3, $4, None) } + | SELECT select_list table_expression { Select($2, $3) } + | SELECT set_quantifier select_list table_expression { Select($3, $4) } select_list : | ASTERISK { [Asterisk] } @@ -510,8 +524,8 @@ as_clause : predicate : | comparison_predicate { $1 } | in_predicate { $1 } -(* | between_predicate { $1 } *) -(* | like_predicate { $1 }*) + | between_predicate { $1 } + | like_predicate { $1 } (*****************) @@ -602,14 +616,14 @@ search_condition: (* 10.9 AGGREGATE FUNCTION *) aggregate_function: - | COUNT LEFT_PAREN ASTERISK RIGHT_PAREN { Count } - | COUNT LEFT_PAREN ASTERISK RIGHT_PAREN filter_clause { Count } - | general_set_function { Aggregate($1) } - | general_set_function filter_clause { Aggregate($1, $2) } + | COUNT LEFT_PAREN ASTERISK RIGHT_PAREN { Aggregate(Function(Count, None, Ref("*")), None) } + | COUNT LEFT_PAREN ASTERISK RIGHT_PAREN filter_clause { Aggregate(Function(Count, None, Ref("*")), Some($5)) } + | general_set_function { Aggregate($1, None) } + | general_set_function filter_clause { Aggregate($1, Some($2)) } general_set_function: | set_function_type LEFT_PAREN value_expression RIGHT_PAREN { Function($1, None, $3) } - | set_function_type LEFT_PAREN set_quantifier value_expression RIGHT_PAREN { Function($1, $3, $4) } + | set_function_type LEFT_PAREN set_quantifier value_expression RIGHT_PAREN { Function($1, Some($3), $4) } set_function_type: | computationnal_operation { $1 } @@ -619,7 +633,7 @@ set_quantifier : | DISTINCT { Distinct } filter_clause : - | FILTER LEFT_PAREN WHERE search_condition RIGHT_PAREN { Filter($4) } + | FILTER LEFT_PAREN WHERE search_condition RIGHT_PAREN { $4 } computationnal_operation: | AVG { Avg } @@ -635,24 +649,24 @@ computationnal_operation: (* 11.1 SCHEMA DEFINITION *) schema_definition: - | CREATE SCHEMA schema_name_clause {} + | CREATE SCHEMA schema_name_clause { CreateSchema($3) } schema_name_clause : - | schema_name {} + | schema_name { $1 } (**************************) (* 11.2 DROP SCHEMA STATEMENT *) drop_schema_statement: - | DROP SCHEMA schema_name {} + | DROP SCHEMA schema_name { DropSchema($3) } (******************************) (* 11.3 TABLE DEFINITION *) table_definition : - | CREATE TABLE table_name {} + | CREATE TABLE table_name { CreateTable($3) } table_scope : | global_or_local TEMPORARY {} @@ -674,7 +688,7 @@ drop_column_definition: (* 11.21 DROP TABLE STATEMENT *) drop_table_statement: - | DROP TABLE table_name {} + | DROP TABLE table_name { DropTable($3) } (******************************) diff --git a/test/test_ast.ml b/test/test_ast.ml index c1cb3e8..4eabde8 100644 --- a/test/test_ast.ml +++ b/test/test_ast.ml @@ -13,7 +13,7 @@ let query_testable = let test_simple_select () = let query = "SELECT a, b FROM t" in let q1 = parse query in - let ast1 = Query( + let ast1 = Select( [ Column( @@ -25,36 +25,42 @@ let test_simple_select () = None ) ], - Some( - [ - Table("t") - ] - ), - None + TableExpression( + Some( + [ + Table("t") + ] + ), + None, + None + ) ) - ) in + in Alcotest.(check query_testable) query q1 ast1; let query = "SELECT * FROM t" in let q2 = parse query in - let ast2 = Query( + let ast2 = Select( [ Asterisk ], - Some( - [ - Table("t") - ] - ), - None + TableExpression( + Some( + [ + Table("t") + ] + ), + None, + None + ) ) - ) in + in Alcotest.(check query_testable) query q2 ast2 ; let query = "SELECT 'b', 'a', DATE '2024-12-25' AS date" in let q3 = parse query in - let ast3 = Query( + let ast3 = Select( [ Column( @@ -72,15 +78,18 @@ let test_simple_select () = None ) ], - None, - None + TableExpression( + None, + None, + None + ) ) - ) in + in Alcotest.(check query_testable) query q3 ast3 let test_default_join () = let q1 = parse "SELECT a FROM t1 JOIN t2 ON b = c" in - let ast1 = Query( + let ast1 = Select( [ Column( @@ -88,32 +97,36 @@ let test_default_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Left, - Table("t2"), - Some( - Condition( - Ref("b"), - Comparison( - Equals, - Ref("c")) + TableExpression( + Some( + [ + Join( + Table("t1"), + Left, + Table("t2"), + Some( + Condition( + Ref("b"), + Comparison( + Equals, + Ref("c") + ) + ) ) ) - ) - ] - ), - None + ] + ), + None, + None + ) ) - ) in + in Alcotest.(check query_testable) "Ok" q1 ast1 let test_left_join () = let query = "SELECT a FROM t1 LEFT JOIN t2 ON a = b" in let q1 = parse query in - let ast1 = Query( + let ast1 = Select( [ Column( @@ -121,29 +134,32 @@ let test_left_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Left, - Table("t2"), - Some( - Condition( - Ref("a"), - Comparison(Equals, Ref("b")) + TableExpression( + Some( + [ + Join( + Table("t1"), + Left, + Table("t2"), + Some( + Condition( + Ref("a"), + Comparison(Equals, Ref("b")) + ) ) ) - ) - ] - ), - None + ] + ), + None, + None + ) ) - ) in + in Alcotest.(check query_testable) query q1 ast1 let test_right_join () = let q1 = parse "SELECT a FROM t1 RIGHT JOIN t2 ON a = b" in - let ast1 = Query( + let ast1 = Select( [ Column( @@ -151,29 +167,33 @@ let test_right_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Right, - Table("t2"), - Some( - Condition( - Ref("a"), - Comparison(Equals, Ref("b")) + TableExpression( + Some( + [ + Join( + Table("t1"), + Right, + Table("t2"), + Some( + Condition( + Ref("a"), + Comparison(Equals, Ref("b")) + ) ) ) - ) - ] - ), - None + ] + ), + None, + None + ) ) - ) in + in Alcotest.(check query_testable) "Ok" q1 ast1 let test_inner_join () = - let q1 = parse "SELECT a FROM t1 INNER JOIN t2 ON a = b" in - let ast1 = Query( + let query = "SELECT a FROM t1 INNER JOIN t2 ON a = b" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -181,29 +201,33 @@ let test_inner_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Inner, - Table("t2"), - Some( - Condition( - Ref("a"), - Comparison(Equals, Ref("b")) + TableExpression( + Some( + [ + Join( + Table("t1"), + Inner, + Table("t2"), + Some( + Condition( + Ref("a"), + Comparison(Equals, Ref("b")) + ) ) ) - ) - ] - ), - None + ] + ), + None, + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 let test_union_join () = - let q1 = parse "SELECT a FROM t1 UNION JOIN t2" in - let ast1 = Query( + let query = "SELECT a FROM t1 UNION JOIN t2" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -211,24 +235,28 @@ let test_union_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Union, - Table("t2"), - None - ) - ] - ), - None + TableExpression( + Some( + [ + Join( + Table("t1"), + Union, + Table("t2"), + None + ) + ] + ), + None, + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 -let test_cross_join () = - let q1 = parse "SELECT a FROM t1 CROSS JOIN t2" in - let ast1 = Query( +let test_cross_join () = + let query = "SELECT a FROM t1 CROSS JOIN t2" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -236,24 +264,28 @@ let test_cross_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Cross, - Table("t2"), - None - ) - ] - ), - None + TableExpression( + Some( + [ + Join( + Table("t1"), + Cross, + Table("t2"), + None + ) + ] + ), + None, + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 let test_natural_join () = - let q1 = parse "SELECT a FROM t1 NATURAL JOIN t2" in - let ast1 = Query( + let query = "SELECT a FROM t1 NATURAL JOIN t2" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -261,24 +293,28 @@ let test_natural_join () = None ) ], - Some( - [ - Join( - Table("t1"), - Natural, - Table("t2"), - None - ) - ] - ), - None + TableExpression( + Some( + [ + Join( + Table("t1"), + Natural, + Table("t2"), + None + ) + ] + ), + None, + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 let test_join_join () = - let q1 = parse "SELECT a FROM t1 JOIN t2 ON a = b JOIN t3 ON a = c" in - let ast1 = Query( + let query = "SELECT a FROM t1 JOIN t2 ON a = b JOIN t3 ON a = c" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -286,37 +322,43 @@ let test_join_join () = None ) ], - Some([ - Join( - Join( - Table("t1"), - Left, - Table("t2"), - Some( - Condition( - Ref("a"), - Comparison(Equals, Ref("b")) + TableExpression( + Some( + [ + Join( + Join( + Table("t1"), + Left, + Table("t2"), + Some( + Condition( + Ref("a"), + Comparison(Equals, Ref("b")) + ) + ) + ), + Left, + Table("t3"), + Some( + Condition( + Ref("a"), + Comparison(Equals, Ref("c")) + ) ) - ) - ), - Left, - Table("t3"), - Some( - Condition( - Ref("a"), - Comparison(Equals, Ref("c")) ) - ) - ) - ]), - None + ] + ), + None, + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 let test_where_equals () = - let q1 = parse "SELECT a FROM t1 WHERE a = a OR a = b" in - let ast1 = Query( + let query = "SELECT a FROM t1 WHERE a = a OR a = b" in + let q1 = parse query in + let ast1 = Select( [ Column( @@ -324,15 +366,35 @@ let test_where_equals () = None ) ], - Some( - [ - Table("t1") - ] - ), - None + TableExpression( + Some( + [ + Table("t1") + ] + ), + Some( + Or( + Condition( + Ref("a"), + Comparison( + Equals, + Ref("a") + ) + ), + Condition( + Ref("a"), + Comparison( + Equals, + Ref("b") + ) + ) + ) + ), + None + ) ) - ) in - Alcotest.(check query_testable) "Ok" q1 ast1 + in + Alcotest.(check query_testable) query q1 ast1 (* let test_aggregtes () =