From 3826237375796f8ae91bf759f78f584512078d71 Mon Sep 17 00:00:00 2001 From: "ms.evilhat" <ms.evilhat@gmail.com> Date: Mon, 6 Feb 2023 15:27:03 +0300 Subject: [PATCH] feat: add arithmetic expressions to projection we need to support arbitrary expressions consisting of logical, comparison and arithmetic operations and as sub-expression of aggregates. previously we added arithmetic exprs to selection ans join. this commit supports arithmetic (and only arithmetic) as a part of projection --- .../test/integration/arithmetic_test.lua | 594 +++++++++++++++--- sbroad-core/src/backend/sql/tree/tests.rs | 179 +++++- sbroad-core/src/frontend/sql.rs | 153 +++-- sbroad-core/src/frontend/sql/ast/tests.rs | 81 ++- sbroad-core/src/frontend/sql/query.pest | 2 +- sbroad-core/src/ir/expression.rs | 38 +- .../sql/tree/arithmetic_projection_plan.yaml | 259 ++++++++ ...an.yaml => arithmetic_selection_plan.yaml} | 0 .../sql/arithmetic_projection_ast.yaml | 56 ++ ...ast.yaml => arithmetic_selection_ast.yaml} | 0 10 files changed, 1192 insertions(+), 170 deletions(-) create mode 100644 sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml rename sbroad-core/tests/artifactory/backend/sql/tree/{arithmetic_plan.yaml => arithmetic_selection_plan.yaml} (100%) create mode 100644 sbroad-core/tests/artifactory/frontend/sql/arithmetic_projection_ast.yaml rename sbroad-core/tests/artifactory/frontend/sql/{arithmetic_ast.yaml => arithmetic_selection_ast.yaml} (100%) diff --git a/sbroad-cartridge/test_app/test/integration/arithmetic_test.lua b/sbroad-cartridge/test_app/test/integration/arithmetic_test.lua index 0d127d0f50..c4ab8f7c1e 100644 --- a/sbroad-cartridge/test_app/test/integration/arithmetic_test.lua +++ b/sbroad-cartridge/test_app/test/integration/arithmetic_test.lua @@ -1,6 +1,7 @@ local t = require('luatest') -local g = t.group('arithmetic') -local g1 = t.group('arithmetic.propetries') +local g = t.group('arithmetic.selection') +local g1 = t.group('arithmetic.join') +local g2 = t.group('arithmetic.projection') local decimal = require("decimal") local helper = require('test.helper.cluster_no_replication') @@ -66,14 +67,25 @@ g1.before_each( function() local api = cluster:server("api-1").net_box - for k = 1,10 do + for i = 1,10 do local r, err = api:call("sbroad.execute", { [[ insert into "arithmetic_space" ("id", "a", "b", "c", "d", "e", "f", "boolean_col", "string_col", "number_col") values (?,?,?,?,?,?,?,?,?,?) ]], - {k, k, k*2, k*3, k, k, k, true, "123", decimal.new('4.6')}, + {i, i, i*2, i*3, i, i, i, true, "123", decimal.new('4.6')}, + }) + t.assert_equals(err, nil) + t.assert_equals(r, {row_count = 1}) + + r, err = api:call("sbroad.execute", { + [[ + insert into "arithmetic_space2" + ("id", "a", "b", "c", "d", "e", "f", "boolean_col", "string_col", "number_col") + values (?,?,?,?,?,?,?,?,?,?) + ]], + {i, i, i, i, i, i, i, false, "123", decimal.new('4.599999')}, }) t.assert_equals(err, nil) t.assert_equals(r, {row_count = 1}) @@ -85,9 +97,11 @@ g1.after_each( function() local storage1 = cluster:server("storage-1-1").net_box storage1:call("box.execute", { [[truncate table "arithmetic_space"]] }) + storage1:call("box.execute", { [[truncate table "arithmetic_space2"]] }) local storage2 = cluster:server("storage-2-1").net_box storage2:call("box.execute", { [[truncate table "arithmetic_space"]] }) + storage2:call("box.execute", { [[truncate table "arithmetic_space2"]] }) end ) @@ -95,6 +109,57 @@ g1.after_all(function() helper.stop_test_cluster() end) +g2.before_all(function() + helper.start_test_cluster(helper.cluster_config) + cluster = helper.cluster +end) + +g2.before_each( + function() + local api = cluster:server("api-1").net_box + + for i = 1, 10 do + local r, err = api:call("sbroad.execute", { + [[ + insert into "arithmetic_space" + ("id", "a", "b", "c", "d", "e", "f", "boolean_col", "string_col", "number_col") + values (?,?,?,?,?,?,?,?,?,?) + ]], + {i, i, i*2, i*3, i, i, i, true, "123", decimal.new('4.6')}, + }) + t.assert_equals(err, nil) + t.assert_equals(r, {row_count = 1}) + + r, err = api:call("sbroad.execute", { + [[ + insert into "arithmetic_space2" + ("id", "a", "b", "c", "d", "e", "f", "boolean_col", "string_col", "number_col") + values (?,?,?,?,?,?,?,?,?,?) + ]], + {i, i, i, i, i, i, i, false, "123", decimal.new('4.599999')}, + }) + t.assert_equals(err, nil) + t.assert_equals(r, {row_count = 1}) + end + end +) + +g2.after_each( + function() + local storage1 = cluster:server("storage-1-1").net_box + storage1:call("box.execute", { [[truncate table "arithmetic_space"]] }) + storage1:call("box.execute", { [[truncate table "arithmetic_space2"]] }) + + local storage2 = cluster:server("storage-2-1").net_box + storage2:call("box.execute", { [[truncate table "arithmetic_space"]] }) + storage2:call("box.execute", { [[truncate table "arithmetic_space2"]] }) + end +) + +g2.after_all(function() + helper.stop_test_cluster() +end) + g.test_arithmetic_invalid = function() local api = cluster:server("api-1").net_box @@ -150,6 +215,61 @@ g.test_arithmetic_invalid = function() ) end +g2.test_arithmetic_invalid = function() + local api = cluster:server("api-1").net_box + + local _, err = api:call("sbroad.execute", { [[select "id" % 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" ^ 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" ++ 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" ** 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" // 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" ** 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" +- 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + local _, err = api:call("sbroad.execute", { [[select "id" +* 2 from "arithmetic_space"]], {} }) + t.assert_str_contains(tostring(err), "rule parsing error") + + -- arithemic operation on boolean col + local _, err = api:call("sbroad.execute", + { [[select "boolean_col" + "boolean_col" from "arithmetic_space"]], {} } + ) + t.assert_str_contains( + tostring(err), + "Type mismatch: can not convert boolean(TRUE) to integer, decimal, double, datetime or interval" + ) + + -- arithemic operation on string col + local _, err = api:call("sbroad.execute", + { [[select "string_col" + "string_col" from "arithmetic_space"]], {} } + ) + t.assert_str_contains( + tostring(err), + "Type mismatch: can not convert string('123') to integer, decimal, double, datetime or interval" + ) + + -- arithemic operation on number col + local _, err = api:call("sbroad.execute", + { [[select "number_col" + "number_col" from "arithmetic_space"]], {} } + ) + t.assert_str_contains( + tostring(err), + "Type mismatch: can not convert number(4.6) to integer, decimal, double, datetime or interval" + ) +end + g.test_arithmetic_valid = function() local api = cluster:server("api-1").net_box @@ -315,83 +435,6 @@ g.test_arithmetic_with_bool = function() ) end -g.test_join_simple_arithmetic = function() - local api = cluster:server("api-1").net_box - - local r, err = api:call("sbroad.execute", - { [[ - SELECT "t3"."id", "t3"."a", "t8"."b" - FROM - (SELECT "id", "a" - FROM "arithmetic_space" - WHERE "c" < 0 - UNION ALL - SELECT "id", "a" - FROM "arithmetic_space" - WHERE "c" > 0) AS "t3" - INNER JOIN - (SELECT "id" as "id1", "b" - FROM "arithmetic_space2" - WHERE "b" < 0 - UNION ALL - SELECT "id" as "id1", "b" - FROM "arithmetic_space2" - WHERE "b" > 0) AS "t8" - ON "t3"."id" + "t3"."a" * 2 = "t8"."id1" + "t8"."b" - WHERE "t3"."id" = 2]], { } }) - - t.assert_equals(err, nil) - t.assert_equals( - r.metadata, - { - {name = "t3.id", type = "integer"}, - {name = "t3.a", type = "integer"}, - {name = "t8.b", type = "integer"}, - } - ) - t.assert_equals( - r.rows, - { { 2, 2, 3 } } - ) - - -- check the same query with params - local r2, err = api:call("sbroad.execute", - { [[ - SELECT "t3"."id", "t3"."a", "t8"."b" - FROM - (SELECT "id", "a" - FROM "arithmetic_space" - WHERE "c" < ? - UNION ALL - SELECT "id", "a" - FROM "arithmetic_space" - WHERE "c" > ?) AS "t3" - INNER JOIN - (SELECT "id" as "id1", "b" - FROM "arithmetic_space2" - WHERE "b" < ? - UNION ALL - SELECT "id" as "id1", "b" - FROM "arithmetic_space2" - WHERE "b" > ?) AS "t8" - ON "t3"."id" + "t3"."a" * ? = "t8"."id1" + "t8"."b" - WHERE "t3"."id" = ?]], { 0, 0, 0, 0, 2, 2} }) - - t.assert_equals(err, nil) - t.assert_equals( - r.metadata, - { - {name = "t3.id", type = "integer"}, - {name = "t3.a", type = "integer"}, - {name = "t8.b", type = "integer"}, - } - ) - t.assert_equals( - r.rows, - r2.rows - ) -end - g.test_selection_simple_arithmetic = function() local api = cluster:server("api-1").net_box @@ -441,7 +484,7 @@ g.test_selection_simple_arithmetic = function() ) end -g1.test_associativity = function() +g.test_associativity = function() local api = cluster:server("api-1").net_box local res_all, err = api:call("sbroad.execute", { [[select "id" from "arithmetic_space"]], {} }) @@ -492,7 +535,7 @@ g1.test_associativity = function() t.assert_not_equals(res.rows, res_all.rows) end -g1.test_commutativity = function() +g.test_commutativity = function() local api = cluster:server("api-1").net_box local res_all, err = api:call("sbroad.execute", { [[select "id" from "arithmetic_space"]], {} }) @@ -534,7 +577,7 @@ g1.test_commutativity = function() t.assert_equals(res.rows, {}) end -g1.test_distributivity = function() +g.test_distributivity = function() local api = cluster:server("api-1").net_box local res_all, err = api:call("sbroad.execute", { [[select "id" from "arithmetic_space"]], {} }) @@ -572,4 +615,387 @@ g1.test_distributivity = function() ]], {} }) t.assert_equals(err, nil) t.assert_not_equals(res.rows, res_all.rows) +end + +g1.test_join_simple_arithmetic = function() + local api = cluster:server("api-1").net_box + + local r, err = api:call("sbroad.execute", + { [[ + SELECT "t3"."id", "t3"."a", "t8"."b" + FROM + (SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" < 0 + UNION ALL + SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" > 0) AS "t3" + INNER JOIN + (SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" < 0 + UNION ALL + SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" > 0) AS "t8" + ON "t3"."id" + "t3"."a" * 2 = "t8"."id1" + "t8"."b" + WHERE "t3"."id" = 2]], { } }) + + t.assert_equals(err, nil) + t.assert_equals( + r.metadata, + { + {name = "t3.id", type = "integer"}, + {name = "t3.a", type = "integer"}, + {name = "t8.b", type = "integer"}, + } + ) + t.assert_equals( + r.rows, + { { 2, 2, 3 } } + ) + + -- check the same query with params + local r2, err = api:call("sbroad.execute", + { [[ + SELECT "t3"."id", "t3"."a", "t8"."b" + FROM + (SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" < ? + UNION ALL + SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" > ?) AS "t3" + INNER JOIN + (SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" < ? + UNION ALL + SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" > ?) AS "t8" + ON "t3"."id" + "t3"."a" * ? = "t8"."id1" + "t8"."b" + WHERE "t3"."id" = ?]], { 0, 0, 0, 0, 2, 2} }) + + t.assert_equals(err, nil) + t.assert_equals( + r.metadata, + { + {name = "t3.id", type = "integer"}, + {name = "t3.a", type = "integer"}, + {name = "t8.b", type = "integer"}, + } + ) + t.assert_equals( + r.rows, + r2.rows + ) +end + +g1.test_projection_selection_join = function() + local api = cluster:server("api-1").net_box + + local r, err = api:call("sbroad.execute", + { [[ + SELECT "t3"."id", "t3"."a", "t8"."b", "t3"."id" + "t3"."a" + "t8"."b" as "sum" + FROM + (SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" < 0 + UNION ALL + SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" > 0) AS "t3" + INNER JOIN + (SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" < 0 + UNION ALL + SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" > 0) AS "t8" + ON "t3"."id" + "t3"."a" * 2 = "t8"."id1" + "t8"."b" + ]], { } }) + + t.assert_equals(err, nil) + t.assert_equals( + r.metadata, + { + {name = "t3.id", type = "integer"}, + {name = "t3.a", type = "integer"}, + {name = "t8.b", type = "integer"}, + {name = "sum", type = "integer"}, + + } + ) + for i=1,table.getn(r.rows) do + t.assert_equals( + r.rows[i][1] + r.rows[i][2] + r.rows[i][3], + r.rows[i][4] + ) + end + + local r, err = api:call("sbroad.execute", + { [[ + SELECT "t3"."id", "t3"."a", "t8"."b", "t3"."id" * "t3"."a" * "t8"."b" + 1 as "mul" + FROM + (SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" < 0 + UNION ALL + SELECT "id", "a" + FROM "arithmetic_space" + WHERE "c" > 0) AS "t3" + INNER JOIN + (SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" < 0 + UNION ALL + SELECT "id" as "id1", "b" + FROM "arithmetic_space2" + WHERE "b" > 0) AS "t8" + ON "t3"."id" + "t3"."a" * 2 = "t8"."id1" + "t8"."b" + ]], { } }) + + t.assert_equals(err, nil) + t.assert_equals( + r.metadata, + { + {name = "t3.id", type = "integer"}, + {name = "t3.a", type = "integer"}, + {name = "t8.b", type = "integer"}, + {name = "mul", type = "integer"}, + + } + ) + for i=1,table.getn(r.rows) do + t.assert_equals( + r.rows[i][1] * r.rows[i][2] * r.rows[i][3] + 1, + r.rows[i][4] + ) + end +end + +g2.test_alias = function() + local api = cluster:server("api-1").net_box + + local res, err = api:call("sbroad.execute", { + [[select "id", "id" + "a", "id" * "a" , "a" from "arithmetic_space"]], {} + }) + t.assert_equals(err, nil) + t.assert_equals(res.metadata, { + {name = "id", type = "integer"}, + {name = "COLUMN_1", type = "integer"}, + {name = "COLUMN_2", type = "integer"}, + {name = "a", type = "integer"}, + }) + + local res, err = api:call("sbroad.execute", { [[ + select "id", "id" + "a" as "sum", "id" * "a" as "mul", "a" from "arithmetic_space" + ]], {}}) + t.assert_equals(err, nil) + t.assert_equals(res.metadata, { + {name = "id", type = "integer"}, + {name = "sum", type = "integer"}, + {name = "mul", type = "integer"}, + {name = "a", type = "integer"}, + }) +end + +g2.test_associativity = function() + local api = cluster:server("api-1").net_box + + -- addition is associative + local res_add1, err = api:call("sbroad.execute", { [[ + select "id", "a" + ("b" + "c") from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_add1.rows, {}) + + local res_add2, err = api:call("sbroad.execute", { [[ + select "id", ("a" + "b") + "c" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_add2.rows, res_add1.rows) + + -- multiplication is associative + local res_mul1, err = api:call("sbroad.execute", { [[ + select "id", "a" * ("b" * "c") from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_mul1.rows, {}) + + local res_mul2, err = api:call("sbroad.execute", { [[ + select "id", ("a" * "b") * "c" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_mul2.rows, res_mul1.rows) + + -- subtraction is left-associative + local res_sub, err = api:call("sbroad.execute", { [[ + select "id", "a" - "b" - "c" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_sub.rows, {}) + + local res_sub1, err = api:call("sbroad.execute", { [[ + select "id", ("a" - "b") - "c" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_sub1.rows, res_sub.rows) + + local res_sub2, err = api:call("sbroad.execute", { [[ + select "id", "a" - ("b" - "c" ) from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_sub2.rows, res_sub.rows) + + -- division is left-associative + local res_div, err = api:call("sbroad.execute", { [[ + select "id", + cast("a" as decimal) / cast("b" as decimal) / cast("c" as decimal) from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div.rows, {}) + + local res_div1, err = api:call("sbroad.execute", { [[ + select "id", (cast("a" as decimal) / cast("b" as decimal)) / cast("c" as decimal) from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_div1.rows, res_div.rows) + + local res_div2, err = api:call("sbroad.execute", { [[ + select "id", cast("a" as decimal) / (cast("b" as decimal) / cast("c" as decimal)) from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div2.rows, res_div.rows) +end + +g2.test_commutativity = function() + local api = cluster:server("api-1").net_box + + -- addition is commutative + local res_add1, err = api:call("sbroad.execute", { [[ + select "id", "a" + "b" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_add1.rows, {}) + + local res_add2, err = api:call("sbroad.execute", { [[ + select "id", "b" + "a" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_add2.rows, res_add1.rows) + + -- multiplication is commutative + local res_mul1, err = api:call("sbroad.execute", { [[ + select "id", "a" * "b" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_mul1.rows, {}) + + local res_mul2, err = api:call("sbroad.execute", { [[ + select "id", "b" * "a" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_equals(res_mul2.rows, res_mul1.rows) + + -- subtraction is not commutative + -- and x [-|/] y = y [-|/] x is true only when + -- x, y have specific condition + local res_sub1, err = api:call("sbroad.execute", { [[ + select "id", "a" - "b" from "arithmetic_space" where "a" != "b" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_sub1.rows, {}) + + local res_sub2, err = api:call("sbroad.execute", { [[ + select "id", "b" - "a" from "arithmetic_space" where "a" != "b" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_sub2.rows, {}) + t.assert_not_equals(res_sub2.rows, res_sub1.rows) + + -- division is not commutative + -- and x [-|/] y = y [-|/] x is true only when + -- x, y have specific condition + local res_div1, err = api:call("sbroad.execute", { [[ + select "id", cast("b" as decimal) / cast("a" as decimal) from "arithmetic_space" + where "a" != "b" or "a" != -1 * "b" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div1.rows, {}) + + local res_div2, err = api:call("sbroad.execute", { [[ + select "id", cast("a" as decimal) / cast("b" as decimal) from "arithmetic_space" + where "a" != "b" or "a" != -1 * "b" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div2.rows, {}) + t.assert_not_equals(res_div2.rows, res_div1.rows) +end + +g2.test_distributivity = function() + local api = cluster:server("api-1").net_box + + -- multiplication if left- and right-distributive over addition|subtraction + local res_mul, err = api:call("sbroad.execute", { [[ + select "id", "a" * "b" + "a" * "c" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_mul.rows, {}) + + local res_mul1, err = api:call("sbroad.execute", { [[ + select "id", "a" * ("b" + "c") from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_mul.rows, {}) + t.assert_equals(res_mul.rows, res_mul1.rows) + + local res_mul2, err = api:call("sbroad.execute", { [[ + select "id", ("b" + "c") * "a" from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_mul2.rows, {}) + t.assert_equals(res_mul.rows, res_mul2.rows) + + -- division is right-distributive over addition|subtraction + local res_div, err = api:call("sbroad.execute", { [[ + select + "id", + cast("a" as decimal) / cast("c" as decimal) + cast("b" as decimal) / cast("c" as decimal) + from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div.rows, {}) + + local res_div1, err = api:call("sbroad.execute", { [[ + select + "id", + (cast("a" as decimal) + cast("b" as decimal)) / cast("c" as decimal) + from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div1.rows, {}) + t.assert_equals(res_div.rows, res_div1.rows) + + local res_div, err = api:call("sbroad.execute", { [[ + select + "id", + cast("a" as decimal) / cast("b" as decimal) + cast("a" as decimal) / cast("c" as decimal) + from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div.rows, {}) + + local res_div2, err = api:call("sbroad.execute", { [[ + select + "id", + cast("a" as decimal) / (cast("b" as decimal) + cast("c" as decimal)) + from "arithmetic_space" + ]], {} }) + t.assert_equals(err, nil) + t.assert_not_equals(res_div.rows, res_div2.rows) end \ No newline at end of file diff --git a/sbroad-core/src/backend/sql/tree/tests.rs b/sbroad-core/src/backend/sql/tree/tests.rs index 7be7d9ca16..44588c37ad 100644 --- a/sbroad-core/src/backend/sql/tree/tests.rs +++ b/sbroad-core/src/backend/sql/tree/tests.rs @@ -92,7 +92,7 @@ fn sql_order_selection() { #[test] #[allow(clippy::too_many_lines)] -fn sql_arithmetic_plan() { +fn sql_arithmetic_selection_plan() { // select a from t where a + (b/c + d*e) * f - b = 1 let mut plan = Plan::default(); let t = Table::new_seg( @@ -163,7 +163,7 @@ fn sql_arithmetic_plan() { .join("backend") .join("sql") .join("tree") - .join("arithmetic_plan.yaml"); + .join("arithmetic_selection_plan.yaml"); let s = fs::read_to_string(path).unwrap(); let expected_plan = Plan::from_yaml(&s).unwrap(); assert_eq!(expected_plan, plan); @@ -248,7 +248,7 @@ fn sql_arithmetic_plan() { assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); // ) assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); - // arithmetic expression multiply: [(b/c + d*e)] * f + // arithmetic expression multiply: [(b/c + d*e)] * [f] assert_eq!(Some(&SyntaxData::PlanId(33)), nodes_iter.next()); // arithmetic operator multiply (*) assert_eq!(Some(&SyntaxData::Operator("*".into())), nodes_iter.next()); @@ -286,3 +286,176 @@ fn sql_arithmetic_plan() { assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); assert_eq!(None, nodes_iter.next()); } + +#[test] +fn sql_arithmetic_projection_plan() { + // select a + (b/c + d*e) * f - b from t + let mut plan = Plan::default(); + let t = Table::new_seg( + "t", + vec![ + Column::new("a", Type::Integer, ColumnRole::User), + Column::new("b", Type::Integer, ColumnRole::User), + Column::new("c", Type::Integer, ColumnRole::User), + Column::new("d", Type::Integer, ColumnRole::User), + Column::new("e", Type::Integer, ColumnRole::User), + Column::new("f", Type::Integer, ColumnRole::User), + Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + ], + &["a"], + SpaceEngine::Memtx, + ) + .unwrap(); + plan.add_rel(t); + let scan_id = plan.add_scan("t", None).unwrap(); + let a_id = plan.add_row_from_child(scan_id, &["a"]).unwrap(); + let b_id = plan.add_row_from_child(scan_id, &["b"]).unwrap(); + let c_id = plan.add_row_from_child(scan_id, &["c"]).unwrap(); + let d_id = plan.add_row_from_child(scan_id, &["d"]).unwrap(); + let e_id = plan.add_row_from_child(scan_id, &["e"]).unwrap(); + let f_id = plan.add_row_from_child(scan_id, &["f"]).unwrap(); + + // b/c + let arith_divide_id = plan + .add_arithmetic_to_plan(b_id, Arithmetic::Divide, c_id, false) + .unwrap(); + // d*e + let arith_multiply_id = plan + .add_arithmetic_to_plan(d_id, Arithmetic::Multiply, e_id, false) + .unwrap(); + // (b/c + d*e) + let arith_addition_id = plan + .add_arithmetic_to_plan(arith_divide_id, Arithmetic::Add, arith_multiply_id, true) + .unwrap(); + // (b/c + d*e) * f + let arith_multiply_id2 = plan + .add_arithmetic_to_plan(arith_addition_id, Arithmetic::Multiply, f_id, false) + .unwrap(); + // a + (b/c + d*e) * f + let arith_addition_id2 = plan + .add_arithmetic_to_plan(a_id, Arithmetic::Add, arith_multiply_id2, false) + .unwrap(); + // a + (b/c + d*e) * f - b + let arith_subract_id = plan + .add_arithmetic_to_plan(arith_addition_id2, Arithmetic::Subtract, b_id, false) + .unwrap(); + + let proj_id = plan + .add_proj_internal(scan_id, &[arith_subract_id]) + .unwrap(); + plan.set_top(proj_id).unwrap(); + + // check the plan + let path = Path::new("") + .join("tests") + .join("artifactory") + .join("backend") + .join("sql") + .join("tree") + .join("arithmetic_projection_plan.yaml"); + let s = fs::read_to_string(path).unwrap(); + let expected_plan = Plan::from_yaml(&s).unwrap(); + assert_eq!(expected_plan, plan); + + let exec_plan = ExecutionPlan::from(plan.clone()); + let top_id = exec_plan.get_ir_plan().get_top().unwrap(); + + // get nodes in the sql-convenient order + let sp = SyntaxPlan::new(&exec_plan, top_id, Snapshot::Latest).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let mut nodes_iter = nodes.into_iter(); + + // projection + assert_eq!(Some(&SyntaxData::PlanId(35)), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(17)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref a + assert_eq!(Some(&SyntaxData::PlanId(16)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic expression: [a] + [(b/c + d*e)] + assert_eq!(Some(&SyntaxData::PlanId(32)), nodes_iter.next()); + // arithmetic operator add (+) + assert_eq!(Some(&SyntaxData::Operator("+".into())), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // arithmetic expression add: ([b/c] + [d*e]) + assert_eq!(Some(&SyntaxData::PlanId(30)), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(19)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref b + assert_eq!(Some(&SyntaxData::PlanId(18)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic expression divide: [b] / [c] + assert_eq!(Some(&SyntaxData::PlanId(28)), nodes_iter.next()); + // arithmetic operator divide (/) + assert_eq!(Some(&SyntaxData::Operator("/".into())), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(21)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref c + assert_eq!(Some(&SyntaxData::PlanId(20)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic operator add (+) + assert_eq!(Some(&SyntaxData::Operator("+".into())), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(23)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref d + assert_eq!(Some(&SyntaxData::PlanId(22)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic expression multiply: [d] * [e] + assert_eq!(Some(&SyntaxData::PlanId(29)), nodes_iter.next()); + // arithmetic operator multiply (*) + assert_eq!(Some(&SyntaxData::Operator("*".into())), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(25)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref e + assert_eq!(Some(&SyntaxData::PlanId(24)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic expression multiply: [(b/c + d*e)] * f + assert_eq!(Some(&SyntaxData::PlanId(31)), nodes_iter.next()); + // arithmetic operator multiply (*) + assert_eq!(Some(&SyntaxData::Operator("*".into())), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(27)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // f + assert_eq!(Some(&SyntaxData::PlanId(26)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // arithmetic expression subtract: [a + (b/c + d*e) * f] - [b] + assert_eq!(Some(&SyntaxData::PlanId(33)), nodes_iter.next()); + // arithmetic operator subtract (-) + assert_eq!(Some(&SyntaxData::Operator("-".into())), nodes_iter.next()); + // row + assert_eq!(Some(&SyntaxData::PlanId(19)), nodes_iter.next()); + // ( + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); + // ref b + assert_eq!(Some(&SyntaxData::PlanId(18)), nodes_iter.next()); + // ) + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); + // from + assert_eq!(Some(&SyntaxData::From), nodes_iter.next()); + // scan + assert_eq!(Some(&SyntaxData::PlanId(15)), nodes_iter.next()); + + assert_eq!(None, nodes_iter.next()); +} diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 3e20382b69..f94346189e 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -131,6 +131,81 @@ impl Ast for AbstractSyntaxTree { let mut betweens: Vec<Between> = Vec::new(); let mut arithmetic_expression_ids: Vec<usize> = Vec::new(); + let get_arithmetic_plan_id = |plan: &mut Plan, + map: &Translation, + arithmetic_expression_ids: &mut Vec<usize>, + rows: &mut HashSet<usize>, + ast_id: usize| { + let plan_id; + // if child of current multiplication or addition is `(expr)` then + // we need to get expr that is child of `()` and add it to the plan + // also we will mark this expr to add in the future `()` + let arithmetic_parse_node = self.nodes.get_node(ast_id)?; + if arithmetic_parse_node.rule == Type::ArithParentheses { + let arithmetic_id = arithmetic_parse_node.children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues( + "ArithParentheses has no children.".into(), + ) + })?; + plan_id = plan.as_row(map.get(*arithmetic_id)?, rows)?; + arithmetic_expression_ids.push(plan_id); + } else { + plan_id = plan.as_row(map.get(ast_id)?, rows)?; + } + + Ok(plan_id) + }; + + let get_arithmetic_cond_id = + |plan: &mut Plan, + current_node: &ParseNode, + map: &Translation, + arithmetic_expression_ids: &mut Vec<usize>, + rows: &mut HashSet<usize>| { + let ast_left_id = current_node.children.first().ok_or_else(|| { + SbroadError::UnexpectedNumberOfValues( + "Multiplication or Addition has no children.".into(), + ) + })?; + let plan_left_id = get_arithmetic_plan_id( + plan, + map, + arithmetic_expression_ids, + rows, + *ast_left_id, + )?; + + let ast_right_id = current_node.children.get(2).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + "that is right node with index 2 among Multiplication or Addition children" + .into(), + ) + })?; + let plan_right_id = get_arithmetic_plan_id( + plan, + map, + arithmetic_expression_ids, + rows, + *ast_right_id, + )?; + + let ast_op_id = current_node.children.get(1).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + "that is center node (operator) with index 1 among Multiplication or Addition children" + .into(), + ) + })?; + + let op_node = self.nodes.get_node(*ast_op_id)?; + let op = Arithmetic::from_node_type(&op_node.rule)?; + + let cond_id = + plan.add_arithmetic_to_plan(plan_left_id, op, plan_right_id, false)?; + Ok(cond_id) + }; + for (_, id) in dft_post.iter(top) { let node = self.nodes.get_node(id)?; match &node.rule { @@ -722,11 +797,21 @@ impl Ast for AbstractSyntaxTree { )); } } + Type::Multiplication | Type::Addition => { + let cond_id = get_arithmetic_cond_id( + &mut plan, + ast_column, + &map, + &mut arithmetic_expression_ids, + &mut rows, + )?; + columns.push(cond_id); + } _ => { return Err(SbroadError::Invalid( Entity::Type, Some(format!( - "expected a Column in projection, got {:?}.", + "expected a Column, Asterisk, Multiplication or Addition in projection, got {:?}.", ast_column.rule )), )); @@ -737,65 +822,13 @@ impl Ast for AbstractSyntaxTree { map.add(id, projection_id); } Type::Multiplication | Type::Addition => { - let plan_left_id: usize; - let plan_right_id: usize; - - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "Multiplication or Addition has no children.".into(), - ) - })?; - - // if left child of current multiplication or addition is `(expr)` then - // we need to get expr that is child of `()` and add it to the plan - // also we will mark this expr to add in the future `()` - let ar_left = self.nodes.get_node(*ast_left_id)?; - if ar_left.rule == Type::ArithParentheses { - let arithmetic_id = ar_left.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "ArithParentheses has no children.".into(), - ) - })?; - plan_left_id = plan.as_row(map.get(*arithmetic_id)?, &mut rows)?; - arithmetic_expression_ids.push(plan_left_id); - } else { - plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; - } - - let ast_right_id = node.children.get(2).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is right node with index 2 among Multiplication or Addition children".into(), - ) - })?; - - // if left child of current multiplication or addition is `(expr)` then - // we need to get expr that is child of `()` and add it to the plan - // also we will mark this expr to add in the future `()` - let ar_right = self.nodes.get_node(*ast_right_id)?; - if ar_right.rule == Type::ArithParentheses { - let arithmetic_id = ar_right.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "ArithParentheses has no children.".into(), - ) - })?; - plan_right_id = plan.as_row(map.get(*arithmetic_id)?, &mut rows)?; - arithmetic_expression_ids.push(plan_right_id); - } else { - plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; - } - - let ast_op_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is center node (operator) with index 1 among Multiplication or Addition children".into(), - ) - })?; - let op_node = self.nodes.get_node(*ast_op_id)?; - - let op = Arithmetic::from_node_type(&op_node.rule)?; - let cond_id = - plan.add_arithmetic_to_plan(plan_left_id, op, plan_right_id, false)?; + let cond_id = get_arithmetic_cond_id( + &mut plan, + node, + &map, + &mut arithmetic_expression_ids, + &mut rows, + )?; map.add(id, cond_id); } Type::Except => { diff --git a/sbroad-core/src/frontend/sql/ast/tests.rs b/sbroad-core/src/frontend/sql/ast/tests.rs index 5232d0d27c..f60ace70c3 100644 --- a/sbroad-core/src/frontend/sql/ast/tests.rs +++ b/sbroad-core/src/frontend/sql/ast/tests.rs @@ -154,12 +154,12 @@ fn invalid_query() { let ast = AbstractSyntaxTree::new(query).unwrap_err(); assert_eq!( format!( - r#"rule parsing error: --> 1:8 + r#"rule parsing error: --> 1:10 | 1 | select a frAm t - | ^--- + | ^--- | - = expected Alias, Asterisk, Function, Cast, Concat, True, False, Null, Decimal, Double, Integer, Unsigned, Row, or Parameter"#, + = expected Multiply, Divide, Add, or Subtract"#, ), format!("{ast}"), ); @@ -185,7 +185,7 @@ fn invalid_condition() { #[test] #[allow(clippy::similar_names)] -fn sql_arithmetic_ast() { +fn sql_arithmetic_selection_ast() { let ast = AbstractSyntaxTree::new("select a from t where a + b = 1").unwrap(); let path = Path::new("") @@ -193,7 +193,7 @@ fn sql_arithmetic_ast() { .join("artifactory") .join("frontend") .join("sql") - .join("arithmetic_ast.yaml"); + .join("arithmetic_selection_ast.yaml"); let s = fs::read_to_string(path).unwrap(); let expected_ast = AbstractSyntaxTree::from_yaml(&s).unwrap(); @@ -287,3 +287,74 @@ fn sql_arithmetic_ast() { assert_eq!(None, iter.next()); } + +#[test] +fn sql_arithmetic_projection_ast() { + let ast = AbstractSyntaxTree::new("select a + b from t").unwrap(); + + let path = Path::new("") + .join("tests") + .join("artifactory") + .join("frontend") + .join("sql") + .join("arithmetic_projection_ast.yaml"); + + let s = fs::read_to_string(path).unwrap(); + let expected_ast = AbstractSyntaxTree::from_yaml(&s).unwrap(); + + assert_eq!(expected_ast, ast); + + // note there is no AliasName or Alias type + let top = ast.top.unwrap(); + let mut dft_post = PostOrder::with_capacity(|node| ast.nodes.ast_iter(node), 64); + let mut iter = dft_post.iter(top); + + let (_, table_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(table_id).unwrap(); + assert_eq!(node.rule, Type::Table); + assert_eq!(node.value, Some("t".to_string())); + + let (_, scan_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(scan_id).unwrap(); + assert_eq!(node.rule, Type::Scan); + + let (_, sel_name_a_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(sel_name_a_id).unwrap(); + assert_eq!(node.rule, Type::ColumnName); + assert_eq!(node.value, Some("a".to_string())); + + let (_, a_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(a_id).unwrap(); + assert_eq!(node.rule, Type::Reference); + + let (_, col_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(col_id).unwrap(); + assert_eq!(node.rule, Type::Column); + + let (_, add_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(add_id).unwrap(); + assert_eq!(node.rule, Type::Add); + + let (_, sel_name_b_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(sel_name_b_id).unwrap(); + assert_eq!(node.rule, Type::ColumnName); + assert_eq!(node.value, Some("b".to_string())); + + let (_, b_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(b_id).unwrap(); + assert_eq!(node.rule, Type::Reference); + + let (_, col_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(col_id).unwrap(); + assert_eq!(node.rule, Type::Column); + + let (_, addition_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(addition_id).unwrap(); + assert_eq!(node.rule, Type::Addition); + + let (_, proj_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(proj_id).unwrap(); + assert_eq!(node.rule, Type::Projection); + + assert_eq!(None, iter.next()); +} diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index ba766cd92d..85e6b8d892 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -9,7 +9,7 @@ Query = _{ Except | UnionAll | Select | Values | Insert } (((^"inner" ~ ^"join") | ^"join") ~ InnerJoin ~ ^"on" ~ Condition)? ~ (^"where" ~ Selection)? } - Projection = { (Asterisk | Column) ~ ("," ~ (Asterisk | Column))*? } + Projection = { (Asterisk | ArithmeticExpr | Column) ~ ("," ~ (Asterisk | ArithmeticExpr | Column))*? } Column = { Alias | Value } Alias = {Value ~ ^"as" ~ AliasName } AliasName = @{ Name } diff --git a/sbroad-core/src/ir/expression.rs b/sbroad-core/src/ir/expression.rs index b34d185d4a..976e1bf70e 100644 --- a/sbroad-core/src/ir/expression.rs +++ b/sbroad-core/src/ir/expression.rs @@ -400,25 +400,29 @@ impl Nodes { ) -> Result<usize, SbroadError> { let mut names: HashSet<String> = HashSet::with_capacity(list.len()); - for alias_node in &list { - if let Node::Expression(Expression::Alias { name, .. }) = - self.arena.get(*alias_node).ok_or_else(|| { - SbroadError::NotFound( + for node_id in &list { + let node = self.arena.get(*node_id).ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + format!("(Alias or Arithmetic) from arena with index {node_id}"), + ) + })?; + + match node { + Node::Expression(Expression::Alias { name, .. }) => { + if !names.insert(String::from(name)) { + return Err(SbroadError::DuplicatedValue(format!( + "row can't be added because `{name}` already has an alias", + ))); + } + } + Node::Expression(Expression::Arithmetic { .. }) => {} + _ => { + return Err(SbroadError::Invalid( Entity::Node, - format!("(Alias) from arena with index {alias_node}"), - ) - })? - { - if !names.insert(String::from(name)) { - return Err(SbroadError::DuplicatedValue(format!( - "row can't be added because `{name}` already has an alias" - ))); + Some("node is not Alias or Arithmetic type".into()), + )); } - } else { - return Err(SbroadError::Invalid( - Entity::Node, - Some("node is not Alias type".into()), - )); } } Ok(self.add_row(list, distribution)) diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml new file mode 100644 index 0000000000..396ce07aaf --- /dev/null +++ b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml @@ -0,0 +1,259 @@ +nodes: + arena: + # 0 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 0 + # 1 + - Expression: + Alias: + name: a + child: 0 + # 2 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 1 + # 3 + - Expression: + Alias: + name: b + child: 2 + # 4 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 2 + # 5 + - Expression: + Alias: + name: c + child: 4 + # 6 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 3 + # 7 + - Expression: + Alias: + name: d + child: 6 + # 8 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 4 + # 9 + - Expression: + Alias: + name: e + child: 8 + # 10 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 5 + # 11 + - Expression: + Alias: + name: f + child: 10 + # 12 + - Expression: + Reference: + parent: 15 + targets: ~ + position: 6 + # 13 + - Expression: + Alias: + name: bucket_id + child: 12 + # 14 + - Expression: + Row: + list: + - 1 + - 3 + - 5 + - 7 + - 9 + - 11 + - 13 + distribution: ~ + # 15 + - Relational: + ScanRelation: + output: 14 + relation: t + # 16 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 0 + # 17 + - Expression: + Row: + list: + - 16 + distribution: ~ + # 18 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 1 + # 19 + - Expression: + Row: + list: + - 18 + distribution: ~ + # 20 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 2 + # 21 + - Expression: + Row: + list: + - 20 + distribution: ~ + # 22 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 3 + # 23 + - Expression: + Row: + list: + - 22 + distribution: ~ + # 24 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 4 + # 25 + - Expression: + Row: + list: + - 24 + distribution: ~ + # 26 + - Expression: + Reference: + parent: 35 + targets: [0] + position: 5 + # 27 + - Expression: + Row: + list: + - 26 + distribution: ~ + # 28 + - Expression: + Arithmetic: + left: 19 + op: divide + right: 21 + with_parentheses: false + # 29 + - Expression: + Arithmetic: + left: 23 + op: multiply + right: 25 + with_parentheses: false + # 30 + - Expression: + Arithmetic: + left: 28 + op: add + right: 29 + with_parentheses: true + # 31 + - Expression: + Arithmetic: + left: 30 + op: multiply + right: 27 + with_parentheses: false + # 32 + - Expression: + Arithmetic: + left: 17 + op: add + right: 31 + with_parentheses: false + # 33 + - Expression: + Arithmetic: + left: 32 + op: subtract + right: 19 + with_parentheses: false + # 34 + - Expression: + Row: + list: + - 33 + distribution: ~ + # 35 + - Relational: + Projection: + children: + - 15 + output: 34 +relations: + tables: + t: + columns: + - name: a + type: Integer + role: User + - name: b + type: Integer + role: User + - name: c + type: Integer + role: User + - name: d + type: Integer + role: User + - name: e + type: Integer + role: User + - name: f + type: Integer + role: User + - name: bucket_id + type: Unsigned + role: Sharding + key: + positions: + - 0 + name: t + engine: Memtx +slices: + slices: [] +top: 35 +is_explain: false +undo: + log: {} +constants: {} diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml similarity index 100% rename from sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml rename to sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml diff --git a/sbroad-core/tests/artifactory/frontend/sql/arithmetic_projection_ast.yaml b/sbroad-core/tests/artifactory/frontend/sql/arithmetic_projection_ast.yaml new file mode 100644 index 0000000000..9e0ea7e5e0 --- /dev/null +++ b/sbroad-core/tests/artifactory/frontend/sql/arithmetic_projection_ast.yaml @@ -0,0 +1,56 @@ +--- +nodes: + arena: + - children: + - 3 + rule: Select + value: ~ + - children: + - 2 + rule: Scan + value: ~ + - children: [] + rule: Table + value: t + - children: + - 1 + - 4 + rule: Projection + value: ~ + - children: + - 9 + - 8 + - 5 + rule: Addition + value: ~ + - children: + - 6 + rule: Column + value: ~ + - children: + - 7 + rule: Reference + value: ~ + - children: [] + rule: ColumnName + value: "b" + - children: [] + rule: Add + value: "+" + - children: + - 10 + rule: Column + value: ~ + - children: + - 11 + rule: Reference + value: ~ + - children: [] + rule: ColumnName + value: "a" +top: 3 +map: + 10: + - 1 + 6: + - 1 \ No newline at end of file diff --git a/sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml b/sbroad-core/tests/artifactory/frontend/sql/arithmetic_selection_ast.yaml similarity index 100% rename from sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml rename to sbroad-core/tests/artifactory/frontend/sql/arithmetic_selection_ast.yaml -- GitLab