From 14f367e058651a30813a65d3586790edcf85e345 Mon Sep 17 00:00:00 2001 From: Arseniy Volynets <vol0ncar@yandex.ru> Date: Sun, 21 Apr 2024 19:08:07 +0000 Subject: [PATCH] feat(sql): support UNION - update sbroad submodule to commit with support of UNION operator - add integration tests for global tables (sharded tables were tested in cartridge tests) - Usage: `select a from t union select b from t2` --- Cargo.lock | 1 + sbroad | 2 +- src/sql/pgproto.rs | 1 + test/conftest.py | 1 - test/int/test_sql.py | 166 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 162 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00e443fc19..35ca2fc848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3003,6 +3003,7 @@ dependencies = [ "serde_yaml 0.8.26", "smol_str", "tarantool 5.0.0", + "time", "uuid 1.8.0", ] diff --git a/sbroad b/sbroad index 0d6f5acf57..b44ff0cdfd 160000 --- a/sbroad +++ b/sbroad @@ -1 +1 @@ -Subproject commit 0d6f5acf57c41ff0f0e194057625b3183e0030d7 +Subproject commit b44ff0cdfd945010ddea151ecda671aaa48e6723 diff --git a/src/sql/pgproto.rs b/src/sql/pgproto.rs index 3d5af4b8bd..942e9d657c 100644 --- a/src/sql/pgproto.rs +++ b/src/sql/pgproto.rs @@ -677,6 +677,7 @@ impl TryFrom<&Node> for CommandTag { | Relational::GroupBy { .. } | Relational::OrderBy { .. } | Relational::Having { .. } + | Relational::Union { .. } | Relational::UnionAll { .. } | Relational::Values { .. } | Relational::ValuesRow { .. } => Ok(CommandTag::Select), diff --git a/test/conftest.py b/test/conftest.py index 1a8a5b956c..a32bc9f1fe 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1895,7 +1895,6 @@ class AuditServer: return None def server(queue: Queue, host: str, port: int) -> None: - class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): QUEUE = queue diff --git a/test/int/test_sql.py b/test/int/test_sql.py index b482bdd947..e3eb08e43d 100644 --- a/test/int/test_sql.py +++ b/test/int/test_sql.py @@ -1034,13 +1034,12 @@ def test_union_all_on_global_tbls(cluster: Cluster): def check_multiple_union_all(): data = i1.sql( """ - select * from ( - select a from g - where a = 2 - union all - select d from s - group by d - ) union all + select a from g + where a = 2 + union all + select d from s + group by d + union all select a from g where b = 1 """, @@ -1083,6 +1082,159 @@ def test_union_all_on_global_tbls(cluster: Cluster): Retriable(rps=5, timeout=6).call(check_complex_segment_child) +def test_union_on_global_tbls(cluster: Cluster): + cluster.deploy(instance_count=1) + i1 = cluster.instances[0] + + ddl = i1.sql( + """ + create table g (a int not null, b int not null, primary key (a)) + using memtx + distributed globally + option (timeout = 3) + """ + ) + assert ddl["row_count"] == 1 + + for i, j in [(1, 1), (2, 2), (3, 2)]: + index = i1.cas("insert", "G", [i, j]) + i1.raft_wait_index(index, 3) + + ddl = i1.sql( + """ + create table s (c int not null, d int not null, primary key (c)) + using memtx + distributed by (c) + option (timeout = 3) + """ + ) + assert ddl["row_count"] == 1 + data = i1.sql("""insert into s values (1, 2), (2, 2), (3, 2);""") + assert data["row_count"] == 3 + + expected = [[1], [2]] + + def check_global_vs_any(): + data = i1.sql( + """ + select b from g + union + select d from s + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_global_vs_any) + + def check_any_vs_global(): + data = i1.sql( + """ + select d from s + union + select b from g + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_any_vs_global) + + def check_global_vs_global(): + data = i1.sql( + """ + select b from g + union + select a from g + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == [ + [1], + [2], + [3], + ] + + Retriable(rps=5, timeout=6).call(check_global_vs_global) + + expected = [[1], [2], [3]] + + def check_global_vs_segment(): + data = i1.sql( + """ + select a from g + union + select c from s + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_global_vs_segment) + + def check_segment_vs_global(): + data = i1.sql( + """ + select c from s + union + select a from g + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_segment_vs_global) + + expected = [[1], [2], [3]] + + def check_single_vs_global(): + data = i1.sql( + """ + select sum(c) - 3 from s + union + select a from g + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_single_vs_global) + + def check_global_vs_single(): + data = i1.sql( + """ + select a from g + union + select sum(c) - 3 from s + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == expected + + Retriable(rps=5, timeout=6).call(check_global_vs_single) + + def check_multiple_union(): + data = i1.sql( + """ + select a from g + where a = 2 + union + select d from s + group by d + union + select a from g + where b = 1 + except + select null from g + where false + """, + timeout=2, + ) + assert sorted(data["rows"], key=lambda x: x[0]) == [[1], [2]] + + Retriable(rps=5, timeout=6).call(check_multiple_union) + + def test_trim(instance: Instance): instance.sql( """ -- GitLab