From d0e54e0d646e04b94f1dca4da879ae57bce3b9c9 Mon Sep 17 00:00:00 2001 From: Denis Smirnov <sd@picodata.io> Date: Wed, 12 Oct 2022 12:59:32 +0700 Subject: [PATCH] fix: incorrect column name generation for empty results --- sbroad-core/src/backend/sql/ir.rs | 16 +-- sbroad-core/src/executor/engine/mock.rs | 13 +++ sbroad-core/src/executor/tests.rs | 3 + .../src/executor/tests/empty_motion.rs | 97 +++++++++++++++++++ 4 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 sbroad-core/src/executor/tests/empty_motion.rs diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs index 7697bdf7a9..7046963270 100644 --- a/sbroad-core/src/backend/sql/ir.rs +++ b/sbroad-core/src/backend/sql/ir.rs @@ -250,12 +250,14 @@ impl ExecutionPlan { SyntaxData::VTable(vtable) => { let cols_count = vtable.get_columns().len(); - let cols = |base_idx| { + let cols = |base_idx: &mut usize| { vtable .get_columns() .iter() - .enumerate() - .map(|(i, c)| format!("COLUMN_{} as \"{}\"", base_idx + i, c.name)) + .map(|c| { + *base_idx += 1; + format!("COLUMN_{} as \"{}\"", base_idx, c.name) + }) .collect::<Vec<String>>() .join(",") }; @@ -278,8 +280,6 @@ impl ExecutionPlan { }; if tuples.is_empty() { - anonymous_col_idx_base += 1; - let values = (0..cols_count) .map(|_| "null") .collect::<Vec<&str>>() @@ -288,7 +288,7 @@ impl ExecutionPlan { write!( sql, "SELECT {} FROM (VALUES ({})) WHERE FALSE", - cols(anonymous_col_idx_base), + cols(&mut anonymous_col_idx_base), values ) .map_err(|e| QueryPlannerError::CustomError(e.to_string()))?; @@ -299,12 +299,12 @@ impl ExecutionPlan { .collect::<Vec<String>>() .join(","); - anonymous_col_idx_base += cols_count * tuples.len() - (cols_count - 1); + anonymous_col_idx_base += cols_count * (tuples.len() - 1); write!( sql, "SELECT {} FROM (VALUES {})", - cols(anonymous_col_idx_base), + cols(&mut anonymous_col_idx_base), values ) .map_err(|e| { diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index 751164ba7d..7e72e75959 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -167,6 +167,19 @@ impl RouterConfigurationMock { Table::new_seg("\"t1\"", columns, sharding_key).unwrap(), ); + let columns = vec![ + Column::new("\"e\"", Type::Unsigned, ColumnRole::User), + Column::new("\"f\"", Type::Unsigned, ColumnRole::User), + Column::new("\"g\"", Type::Unsigned, ColumnRole::User), + Column::new("\"h\"", Type::Unsigned, ColumnRole::User), + Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + ]; + let sharding_key: &[&str] = &["\"e\"", "\"f\""]; + tables.insert( + "\"t2\"".to_string(), + Table::new_seg("\"t2\"", columns, sharding_key).unwrap(), + ); + RouterConfigurationMock { tables, bucket_count: 10000, diff --git a/sbroad-core/src/executor/tests.rs b/sbroad-core/src/executor/tests.rs index f1f54f9a36..a6feabe7e0 100644 --- a/sbroad-core/src/executor/tests.rs +++ b/sbroad-core/src/executor/tests.rs @@ -1352,6 +1352,9 @@ mod between; #[cfg(test)] mod bucket_id; +#[cfg(test)] +mod empty_motion; + #[cfg(test)] mod not_in; diff --git a/sbroad-core/src/executor/tests/empty_motion.rs b/sbroad-core/src/executor/tests/empty_motion.rs new file mode 100644 index 0000000000..ab51d06c34 --- /dev/null +++ b/sbroad-core/src/executor/tests/empty_motion.rs @@ -0,0 +1,97 @@ +use pretty_assertions::assert_eq; + +use crate::backend::sql::ir::PatternWithParams; +use crate::executor::engine::mock::RouterRuntimeMock; +use crate::executor::result::ProducerResult; +use crate::executor::vtable::VirtualTable; +use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::transformation::redistribution::{DataGeneration, MotionPolicy}; +use crate::ir::value::Value; + +use super::*; + +#[test] +fn empty_motion1_test() { + let sql = r#"SELECT * FROM ( + SELECT "t"."a", "t"."b" FROM "t" INNER JOIN "t2" ON "t"."a" = "t2"."g" and "t"."b" = "t2"."h" + WHERE "t"."a" = 0 + EXCEPT + SELECT "t"."a", "t"."b" FROM "t" INNER JOIN "t2" ON "t"."a" = "t2"."g" and "t"."b" = "t2"."h" + WHERE "t"."a" = 1 + ) as q"#; + + let coordinator = RouterRuntimeMock::new(); + + let mut query = Query::new(&coordinator, sql, vec![]).unwrap(); + let motion1_id = query.exec_plan.get_ir_plan().clone_slices().unwrap()[0][0]; + let mut virtual_t1 = t2_empty(); + if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion1_id) + { + query + .reshard_vtable(&mut virtual_t1, key, &DataGeneration::None) + .unwrap(); + } + query.coordinator.add_virtual_table(motion1_id, virtual_t1); + let motion2_id = query.exec_plan.get_ir_plan().clone_slices().unwrap()[0][1]; + let mut virtual_t2 = t2_empty(); + if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion2_id) + { + query + .reshard_vtable(&mut virtual_t2, key, &DataGeneration::None) + .unwrap(); + } + query.coordinator.add_virtual_table(motion2_id, virtual_t2); + + let result = *query + .dispatch() + .unwrap() + .downcast::<ProducerResult>() + .unwrap(); + + let mut expected = ProducerResult::new(); + expected.rows.extend(vec![vec![ + Value::String("Execute query on all buckets".into()), + Value::String(String::from(PatternWithParams::new( + format!( + "{} {} {} {} {} {} {} {} {} {} {} {} {} {}", + r#"SELECT "Q"."a", "Q"."b" FROM"#, + r#"(SELECT "t"."a", "t"."b" FROM"#, + r#"(SELECT "t"."a", "t"."b", "t"."c", "t"."d" FROM "t") as "t""#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_1 as "g",COLUMN_2 as "h" FROM (VALUES (null,null)) WHERE FALSE) as "t2""#, + r#"ON ("t"."a", "t"."b") = ("t2"."g", "t2"."h")"#, + r#"WHERE ("t"."a") = (?)"#, + r#"EXCEPT"#, + r#"SELECT "t"."a", "t"."b" FROM"#, + r#"(SELECT "t"."a", "t"."b", "t"."c", "t"."d" FROM "t") as "t""#, + r#"INNER JOIN"#, + r#"(SELECT COLUMN_3 as "g",COLUMN_4 as "h" FROM (VALUES (null,null)) WHERE FALSE) as "t2""#, + r#"ON ("t"."a", "t"."b") = ("t2"."g", "t2"."h")"#, + r#"WHERE ("t"."a") = (?)) as "Q""#, + ), + vec![Value::from(0_u64), Value::from(1_u64)], + ))), + ]]); + + assert_eq!(expected, result); +} + +fn t2_empty() -> VirtualTable { + let mut virtual_table = VirtualTable::new(); + + virtual_table.add_column(Column { + name: "g".into(), + r#type: Type::Integer, + role: ColumnRole::User, + }); + + virtual_table.add_column(Column { + name: "h".into(), + r#type: Type::Integer, + role: ColumnRole::User, + }); + + virtual_table.set_alias("\"t2\"").unwrap(); + + virtual_table +} -- GitLab