diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 59ab32959bb7f1670096c1579c7cb05dcf7cd79a..ff923d08fdf3ea2a15fdd57986bfc76b1ee50e34 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -41,7 +41,7 @@ expression ::= (table '.')? column | 'TO_DATE' '(' expression',' format ')' | 'TO_CHAR' '(' expression',' format ')' | 'TRIM' '(' ((('LEADING' | 'TRAILING' | 'BOTH')? expression) | ('LEADING' | 'TRAILING' | 'BOTH')) 'FROM' expression ')' - | 'CASE' expression? ('WHEN' expression 'THEN' expression)* ('ELSE' expression)? 'END' + | 'CASE' expression? ('WHEN' expression 'THEN' expression)+ ('ELSE' expression)? 'END' aggregate ::= ('AVG' | 'COUNT' | 'MAX' | 'MIN' | 'SUM' | 'TOTAL') '(' expression ')' | 'GROUP_CONCAT' '(' expression ',' "'" string "'" ')' cast ::= 'CAST' '(' expression 'AS' type ')' diff --git a/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua b/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua index 9d161672ddb690c90cd7d147373f7a1ffe030d46..011a80b6aeb30de7c8a0bb17daf658add07660f2 100644 --- a/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua +++ b/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua @@ -210,6 +210,50 @@ arbitrary_projection.test_arbitrary_valid = function() }, }) + local r, err = api:call("sbroad.execute", { [[ + SELECT + "id", + "val", + CASE "id" + WHEN 5 THEN 'five' + WHEN "val" THEN 'equal' + END "case_result" + FROM "arithmetic_space" + INNER JOIN + (SELECT "COLUMN_2" as "val" FROM (VALUES (1), (2))) AS "values" + ON true + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + { name = "arithmetic_space.id", type = "integer"}, + { name = "values.val", type = "any"}, + { name = "case_result", type = "any"}, + }, + rows = { + {1, 1, 'equal'}, + {5, 1, 'five'}, + {8, 1, box.NULL}, + {9, 1, box.NULL}, + {10, 1, box.NULL}, + {1, 2, box.NULL}, + {5, 2, 'five'}, + {8, 2, box.NULL}, + {9, 2, box.NULL}, + {10, 2, box.NULL}, + {2, 1, box.NULL}, + {3, 1, box.NULL}, + {4, 1, box.NULL}, + {6, 1, box.NULL}, + {7, 1, box.NULL}, + {2, 2, 'equal'}, + {3, 2, box.NULL}, + {4, 2, box.NULL}, + {6, 2, box.NULL}, + {7, 2, box.NULL}, + }, + }) + local r, err = api:call("sbroad.execute", { [[ SELECT "id", @@ -238,4 +282,40 @@ arbitrary_projection.test_arbitrary_valid = function() {7, 'first'}, }, }) + + local r, err = api:call("sbroad.execute", { [[ + SELECT + "id", + CASE + WHEN false THEN 'never' + WHEN "id" < 3 THEN 1 + WHEN "id" > 3 AND "id" < 8 THEN 2 + ELSE + CASE + WHEN "id" = 8 THEN 3 + WHEN "id" = 9 THEN 4 + ELSE 0.42 + END + END + FROM "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "id", type = "integer"}, + {name = "COL_1", type = "any"}, + }, + rows = { + {1, 1}, + {5, 2}, + {8, 3}, + {9, 4}, + {10, 0.42}, + {2, 1}, + {3, 0.42}, + {4, 2}, + {6, 2}, + {7, 2}, + }, + }) end diff --git a/sbroad-core/src/frontend/sql/ir.rs b/sbroad-core/src/frontend/sql/ir.rs index df844245082dda11583103064f113d327bbbd870..24ce1df6026cfc5f8ec8607cc4eed212783744cb 100644 --- a/sbroad-core/src/frontend/sql/ir.rs +++ b/sbroad-core/src/frontend/sql/ir.rs @@ -367,21 +367,21 @@ impl Plan { } => { if let Some(search_expr) = search_expr { *search_expr = *map.get(search_expr).unwrap_or_else(|| { - panic!("Search expr not found for subtree cloning.") + panic!("Search expression not found for subtree cloning.") }); } for (cond_expr, res_expr) in when_blocks { - *cond_expr = *map - .get(cond_expr) - .unwrap_or_else(|| panic!("Cond expr not found for subtree cloning.")); - *res_expr = *map - .get(res_expr) - .unwrap_or_else(|| panic!("Res expr not found for subtree cloning.")); + *cond_expr = *map.get(cond_expr).unwrap_or_else(|| { + panic!("Condition expression not found for subtree cloning.") + }); + *res_expr = *map.get(res_expr).unwrap_or_else(|| { + panic!("Result expression not found for subtree cloning.") + }); } if let Some(else_expr) = else_expr { - *else_expr = *map - .get(else_expr) - .unwrap_or_else(|| panic!("Else expr not found for subtree cloning.")); + *else_expr = *map.get(else_expr).unwrap_or_else(|| { + panic!("Else expression not found for subtree cloning.") + }); } } } diff --git a/sbroad-core/src/ir/expression/types.rs b/sbroad-core/src/ir/expression/types.rs index 4a37858d38e2ea82fb5754363929e2828d337cd6..deaa1c89dcfdaaa9d5b380ef93efe6a146eba599 100644 --- a/sbroad-core/src/ir/expression/types.rs +++ b/sbroad-core/src/ir/expression/types.rs @@ -50,16 +50,24 @@ impl Expression { .. } => { let mut case_type = None; + let check_types_corresponds = |case_type: &Type, ret_expr_type: &Type| { + if case_type != ret_expr_type { + return if matches!(ret_expr_type, Type::Array) + || matches!(ret_expr_type, Type::Map) + { + Some(Type::Any) + } else { + Some(Type::Scalar) + }; + } + None + }; + for (_, ret_expr) in when_blocks { let ret_expr_type = plan.get_node_type(*ret_expr)?; if let Some(case_type) = &case_type { - if case_type != &ret_expr_type { - if matches!(ret_expr_type, Type::Array) - || matches!(ret_expr_type, Type::Map) - { - return Ok(Type::Any); - } - return Ok(Type::Scalar); + if let Some(ret_type) = check_types_corresponds(case_type, &ret_expr_type) { + return Ok(ret_type); } } else { case_type = Some(ret_expr_type); @@ -68,13 +76,10 @@ impl Expression { let case_type_unwrapped = case_type.expect("Case WHEN type must be known"); if let Some(else_expr) = else_expr { let else_expr_type = plan.get_node_type(*else_expr)?; - if case_type_unwrapped != else_expr_type { - if matches!(else_expr_type, Type::Array) - || matches!(else_expr_type, Type::Map) - { - return Ok(Type::Any); - } - return Ok(Type::Scalar); + if let Some(ret_type) = + check_types_corresponds(&case_type_unwrapped, &else_expr_type) + { + return Ok(ret_type); } } Ok(case_type_unwrapped)