diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs index 0d9fc14be65ae65cffc1ae24495ecc7e2f64680c..f2cec5735b305a7621c957ab36e15e936f92512b 100644 --- a/src/executor/engine/cartridge/backend/sql/ir.rs +++ b/src/executor/engine/cartridge/backend/sql/ir.rs @@ -3,8 +3,8 @@ use itertools::Itertools; use crate::errors::QueryPlannerError; use crate::executor::ir::ExecutionPlan; use crate::ir::expression::Expression; -use crate::ir::operator::Relational; use crate::ir::Node; +use crate::ir::operator::Relational; use super::tree::{SyntaxData, SyntaxPlan}; @@ -173,26 +173,46 @@ impl<'e> ExecutionPlan<'e> { // increase base of global counter of anonymous cols let cols_count = vtable.get_columns().len(); let rows_count = vtable.get_tuples().len(); - anonymous_col_idx_base += cols_count * rows_count - (cols_count - 1); - - let columns = vtable - .get_columns() - .iter() - .enumerate() - .map(|(i, c)| { - format!("COLUMN_{} as \"{}\"", anonymous_col_idx_base + i, c.name) - }) - .collect::<Vec<String>>() - .join(","); - - let tuples = vtable - .get_tuples() - .iter() - .map(|t| format!("({})", (t.iter().map(ToString::to_string)).join(","))) - .collect::<Vec<String>>() - .join(","); - - sql.push_str(&format!("SELECT {} FROM (VALUES {})", columns, tuples)); + + let cols = |base_idx| { + vtable + .get_columns() + .iter() + .enumerate() + .map(|(i, c)| format!("COLUMN_{} as \"{}\"", base_idx + i, c.name)) + .collect::<Vec<String>>() + .join(",") + }; + + if rows_count == 0 { + anonymous_col_idx_base += 1; + + let tuples = (0..cols_count) + .map(|_| "null") + .collect::<Vec<&str>>() + .join(","); + + sql.push_str(&format!( + "SELECT {} FROM (VALUES ({})) WHERE FALSE", + cols(anonymous_col_idx_base), + tuples + )); + } else { + anonymous_col_idx_base += cols_count * rows_count - (cols_count - 1); + + let tuples = vtable + .get_tuples() + .iter() + .map(|t| format!("({})", (t.iter().map(ToString::to_string)).join(","))) + .collect::<Vec<String>>() + .join(","); + + sql.push_str(&format!( + "SELECT {} FROM (VALUES {})", + cols(anonymous_col_idx_base), + tuples + )); + } } } } diff --git a/test_app/test/integration/api_test.lua b/test_app/test/integration/api_test.lua index b8222a80f91c9b356a2702411946aa03c7d0513a..2b37b11178c9e159e88e6f6498c31aaf19a3441c 100644 --- a/test_app/test/integration/api_test.lua +++ b/test_app/test/integration/api_test.lua @@ -272,6 +272,53 @@ g.test_anonymous_cols_naming = function() OR "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" > 0) ]] }) + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "id", type = "integer"}, + {name = "name", type = "string"}, + {name = "product_units", type = "integer"}, + {name = "bucket_id", type = "unsigned"}, + }, + rows = { + {1, "123", 1, 360} + }, + }) +end + +g.test_empty_motion_result = function() + local api = cluster:server("api-1").net_box + + local r, err = api:call("query", { [[SELECT "id", "name" FROM "testing_space" + WHERE "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" < 0)]] }) + + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "id", type = "integer"}, + {name = "name", type = "string"}, + }, + rows = {}, + }) + + r, err = api:call("query", { [[SELECT "id", "name" FROM "testing_space" + WHERE ("id", "name") in (SELECT "id", "name" FROM "space_simple_shard_key_hist" WHERE "sysOp" < 0)]] }) + + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "id", type = "integer"}, + {name = "name", type = "string"}, + }, + rows = {}, + }) + + + r, err = api:call("query", { [[SELECT * FROM "testing_space" + WHERE "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" > 0) + OR "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" < 0) + ]] }) + t.assert_equals(err, nil) t.assert_equals(r, { metadata = {