From 300aa272177fe8a8e344b54511c72d74e37603a5 Mon Sep 17 00:00:00 2001
From: "ms.evilhat" <ms.evilhat@gmail.com>
Date: Thu, 2 Feb 2023 09:21:11 +0300
Subject: [PATCH] test: add unit tests for arithmetic expessions

---
 sbroad-core/src/backend/sql/tree/tests.rs     | 198 ++++++++-
 sbroad-core/src/frontend/sql/ast/tests.rs     | 104 +++++
 .../backend/sql/tree/arithmetic_plan.yaml     | 383 ++++++++++++++++++
 .../frontend/sql/arithmetic_ast.yaml          |  90 ++++
 4 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100644 sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml
 create mode 100644 sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml

diff --git a/sbroad-core/src/backend/sql/tree/tests.rs b/sbroad-core/src/backend/sql/tree/tests.rs
index fc23af479b..49be7fcf1d 100644
--- a/sbroad-core/src/backend/sql/tree/tests.rs
+++ b/sbroad-core/src/backend/sql/tree/tests.rs
@@ -4,7 +4,7 @@ use std::path::Path;
 use pretty_assertions::assert_eq;
 
 use crate::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan};
-use crate::ir::operator::Bool;
+use crate::ir::operator::{Arithmetic, Bool};
 use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type};
 use crate::ir::tree::Snapshot;
 use crate::ir::value::Value;
@@ -89,3 +89,199 @@ fn sql_order_selection() {
     assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); // )
     assert_eq!(None, nodes_iter.next());
 }
+
+#[test]
+fn sql_arithmetic_plan() {
+    // select a from t where a + (b/c + d*e) * f - b = 1
+    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();
+    let const_1 = plan.nodes.add_const(Value::from(1_u64));
+    let const_row = plan.nodes.add_row(vec![const_1], None);
+
+    // 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();
+    // a + (b/c + d*e) * f - b = 1
+    let eq_id = plan
+        .nodes
+        .add_bool(arith_subract_id, Bool::Eq, const_row)
+        .unwrap();
+    // where a + (b/c + d*e) * f - b = 1
+    let select_id = plan.add_select(&[scan_id], eq_id).unwrap();
+
+    let proj_id = plan.add_proj(select_id, &["a"]).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_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(56)), nodes_iter.next());
+    // alias
+    assert_eq!(Some(&SyntaxData::PlanId(54)), nodes_iter.next());
+    // ref
+    assert_eq!(Some(&SyntaxData::PlanId(53)), nodes_iter.next());
+    // from
+    assert_eq!(Some(&SyntaxData::From), nodes_iter.next());
+    // scan
+    assert_eq!(Some(&SyntaxData::PlanId(15)), nodes_iter.next());
+    // selection
+    assert_eq!(Some(&SyntaxData::PlanId(52)), 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(34)), 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(32)), 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(30)), 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(31)), 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(33)), 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());
+    // ref 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(35)), 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());
+    // bool expression eq: [a + (b/c + d*e) * f - b] = [1]
+    assert_eq!(Some(&SyntaxData::PlanId(36)), nodes_iter.next());
+    // bool operator eq (=)
+    assert_eq!(Some(&SyntaxData::Operator("=".into())), nodes_iter.next());
+    // row
+    assert_eq!(Some(&SyntaxData::PlanId(29)), nodes_iter.next());
+    // (
+    assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next());
+    // parameter
+    assert_eq!(Some(&SyntaxData::Parameter(28)), nodes_iter.next());
+    // )
+    assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next());
+    assert_eq!(None, nodes_iter.next());
+}
diff --git a/sbroad-core/src/frontend/sql/ast/tests.rs b/sbroad-core/src/frontend/sql/ast/tests.rs
index 2d46560c64..c1c2e1663a 100644
--- a/sbroad-core/src/frontend/sql/ast/tests.rs
+++ b/sbroad-core/src/frontend/sql/ast/tests.rs
@@ -182,3 +182,107 @@ fn invalid_condition() {
         format!("{ast}"),
     )
 }
