From 2c5c7fc6dba09b5d12e9f8f2b507f5b953e36dee Mon Sep 17 00:00:00 2001 From: EmirVildanov <reddog201030@gmail.com> Date: Mon, 11 Sep 2023 22:44:12 +0300 Subject: [PATCH] feat: refactor explain operator --- .../test/integration/explain_test.lua | 49 ++- .../test/integration/left_outer_join_test.lua | 2 +- sbroad-core/src/frontend/sql/ir/tests.rs | 64 ++-- .../src/frontend/sql/ir/tests/params.rs | 4 +- sbroad-core/src/ir/explain.rs | 359 +++++++----------- sbroad-core/src/ir/explain/tests.rs | 2 +- sbroad-core/src/ir/explain/tests/concat.rs | 4 +- 7 files changed, 202 insertions(+), 282 deletions(-) diff --git a/sbroad-cartridge/test_app/test/integration/explain_test.lua b/sbroad-cartridge/test_app/test/integration/explain_test.lua index fd14121ca4..861428caed 100644 --- a/sbroad-cartridge/test_app/test/integration/explain_test.lua +++ b/sbroad-cartridge/test_app/test/integration/explain_test.lua @@ -324,7 +324,7 @@ g.test_explain_arithmetic_projection = function() t.assert_equals( r, { - "projection ((\"arithmetic_space\".\"id\"::integer) + (2::unsigned) -> \"COL_1\")", + "projection (ROW(\"arithmetic_space\".\"id\"::integer) + ROW(2::unsigned) -> \"COL_1\")", " scan \"arithmetic_space\"", "execution options:", "sql_vdbe_max_steps = 45000", @@ -338,9 +338,9 @@ g.test_explain_arithmetic_projection = function() t.assert_equals(err, nil) t.assert_equals( r, - -- luacheck: max line length 160 + -- luacheck: max line length 210 { - "projection ((\"arithmetic_space\".\"a\"::integer) + (\"arithmetic_space\".\"b\"::integer) * (\"arithmetic_space\".\"c\"::integer) -> \"COL_1\")", + "projection (ROW(\"arithmetic_space\".\"a\"::integer) + ROW(\"arithmetic_space\".\"b\"::integer) * ROW(\"arithmetic_space\".\"c\"::integer) -> \"COL_1\")", " scan \"arithmetic_space\"", "execution options:", "sql_vdbe_max_steps = 45000", @@ -354,29 +354,44 @@ g.test_explain_arithmetic_projection = function() t.assert_equals(err, nil) t.assert_equals( r, - -- luacheck: max line length 160 + -- luacheck: max line length 210 { - "projection (((\"arithmetic_space\".\"a\"::integer) + (\"arithmetic_space\".\"b\"::integer)) * (\"arithmetic_space\".\"c\"::integer) -> \"COL_1\")", + "projection ((ROW(\"arithmetic_space\".\"a\"::integer) + ROW(\"arithmetic_space\".\"b\"::integer)) * ROW(\"arithmetic_space\".\"c\"::integer) -> \"COL_1\")", " scan \"arithmetic_space\"", "execution options:", "sql_vdbe_max_steps = 45000", "vtable_max_rows = 5000", } ) -end - -g.test_explain_arbitrary_projection = function() - local api = cluster:server("api-1").net_box - - -- currently explain does not support projection with bool and unary expressions - -- arbitraty expression consisted of bool - local _, err = api:call("sbroad.execute", { + local r, err = api:call("sbroad.execute", { [[EXPLAIN select "a" > "b" from "arithmetic_space"]], {} }) - t.assert_str_contains(tostring(err), "is not supported for yet") + t.assert_equals(err, nil) + t.assert_equals( + r, + -- luacheck: max line length 160 + { + "projection (ROW(\"arithmetic_space\".\"a\"::integer) > ROW(\"arithmetic_space\".\"b\"::integer) -> \"COL_1\")", + " scan \"arithmetic_space\"", + "execution options:", + "sql_vdbe_max_steps = 45000", + "vtable_max_rows = 5000", + } + ) - -- arbitraty expression consisted of unary - local _, err = api:call("sbroad.execute", { [[EXPLAIN select "a" is null from "arithmetic_space"]], {} }) - t.assert_str_contains(tostring(err), "is not supported for yet") + local r, err = api:call("sbroad.execute", { + [[EXPLAIN select "a" is null from "arithmetic_space"]], {} + }) + t.assert_equals(err, nil) + t.assert_equals( + r, + { + "projection (ROW(\"arithmetic_space\".\"a\"::integer) is null -> \"COL_1\")", + " scan \"arithmetic_space\"", + "execution options:", + "sql_vdbe_max_steps = 45000", + "vtable_max_rows = 5000", + } + ) end diff --git a/sbroad-cartridge/test_app/test/integration/left_outer_join_test.lua b/sbroad-cartridge/test_app/test/integration/left_outer_join_test.lua index c69df96443..3aa8d73438 100644 --- a/sbroad-cartridge/test_app/test/integration/left_outer_join_test.lua +++ b/sbroad-cartridge/test_app/test/integration/left_outer_join_test.lua @@ -417,7 +417,7 @@ left_join.test_sq_with_full_motion = function() "subquery $0:", "motion [policy: full]", " scan", - " projection ((\"arithmetic_space\".\"a\"::integer) + (1::unsigned) -> \"COL_1\")", + " projection (ROW(\"arithmetic_space\".\"a\"::integer) + ROW(1::unsigned) -> \"COL_1\")", " scan \"arithmetic_space\"", "execution options:", "sql_vdbe_max_steps = 45000", diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs index 2a9fbfde57..929e2f1a0e 100644 --- a/sbroad-core/src/frontend/sql/ir/tests.rs +++ b/sbroad-core/src/frontend/sql/ir/tests.rs @@ -35,7 +35,7 @@ fn front_sql2() { let expected_explain = String::from( r#"projection ("hash_testing"."identification_number"::integer -> "identification_number", "hash_testing"."product_code"::string -> "product_code") - selection ROW("hash_testing"."identification_number"::integer) = ROW(1::unsigned) and ROW("hash_testing"."product_code"::string) = ROW('1'::string) or ROW("hash_testing"."identification_number"::integer) = ROW(2::unsigned) and ROW("hash_testing"."product_code"::string) = ROW('2'::string) + selection (ROW("hash_testing"."identification_number"::integer) = ROW(1::unsigned) and ROW("hash_testing"."product_code"::string) = ROW('1'::string) or ROW("hash_testing"."identification_number"::integer) = ROW(2::unsigned) and ROW("hash_testing"."product_code"::string) = ROW('2'::string)) scan "hash_testing" execution options: sql_vdbe_max_steps = 45000 @@ -100,7 +100,7 @@ fn front_sql4() { let expected_explain = String::from( r#"projection ("t3"."identification_number"::integer -> "identification_number", "t3"."product_code"::string -> "product_code") - selection ROW("t3"."identification_number"::integer) = ROW(1::unsigned) or ROW("t3"."identification_number"::integer) = ROW(2::unsigned) or ROW("t3"."identification_number"::integer) = ROW(3::unsigned) and ROW("t3"."product_code"::string) = ROW('1'::string) or ROW("t3"."product_code"::string) = ROW('2'::string) + selection (ROW("t3"."identification_number"::integer) = ROW(1::unsigned) or (ROW("t3"."identification_number"::integer) = ROW(2::unsigned) or ROW("t3"."identification_number"::integer) = ROW(3::unsigned))) and (ROW("t3"."product_code"::string) = ROW('1'::string) or ROW("t3"."product_code"::string) = ROW('2'::string)) scan "t3" union all projection ("hash_testing"."identification_number"::integer -> "identification_number", "hash_testing"."product_code"::string -> "product_code") @@ -772,7 +772,7 @@ fn front_sql_aggregates() { let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( - r#"projection ("column_12"::unsigned -> "b", (sum(("count_28"::integer))::decimal) + (sum(("count_30"::integer))::decimal) -> "COL_1") + r#"projection ("column_12"::unsigned -> "b", ROW(sum(("count_28"::integer))::decimal) + ROW(sum(("count_30"::integer))::decimal) -> "COL_1") group by ("column_12"::unsigned) output: ("column_12"::unsigned -> "column_12", "count_28"::integer -> "count_28", "count_30"::integer -> "count_30") motion [policy: segment([ref("column_12")])] scan @@ -795,7 +795,7 @@ fn front_sql_avg_aggregate() { let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( - r#"projection ((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal) -> "COL_1", avg(distinct ("column_15"::decimal::double))::decimal -> "COL_2", ((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal)) * ((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal)) -> "COL_3") + r#"projection ((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal) -> "COL_1", avg(distinct ("column_15"::decimal::double))::decimal -> "COL_2", ROW((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal)) * ROW((sum(("sum_13"::decimal::double))::decimal / sum(("count_13"::integer::double))::decimal)) -> "COL_3") motion [policy: full] scan projection ("t"."b"::unsigned -> "column_15", sum(("t"."b"::unsigned))::decimal -> "sum_13", count(("t"."b"::unsigned))::integer -> "count_13") @@ -992,7 +992,7 @@ fn front_sql_aggregates_with_subexpressions() { group by ("column_12"::unsigned) output: ("column_12"::unsigned -> "column_12", "count_39"::integer -> "count_39", "count_35"::integer -> "count_35") motion [policy: segment([ref("column_12")])] scan - projection ("t"."b"::unsigned -> "column_12", count(("FUNC"(("t"."a"::unsigned))::integer))::integer -> "count_39", count((("t"."a"::unsigned) * ("t"."b"::unsigned) + (1::unsigned)))::integer -> "count_35") + projection ("t"."b"::unsigned -> "column_12", count(("FUNC"(("t"."a"::unsigned))::integer))::integer -> "count_39", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned) + ROW(1::unsigned)))::integer -> "count_35") group by ("t"."b"::unsigned) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: @@ -1039,8 +1039,8 @@ fn front_sql_aggregates_with_distinct2() { group by ("column_12"::unsigned) output: ("column_12"::unsigned -> "column_12", "column_34"::unsigned -> "column_34") motion [policy: segment([ref("column_12")])] scan - projection ("t"."b"::unsigned -> "column_12", ("t"."a"::unsigned) + ("t"."b"::unsigned) + (3::unsigned) -> "column_34") - group by ("t"."b"::unsigned, ("t"."a"::unsigned) + ("t"."b"::unsigned) + (3::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection ("t"."b"::unsigned -> "column_12", ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) + ROW(3::unsigned) -> "column_34") + group by ("t"."b"::unsigned, ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) + ROW(3::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1061,8 +1061,8 @@ fn front_sql_aggregates_with_distinct3() { r#"projection (sum(distinct ("column_19"::decimal))::decimal -> "COL_1") motion [policy: full] scan - projection (("t"."a"::unsigned) + ("t"."b"::unsigned) + (3::unsigned) -> "column_19") - group by (("t"."a"::unsigned) + ("t"."b"::unsigned) + (3::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) + ROW(3::unsigned) -> "column_19") + group by (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) + ROW(3::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1186,7 +1186,7 @@ fn front_sql_aggregate_without_groupby() { r#"projection (sum(("sum_20"::decimal))::decimal -> "COL_1") motion [policy: full] scan - projection (sum((("t"."a"::unsigned) * ("t"."b"::unsigned) + (1::unsigned)))::decimal -> "sum_20") + projection (sum((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned) + ROW(1::unsigned)))::decimal -> "sum_20") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1456,8 +1456,8 @@ fn front_sql_groupby_expression() { group by ("column_16"::unsigned) output: ("column_16"::unsigned -> "column_16") motion [policy: segment([ref("column_16")])] scan - projection (("t"."a"::unsigned) + ("t"."b"::unsigned) -> "column_16") - group by (("t"."a"::unsigned) + ("t"."b"::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_16") + group by (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1476,12 +1476,12 @@ fn front_sql_groupby_expression2() { let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( - r#"projection ("column_16"::unsigned + (sum(("count_35"::integer))::decimal) -> "COL_1") + r#"projection ("column_16"::unsigned + ROW(sum(("count_35"::integer))::decimal) -> "COL_1") group by ("column_16"::unsigned) output: ("column_16"::unsigned -> "column_16", "count_35"::integer -> "count_35") motion [policy: segment([ref("column_16")])] scan - projection ((("t"."a"::unsigned) + ("t"."b"::unsigned)) -> "column_16", count(("t"."a"::unsigned))::integer -> "count_35") - group by ((("t"."a"::unsigned) + ("t"."b"::unsigned))) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection ((ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned)) -> "column_16", count(("t"."a"::unsigned))::integer -> "count_35") + group by ((ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned))) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1499,12 +1499,12 @@ fn front_sql_groupby_expression3() { let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( - r#"projection ("column_16"::unsigned -> "COL_1", "column_26"::unsigned * (sum(("sum_55"::decimal))::decimal) / (sum(("count_61"::integer))::decimal) -> "COL_2") + r#"projection ("column_16"::unsigned -> "COL_1", "column_26"::unsigned * ROW(sum(("sum_55"::decimal))::decimal) / ROW(sum(("count_61"::integer))::decimal) -> "COL_2") group by ("column_16"::unsigned, "column_26"::unsigned) output: ("column_26"::unsigned -> "column_26", "column_16"::unsigned -> "column_16", "sum_55"::decimal -> "sum_55", "count_61"::integer -> "count_61") motion [policy: segment([ref("column_16"), ref("column_26")])] scan - projection ((("t"."c"::unsigned) * ("t"."d"::unsigned)) -> "column_26", ("t"."a"::unsigned) + ("t"."b"::unsigned) -> "column_16", sum((("t"."c"::unsigned) * ("t"."d"::unsigned)))::decimal -> "sum_55", count((("t"."a"::unsigned) * ("t"."b"::unsigned)))::integer -> "count_61") - group by (("t"."a"::unsigned) + ("t"."b"::unsigned), (("t"."c"::unsigned) * ("t"."d"::unsigned))) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection ((ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)) -> "column_26", ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_16", sum((ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)))::decimal -> "sum_55", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned)))::integer -> "count_61") + group by (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned), (ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned))) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1526,8 +1526,8 @@ fn front_sql_groupby_expression4() { group by ("column_16"::unsigned, "column_17"::unsigned) output: ("column_17"::unsigned -> "column_17", "column_16"::unsigned -> "column_16") motion [policy: segment([ref("column_16"), ref("column_17")])] scan - projection ("t"."a"::unsigned -> "column_17", ("t"."a"::unsigned) + ("t"."b"::unsigned) -> "column_16") - group by (("t"."a"::unsigned) + ("t"."b"::unsigned), "t"."a"::unsigned) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection ("t"."a"::unsigned -> "column_17", ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_16") + group by (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned), "t"."a"::unsigned) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -1655,7 +1655,7 @@ fn front_sql_left_join_single_left() { left join on ROW("T1"."A"::decimal) = ROW("T2"."B"::unsigned) motion [policy: segment([ref("A")])] scan "T1" - projection ((sum(("sum_13"::decimal))::decimal) / (3::unsigned) -> "A") + projection (ROW(sum(("sum_13"::decimal))::decimal) / ROW(3::unsigned) -> "A") motion [policy: full] scan projection (sum(("test_space"."id"::unsigned))::decimal -> "sum_13") @@ -1689,7 +1689,7 @@ fn front_sql_left_join_single_left2() { left join on ROW("T1"."A"::decimal) + ROW(3::unsigned) <> ROW("T2"."B"::unsigned) motion [policy: segment([ref("A")])] scan "T1" - projection ((sum(("sum_13"::decimal))::decimal) / (3::unsigned) -> "A") + projection (ROW(sum(("sum_13"::decimal))::decimal) / ROW(3::unsigned) -> "A") motion [policy: full] scan projection (sum(("test_space"."id"::unsigned))::decimal -> "sum_13") @@ -1723,7 +1723,7 @@ fn front_sql_left_join_single_both() { left join on ROW("T1"."A"::decimal) <> ROW("T2"."B"::integer) motion [policy: segment([ref("A")])] scan "T1" - projection ((sum(("sum_13"::decimal))::decimal) / (3::unsigned) -> "A") + projection (ROW(sum(("sum_13"::decimal))::decimal) / ROW(3::unsigned) -> "A") motion [policy: full] scan projection (sum(("test_space"."id"::unsigned))::decimal -> "sum_13") @@ -1809,7 +1809,7 @@ fn front_sql_having2() { let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( - r#"projection ((sum(("sum_38"::decimal))::decimal) * (count(distinct ("column_39"::integer))::integer) -> "COL_1", sum(("sum_38"::decimal))::decimal -> "COL_2") + r#"projection (ROW(sum(("sum_38"::decimal))::decimal) * ROW(count(distinct ("column_39"::integer))::integer) -> "COL_1", sum(("sum_38"::decimal))::decimal -> "COL_2") having ROW(sum(distinct ("column_39"::decimal))::decimal) > ROW(1::unsigned) and ROW(sum(("sum_38"::decimal))::decimal) > ROW(1::unsigned) motion [policy: full] scan @@ -2005,7 +2005,7 @@ fn front_sql_unique_local_aggregates() { let plan = sql_to_optimized_ir(input, vec![]); // here we must compute only two aggregates at local stage: sum(a), count(a) let expected_explain = String::from( - r#"projection (sum(("sum_13"::decimal))::decimal -> "COL_1", sum(("count_16"::integer))::decimal -> "COL_2", (sum(("sum_13"::decimal))::decimal) + (sum(("count_16"::integer))::decimal) -> "COL_3") + r#"projection (sum(("sum_13"::decimal))::decimal -> "COL_1", sum(("count_16"::integer))::decimal -> "COL_2", ROW(sum(("sum_13"::decimal))::decimal) + ROW(sum(("count_16"::integer))::decimal) -> "COL_3") motion [policy: full] scan projection (count(("t"."a"::unsigned))::integer -> "count_16", sum(("t"."a"::unsigned))::decimal -> "sum_13") @@ -2097,8 +2097,8 @@ fn front_sql_select_distinct() { group by ("column_22"::unsigned, "column_27"::unsigned) output: ("column_22"::unsigned -> "column_22", "column_27"::unsigned -> "column_27") motion [policy: segment([ref("column_22"), ref("column_27")])] scan - projection ("t"."a"::unsigned -> "column_22", ("t"."a"::unsigned) + ("t"."b"::unsigned) -> "column_27") - group by ("t"."a"::unsigned, ("t"."a"::unsigned) + ("t"."b"::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") + projection ("t"."a"::unsigned -> "column_22", ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_27") + group by ("t"."a"::unsigned, ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned)) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id") scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -2241,7 +2241,7 @@ fn front_sql_insert_1() { r#"insert "t" on conflict: fail motion [policy: segment([value(NULL), ref("a")])] projection ("t"."a"::unsigned -> "a") - selection ROW("t"."a"::unsigned) = ROW(1::unsigned) and ROW("t"."b"::unsigned) = ROW(2::unsigned) or ROW("t"."a"::unsigned) = ROW(2::unsigned) and ROW("t"."b"::unsigned) = ROW(3::unsigned) + selection (ROW("t"."a"::unsigned) = ROW(1::unsigned) and ROW("t"."b"::unsigned) = ROW(2::unsigned) or ROW("t"."a"::unsigned) = ROW(2::unsigned) and ROW("t"."b"::unsigned) = ROW(3::unsigned)) scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -2282,7 +2282,7 @@ fn front_sql_insert_3() { r#"insert "t" on conflict: fail motion [policy: segment([ref("b"), ref("a")])] projection ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b") - selection ROW("t"."a"::unsigned) = ROW(1::unsigned) and ROW("t"."b"::unsigned) = ROW(2::unsigned) or ROW("t"."a"::unsigned) = ROW(3::unsigned) and ROW("t"."b"::unsigned) = ROW(4::unsigned) + selection (ROW("t"."a"::unsigned) = ROW(1::unsigned) and ROW("t"."b"::unsigned) = ROW(2::unsigned) or ROW("t"."a"::unsigned) = ROW(3::unsigned) and ROW("t"."b"::unsigned) = ROW(4::unsigned)) scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -2438,7 +2438,7 @@ fn front_sql_update2() { r#"update "t" "c" = COL_0 motion [policy: local] - projection (("t"."a"::unsigned) + ("t"."b"::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) + projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) scan "t" execution options: sql_vdbe_max_steps = 45000 @@ -2457,7 +2457,7 @@ fn front_sql_update3() { r#"update "t" "c" = COL_0 motion [policy: local] - projection (("t"."a"::unsigned) + ("t"."b"::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) + projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) selection ROW("t"."c"::unsigned) = ROW(1::unsigned) scan "t" execution options: @@ -2482,7 +2482,7 @@ fn front_sql_update4() { "d" = COL_0 "c" = COL_0 motion [policy: local] - projection (("b1"::integer) * (2::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) + projection (ROW("b1"::integer) * ROW(2::unsigned) -> COL_0, "t"."b"::unsigned -> COL_1) join on ROW("t"."c"::unsigned) = ROW("b1"::integer) scan "t" projection ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d") diff --git a/sbroad-core/src/frontend/sql/ir/tests/params.rs b/sbroad-core/src/frontend/sql/ir/tests/params.rs index 1d20bda305..076554d26f 100644 --- a/sbroad-core/src/frontend/sql/ir/tests/params.rs +++ b/sbroad-core/src/frontend/sql/ir/tests/params.rs @@ -101,7 +101,7 @@ fn front_params5() { let expected_explain = String::from( r#"projection ("test_space"."id"::unsigned -> "id") - selection ROW("test_space"."sys_op"::unsigned) = ROW(0::integer) or ROW("test_space"."id"::unsigned) in ROW($0) + selection (ROW("test_space"."sys_op"::unsigned) = ROW(0::integer) or ROW("test_space"."id"::unsigned) in ROW($0)) scan "test_space" subquery $0: motion [policy: segment([ref("sysFrom")])] @@ -138,7 +138,7 @@ fn front_params6() { let expected_explain = String::from( r#"projection ("test_space"."id"::unsigned -> "id") - selection ROW("test_space"."sys_op"::unsigned) = ROW(0::integer) or ROW("test_space"."id"::unsigned) not in ROW($0) + selection (ROW("test_space"."sys_op"::unsigned) = ROW(0::integer) or ROW("test_space"."id"::unsigned) not in ROW($0)) scan "test_space" subquery $0: motion [policy: full] diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs index 7c020e08f9..129c5b4c3d 100644 --- a/sbroad-core/src/ir/explain.rs +++ b/sbroad-core/src/ir/explain.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter, Write as _}; +use std::mem::take; use itertools::Itertools; use serde::Serialize; @@ -22,30 +23,46 @@ use super::value::Value; enum ColExpr { Alias(Box<ColExpr>, String), Arithmetic(Box<ColExpr>, BinaryOp, Box<ColExpr>, bool), + Bool(Box<ColExpr>, BinaryOp, Box<ColExpr>), + Unary(Unary, Box<ColExpr>), Column(String, Type), Cast(Box<ColExpr>, CastType), Concat(Box<ColExpr>, Box<ColExpr>), - StableFunction(String, Box<ColExpr>, bool, Type), - Row(Vec<ColExpr>), + StableFunction(String, Vec<ColExpr>, bool, Type), + Row(Row), None, } impl Display for ColExpr { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s = match &self { - ColExpr::Alias(expr, name) => format!("({expr} -> {name})"), + ColExpr::Alias(expr, name) => format!("{expr} -> {name}"), ColExpr::Arithmetic(left, op, right, with_parentheses) => match with_parentheses { false => format!("{left} {op} {right}"), true => format!("({left} {op} {right})"), }, + ColExpr::Bool(left, op, right) => { + if let BinaryOp::BoolOp(Bool::Or) = op { + format!("({left} {op} {right})") + } else { + format!("{left} {op} {right}") + } + } + ColExpr::Unary(op, expr) => match op { + Unary::IsNull | Unary::IsNotNull => format!("{expr} {op}"), + Unary::Exists | Unary::NotExists => format!("{op} {expr}"), + }, ColExpr::Column(c, col_type) => format!("{c}::{col_type}"), ColExpr::Cast(v, t) => format!("{v}::{t}"), ColExpr::Concat(l, r) => format!("{l} || {r}"), - ColExpr::StableFunction(name, arg, is_distinct, func_type) => format!( - "{name}({}{arg})::{func_type}", - if *is_distinct { "distinct " } else { "" } - ), - ColExpr::Row(list) => format!("({})", list.iter().format(", ")), + ColExpr::StableFunction(name, args, is_distinct, func_type) => { + let formatted_args = format!("({})", args.iter().format(", ")); + format!( + "{name}({}{formatted_args})::{func_type}", + if *is_distinct { "distinct " } else { "" } + ) + } + ColExpr::Row(row) => row.to_string(), ColExpr::None => String::new(), }; @@ -67,8 +84,12 @@ impl Default for ColExpr { impl ColExpr { #[allow(dead_code, clippy::too_many_lines)] - fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { - let mut stack: Vec<ColExpr> = Vec::new(); + fn new( + plan: &Plan, + subtree_top: usize, + sq_ref_map: &SubQueryRefMap, + ) -> Result<Self, SbroadError> { + let mut stack: Vec<(ColExpr, usize)> = Vec::new(); let mut dft_post = PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, false), EXPR_CAPACITY); @@ -77,19 +98,18 @@ impl ColExpr { match ¤t_node { Expression::Cast { to, .. } => { - let expr = stack.pop().ok_or_else(|| { + let (expr, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing CAST expression".to_string(), ) })?; let cast_expr = ColExpr::Cast(Box::new(expr), to.clone()); - stack.push(cast_expr); + stack.push((cast_expr, id)); } Expression::CountAsterisk => { - stack.push(ColExpr::Column( - "*".to_string(), - current_node.calculate_type(plan)?, - )); + let count_asterisk_expr = + ColExpr::Column("*".to_string(), current_node.calculate_type(plan)?); + stack.push((count_asterisk_expr, id)); } Expression::Reference { position, .. } => { let mut col_name = String::new(); @@ -105,29 +125,27 @@ impl ColExpr { let alias = plan.get_alias_from_reference_node(current_node)?; col_name.push_str(alias); - stack.push(ColExpr::Column( - col_name, - current_node.calculate_type(plan)?, - )); + let ref_expr = ColExpr::Column(col_name, current_node.calculate_type(plan)?); + stack.push((ref_expr, id)); } Expression::Concat { .. } => { - let right = stack.pop().ok_or_else(|| { + let (right, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing CONCAT expression".to_string(), ) })?; - let left = stack.pop().ok_or_else(|| { + let (left, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing CONCAT expression".to_string(), ) })?; let concat_expr = ColExpr::Concat(Box::new(left), Box::new(right)); - stack.push(concat_expr); + stack.push((concat_expr, id)); } Expression::Constant { value } => { let expr = ColExpr::Column(value.to_string(), current_node.calculate_type(plan)?); - stack.push(expr); + stack.push((expr, id)); } Expression::StableFunction { name, @@ -138,7 +156,7 @@ impl ColExpr { let mut len = children.len(); let mut args: Vec<ColExpr> = Vec::with_capacity(len); while len > 0 { - let arg = stack.pop().ok_or_else(|| { + let (arg, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( format!("stack is empty, expected to pop {len} element while processing STABLE FUNCTION expression"), ) @@ -147,18 +165,17 @@ impl ColExpr { len -= 1; } args.reverse(); - let args_expr = ColExpr::Row(args); let func_expr = ColExpr::StableFunction( name.clone(), - Box::new(args_expr), + args, *is_distinct, func_type.clone(), ); - stack.push(func_expr); + stack.push((func_expr, id)); } Expression::Row { list, .. } => { let mut len = list.len(); - let mut row: Vec<ColExpr> = Vec::with_capacity(len); + let mut row: Vec<(ColExpr, usize)> = Vec::with_capacity(len); while len > 0 { let expr = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( @@ -168,8 +185,10 @@ impl ColExpr { row.push(expr); len -= 1; } + row.reverse(); + let row = Row::from_col_exprs_with_ids(plan, &mut row, sq_ref_map)?; let row_expr = ColExpr::Row(row); - stack.push(row_expr); + stack.push((row_expr, id)); } Expression::Arithmetic { left: _, @@ -177,13 +196,13 @@ impl ColExpr { right: _, with_parentheses, } => { - let right = stack.pop().ok_or_else(|| { + let (right, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing ARITHMETIC expression".to_string(), ) })?; - let left = stack.pop().ok_or_else(|| { + let (left, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing ARITHMETIC expression".to_string(), ) @@ -196,91 +215,81 @@ impl ColExpr { *with_parentheses, ); - stack.push(ar_expr); + stack.push((ar_expr, id)); } Expression::Alias { name, .. } => { - let expr = stack.pop().ok_or_else(|| { + let (expr, _) = stack.pop().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues( "stack is empty while processing ALIAS expression".to_string(), ) })?; let alias_expr = ColExpr::Alias(Box::new(expr), name.clone()); - stack.push(alias_expr); + stack.push((alias_expr, id)); } - Expression::Bool { .. } | Expression::Unary { .. } => { - return Err(SbroadError::Unsupported( - Entity::Expression, - Some(format!( - "Column expression node [{current_node:?}] is not supported for yet" - )), - )); - } - } - } - - stack - .pop() - .ok_or_else(|| SbroadError::UnexpectedNumberOfValues("stack is empty".to_string())) - } -} - -#[derive(Debug, Serialize, Default)] -struct Col { - /// Column alias from sql query - alias: Option<String>, - - /// Column expression (e.g. column name, function, etc.) - col: ColExpr, -} + Expression::Bool { op, .. } => { + let (right, _) = stack.pop().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing BOOL expression".to_string(), + ) + })?; -impl Col { - #[allow(dead_code)] - fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { - let mut column = Col::default(); + let (left, _) = stack.pop().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing BOOL expression".to_string(), + ) + })?; - let mut dft_post = - PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, true), EXPR_CAPACITY); - for (_, id) in dft_post.iter(subtree_top) { - let current_node = plan.get_expression_node(id)?; + let bool_expr = ColExpr::Bool( + Box::new(left), + BinaryOp::BoolOp(op.clone()), + Box::new(right), + ); - if let Expression::Alias { name, .. } = ¤t_node { - column.alias = Some(name.to_string()); - } else { - column.col = ColExpr::new(plan, id)?; + stack.push((bool_expr, id)); + } + Expression::Unary { op, .. } => { + let (expr, _) = stack.pop().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues( + "stack is empty while processing UNARY expression".to_string(), + ) + })?; + let alias_expr = ColExpr::Unary(op.clone(), Box::new(expr)); + stack.push((alias_expr, id)); + } } } - Ok(column) + let (expr, _) = stack + .pop() + .ok_or_else(|| SbroadError::UnexpectedNumberOfValues("stack is empty".to_string()))?; + Ok(expr) } } -impl Display for Col { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut s = String::from(&self.col); - - if let Some(a) = &self.alias { - write!(s, " -> {a}")?; - } - - write!(f, "{s}") - } -} +/// Alias for map of (`SubQuery` id -> it's offset). +/// Offset = `SubQuery` index (e.g. in case there are several `SubQueries` in Selection WHERE condition +/// index will indicate to which of them Reference is pointing). +type SubQueryRefMap = HashMap<usize, usize>; #[derive(Debug, Serialize)] struct Projection { /// List of colums in sql query - cols: Vec<Col>, + cols: Vec<ColExpr>, } impl Projection { #[allow(dead_code)] - fn new(plan: &Plan, output_id: usize) -> Result<Self, SbroadError> { + fn new( + plan: &Plan, + output_id: usize, + sq_ref_map: &SubQueryRefMap, + ) -> Result<Self, SbroadError> { let mut result = Projection { cols: vec![] }; let alias_list = plan.get_expression_node(output_id)?; for col_node_id in alias_list.get_row_list()? { - let col = Col::new(plan, *col_node_id)?; + let col = ColExpr::new(plan, *col_node_id, sq_ref_map)?; result.cols.push(col); } @@ -307,25 +316,30 @@ impl Display for Projection { #[derive(Debug, Serialize)] struct GroupBy { /// List of colums in sql query - gr_cols: Vec<Col>, - output_cols: Vec<Col>, + gr_cols: Vec<ColExpr>, + output_cols: Vec<ColExpr>, } impl GroupBy { #[allow(dead_code)] - fn new(plan: &Plan, gr_cols: &Vec<usize>, output_id: usize) -> Result<Self, SbroadError> { + fn new( + plan: &Plan, + gr_cols: &Vec<usize>, + output_id: usize, + sq_ref_map: &SubQueryRefMap, + ) -> Result<Self, SbroadError> { let mut result = GroupBy { gr_cols: vec![], output_cols: vec![], }; for col_node_id in gr_cols { - let col = Col::new(plan, *col_node_id)?; + let col = ColExpr::new(plan, *col_node_id, sq_ref_map)?; result.gr_cols.push(col); } let alias_list = plan.get_expression_node(output_id)?; for col_node_id in alias_list.get_row_list()? { - let col = Col::new(plan, *col_node_id)?; + let col = ColExpr::new(plan, *col_node_id, sq_ref_map)?; result.output_cols.push(col); } Ok(result) @@ -495,16 +509,14 @@ impl Display for Ref { #[derive(Debug, Serialize)] enum RowVal { - Const(Value), - Column(Col), + ColumnExpr(ColExpr), SqRef(Ref), } impl Display for RowVal { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s = match &self { - RowVal::Const(c) => format!("{c}::{}", c.get_type()), - RowVal::Column(c) => c.to_string(), + RowVal::ColumnExpr(c) => c.to_string(), RowVal::SqRef(r) => r.to_string(), }; @@ -528,29 +540,26 @@ impl Row { self.cols.push(row); } - fn from_ir_nodes( + fn from_col_exprs_with_ids( plan: &Plan, - node_ids: &[usize], - ref_map: &HashMap<usize, usize>, + exprs_with_ids: &mut Vec<(ColExpr, usize)>, + sq_ref_map: &SubQueryRefMap, ) -> Result<Self, SbroadError> { let mut row = Row::new(); - for child in node_ids { - let current_node = plan.get_expression_node(*child)?; + for (col_expr, expr_id) in take(exprs_with_ids) { + let current_node = plan.get_expression_node(expr_id)?; match ¤t_node { - Expression::Constant { value, .. } => { - row.add_col(RowVal::Const(value.clone())); - } Expression::Reference { .. } => { - let rel_id: usize = *plan.get_relational_from_reference_node(*child)?; + let rel_id: usize = *plan.get_relational_from_reference_node(expr_id)?; let rel_node = plan.get_relation_node(rel_id)?; if plan.is_additional_child(rel_id)? { if let Relational::ScanSubQuery { .. } | Relational::Motion { .. } = rel_node { - let sq_offset = ref_map.get(&rel_id).ok_or_else(|| { + let sq_offset = sq_ref_map.get(&rel_id).ok_or_else(|| { SbroadError::NotFound( Entity::SubQuery, format!("with index {rel_id} in the map"), @@ -566,27 +575,11 @@ impl Row { )); } } else { - let col = Col::new(plan, *child)?; - row.add_col(RowVal::Column(col)); + let col = ColExpr::new(plan, expr_id, sq_ref_map)?; + row.add_col(RowVal::ColumnExpr(col)); } } - Expression::Bool { .. } - | Expression::Arithmetic { .. } - | Expression::Cast { .. } - | Expression::Concat { .. } - | Expression::StableFunction { .. } - | Expression::Row { .. } - | Expression::Alias { .. } - | Expression::Unary { .. } => { - let col = Col::new(plan, *child)?; - row.add_col(RowVal::Column(col)); - } - Expression::CountAsterisk => { - return Err(SbroadError::Invalid( - Entity::Plan, - Some("CountAsterisk can't be present among Row children!".into()), - )) - } + _ => row.add_col(RowVal::ColumnExpr(col_expr)), } } @@ -612,71 +605,6 @@ enum BinaryOp { ArithOp(Arithmetic), BoolOp(Bool), } -/// Recursive type which describe `WHERE` cause in explain -#[derive(Debug, Serialize)] -enum Selection { - Row(Row), - BinaryOp { - left: Box<Selection>, - op: BinaryOp, - right: Box<Selection>, - }, - UnaryOp { - op: Unary, - child: Box<Selection>, - }, -} - -impl Selection { - #[allow(dead_code)] - fn new( - plan: &Plan, - subtree_node_id: usize, - ref_map: &HashMap<usize, usize>, - ) -> Result<Self, SbroadError> { - let current_node = plan.get_expression_node(subtree_node_id)?; - - let result = match current_node { - Expression::Bool { left, op, right } => Selection::BinaryOp { - left: Box::new(Selection::new(plan, *left, ref_map)?), - op: BinaryOp::BoolOp(op.clone()), - right: Box::new(Selection::new(plan, *right, ref_map)?), - }, - Expression::Arithmetic { - left, op, right, .. - } => Selection::BinaryOp { - left: Box::new(Selection::new(plan, *left, ref_map)?), - op: BinaryOp::ArithOp(op.clone()), - right: Box::new(Selection::new(plan, *right, ref_map)?), - }, - Expression::Row { list, .. } => { - let row = Row::from_ir_nodes(plan, list, ref_map)?; - Selection::Row(row) - } - Expression::Unary { op, child } => Selection::UnaryOp { - op: op.clone(), - child: Box::new(Selection::new(plan, *child, ref_map)?), - }, - Expression::Reference { .. } - | Expression::Cast { .. } - | Expression::StableFunction { .. } - | Expression::Concat { .. } - | Expression::Constant { .. } - | Expression::Alias { .. } => { - let row = Row::from_ir_nodes(plan, &[subtree_node_id], ref_map)?; - Selection::Row(row) - } - Expression::CountAsterisk => { - return Err(SbroadError::Invalid( - Entity::Plan, - Some("CountAsterisk can't be present in Selection filter!".into()), - )) - } - }; - - Ok(result) - } -} impl Display for BinaryOp { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -689,23 +617,6 @@ impl Display for BinaryOp { } } -impl Display for Selection { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let s = match &self { - Selection::Row(r) => r.to_string(), - Selection::BinaryOp { left, op, right } => { - format!("{left} {op} {right}") - } - Selection::UnaryOp { op, child } => match op { - Unary::IsNull | Unary::IsNotNull => format!("{child} {op}"), - Unary::Exists | Unary::NotExists => format!("{op} {child}"), - }, - }; - - write!(f, "{s}") - } -} - #[derive(Debug, Serialize)] struct SubQuery { /// Subquery alias. For subquery in `WHERE` cause alias is `None`. @@ -803,7 +714,7 @@ impl Display for Target { #[derive(Debug, Serialize)] struct InnerJoin { - condition: Selection, + condition: ColExpr, kind: JoinKind, } @@ -828,13 +739,13 @@ enum ExplainNode { Except, GroupBy(GroupBy), InnerJoin(InnerJoin), - ValueRow(Row), + ValueRow(ColExpr), Value, Insert(String, ConflictStrategy), Projection(Projection), Scan(Scan), - Selection(Selection), - Having(Selection), + Selection(ColExpr), + Having(ColExpr), UnionAll, Update(Update), SubQuery(SubQuery), @@ -871,10 +782,8 @@ struct ExplainTreePart { /// Level hepls to detect count of idents #[serde(skip_serializing)] level: usize, - /// Current node of sql query current: Option<ExplainNode>, - /// Children nodes of current sql node children: Vec<ExplainTreePart>, } @@ -984,7 +893,7 @@ impl FullExplain { ) })?; current_node.children.push(child); - let p = GroupBy::new(ir, gr_cols, *output)?; + let p = GroupBy::new(ir, gr_cols, *output, &HashMap::new())?; Some(ExplainNode::GroupBy(p)) } Relational::Projection { output, .. } => { @@ -995,7 +904,7 @@ impl FullExplain { ) })?; current_node.children.push(child); - let p = Projection::new(ir, *output)?; + let p = Projection::new(ir, *output, &HashMap::new())?; Some(ExplainNode::Projection(p)) } Relational::ScanRelation { @@ -1013,8 +922,7 @@ impl FullExplain { | Relational::Having { children, filter, .. } => { - let mut sq_ref_map: HashMap<usize, usize> = - HashMap::with_capacity(children.len() - 1); + let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len() - 1); if let Some((_, other)) = children.split_first() { for sq_id in other.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { @@ -1038,10 +946,10 @@ impl FullExplain { )); } let filter_id = ir.undo.get_oldest(filter).map_or_else(|| *filter, |id| *id); - let s = Selection::new(ir, filter_id, &sq_ref_map)?; + let selection = ColExpr::new(ir, filter_id, &sq_ref_map)?; let explain_node = match &node { - Relational::Selection { .. } => ExplainNode::Selection(s), - Relational::Having { .. } => ExplainNode::Having(s), + Relational::Selection { .. } => ExplainNode::Selection(selection), + Relational::Having { .. } => ExplainNode::Having(selection), _ => return Err(SbroadError::DoSkip), }; Some(explain_node) @@ -1139,8 +1047,7 @@ impl FullExplain { )); } let (_, subquery_ids) = children.split_at(2); - let mut sq_ref_map: HashMap<usize, usize> = - HashMap::with_capacity(children.len() - 2); + let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len() - 2); for sq_id in subquery_ids.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { @@ -1162,15 +1069,14 @@ impl FullExplain { )); } - let condition = Selection::new(ir, *condition, &sq_ref_map)?; + let condition = ColExpr::new(ir, *condition, &sq_ref_map)?; Some(ExplainNode::InnerJoin(InnerJoin { condition, kind: kind.clone(), })) } Relational::ValuesRow { data, children, .. } => { - let mut sq_ref_map: HashMap<usize, usize> = - HashMap::with_capacity(children.len()); + let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len()); for sq_id in children.iter().rev() { let sq_node = stack.pop().ok_or_else(|| { @@ -1184,8 +1090,7 @@ impl FullExplain { sq_ref_map.insert(*sq_id, offset); } - let values = ir.get_expression_node(*data)?.get_row_list()?; - let row = Row::from_ir_nodes(ir, values, &sq_ref_map)?; + let row = ColExpr::new(ir, *data, &sq_ref_map)?; Some(ExplainNode::ValueRow(row)) } diff --git a/sbroad-core/src/ir/explain/tests.rs b/sbroad-core/src/ir/explain/tests.rs index 44aecc556e..4489affa7f 100644 --- a/sbroad-core/src/ir/explain/tests.rs +++ b/sbroad-core/src/ir/explain/tests.rs @@ -208,7 +208,7 @@ WHERE "id" IN (SELECT "id" let mut actual_explain = String::new(); actual_explain.push_str(r#"projection ("t"."id"::unsigned -> "id", "t"."FIRST_NAME"::string -> "FIRST_NAME") - selection ROW("t"."id"::unsigned) in ROW($0) or ROW("t"."id"::unsigned) in ROW($1) + selection (ROW("t"."id"::unsigned) in ROW($0) or ROW("t"."id"::unsigned) in ROW($1)) scan "t" union all projection ("test_space"."id"::unsigned -> "id", "test_space"."FIRST_NAME"::string -> "FIRST_NAME") diff --git a/sbroad-core/src/ir/explain/tests/concat.rs b/sbroad-core/src/ir/explain/tests/concat.rs index 5d561dbf8f..f46fedb427 100644 --- a/sbroad-core/src/ir/explain/tests/concat.rs +++ b/sbroad-core/src/ir/explain/tests/concat.rs @@ -6,7 +6,7 @@ fn concat1_test() { r#"SELECT CAST('1' as string) || 'hello' FROM "t1""#, &format!( "{}\n{}\n{}\n{}\n{}\n", - r#"projection (('1'::string::string) || ('hello'::string) -> "COL_1")"#, + r#"projection (ROW('1'::string::string) || ROW('hello'::string) -> "COL_1")"#, r#" scan "t1""#, r#"execution options:"#, r#"sql_vdbe_max_steps = 45000"#, @@ -22,7 +22,7 @@ fn concat2_test() { &format!( "{}\n{}\n{}\n{}\n{}\n{}\n", r#"projection ("t1"."a"::string -> "a")"#, - r#" selection ROW(('1'::string::string) || (("FUNC"(('hello'::string))::integer) || ('2'::string))) = ROW(42::unsigned)"#, + r#" selection ROW(ROW('1'::string::string) || ROW(ROW("FUNC"(('hello'::string))::integer) || ROW('2'::string))) = ROW(42::unsigned)"#, r#" scan "t1""#, r#"execution options:"#, r#"sql_vdbe_max_steps = 45000"#, -- GitLab