From c0ca3420bb8f4fbdd503856770715a426ba8d608 Mon Sep 17 00:00:00 2001 From: Denis Smirnov <sd@picodata.io> Date: Tue, 14 Jun 2022 12:50:47 +0700 Subject: [PATCH] feat: separate parameters from the query pattern. Now coordinator dispatches SQL patterns with separated parameters. It would help us to cache patterns on the segments as prepared statements one day and skip SQL parsing. Also we have faced a Tarantool bug https://github.com/tarantool/tarantool/issues/7283 and made a hot fix in the Picodata fork. As a result we have also migrated sbroad CI to our fork. --- .gitlab-ci.yml | 3 +- .gitmodules | 3 +- Makefile | 5 +- ci/Dockerfile | 6 +- deps/tarantool | 2 +- src/executor/engine/cartridge.rs | 34 +- .../engine/cartridge/backend/sql/ir.rs | 47 +- .../engine/cartridge/backend/sql/ir/tests.rs | 131 +++-- .../engine/cartridge/backend/sql/tree.rs | 35 +- .../cartridge/backend/sql/tree/tests.rs | 2 +- src/executor/engine/mock.rs | 4 +- src/executor/result.rs | 2 + src/executor/tests.rs | 536 +++++++++++------- src/frontend/sql/ir/tests.rs | 226 +++++--- src/ir/transformation/bool_in/tests.rs | 42 +- src/ir/transformation/dnf/tests.rs | 102 +++- .../equality_propagation/tests.rs | 80 ++- src/ir/transformation/helpers.rs | 9 +- src/ir/transformation/merge_tuples/tests.rs | 61 +- src/ir/transformation/split_columns/tests.rs | 40 +- src/ir/value.rs | 20 +- src/ir/value/double.rs | 10 + test_app/test/integration/api_test.lua | 8 +- .../test/integration/target_queries_test.lua | 48 +- .../sql_order_selection_syntax_nodes.yaml | 4 +- 25 files changed, 983 insertions(+), 477 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 22fa819627..4f8dd46baf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ cache: default: tags: - picodata - image: registry.gitlab.com/picodata/picodata/sbroad/sbroad-builder:0.1.0 + image: registry.gitlab.com/picodata/picodata/sbroad/sbroad-builder:0.2.0 build: stage: build @@ -36,6 +36,7 @@ test: - git submodule foreach --recursive git clean -xfd - git reset --hard - git submodule foreach --recursive git reset --hard + - git submodule sync --recursive - git submodule update --init --recursive stage: test script: diff --git a/.gitmodules b/.gitmodules index d61d03800d..073cdad4b5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "deps/tarantool"] path = deps/tarantool - url = https://github.com/picodata/tarantool.git + url = https://sbroad-cargo:i8h8BZWfphEKz8hsZBnz@git.picodata.io/picodata/tarantool.git + branch = 2.10.0-picodata \ No newline at end of file diff --git a/Makefile b/Makefile index 6af69b6486..22fdd70781 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ bench_check: MOCK_DEC_NUMBER=1 cargo bench --no-run build_debug: - MOCK_DEC_NUMBER=0 cargo build --debug + MOCK_DEC_NUMBER=0 cargo build build_release: MOCK_DEC_NUMBER=0 cargo build --release @@ -27,12 +27,13 @@ build_test_app: clean: rm -rf target/release/libsbroad.* + rm -rf target/debug/libsbroad.* integration_test_app: cd test_app && rm -rf tmp/tarantool.log && TARANTOOL_LOG_LEVEL=7 TARANTOOL_LOG=tmp/tarantool.log ./.rocks/bin/luatest --coverage -v test/ init: - git submodule update --init --recursive + git submodule update --init --recursive --remote lint: cargo fmt --all -- --check diff --git a/ci/Dockerfile b/ci/Dockerfile index e56129f3e0..15a361050b 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,9 +1,9 @@ -FROM tarantool/tarantool:2.x-centos7 +FROM docker-public.binary.picodata.io/tarantool:2.10.0 ENV PATH=/root/.cargo/bin:${PATH} -RUN curl -L https://tarantool.io/UaooCnt/release/2.8/installer.sh | bash; \ - yum -y update && yum -y install -y wget openssl-devel cartridge-cli readline-devel; \ +RUN curl -L https://tarantool.io/UaooCnt/release/2/installer.sh | bash; \ + yum -y update && yum -y install -y wget openssl-devel cartridge-cli readline-devel libicu-devel; \ yum groupinstall -y "Development Tools"; \ yum clean all; \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile default; \ diff --git a/deps/tarantool b/deps/tarantool index 47e6bd3626..a10f8af6e9 160000 --- a/deps/tarantool +++ b/deps/tarantool @@ -1 +1 @@ -Subproject commit 47e6bd362664e570a6c9d80c63b62b8d3d0bf6f8 +Subproject commit a10f8af6e932389163de9193a9d9f013214d800a diff --git a/src/executor/engine/cartridge.rs b/src/executor/engine/cartridge.rs index ff38be2418..e438ba55b5 100644 --- a/src/executor/engine/cartridge.rs +++ b/src/executor/engine/cartridge.rs @@ -12,6 +12,7 @@ use tarantool::tlua::LuaFunction; use crate::errors::QueryPlannerError; use crate::executor::bucket::Buckets; +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::executor::engine::cartridge::cache::lru::{LRUCache, DEFAULT_CAPACITY}; use crate::executor::engine::cartridge::cache::ClusterAppConfig; use crate::executor::engine::{Engine, LocalMetadata}; @@ -26,7 +27,7 @@ use crate::ir::Plan; use self::hash::bucket_id_by_tuple; -mod backend; +pub mod backend; pub mod cache; pub mod hash; @@ -171,7 +172,7 @@ impl Engine for Runtime { let nodes = plan.get_sql_order(top_id)?; let is_data_modifier = plan.subtree_modifies_data(top_id)?; - let mut rs_query: HashMap<String, String> = HashMap::new(); + let mut rs_query: HashMap<String, PatternWithParams> = HashMap::new(); if let Buckets::Filtered(bucket_set) = buckets { let random_bucket = self.get_random_bucket(); let buckets = if bucket_set.is_empty() { @@ -195,8 +196,9 @@ impl Engine for Runtime { .iter() .copied() .collect::<HashSet<u64, RepeatableState>>(); - let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::new_filtered(bucket_set))?; - rs_query.insert(rs.to_string(), sql); + let pattern_with_params = + plan.syntax_nodes_as_sql(&nodes, &Buckets::new_filtered(bucket_set))?; + rs_query.insert(rs.to_string(), pattern_with_params); } if rs_query.is_empty() { @@ -209,8 +211,8 @@ impl Engine for Runtime { return self.exec_on_some(&rs_query, is_data_modifier); } - let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::All)?; - self.exec_on_all(&sql, is_data_modifier) + let pattern_with_params = plan.syntax_nodes_as_sql(&nodes, &Buckets::All)?; + self.exec_on_all(&pattern_with_params, is_data_modifier) } /// Transform sub query results into a virtual table. @@ -343,7 +345,7 @@ impl Runtime { fn read_on_some( &self, - rs_query: &HashMap<String, String>, + rs_query: &HashMap<String, PatternWithParams>, ) -> Result<Box<dyn Any>, QueryPlannerError> { let lua = tarantool::lua_state(); @@ -369,7 +371,7 @@ impl Runtime { fn write_on_some( &self, - rs_query: &HashMap<String, String>, + rs_query: &HashMap<String, PatternWithParams>, ) -> Result<Box<dyn Any>, QueryPlannerError> { let lua = tarantool::lua_state(); @@ -395,7 +397,7 @@ impl Runtime { fn exec_on_some( &self, - rs_query: &HashMap<String, String>, + rs_query: &HashMap<String, PatternWithParams>, is_data_modifier: bool, ) -> Result<Box<dyn Any>, QueryPlannerError> { if is_data_modifier { @@ -405,7 +407,7 @@ impl Runtime { } } - fn read_on_all(&self, query: &str) -> Result<Box<dyn Any>, QueryPlannerError> { + fn read_on_all(&self, query: &PatternWithParams) -> Result<Box<dyn Any>, QueryPlannerError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua.get("read_on_all").ok_or_else(|| { @@ -428,7 +430,7 @@ impl Runtime { } } - fn write_on_all(&self, query: &str) -> Result<Box<dyn Any>, QueryPlannerError> { + fn write_on_all(&self, query: &PatternWithParams) -> Result<Box<dyn Any>, QueryPlannerError> { let lua = tarantool::lua_state(); let exec_sql: LuaFunction<_> = lua.get("write_on_all").ok_or_else(|| { @@ -453,7 +455,7 @@ impl Runtime { fn exec_on_all( &self, - query: &str, + query: &PatternWithParams, is_data_modifier: bool, ) -> Result<Box<dyn Any>, QueryPlannerError> { if is_data_modifier { @@ -570,7 +572,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> { for rs_uuid, query in pairs(tbl_rs_query) do local replica = vshard.router.routeall()[rs_uuid] - local future, err = replica:callbre("box.execute", { query }, {is_async = true}) + local future, err = replica:callbre("box.execute", { query['pattern'], query['params'] }, {is_async = true}) if err ~= nil then error(err) end @@ -603,7 +605,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> { for rs_uuid, query in pairs(tbl_rs_query) do local replica = vshard.router.routeall()[rs_uuid] - local future, err = replica:callrw("box.execute", { query }, {is_async = true}) + local future, err = replica:callrw("box.execute", { query['pattern'], query['params'] }, {is_async = true}) if err ~= nil then error(err) end @@ -635,7 +637,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> { local futures = {} for _, replica in pairs(replicas) do - local future, err = replica:callbre("box.execute", { query }, {is_async = true}) + local future, err = replica:callbre("box.execute", { query['pattern'], query['params'] }, {is_async = true}) if err ~= nil then error(err) end @@ -669,7 +671,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> { local futures = {} for _, replica in pairs(replicas) do - local future, err = replica:callrw("box.execute", { query }, {is_async = true}) + local future, err = replica:callrw("box.execute", { query['pattern'], query['params'] }, {is_async = true}) if err ~= nil then error(err) end diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs index a8a56fef07..e648761286 100644 --- a/src/executor/engine/cartridge/backend/sql/ir.rs +++ b/src/executor/engine/cartridge/backend/sql/ir.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use tarantool::tlua; use crate::errors::QueryPlannerError; use crate::executor::bucket::Buckets; @@ -6,10 +7,30 @@ use crate::executor::ir::ExecutionPlan; use crate::executor::vtable::VTableTuple; use crate::ir::expression::Expression; use crate::ir::operator::Relational; +use crate::ir::value::Value; use crate::ir::Node; use super::tree::{SyntaxData, SyntaxPlan}; +#[derive(Debug, PartialEq, tlua::Push)] +pub struct PatternWithParams { + pub pattern: String, + pub params: Vec<Value>, +} + +impl PatternWithParams { + #[must_use] + pub fn new(pattern: String, params: Vec<Value>) -> Self { + PatternWithParams { pattern, params } + } +} + +impl From<PatternWithParams> for String { + fn from(p: PatternWithParams) -> Self { + format!("pattern: {}, parameters: {:?}", p.pattern, p.params) + } +} + impl ExecutionPlan { /// Traverse plan sub-tree (pointed by top) in the order /// convenient for SQL serialization. @@ -58,7 +79,9 @@ impl ExecutionPlan { &self, nodes: &[SyntaxData], buckets: &Buckets, - ) -> Result<String, QueryPlannerError> { + ) -> Result<PatternWithParams, QueryPlannerError> { + let mut params: Vec<Value> = Vec::new(); + let mut sql = String::new(); let delim = " "; @@ -166,6 +189,18 @@ impl ExecutionPlan { }, } } + SyntaxData::Parameter(id) => { + sql.push('?'); + let value = ir_plan.get_expression_node(*id)?; + if let Expression::Constant { value, .. } = value { + params.push(value.clone()); + } else { + return Err(QueryPlannerError::CustomError(format!( + "Parameter {:?} is not a constant", + value + ))); + } + } SyntaxData::VTable(vtable) => { let cols_count = vtable.get_columns().len(); @@ -212,7 +247,7 @@ impl ExecutionPlan { } else { let values = tuples .iter() - .map(|t| format!("({})", (t.iter().map(ToString::to_string)).join(","))) + .map(|t| format!("({})", (t.iter().map(|_| "?")).join(","))) .collect::<Vec<String>>() .join(","); @@ -223,11 +258,17 @@ impl ExecutionPlan { cols(anonymous_col_idx_base), values )); + + for t in tuples { + for v in t { + params.push(v.clone()); + } + } } } } } - Ok(sql) + Ok(PatternWithParams::new(sql, params)) } /// Checks if the given query subtree modifies data or not. diff --git a/src/executor/engine/cartridge/backend/sql/ir/tests.rs b/src/executor/engine/cartridge/backend/sql/ir/tests.rs index cf90041a4c..6a01182dcc 100644 --- a/src/executor/engine/cartridge/backend/sql/ir/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/ir/tests.rs @@ -5,6 +5,9 @@ use crate::executor::engine::mock::MetadataMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; use crate::frontend::Ast; +use crate::ir::value::Value; + +use super::*; #[test] fn one_table_projection() { @@ -23,11 +26,15 @@ fn one_table_projection() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {}", - r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing""#, - r#"WHERE ("hash_testing"."identification_number") = (1)"# + PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT "hash_testing"."identification_number" as "identification_number","#, + r#""hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") = (?)"#, + ), + vec![Value::from(1_u64)] ), sql ); @@ -50,12 +57,16 @@ fn one_table_with_asterisk() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {}", - r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code","#, - r#""hash_testing"."product_units" as "product_units", "hash_testing"."sys_op" as "sys_op""#, - r#"FROM "hash_testing""#, - r#"WHERE ("hash_testing"."identification_number") = (1)"# + PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "hash_testing"."identification_number" as "identification_number","#, + r#""hash_testing"."product_code" as "product_code","#, + r#""hash_testing"."product_units" as "product_units", "hash_testing"."sys_op" as "sys_op""#, + r#"FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") = (?)"# + ), + vec![Value::from(1_u64)] ), sql ); @@ -83,11 +94,16 @@ fn union_all() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {}", - r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#, - r#"UNION ALL"#, - r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."product_code") = ('a')"# + PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") = (?)"#, + r#"UNION ALL"#, + r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist""#, + r#"WHERE ("hash_testing_hist"."product_code") = (?)"# + ), + vec![Value::from(1_u64), Value::from("a")], ), sql ); @@ -112,12 +128,15 @@ fn from_sub_query() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {}", - r#"SELECT t1."product_code" as "product_code" FROM"#, - r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, - r#"WHERE ("hash_testing"."identification_number") = (1)) as t1"#, - r#"WHERE (t1."product_code") = ('a')"# + PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT t1."product_code" as "product_code" FROM"#, + r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") = (?)) as t1"#, + r#"WHERE (t1."product_code") = (?)"# + ), + vec![Value::from(1_u64), Value::from("a")], ), sql ); @@ -146,13 +165,18 @@ fn from_sub_query_with_union() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {} {}", - r#"SELECT "t1"."product_code" as "product_code" FROM"#, - r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#, - r#"UNION ALL"#, - r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."product_code") = ('a')) as "t1""#, - r#"WHERE ("t1"."product_code") = ('a')"#, + PatternWithParams::new( + format!( + "{} {} {} {} {} {} {}", + r#"SELECT "t1"."product_code" as "product_code" FROM"#, + r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") = (?)"#, + r#"UNION ALL"#, + r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist""#, + r#"WHERE ("hash_testing_hist"."product_code") = (?)) as "t1""#, + r#"WHERE ("t1"."product_code") = (?)"#, + ), + vec![Value::from(1_u64), Value::from("a"), Value::from("a")], ), sql ); @@ -175,12 +199,15 @@ fn inner_join() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {}", - r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, - r#"INNER JOIN (SELECT "history"."id" as "id" FROM "history") as "history""#, - r#"ON ("hash_testing"."identification_number") = ("history"."id")"#, - r#"WHERE ("hash_testing"."product_code") = ('a')"#, + PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"INNER JOIN (SELECT "history"."id" as "id" FROM "history") as "history""#, + r#"ON ("hash_testing"."identification_number") = ("history"."id")"#, + r#"WHERE ("hash_testing"."product_code") = (?)"#, + ), + vec![Value::from("a")], ), sql ); @@ -204,13 +231,16 @@ fn inner_join_with_sq() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {} {}", - r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, - r#"INNER JOIN"#, - r#"(SELECT "history"."id" as "id" FROM "history" WHERE ("history"."id") = (1)) as "t""#, - r#"ON ("hash_testing"."identification_number") = ("t"."id")"#, - r#"WHERE ("hash_testing"."product_code") = ('a')"#, + PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"INNER JOIN"#, + r#"(SELECT "history"."id" as "id" FROM "history" WHERE ("history"."id") = (?)) as "t""#, + r#"ON ("hash_testing"."identification_number") = ("t"."id")"#, + r#"WHERE ("hash_testing"."product_code") = (?)"#, + ), + vec![Value::from(1_u64), Value::from("a")], ), sql ); @@ -232,13 +262,16 @@ fn selection_with_sq() { let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( - format!( - "{} {} {} {} {}", - r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, - r#"WHERE ("hash_testing"."identification_number") in"#, - r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#, - r#"WHERE ("hash_testing_hist"."product_code") = ('b'))"#, - r#"and ("hash_testing"."product_code") < ('a')"#, + PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number") in"#, + r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#, + r#"WHERE ("hash_testing_hist"."product_code") = (?))"#, + r#"and ("hash_testing"."product_code") < (?)"#, + ), + vec![Value::from("b"), Value::from("a")], ), sql ); diff --git a/src/executor/engine/cartridge/backend/sql/tree.rs b/src/executor/engine/cartridge/backend/sql/tree.rs index ef4ae73d00..9a4b725590 100644 --- a/src/executor/engine/cartridge/backend/sql/tree.rs +++ b/src/executor/engine/cartridge/backend/sql/tree.rs @@ -29,6 +29,8 @@ pub enum SyntaxData { Operator(String), /// plan node id PlanId(usize), + /// parameter (a wrapper over a plan constants) + Parameter(usize), /// virtual table VTable(VirtualTable), } @@ -113,6 +115,14 @@ impl SyntaxNode { } } + fn new_parameter(id: usize) -> Self { + SyntaxNode { + data: SyntaxData::Parameter(id), + left: None, + right: Vec::new(), + } + } + fn left_id_or_err(&self) -> Result<usize, QueryPlannerError> { match self.left { Some(id) => Ok(id), @@ -210,19 +220,24 @@ impl SyntaxNodes { /// Push a new syntax node to arena pub fn push_syntax_node(&mut self, node: SyntaxNode) -> usize { let id = self.next_id(); - if let SyntaxData::PlanId(plan_id) = node.data { - self.map.insert(plan_id, id); + match node.data { + SyntaxData::PlanId(plan_id) | SyntaxData::Parameter(plan_id) => { + self.map.insert(plan_id, id); + } + _ => {} } self.arena.push(node); id } /// Get next node id + #[must_use] pub fn next_id(&self) -> usize { self.arena.len() } /// Constructor with pre-allocated memory + #[must_use] pub fn with_capacity(capacity: usize) -> Self { SyntaxNodes { arena: Vec::with_capacity(capacity), @@ -388,6 +403,10 @@ pub struct SyntaxPlan<'p> { #[allow(dead_code)] impl<'p> SyntaxPlan<'p> { + /// Add an IR plan node to the syntax tree. + /// + /// # Errors + /// - Failed to translate an IR plan node to a syntax node. #[allow(clippy::too_many_lines)] pub fn add_plan_node(&mut self, id: usize) -> Result<usize, QueryPlannerError> { let ir_plan = self.plan.get_ir_plan(); @@ -593,7 +612,11 @@ impl<'p> SyntaxPlan<'p> { } }, Node::Expression(expr) => match expr { - Expression::Constant { .. } | Expression::Reference { .. } => { + Expression::Constant { .. } => { + let sn = SyntaxNode::new_parameter(id); + Ok(self.nodes.push_syntax_node(sn)) + } + Expression::Reference { .. } => { let sn = SyntaxNode::new_pointer(id, None, &[]); Ok(self.nodes.push_syntax_node(sn)) } @@ -788,6 +811,12 @@ impl<'p> SyntaxPlan<'p> { } } + /// Build a new syntax tree from the execution plan. + /// + /// # Errors + /// - Failed to ad an IR plan to the syntax tree + /// - Failed to get to the top of the syntax tree + /// - Failed to move projection nodes under their scans pub fn new(plan: &'p ExecutionPlan, top: usize) -> Result<Self, QueryPlannerError> { let mut sp = SyntaxPlan::empty(plan); let ir_plan = plan.get_ir_plan(); diff --git a/src/executor/engine/cartridge/backend/sql/tree/tests.rs b/src/executor/engine/cartridge/backend/sql/tree/tests.rs index 5f32269170..5955489b53 100644 --- a/src/executor/engine/cartridge/backend/sql/tree/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/tree/tests.rs @@ -81,7 +81,7 @@ fn sql_order_selection() { assert_eq!(Some(SyntaxData::Operator("=".into())), nodes_iter.next()); // = assert_eq!(Some(SyntaxData::PlanId(7)), nodes_iter.next()); // row assert_eq!(Some(SyntaxData::OpenParenthesis), nodes_iter.next()); // ( - assert_eq!(Some(SyntaxData::PlanId(6)), nodes_iter.next()); // const 1 + assert_eq!(Some(SyntaxData::Parameter(6)), nodes_iter.next()); // parameter assert_eq!(Some(SyntaxData::CloseParenthesis), nodes_iter.next()); // ) assert_eq!(None, nodes_iter.next()); } diff --git a/src/executor/engine/mock.rs b/src/executor/engine/mock.rs index ff71c1dd41..26d99c2c00 100644 --- a/src/executor/engine/mock.rs +++ b/src/executor/engine/mock.rs @@ -261,13 +261,13 @@ impl Engine for EngineMock { match buckets { Buckets::All => { let sql = plan.syntax_nodes_as_sql(&nodes, buckets)?; - result.extend(exec_on_all(&sql))?; + result.extend(exec_on_all(&String::from(sql).as_str()))?; } Buckets::Filtered(list) => { for bucket in list { let bucket_set: HashSet<u64, RepeatableState> = collection! { *bucket }; let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::Filtered(bucket_set))?; - let temp_result = exec_on_some(*bucket, &sql); + let temp_result = exec_on_some(*bucket, &String::from(sql).as_str()); result.extend(temp_result)?; } } diff --git a/src/executor/result.rs b/src/executor/result.rs index c470966fa9..f6b978b5cf 100644 --- a/src/executor/result.rs +++ b/src/executor/result.rs @@ -45,6 +45,8 @@ impl TryInto<Column> for &MetadataColumn { "integer" => Ok(Column::new(&self.name, Type::Integer, ColumnRole::User)), "scalar" => Ok(Column::new(&self.name, Type::Scalar, ColumnRole::User)), "string" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)), + "text" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)), + "varchar" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)), "unsigned" => Ok(Column::new(&self.name, Type::Unsigned, ColumnRole::User)), _ => Err(QueryPlannerError::CustomError(format!( "unsupported column type: {}", diff --git a/src/executor/tests.rs b/src/executor/tests.rs index c9689d7fb4..9b140b9d87 100644 --- a/src/executor/tests.rs +++ b/src/executor/tests.rs @@ -1,5 +1,6 @@ use pretty_assertions::assert_eq; +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::EngineMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; @@ -22,12 +23,17 @@ fn shard_query() { let param1 = Value::from(1_u64); let bucket = query.engine.determine_bucket_id(&[¶m1]); - expected - .rows - .push(vec![ - Value::from(format!("Execute query on a bucket [{}]", bucket)), - Value::from(r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space" WHERE ("test_space"."id") = (1)"#), - ]); + expected.rows.push(vec![ + Value::from(format!("Execute query on a bucket [{}]", bucket)), + Value::from(String::from(PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#, + r#"WHERE ("test_space"."id") = (?)"# + ), + vec![param1], + ))), + ]); assert_eq!(expected, result); } @@ -57,17 +63,19 @@ fn shard_union_query() { .push(vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), Value::String( - format!( - "{} {}{} {} {}{} {}", - r#"SELECT "t3"."id" as "id""#, - r#"FROM ("#, - r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") = (1)"#, - r#"UNION ALL"#, - r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") > (1)"#, - r#") as "t3""#, - r#"WHERE ("t3"."id") = (1)"#, - ) - ) + String::from( + PatternWithParams::new( + format!( + "{} {}{} {} {}{} {}", + r#"SELECT "t3"."id" as "id""#, + r#"FROM ("#, + r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") = (?)"#, + r#"UNION ALL"#, + r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") > (?)"#, + r#") as "t3""#, + r#"WHERE ("t3"."id") = (?)"#, + ), vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)]), + )) ]); assert_eq!(expected, result); @@ -84,18 +92,22 @@ fn map_reduce_query() { let mut expected = ProducerResult::new(); let param1 = Value::from(1_u64); - let param457 = Value::from(457_u64); + let param457 = Value::from("457"); let bucket = query.engine.determine_bucket_id(&[¶m1, ¶m457]); expected.rows.push(vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), Value::String( - format!( - "{} {} {}", - r#"SELECT "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing""#, - r#"WHERE ("hash_testing"."identification_number", "hash_testing"."product_code") = (1, '457')"#, + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing""#, + r#"WHERE ("hash_testing"."identification_number", "hash_testing"."product_code") = (?, ?)"#, + ), vec![param1, param457], + ) ) ) ]); @@ -133,21 +145,33 @@ fn linker_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket3)), - Value::String(format!( - "{} {} {}", - r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space""#, - r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space""#, + r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + ), vec![param3], + ) + ) + ), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {} {}", - r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space""#, - r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space""#, + r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + ), vec![param2], + ) + ) + ), ], ]); @@ -195,39 +219,41 @@ fn union_linker_test() { vec![ Value::String(format!("Execute query on a bucket [{}]", bucket3)), Value::String( + String::from(PatternWithParams::new( format!( "{} {}{} {} {} {} {} {} {}{} {}", r#"SELECT "t1"."id" as "id", "t1"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM ("#, r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM "test_space""#, - r#"WHERE ("test_space"."sys_op") < (0)"#, + r#"WHERE ("test_space"."sys_op") < (?)"#, r#"UNION ALL"#, r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM "test_space_hist""#, - r#"WHERE ("test_space_hist"."sys_op") > (0)"#, + r#"WHERE ("test_space_hist"."sys_op") > (?)"#, r#") as "t1""#, - r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"# - ) - ) + r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + ), vec![Value::from(0_u64), Value::from(0_u64), Value::from(3_u64)]) + )) ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), Value::String( + String::from(PatternWithParams::new( format!( "{} {}{} {} {} {} {} {} {}{} {}", r#"SELECT "t1"."id" as "id", "t1"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM ("#, r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM "test_space""#, - r#"WHERE ("test_space"."sys_op") < (0)"#, + r#"WHERE ("test_space"."sys_op") < (?)"#, r#"UNION ALL"#, r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, r#"FROM "test_space_hist""#, - r#"WHERE ("test_space_hist"."sys_op") > (0)"#, + r#"WHERE ("test_space_hist"."sys_op") > (?)"#, r#") as "t1""#, - r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"# - ) + r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + ), vec![Value::from(0_u64), Value::from(0_u64), Value::from(2_u64)])) ) ], ]); @@ -283,25 +309,37 @@ WHERE "t3"."id" = 2 AND "t8"."identification_number" = 2"#; vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), Value::String( - format!( - "{}, {}, {} {}{} {} {} {} {} {} {}{} {} {}{} {} {}", - r#"SELECT "t3"."id" as "id""#, - r#""t3"."FIRST_NAME" as "FIRST_NAME""#, - r#""t8"."identification_number" as "identification_number""#, - r#"FROM ("#, - r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space""#, - r#"WHERE (0) > ("test_space"."sys_op") and ("test_space"."sysFrom") >= (0)"#, - r#"UNION ALL"#, - r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space_hist""#, - r#"WHERE ("test_space_hist"."sysFrom") <= (0)"#, - r#") as "t3""#, - r#"INNER JOIN"#, - r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (2))"#, - r#") as "t8""#, - r#"ON ("t3"."id") = ("t8"."identification_number")"#, - r#"WHERE ("t3"."id", "t3"."id", "t8"."identification_number") = ("t8"."identification_number", 2, 2)"# + String::from( + PatternWithParams::new( + format!( + "{}, {}, {} {}{} {} {} {} {} {} {}{} {} {}{} {} {}", + r#"SELECT "t3"."id" as "id""#, + r#""t3"."FIRST_NAME" as "FIRST_NAME""#, + r#""t8"."identification_number" as "identification_number""#, + r#"FROM ("#, + r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space""#, + r#"WHERE (?) > ("test_space"."sys_op") and ("test_space"."sysFrom") >= (?)"#, + r#"UNION ALL"#, + r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space_hist""#, + r#"WHERE ("test_space_hist"."sysFrom") <= (?)"#, + r#") as "t3""#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?))"#, + r#") as "t8""#, + r#"ON ("t3"."id") = ("t8"."identification_number")"#, + r#"WHERE ("t3"."id", "t3"."id", "t8"."identification_number") = ("t8"."identification_number", ?, ?)"# + ), + vec![ + Value::from(0_u64), + Value::from(0_u64), + Value::from(0_u64), + Value::from(2_u64), + Value::from(2_u64), + Value::from(2_u64) + ] + ) ) ) ], @@ -353,13 +391,16 @@ fn join_linker2_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {} {} {}", - r#"SELECT "t1"."id" as "id" FROM "test_space" as "t1""#, - r#"INNER JOIN"#, - r#"(SELECT COLUMN_1 as "id1",COLUMN_2 as "id2" FROM (VALUES (1,1)))"#, - r#"as "t2" ON ("t1"."id") = (1)"# - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT "t1"."id" as "id" FROM "test_space" as "t1""#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_1 as "id1",COLUMN_2 as "id2" FROM (VALUES (?,?)))"#, + r#"as "t2" ON ("t1"."id") = (?)"# + ), + vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)], + ))), ]]); assert_eq!(expected, result); } @@ -409,14 +450,23 @@ fn join_linker3_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {} {} {} {}", - r#"SELECT "t2"."id1" as "id1" FROM"#, - r#"(SELECT "test_space"."id" as "id" FROM "test_space") as "t1""#, - r#"INNER JOIN"#, - r#"(SELECT COLUMN_3 as "id1",COLUMN_4 as "id2" FROM (VALUES (1,1),(2,2))) as "t2""#, - r#"ON ("t2"."id1") = (1)"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "t2"."id1" as "id1" FROM"#, + r#"(SELECT "test_space"."id" as "id" FROM "test_space") as "t1""#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_3 as "id1",COLUMN_4 as "id2" FROM (VALUES (?,?),(?,?))) as "t2""#, + r#"ON ("t2"."id1") = (?)"#, + ), + vec![ + Value::from(1_u64), + Value::from(1_u64), + Value::from(2_u64), + Value::from(2_u64), + Value::from(1_u64), + ], + ))), ]]); assert_eq!(expected, result); } @@ -482,25 +532,31 @@ fn join_linker4_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {} {} {} {}", - r#"SELECT t1."id" as "id" FROM "test_space" as t1"#, - r#"INNER JOIN"#, - r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (2))) as t2"#, - r#"ON (t1."id") = (t2."r_id")"#, - r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (2),(3)))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT t1."id" as "id" FROM "test_space" as t1"#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (?))) as t2"#, + r#"ON (t1."id") = (t2."r_id")"#, + r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (?),(?)))"#, + ), + vec![Value::from(2_u64), Value::from(2_u64), Value::from(3_u64)], + ))), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {} {} {} {}", - r#"SELECT t1."id" as "id" FROM "test_space" as t1"#, - r#"INNER JOIN"#, - r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (1))) as t2"#, - r#"ON (t1."id") = (t2."r_id")"#, - r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (2),(3)))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT t1."id" as "id" FROM "test_space" as t1"#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (?))) as t2"#, + r#"ON (t1."id") = (t2."r_id")"#, + r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (?),(?)))"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)], + ))), ], ]); assert_eq!(expected, result); @@ -550,35 +606,41 @@ fn anonymous_col_index_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket3)), - Value::String(format!( - "{} {} {} {} {} {} {} {} {} {}", - "SELECT", - r#""test_space"."id" as "id","#, - r#""test_space"."sysFrom" as "sysFrom","#, - r#""test_space"."FIRST_NAME" as "FIRST_NAME","#, - r#""test_space"."sys_op" as "sys_op""#, - r#"FROM "test_space""#, - r#"WHERE (("test_space"."id") in"#, - r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"#, - r#"or ("test_space"."id") in"#, - r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (3))))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {} {} {} {} {} {}", + "SELECT", + r#""test_space"."id" as "id","#, + r#""test_space"."sysFrom" as "sysFrom","#, + r#""test_space"."FIRST_NAME" as "FIRST_NAME","#, + r#""test_space"."sys_op" as "sys_op""#, + r#"FROM "test_space""#, + r#"WHERE (("test_space"."id") in"#, + r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + r#"or ("test_space"."id") in"#, + r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (?))))"#, + ), + vec![Value::from(3_u64), Value::from(3_u64)], + ))), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {} {} {} {} {} {} {} {} {}", - "SELECT", - r#""test_space"."id" as "id","#, - r#""test_space"."sysFrom" as "sysFrom","#, - r#""test_space"."FIRST_NAME" as "FIRST_NAME","#, - r#""test_space"."sys_op" as "sys_op""#, - r#"FROM "test_space""#, - r#"WHERE (("test_space"."id") in"#, - r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"#, - r#"or ("test_space"."id") in"#, - r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (2))))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {} {} {} {} {} {}", + "SELECT", + r#""test_space"."id" as "id","#, + r#""test_space"."sysFrom" as "sysFrom","#, + r#""test_space"."FIRST_NAME" as "FIRST_NAME","#, + r#""test_space"."sys_op" as "sys_op""#, + r#"FROM "test_space""#, + r#"WHERE (("test_space"."id") in"#, + r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#, + r#"or ("test_space"."id") in"#, + r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (?))))"#, + ), + vec![Value::from(2_u64), Value::from(2_u64)], + ))), ], ]); @@ -599,12 +661,15 @@ fn sharding_column1_test() { let bucket = query.engine.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {} {}", - r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#, - r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op""#, - r#"FROM "test_space" WHERE ("test_space"."id") = (1)"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#, + r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op""#, + r#"FROM "test_space" WHERE ("test_space"."id") = (?)"#, + ), + vec![Value::from(1_u64)], + ))), ]); assert_eq!(expected, result); } @@ -623,12 +688,18 @@ fn sharding_column2_test() { let bucket = query.engine.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {} {}", - r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#, - r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op","#, - r#""test_space"."bucket_id" as "bucket_id" FROM "test_space" WHERE ("test_space"."id") = (1)"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#, + r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op","#, + r#""test_space"."bucket_id" as "bucket_id" FROM "test_space" WHERE ("test_space"."id") = (?)"#, + ), vec![Value::from(1_u64)], + ) + ) + ), ]); assert_eq!(expected, result); } @@ -669,19 +740,25 @@ fn insert1_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "bucket_id")"#, - r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (1,2156))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "bucket_id")"#, + r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (?,?))"#, + ), + vec![Value::from(1_u64), Value::from(2156_u64)], + ))), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "bucket_id")"#, - r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (2,3832))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "bucket_id")"#, + r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (?,?))"#, + ), + vec![Value::from(2_u64), Value::from(3832_u64)], + ))), ], ]); assert_eq!(expected, result); @@ -725,11 +802,17 @@ fn insert2_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, - r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (1,2,550))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, + r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#, + ), vec![Value::from(1_u64), Value::from(2_u64), Value::from(550_u64)], + ) + ) + ), ]]); assert_eq!(expected, result); } @@ -775,19 +858,31 @@ fn insert3_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, - r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (1,2,4427))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, + r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#, + ), vec![Value::from(1_u64), Value::from(2_u64), Value::from(4427_u64)], + ) + ) + ), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, - r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (3,4,7100))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, + r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#, + ), vec![Value::from(3_u64), Value::from(4_u64), Value::from(7100_u64)], + ) + ) + ), ], ]); assert_eq!(expected, result); @@ -830,11 +925,17 @@ fn insert4_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, - r#"SELECT COLUMN_1 as "b",COLUMN_2 as "a",COLUMN_3 as "bucket_id" FROM (VALUES (2,1,550))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, + r#"SELECT COLUMN_1 as "b",COLUMN_2 as "a",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#, + ), vec![Value::from(2_u64), Value::from(1_u64), Value::from(550_u64)], + ) + ) + ), ]]); assert_eq!(expected, result); } @@ -875,11 +976,25 @@ fn insert5_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {}", - r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, - r#"SELECT COLUMN_4 as "a",COLUMN_5 as "b",COLUMN_6 as "bucket_id" FROM (VALUES (5,6,8788),(5,6,8788))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("b", "a", "bucket_id")"#, + r#"SELECT COLUMN_4 as "a",COLUMN_5 as "b",COLUMN_6 as "bucket_id" FROM (VALUES (?,?,?),(?,?,?))"#, + ), + vec![ + Value::from(5_u64), + Value::from(6_u64), + Value::from(8788_u64), + Value::from(5_u64), + Value::from(6_u64), + Value::from(8788_u64) + ], + ) + ) + ), ]]); assert_eq!(expected, result); } @@ -924,21 +1039,45 @@ fn insert6_test() { expected.rows.extend(vec![ vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {} {}", - r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, - r#"SELECT COLUMN_4 as "COLUMN_5",COLUMN_5 as "COLUMN_6",COLUMN_6 as "bucket_id""#, - r#"FROM (VALUES (1,2,550),(1,2,550))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, + r#"SELECT COLUMN_4 as "COLUMN_5",COLUMN_5 as "COLUMN_6",COLUMN_6 as "bucket_id""#, + r#"FROM (VALUES (?,?,?),(?,?,?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::from(550_u64), + Value::from(1_u64), + Value::from(2_u64), + Value::from(550_u64) + ], + ) + ) + ), ], vec![ Value::String(format!("Execute query on a bucket [{}]", bucket2)), - Value::String(format!( - "{} {} {}", - r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, - r#"SELECT COLUMN_1 as "COLUMN_5",COLUMN_2 as "COLUMN_6",COLUMN_3 as "bucket_id""#, - r#"FROM (VALUES (3,4,8906))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {} {}", + r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, + r#"SELECT COLUMN_1 as "COLUMN_5",COLUMN_2 as "COLUMN_6",COLUMN_3 as "bucket_id""#, + r#"FROM (VALUES (?,?,?))"#, + ), vec![ + Value::from(3_u64), + Value::from(4_u64), + Value::from(8906_u64), + ], + ) + ) + ), ], ]); assert_eq!(expected, result); @@ -999,12 +1138,24 @@ fn insert8_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket)), - Value::String(format!( - "{} {}{}", - r#"INSERT INTO "hash_testing" ("identification_number", "product_code", "product_units", "sys_op", "bucket_id")"#, - r#"SELECT COLUMN_1 as "identification_number",COLUMN_2 as "product_code",COLUMN_3 as "product_units","#, - r#"COLUMN_4 as "sys_op",COLUMN_5 as "bucket_id" FROM (VALUES (1,'two',true,4,3016))"#, - )), + Value::String( + String::from( + PatternWithParams::new( + format!( + "{} {}{}", + r#"INSERT INTO "hash_testing" ("identification_number", "product_code", "product_units", "sys_op", "bucket_id")"#, + r#"SELECT COLUMN_1 as "identification_number",COLUMN_2 as "product_code",COLUMN_3 as "product_units","#, + r#"COLUMN_4 as "sys_op",COLUMN_5 as "bucket_id" FROM (VALUES (?,?,?,?,?))"#, + ), vec![ + Value::from(1_u64), + Value::from("two"), + Value::from(true), + Value::from(4_u64), + Value::from(3016_u64), + ], + ) + ) + ), ]]); assert_eq!(expected, result); } @@ -1042,12 +1193,15 @@ fn insert9_test() { expected.rows.extend(vec![vec![ Value::String(format!("Execute query on a bucket [{}]", bucket1)), - Value::String(format!( - "{} {} {}", - r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, - r#"SELECT COLUMN_1 as "COLUMN_1",COLUMN_2 as "COLUMN_2",COLUMN_3 as "bucket_id""#, - r#"FROM (VALUES (1,2,550))"#, - )), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {}", + r#"INSERT INTO "t" ("a", "b", "bucket_id")"#, + r#"SELECT COLUMN_1 as "COLUMN_1",COLUMN_2 as "COLUMN_2",COLUMN_3 as "bucket_id""#, + r#"FROM (VALUES (?,?,?))"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(550_u64)], + ))), ]]); assert_eq!(expected, result); } diff --git a/src/frontend/sql/ir/tests.rs b/src/frontend/sql/ir/tests.rs index d6f739cebb..78c6ac0ccf 100644 --- a/src/frontend/sql/ir/tests.rs +++ b/src/frontend/sql/ir/tests.rs @@ -1,4 +1,5 @@ use crate::errors::QueryPlannerError; +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::MetadataMock; use crate::frontend::sql::ast::AbstractSyntaxTree; use crate::frontend::Ast; @@ -13,10 +14,14 @@ fn no_transform(_plan: &mut Plan) {} fn front_sql1() { let input = r#"SELECT "identification_number", "product_code" FROM "hash_testing" WHERE "identification_number" = 1"#; - let expected = format!( - "{} {}", - r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "hash_testing"."identification_number" as "identification_number","#, + r#""hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (?)"#, + ), + vec![Value::from(1_u64)], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -28,11 +33,19 @@ fn front_sql2() { FROM "hash_testing" WHERE "identification_number" = 1 AND "product_code" = '1' OR "identification_number" = 2 AND "product_code" = '2'"#; - let expected = format!( - "{} {} {}", - r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing" WHERE (("hash_testing"."identification_number") = (1) and ("hash_testing"."product_code") = ('1')"#, - r#"or ("hash_testing"."identification_number") = (2) and ("hash_testing"."product_code") = ('2'))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing" WHERE (("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?)"#, + r#"or ("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?))"#, + ), + vec![ + Value::from(1_u64), + Value::from("1"), + Value::from(2_u64), + Value::from("2"), + ], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -50,15 +63,18 @@ fn front_sql3() { FROM "hash_testing_hist" WHERE "sys_op" > 1) AS "t3" WHERE "identification_number" = 1"#; - let expected = format!( - "{} {} {} {} {} {} {}", - r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#, - r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (1)"#, - r#"UNION ALL"#, - r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#, - r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (1)) as "t3""#, - r#"WHERE ("t3"."identification_number") = (1)"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {} {} {} {}", + r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#, + r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (?)"#, + r#"UNION ALL"#, + r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#, + r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)) as "t3""#, + r#"WHERE ("t3"."identification_number") = (?)"#, + ), + vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -80,16 +96,27 @@ fn front_sql4() { OR "identification_number" = 3)) AND ("product_code" = '1' OR "product_code" = '2')"#; - let expected = format!( - "{} {} {} {} {} {} {} {}", - r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#, - r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (1)"#, - r#"UNION ALL"#, - r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#, - r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (1)) as "t3""#, - r#"WHERE (("t3"."identification_number") = (1) or (("t3"."identification_number") = (2) or ("t3"."identification_number") = (3)))"#, - r#"and (("t3"."product_code") = ('1') or ("t3"."product_code") = ('2'))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {} {} {} {} {}", + r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#, + r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (?)"#, + r#"UNION ALL"#, + r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#, + r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)) as "t3""#, + r#"WHERE (("t3"."identification_number") = (?) or (("t3"."identification_number") = (?) or ("t3"."identification_number") = (?)))"#, + r#"and (("t3"."product_code") = (?) or ("t3"."product_code") = (?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(1_u64), + Value::from(1_u64), + Value::from(2_u64), + Value::from(3_u64), + Value::from("1"), + Value::from("2"), + ], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -100,12 +127,15 @@ fn front_sql5() { let input = r#"SELECT "identification_number", "product_code" FROM "hash_testing" WHERE "identification_number" in ( SELECT "identification_number" FROM "hash_testing_hist" WHERE "product_code" = 'a')"#; - let expected = format!( - "{} {} {} {}", - r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, - r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") in"#, - r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#, - r#"WHERE ("hash_testing_hist"."product_code") = ('a'))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#, + r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") in"#, + r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#, + r#"WHERE ("hash_testing_hist"."product_code") = (?))"#, + ), + vec![Value::from("a")], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -117,12 +147,15 @@ fn front_sql6() { INNER JOIN (SELECT "id" FROM "test_space") as t ON "hash_testing"."identification_number" = t."id" WHERE "hash_testing"."identification_number" = 5 and "hash_testing"."product_code" = '123'"#; - let expected = format!( - "{} {} {} {}", - r#"SELECT t."id" as "id", "hash_testing"."product_units" as "product_units""#, - r#"FROM "hash_testing" INNER JOIN (SELECT "test_space"."id" as "id" FROM "test_space") as t"#, - r#"ON ("hash_testing"."identification_number") = (t."id")"#, - r#"WHERE ("hash_testing"."identification_number") = (5) and ("hash_testing"."product_code") = ('123')"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {}", + r#"SELECT t."id" as "id", "hash_testing"."product_units" as "product_units""#, + r#"FROM "hash_testing" INNER JOIN (SELECT "test_space"."id" as "id" FROM "test_space") as t"#, + r#"ON ("hash_testing"."identification_number") = (t."id")"#, + r#"WHERE ("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?)"#, + ), + vec![Value::from(5_u64), Value::from("123")], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -156,10 +189,13 @@ fn front_sql7() { fn front_sql8() { let input = r#"SELECT t."identification_number", "product_code" FROM "hash_testing" as t WHERE t."identification_number" = 1"#; - let expected = format!( - "{} {}", - r#"SELECT t."identification_number" as "identification_number", t."product_code" as "product_code""#, - r#"FROM "hash_testing" as t WHERE (t."identification_number") = (1)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT t."identification_number" as "identification_number", t."product_code" as "product_code""#, + r#"FROM "hash_testing" as t WHERE (t."identification_number") = (?)"#, + ), + vec![Value::from(1_u64)], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -187,26 +223,38 @@ fn front_sql9() { WHERE "sys_op" <= 0) AS "t8" ON "t3"."id" = "t8"."identification_number" WHERE "id" = 1 AND "t8"."identification_number" = 1 AND "product_code" = '123'"#; - let expected = format!( - "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", - r#"SELECT "t3"."id" as "id", "t3"."FIRST_NAME" as "FIRST_NAME","#, - r#""t8"."identification_number" as "identification_number","#, - r#""t8"."product_code" as "product_code" FROM"#, - r#"(SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space" WHERE ("test_space"."sys_op") < (0) and ("test_space"."sysFrom") >= (0)"#, - r#"UNION ALL"#, - r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, - r#"FROM "test_space_hist" WHERE ("test_space_hist"."sysFrom") <= (0))"#, - r#"as "t3""#, - r#"INNER JOIN"#, - r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number","#, - r#""hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (0)"#, - r#"UNION ALL"#, - r#"SELECT "hash_single_testing_hist"."identification_number" as "identification_number","#, - r#""hash_single_testing_hist"."product_code" as "product_code" FROM "hash_single_testing_hist""#, - r#"WHERE ("hash_single_testing_hist"."sys_op") <= (0))"#, - r#"as "t8" ON ("t3"."id") = ("t8"."identification_number")"#, - r#"WHERE ("t3"."id") = (1) and ("t8"."identification_number") = (1) and ("t8"."product_code") = ('123')"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", + r#"SELECT "t3"."id" as "id", "t3"."FIRST_NAME" as "FIRST_NAME","#, + r#""t8"."identification_number" as "identification_number","#, + r#""t8"."product_code" as "product_code" FROM"#, + r#"(SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space" WHERE ("test_space"."sys_op") < (?) and ("test_space"."sysFrom") >= (?)"#, + r#"UNION ALL"#, + r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#, + r#"FROM "test_space_hist" WHERE ("test_space_hist"."sysFrom") <= (?))"#, + r#"as "t3""#, + r#"INNER JOIN"#, + r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number","#, + r#""hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)"#, + r#"UNION ALL"#, + r#"SELECT "hash_single_testing_hist"."identification_number" as "identification_number","#, + r#""hash_single_testing_hist"."product_code" as "product_code" FROM "hash_single_testing_hist""#, + r#"WHERE ("hash_single_testing_hist"."sys_op") <= (?))"#, + r#"as "t8" ON ("t3"."id") = ("t8"."identification_number")"#, + r#"WHERE ("t3"."id") = (?) and ("t8"."identification_number") = (?) and ("t8"."product_code") = (?)"#, + ), + vec![ + Value::from(0_u64), + Value::from(0_u64), + Value::from(0_u64), + Value::from(0_u64), + Value::from(0_u64), + Value::from(1_u64), + Value::from(1_u64), + Value::from("123"), + ], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -215,9 +263,17 @@ fn front_sql9() { #[test] fn front_sql10() { let input = r#"INSERT INTO "t" VALUES(1, 2, 3, 4)"#; - let expected = format!( - "{}", - r#"INSERT INTO "t" ("a", "b", "c", "d") VALUES (1, 2, 3, 4)"#, + let expected = PatternWithParams::new( + format!( + "{}", + r#"INSERT INTO "t" ("a", "b", "c", "d") VALUES (?, ?, ?, ?)"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::from(3_u64), + Value::from(4_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -226,7 +282,10 @@ fn front_sql10() { #[test] fn front_sql11() { let input = r#"INSERT INTO "t" ("a", "c") VALUES(1, 2)"#; - let expected = format!("{}", r#"INSERT INTO "t" ("a", "c") VALUES (1, 2)"#,); + let expected = PatternWithParams::new( + format!("{}", r#"INSERT INTO "t" ("a", "c") VALUES (?, ?)"#,), + vec![Value::from(1_u64), Value::from(2_u64)], + ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); } @@ -264,9 +323,12 @@ fn front_sql13() { #[test] fn front_sql14() { let input = r#"INSERT INTO "t" ("a", "c") SELECT "b", "d" FROM "t""#; - let expected = format!( - "{} {}", - r#"INSERT INTO "t" ("a", "c")"#, r#"SELECT "t"."b" as "b", "t"."d" as "d" FROM "t""#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"INSERT INTO "t" ("a", "c")"#, r#"SELECT "t"."b" as "b", "t"."d" as "d" FROM "t""#, + ), + vec![], ); assert_eq!(sql_to_sql(input, &[], &no_transform), expected); @@ -292,10 +354,13 @@ fn front_params1() { let pattern = r#"SELECT "id", "FIRST_NAME" FROM "test_space" WHERE "sys_op" = ? AND "sysFrom" > ?"#; let params = vec![Value::from(0_i64), Value::from(1_i64)]; - let expected = format!( - "{} {}", - r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#, - r#"WHERE ("test_space"."sys_op") = (0) and ("test_space"."sysFrom") > (1)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#, + r#"WHERE ("test_space"."sys_op") = (?) and ("test_space"."sysFrom") > (?)"#, + ), + params.clone(), ); assert_eq!(sql_to_sql(pattern, ¶ms, &no_transform), expected); @@ -306,10 +371,13 @@ fn front_params2() { let pattern = r#"SELECT "id" FROM "test_space" WHERE "sys_op" = ? AND "FIRST_NAME" = ?"#; let params = vec![Value::Null, Value::from("hello")]; - let expected = format!( - "{} {}", - r#"SELECT "test_space"."id" as "id" FROM "test_space""#, - r#"WHERE ("test_space"."sys_op") = (NULL) and ("test_space"."FIRST_NAME") = ('hello')"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "test_space"."id" as "id" FROM "test_space""#, + r#"WHERE ("test_space"."sys_op") = (?) and ("test_space"."FIRST_NAME") = (?)"#, + ), + params.clone(), ); assert_eq!(sql_to_sql(pattern, ¶ms, &no_transform), expected); diff --git a/src/ir/transformation/bool_in/tests.rs b/src/ir/transformation/bool_in/tests.rs index d50f26208c..e6034c73c9 100644 --- a/src/ir/transformation/bool_in/tests.rs +++ b/src/ir/transformation/bool_in/tests.rs @@ -1,4 +1,6 @@ +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::ir::transformation::helpers::sql_to_sql; +use crate::ir::value::Value; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -9,10 +11,13 @@ fn replace_in_operator(plan: &mut Plan) { #[test] fn bool_in1() { let input = r#"SELECT "a" FROM "t" WHERE "a" IN (1, 2, 3)"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ((("t"."a") = (1) or ("t"."a") = (2)) or ("t"."a") = (3))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ((("t"."a") = (?) or ("t"."a") = (?)) or ("t"."a") = (?))"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)], ); assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected); @@ -22,10 +27,20 @@ fn bool_in1() { fn bool_in2() { let input = r#"SELECT "a" FROM "t" WHERE ("a", "b") IN ((1, 10), (2, 20), (3, 30))"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ((("t"."a", "t"."b") = (1, 10) or ("t"."a", "t"."b") = (2, 20)) or ("t"."a", "t"."b") = (3, 30))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ((("t"."a", "t"."b") = (?, ?) or ("t"."a", "t"."b") = (?, ?)) or ("t"."a", "t"."b") = (?, ?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(10_u64), + Value::from(2_u64), + Value::from(20_u64), + Value::from(3_u64), + Value::from(30_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected); @@ -34,10 +49,13 @@ fn bool_in2() { #[test] fn bool_in3() { let input = r#"SELECT "a" FROM "t" WHERE "a" IN (1, 2) AND "b" IN (3)"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) or ("t"."a") = (2)) and ("t"."b") = (3)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) or ("t"."a") = (?)) and ("t"."b") = (?)"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)], ); assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected); diff --git a/src/ir/transformation/dnf/tests.rs b/src/ir/transformation/dnf/tests.rs index d7c70e2100..d50cb50d0d 100644 --- a/src/ir/transformation/dnf/tests.rs +++ b/src/ir/transformation/dnf/tests.rs @@ -1,4 +1,6 @@ +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::ir::transformation::helpers::sql_to_sql; +use crate::ir::value::Value; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -10,11 +12,20 @@ fn set_dnf(plan: &mut Plan) { fn dnf1() { let input = r#"SELECT "a" FROM "t" WHERE ("a" = 1 AND "b" = 2 OR "a" = 3) AND "c" = 4"#; - let expected = format!( - "{} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and ("t"."b") = (2) and ("t"."c") = (4)"#, - r#"or ("t"."a") = (3) and ("t"."c") = (4))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and ("t"."b") = (?) and ("t"."c") = (?)"#, + r#"or ("t"."a") = (?) and ("t"."c") = (?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::from(4_u64), + Value::from(3_u64), + Value::from(4_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); @@ -24,11 +35,23 @@ fn dnf1() { fn dnf2() { let input = r#"SELECT "a" FROM "t" WHERE ("a" = 1 OR "b" = 2) AND ("a" = 3 OR "c" = 4)"#; - let expected = format!( - "{} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (((("t"."a") = (3) and ("t"."a") = (1) or ("t"."c") = (4) and ("t"."a") = (1))"#, - r#"or ("t"."a") = (3) and ("t"."b") = (2)) or ("t"."c") = (4) and ("t"."b") = (2))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (((("t"."a") = (?) and ("t"."a") = (?) or ("t"."c") = (?) and ("t"."a") = (?))"#, + r#"or ("t"."a") = (?) and ("t"."b") = (?)) or ("t"."c") = (?) and ("t"."b") = (?))"#, + ), + vec![ + Value::from(3_u64), + Value::from(1_u64), + Value::from(4_u64), + Value::from(1_u64), + Value::from(3_u64), + Value::from(2_u64), + Value::from(4_u64), + Value::from(2_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); @@ -38,10 +61,18 @@ fn dnf2() { fn dnf3() { let input = r#"SELECT "a" FROM "t" WHERE ("a" = 1 OR "b" = 2) AND NULL"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and (NULL) or ("t"."b") = (2) and (NULL))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and (?) or ("t"."b") = (?) and (?))"#, + ), + vec![ + Value::from(1_u64), + Value::Null, + Value::from(2_u64), + Value::Null, + ], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); @@ -51,10 +82,18 @@ fn dnf3() { fn dnf4() { let input = r#"SELECT "a" FROM "t" WHERE ("a" = 1 OR "b" = 2) AND true"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and (true) or ("t"."b") = (2) and (true))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and (?) or ("t"."b") = (?) and (?))"#, + ), + vec![ + Value::from(1_u64), + Value::Boolean(true), + Value::from(2_u64), + Value::Boolean(true), + ], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); @@ -64,10 +103,18 @@ fn dnf4() { fn dnf5() { let input = r#"SELECT "a" FROM "t" WHERE ("a" = 1 OR "b" = 2) AND ((false))"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and ((false)) or ("t"."b") = (2) and ((false)))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and ((?)) or ("t"."b") = (?) and ((?)))"#, + ), + vec![ + Value::from(1_u64), + Value::Boolean(false), + Value::from(2_u64), + Value::Boolean(false), + ], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); @@ -77,10 +124,13 @@ fn dnf5() { fn dnf6() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 and "c" = 1 OR "b" = 2"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and ("t"."c") = (1) or ("t"."b") = (2))"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and ("t"."c") = (?) or ("t"."b") = (?))"#, + ), + vec![Value::from(1_u64), Value::from(1_u64), Value::from(2_u64)], ); assert_eq!(sql_to_sql(input, &[], &set_dnf), expected); diff --git a/src/ir/transformation/equality_propagation/tests.rs b/src/ir/transformation/equality_propagation/tests.rs index 013bcd142a..797712d576 100644 --- a/src/ir/transformation/equality_propagation/tests.rs +++ b/src/ir/transformation/equality_propagation/tests.rs @@ -1,4 +1,6 @@ +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::ir::transformation::helpers::sql_to_sql; +use crate::ir::value::Value; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -11,11 +13,19 @@ fn equality_propagation1() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 AND "b" = 2 AND "c" = 1 OR "d" = 1"#; - let expected = format!( - "{} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a") = (1) and ("t"."b") = (2) and ("t"."c") = (1)"#, - r#"and ("t"."c") = ("t"."a") or ("t"."d") = (1))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a") = (?) and ("t"."b") = (?) and ("t"."c") = (?)"#, + r#"and ("t"."c") = ("t"."a") or ("t"."d") = (?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::from(1_u64), + Value::from(1_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected); @@ -26,9 +36,12 @@ fn equality_propagation2() { let input = r#"SELECT "a" FROM "t" WHERE "a" = NULL AND "b" = NULL"#; - let expected = format!( - "{}", - r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (NULL) and ("t"."b") = (NULL)"#, + let expected = PatternWithParams::new( + format!( + "{}", + r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?) and ("t"."b") = (?)"#, + ), + vec![Value::Null, Value::Null], ); assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected); @@ -39,10 +52,13 @@ fn equality_propagation3() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 AND "b" = null AND "a" = null"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ("t"."a") = (1) and ("t"."b") = (NULL) and ("t"."a") = (NULL)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ("t"."a") = (?) and ("t"."b") = (?) and ("t"."a") = (?)"#, + ), + vec![Value::from(1_u64), Value::Null, Value::Null], ); assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected); @@ -53,11 +69,19 @@ fn equality_propagation4() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 AND "b" = null AND "a" = null AND "b" = 1"#; - let expected = format!( - "{} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ("t"."a") = (1) and ("t"."b") = (NULL) and ("t"."a") = (NULL)"#, - r#"and ("t"."b") = (1) and ("t"."b") = ("t"."a")"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ("t"."a") = (?) and ("t"."b") = (?) and ("t"."a") = (?)"#, + r#"and ("t"."b") = (?) and ("t"."b") = ("t"."a")"#, + ), + vec![ + Value::from(1_u64), + Value::Null, + Value::Null, + Value::from(1_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected); @@ -68,13 +92,21 @@ fn equality_propagation5() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 AND "b" = 1 AND "c" = 1 AND "d" = 1"#; - let expected = format!( - "{} {} {} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ("t"."a") = (1) and ("t"."b") = (1)"#, - r#"and ("t"."c") = (1) and ("t"."d") = (1)"#, - r#"and ("t"."c") = ("t"."b") and ("t"."b") = ("t"."a")"#, - r#"and ("t"."a") = ("t"."d")"#, + let expected = PatternWithParams::new( + format!( + "{} {} {} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ("t"."a") = (?) and ("t"."b") = (?)"#, + r#"and ("t"."c") = (?) and ("t"."d") = (?)"#, + r#"and ("t"."c") = ("t"."b") and ("t"."b") = ("t"."a")"#, + r#"and ("t"."a") = ("t"."d")"#, + ), + vec![ + Value::from(1_u64), + Value::from(1_u64), + Value::from(1_u64), + Value::from(1_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected); diff --git a/src/ir/transformation/helpers.rs b/src/ir/transformation/helpers.rs index 6cd6305454..4d7a38caed 100644 --- a/src/ir/transformation/helpers.rs +++ b/src/ir/transformation/helpers.rs @@ -1,6 +1,7 @@ //! IR test helpers. use crate::executor::bucket::Buckets; +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::MetadataMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; @@ -18,9 +19,13 @@ pub fn sql_to_ir(query: &str, params: &[Value]) -> Plan { plan } -/// Compiles and transforms an SQL query to a new SQL. +/// Compiles and transforms an SQL query to a new parameterized SQL. #[allow(dead_code)] -pub fn sql_to_sql(query: &str, params: &[Value], f_transform: &dyn Fn(&mut Plan)) -> String { +pub fn sql_to_sql( + query: &str, + params: &[Value], + f_transform: &dyn Fn(&mut Plan), +) -> PatternWithParams { let mut plan = sql_to_ir(query, params); f_transform(&mut plan); let ex_plan = ExecutionPlan::from(plan); diff --git a/src/ir/transformation/merge_tuples/tests.rs b/src/ir/transformation/merge_tuples/tests.rs index a3aa2415bc..479ea130fa 100644 --- a/src/ir/transformation/merge_tuples/tests.rs +++ b/src/ir/transformation/merge_tuples/tests.rs @@ -1,4 +1,6 @@ +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::ir::transformation::helpers::sql_to_sql; +use crate::ir::value::Value; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -9,10 +11,18 @@ fn merge_tuples(plan: &mut Plan) { #[test] fn merge_tuples1() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 and "b" = 2 and "c" < 3 and 4 < "a""#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE ("t"."a", "t"."b") = (1, 2) and (3, "t"."a") > ("t"."c", 4)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE ("t"."a", "t"."b") = (?, ?) and (?, "t"."a") > ("t"."c", ?)"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::from(3_u64), + Value::from(4_u64), + ], ); assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected); @@ -23,11 +33,21 @@ fn merge_tuples2() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 and null and "b" = 2 or true and "c" >= 3 and 4 <= "a""#; - let expected = format!( - "{} {} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, - r#"WHERE (("t"."a", "t"."b") = (1, 2) and (NULL)"#, - r#"or ("t"."c", "t"."a") >= (3, 4) and (true))"#, + let expected = PatternWithParams::new( + format!( + "{} {} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, + r#"WHERE (("t"."a", "t"."b") = (?, ?) and (?)"#, + r#"or ("t"."c", "t"."a") >= (?, ?) and (?))"#, + ), + vec![ + Value::from(1_u64), + Value::from(2_u64), + Value::Null, + Value::from(3_u64), + Value::from(4_u64), + Value::Boolean(true), + ], ); assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected); @@ -36,7 +56,10 @@ fn merge_tuples2() { #[test] fn merge_tuples3() { let input = r#"SELECT "a" FROM "t" WHERE true"#; - let expected = format!("{}", r#"SELECT "t"."a" as "a" FROM "t" WHERE true"#); + let expected = PatternWithParams::new( + format!("{}", r#"SELECT "t"."a" as "a" FROM "t" WHERE ?"#), + vec![Value::Boolean(true)], + ); assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected); } @@ -44,9 +67,12 @@ fn merge_tuples3() { #[test] fn merge_tuples4() { let input = r#"SELECT "a" FROM "t" WHERE ("a", "b") = (1, 2) and 3 = "c""#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."a", "t"."b", "t"."c") = (1, 2, 3)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."a", "t"."b", "t"."c") = (?, ?, ?)"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)], ); assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected); @@ -55,9 +81,12 @@ fn merge_tuples4() { #[test] fn merge_tuples5() { let input = r#"SELECT "a" FROM "t" WHERE 3 < "c" and ("a", "b") > (1, 2)"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."c", "t"."a", "t"."b") > (3, 1, 2)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."c", "t"."a", "t"."b") > (?, ?, ?)"#, + ), + vec![Value::from(3_u64), Value::from(1_u64), Value::from(2_u64)], ); assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected); diff --git a/src/ir/transformation/split_columns/tests.rs b/src/ir/transformation/split_columns/tests.rs index 77d587366c..2dfb5517d3 100644 --- a/src/ir/transformation/split_columns/tests.rs +++ b/src/ir/transformation/split_columns/tests.rs @@ -1,7 +1,9 @@ +use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::MetadataMock; use crate::frontend::sql::ast::AbstractSyntaxTree; use crate::frontend::Ast; use crate::ir::transformation::helpers::sql_to_sql; +use crate::ir::value::Value; use crate::ir::Plan; use pretty_assertions::assert_eq; @@ -12,9 +14,12 @@ fn split_columns(plan: &mut Plan) { #[test] fn split_columns1() { let input = r#"SELECT "a" FROM "t" WHERE ("a", 2) = (1, "b")"#; - let expected = format!( - "{}", - r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (1) and (2) = ("t"."b")"#, + let expected = PatternWithParams::new( + format!( + "{}", + r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?) and (?) = ("t"."b")"#, + ), + vec![Value::from(1_u64), Value::from(2_u64)], ); assert_eq!(sql_to_sql(input, &[], &split_columns), expected); @@ -23,9 +28,12 @@ fn split_columns1() { #[test] fn split_columns2() { let input = r#"SELECT "a" FROM "t" WHERE "a" = 1"#; - let expected = format!( - "{}", - r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (1)"#, + let expected = PatternWithParams::new( + format!( + "{}", + r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?)"#, + ), + vec![Value::from(1_u64)], ); assert_eq!(sql_to_sql(input, &[], &split_columns), expected); @@ -54,9 +62,12 @@ fn split_columns3() { #[test] fn split_columns4() { let input = r#"SELECT "a" FROM "t" WHERE "a" in (1, 2)"#; - let expected = format!( - "{}", - r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") in (1, 2)"#, + let expected = PatternWithParams::new( + format!( + "{}", + r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") in (?, ?)"#, + ), + vec![Value::from(1_u64), Value::from(2_u64)], ); assert_eq!(sql_to_sql(input, &[], &split_columns), expected); @@ -65,10 +76,13 @@ fn split_columns4() { #[test] fn split_columns5() { let input = r#"SELECT "a" FROM "t" WHERE ("a", 2) < (1, "b") and "a" > 2"#; - let expected = format!( - "{} {}", - r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") < (1) and (2) < ("t"."b")"#, - r#"and ("t"."a") > (2)"#, + let expected = PatternWithParams::new( + format!( + "{} {}", + r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") < (?) and (?) < ("t"."b")"#, + r#"and ("t"."a") > (?)"#, + ), + vec![Value::from(1_u64), Value::from(2_u64), Value::from(2_u64)], ); assert_eq!(sql_to_sql(input, &[], &split_columns), expected); diff --git a/src/ir/value.rs b/src/ir/value.rs index d708848f1c..9264eccbe0 100644 --- a/src/ir/value.rs +++ b/src/ir/value.rs @@ -63,8 +63,8 @@ impl fmt::Display for Value { Value::Null => write!(f, "NULL"), Value::Unsigned(v) => write!(f, "{}", v), Value::Integer(v) => write!(f, "{}", v), - Value::Double(v) => write!(f, "{}", v), - Value::Decimal(v) => write!(f, "{}", v), + Value::Double(v) => fmt::Display::fmt(&v, f), + Value::Decimal(v) => fmt::Display::fmt(v, f), Value::String(v) => write!(f, "'{}'", v), } } @@ -337,6 +337,22 @@ impl From<Value> for String { } } +impl<L: tlua::AsLua> tlua::Push<L> for Value { + type Err = tlua::Void; + + fn push_to_lua(&self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> { + match self { + Value::Boolean(v) => v.push_to_lua(lua), + Value::Integer(v) => v.push_to_lua(lua), + Value::Decimal(v) => v.push_to_lua(lua), + Value::Double(v) => v.push_to_lua(lua), + Value::Unsigned(v) => v.push_to_lua(lua), + Value::String(v) => v.push_to_lua(lua), + Value::Null => tlua::Null.push_to_lua(lua), + } + } +} + impl<L> tlua::PushInto<L> for Value where L: tlua::AsLua, diff --git a/src/ir/value/double.rs b/src/ir/value/double.rs index bc1e554044..bf639848fb 100644 --- a/src/ir/value/double.rs +++ b/src/ir/value/double.rs @@ -62,6 +62,14 @@ impl FromStr for Double { } } +impl<L: tlua::AsLua> tlua::Push<L> for Double { + type Err = tlua::Void; + + fn push_to_lua(&self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> { + self.value.push_to_lua(lua) + } +} + impl<L> tlua::PushInto<L> for Double where L: tlua::AsLua, @@ -71,7 +79,9 @@ where Ok(tlua::push_userdata(self.value, lua, |_| {})) } } + impl<L> tlua::PushOneInto<L> for Double where L: tlua::AsLua {} + impl<L> tlua::LuaRead<L> for Double where L: tlua::AsLua, diff --git a/test_app/test/integration/api_test.lua b/test_app/test/integration/api_test.lua index a4168fd247..9f25efd66e 100644 --- a/test_app/test/integration/api_test.lua +++ b/test_app/test/integration/api_test.lua @@ -63,14 +63,14 @@ g.test_bucket_id_calculation = function() t.assert_equals(r, 360) r, err = api:call("calculate_bucket_id_by_dict", { "testing_space", { id = 1 }}) - t.assert_equals(err, "CustomError(\"The dict of args missed key/value to calculate bucket_id. Column: name\")") + t.assert_str_contains(tostring(err), [[dict of args missed key/value to calculate bucket_id]]) end g.test_incorrect_query = function() local api = cluster:server("api-1").net_box local _, err = api:call("query", { [[SELECT * FROM "testing_space" INNER JOIN "testing_space"]], {} }) - t.assert_equals(err, "CustomError(\"Parsing error: Error { variant: ParsingError { positives: [SubQuery], negatives: [] }, location: Pos(41), line_col: Pos((1, 42)), path: None, line: \\\"SELECT * FROM \\\\\\\"testing_space\\\\\\\" INNER JOIN \\\\\\\"testing_space\\\\\\\"\\\", continued_line: None }\")") + t.assert_str_contains(tostring(err), "Parsing error") end g.test_join_query_is_valid = function() @@ -278,7 +278,7 @@ g.test_join_motion_query = function() metadata = { {name = "id", type = "integer"}, {name = "name", type = "string"}, - {name = "product_units", type = "integer"}, + {name = "product_units", type = "any"}, }, rows = { { 1, "ok", 5 }, @@ -446,7 +446,7 @@ g.test_invalid_explain = function() local _, err = api:call("explain", { [[SELECT "id", "name" FROM "testing_space" WHERE "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" < 0)]] }) - t.assert_equals(err, "Explain hasn't supported node Motion { children: [43], policy: Full, generation: None, output: 81 } yet") + t.assert_str_contains(tostring(err), "Explain hasn't supported node Motion") end g.test_valid_explain = function() diff --git a/test_app/test/integration/target_queries_test.lua b/test_app/test/integration/target_queries_test.lua index 76c6093304..19fce96154 100644 --- a/test_app/test/integration/target_queries_test.lua +++ b/test_app/test/integration/target_queries_test.lua @@ -534,9 +534,9 @@ WHERE "t3"."col1" = 1 AND "t8"."cola" = 1]], {} }) { name = "col1", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -579,9 +579,9 @@ WHERE "t3"."col1" = ? AND ("t8"."cola" = ? { name = "col1", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -620,9 +620,9 @@ AND ("t8"."cola" = ? AND "t8"."colb" = ?)]], { 0, 0, 0, 0, 0, 0, 1, 2, 1, 2 } }) { name = "col2", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -662,9 +662,9 @@ AND ("t8"."cola" = 1 AND ("t8"."colb" = 2 AND "t3"."amount" > 0))]], {} }) { name = "col2", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -702,9 +702,9 @@ WHERE "t3"."col1" = ? AND "t8"."cola" = ?]], { 1, 2 } }) { name = "col1", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -743,9 +743,9 @@ WHERE "t3"."col1" = ? AND "t3"."col2" = 2 AND ("t8"."cola" = 1 AND "t8"."colb" = { name = "col2", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -783,9 +783,9 @@ WHERE "t3"."col1" = 1 AND ("t3"."col2" = 1 AND "t8"."colb" = 2)]], {} }) { name = "col2", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { @@ -822,9 +822,9 @@ WHERE "t3"."col1" = 1 AND "t3"."col2" = 1]], {} }) { name = "col2", type = "integer" }, { name = "account_id", type = "integer" }, { name = "amount", type = "integer" }, - { name = "id", type = "integer" }, - { name = "cola", type = "integer" }, - { name = "colb", type = "integer" }, + { name = "id", type = "any" }, + { name = "cola", type = "any" }, + { name = "colb", type = "any" }, }) t.assert_items_equals(r.rows, { diff --git a/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml b/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml index 2784125792..8787c2e238 100644 --- a/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml +++ b/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml @@ -13,7 +13,7 @@ # - = 15 # - row 14 # - ( 12 -# - const_1 11 +# - parameter 11 # - ) 13 --- arena: @@ -77,7 +77,7 @@ arena: - 9 #11 - data: - PlanId: 6 + Parameter: 6 left: ~ right: [] #12 -- GitLab