+
+#[test]
+fn sql_arithmetic_ast() {
+    let ast = AbstractSyntaxTree::new("select a from t where a + b = 1").unwrap();
+
+    let path = Path::new("")
+        .join("tests")
+        .join("artifactory")
+        .join("frontend")
+        .join("sql")
+        .join("arithmetic_ast.yaml");
+
+    let s = fs::read_to_string(path).unwrap();
+    let expected_ast = AbstractSyntaxTree::from_yaml(&s).unwrap();
+
+    assert_eq!(expected_ast, ast);
+
+    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 (_, num_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(num_id).unwrap();
+    assert_eq!(node.rule, Type::Unsigned);
+    assert_eq!(node.value, Some("1".to_string()));
+
+    let (_, eq_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(eq_id).unwrap();
+    assert_eq!(node.rule, Type::Eq);
+
+    let (_, selection_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(selection_id).unwrap();
+    assert_eq!(node.rule, Type::Selection);
+
+    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 (_, str_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(str_id).unwrap();
+    assert_eq!(node.rule, Type::Reference);
+
+    let (_, alias_name_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(alias_name_id).unwrap();
+    assert_eq!(node.rule, Type::AliasName);
+
+    let (_, alias_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(alias_id).unwrap();
+    assert_eq!(node.rule, Type::Alias);
+
+    let (_, col_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(col_id).unwrap();
+    assert_eq!(node.rule, Type::Column);
+
+    let (_, projection_id) = iter.next().unwrap();
+    let node = ast.nodes.get_node(projection_id).unwrap();
+    assert_eq!(node.rule, Type::Projection);
+
+    assert_eq!(None, iter.next());
+}
diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml
new file mode 100644
index 0000000000..111fee0449
--- /dev/null
+++ b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_plan.yaml
@@ -0,0 +1,383 @@
+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: 52
+          targets: [0]
+          position: 0
+    # 17
+    - Expression:
+        Row:
+          list:
+            - 16
+          distribution: ~
+    # 18
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 1
+    # 19
+    - Expression:
+        Row:
+          list:
+            - 18
+          distribution: ~
+    # 20
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 2
+    # 21
+    - Expression:
+        Row:
+          list:
+            - 20
+          distribution: ~
+    # 22
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 3
+    # 23
+    - Expression:
+        Row:
+          list:
+            - 22
+          distribution: ~
+    # 24
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 4
+    # 25
+    - Expression:
+        Row:
+          list:
+            - 24
+          distribution: ~
+    # 26
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 5
+    # 27
+    - Expression:
+        Row:
+          list:
+            - 26
+          distribution: ~
+    # 28
+    - Expression:
+        Constant:
+          value:
+            Unsigned: 1
+    # 29
+    - Expression:
+        Row:
+          list:
+            - 28
+          distribution: ~
+    # 30
+    - Expression:
+        Arithmetic:
+          left: 19
+          op: divide
+          right: 21
+          with_parentheses: false
+    # 31
+    - Expression:
+        Arithmetic:
+          left: 23
+          op: multiply
+          right: 25
+          with_parentheses: false
+    # 32
+    - Expression:
+        Arithmetic:
+          left: 30
+          op: add
+          right: 31
+          with_parentheses: true
+    # 33
+    - Expression:
+        Arithmetic:
+          left: 32
+          op: multiply
+          right: 27
+          with_parentheses: false
+    # 34
+    - Expression:
+        Arithmetic:
+          left: 17
+          op: add
+          right: 33
+          with_parentheses: false
+    # 35
+    - Expression:
+        Arithmetic:
+          left: 34
+          op: subtract
+          right: 19
+          with_parentheses: false
+    # 36
+    - Expression:
+        Bool:
+          left: 35
+          op: eq
+          right: 29
+    # 37
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 0
+    # 38
+    - Expression:
+        Alias:
+          name: a
+          child: 37
+    # 39
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 1
+    # 40
+    - Expression:
+        Alias:
+          name: b
+          child: 39
+    # 41
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 2
+    # 42
+    - Expression:
+        Alias:
+          name: c
+          child: 41
+    # 43
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 3
+    # 44
+    - Expression:
+        Alias:
+          name: d
+          child: 43
+    # 45
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 4
+    # 46
+    - Expression:
+        Alias:
+          name: e
+          child: 45
+    # 47
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 5
+    # 48
+    - Expression:
+        Alias:
+          name: f
+          child: 47
+    # 49
+    - Expression:
+        Reference:
+          parent: 52
+          targets: [0]
+          position: 6
+    # 50
+    - Expression:
+        Alias:
+          name: bucket_id
+          child: 49
+    # 51
+    - Expression:
+        Row:
+          list:
+            - 38
+            - 40
+            - 42
+            - 44
+            - 46
+            - 48
+            - 50
+          distribution: ~
+    # 52
+    - Relational:
+        Selection:
+          children:
+            - 15
+          filter: 36
+          output: 51
+    # 53
+    - Expression:
+        Reference:
+          parent: 56
+          targets: [0]
+          position: 0
+    # 54
+    - Expression:
+        Alias:
+          name: a
+          child: 53
+    # 55
+    - Expression:
+        Row:
+          list:
+            - 54
+          distribution: ~
+    # 56
+    - Relational:
+        Projection:
+          children:
+            - 52
+          output: 55
+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: 56
+is_explain: false
+undo:
+  log: {}
+constants: {}
diff --git a/sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml b/sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml
new file mode 100644
index 0000000000..07d0be3fa2
--- /dev/null
+++ b/sbroad-core/tests/artifactory/frontend/sql/arithmetic_ast.yaml
@@ -0,0 +1,90 @@
+---
+nodes:
+  arena:
+    - children:
+        - 14
+      rule: Select
+      value: ~
+    - children:
+        - 12
+        - 2
+      rule: Selection
+      value: ~
+    - children:
+        - 4
+        - 3
+      rule: Eq
+      value: ~
+    - children: []
+      rule: Unsigned
+      value: 1
+    - 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"
+    - children:
+        - 13
+      rule: Scan
+      value: ~
+    - children: []
+      rule: Table
+      value: "t"
+    - children:
+        - 1
+        - 15
+      rule: Projection
+      value: ~
+    - children:
+        - 19
+      rule: Column
+      value: ~
+    - children:
+        - 17
+      rule: Reference
+      value: ~
+    - children: []
+      rule: ColumnName
+      value: "a"
+    - children: []
+      rule: AliasName
+      value: "a"
+    - children:
+        - 16
+        - 18
+      rule: Alias
+      value: ~
+top: 14
+map:
+  16:
+    - 1
+  6:
+    - 12
+  10:
+    - 12
\ No newline at end of file
-- 
GitLab