From b3e94ecfff8e07f277559adc53b982047f1517e4 Mon Sep 17 00:00:00 2001
From: Arseniy Volynets <a.volynets@picodata.io>
Date: Mon, 23 Sep 2024 11:00:47 +0300
Subject: [PATCH] feat(sql): support selects without scans

---
 CHANGELOG.md                    |  2 +-
 sbroad                          |  2 +-
 src/pgproto/backend/describe.rs |  1 +
 src/sql/router.rs               | 16 +++----
 test/int/test_sql.py            | 80 +++++++++++++++++++++++++++++++++
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aba9ba85ed..9e8ecada3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,7 +50,7 @@ with the `YY.MINOR.MICRO` scheme.
 - SQL supports `lower` and `upper` string functions
 - Execute option `sql_vdbe_max_steps` was renamed to
 `vdbe_max_steps`
-
+- SQL supports `SELECT` statements without scans: `select 1`
 
 ## [24.5.1] - 2024-09-04
 
diff --git a/sbroad b/sbroad
index 277d570ea0..97d2b81478 160000
--- a/sbroad
+++ b/sbroad
@@ -1 +1 @@
-Subproject commit 277d570ea01e33aacc06d72e8fb3d31d4d4e4c4e
+Subproject commit 97d2b81478414c154cfa10c30c303f5ce0c8e0b7
diff --git a/src/pgproto/backend/describe.rs b/src/pgproto/backend/describe.rs
index 7f077ed4ac..4d8465f68d 100644
--- a/src/pgproto/backend/describe.rs
+++ b/src/pgproto/backend/describe.rs
@@ -227,6 +227,7 @@ impl TryFrom<&Node<'_>> for CommandTag {
                 | Relational::UnionAll { .. }
                 | Relational::Values { .. }
                 | Relational::ValuesRow { .. }
+                | Relational::SelectWithoutScan { .. }
                 | Relational::Limit { .. } => Ok(CommandTag::Select),
             },
             Node::Invalid(_) | Node::Expression(_) | Node::Parameter(_) => {
diff --git a/src/sql/router.rs b/src/sql/router.rs
index 328cb4b749..edd51f6d72 100644
--- a/src/sql/router.rs
+++ b/src/sql/router.rs
@@ -266,14 +266,6 @@ impl Router for RouterRuntime {
     type MetadataProvider = RouterMetadata;
     type VshardImplementor = Tier;
 
-    fn materialize_values(
-        &self,
-        exec_plan: &mut ExecutionPlan,
-        values_id: NodeId,
-    ) -> Result<VirtualTable, SbroadError> {
-        materialize_values(self, exec_plan, values_id)
-    }
-
     fn metadata(&self) -> &impl MutexLike<Self::MetadataProvider> {
         &self.metadata
     }
@@ -329,6 +321,14 @@ impl Router for RouterRuntime {
         let tier_name = tier_name.unwrap_or(&current_instance_tier_name);
         get_tier_info(tier_name)
     }
+
+    fn materialize_values(
+        &self,
+        exec_plan: &mut ExecutionPlan,
+        values_id: NodeId,
+    ) -> Result<VirtualTable, SbroadError> {
+        materialize_values(self, exec_plan, values_id)
+    }
 }
 
 pub(crate) fn calculate_bucket_id(tuple: &[&Value], bucket_count: u64) -> Result<u64, SbroadError> {
diff --git a/test/int/test_sql.py b/test/int/test_sql.py
index dc6217d5ff..0a6488006b 100644
--- a/test/int/test_sql.py
+++ b/test/int/test_sql.py
@@ -5319,3 +5319,83 @@ def test_like(instance: Instance):
         r"""select '%UU_' ilike '\%uu\_' escape '\' from (values (1))"""
     )
     assert data[0] == [True]
+
+
+def test_select_without_scan(cluster: Cluster):
+    cluster.deploy(instance_count=2)
+    i1, i2 = cluster.instances
+
+    cluster.wait_until_instance_has_this_many_active_buckets(i1, 1500)
+    cluster.wait_until_instance_has_this_many_active_buckets(i2, 1500)
+
+    ddl = i1.sql("create table t (a int primary key)")
+    assert ddl["row_count"] == 1
+
+    data = i1.sql("select 1", strip_metadata=False)
+    assert data["metadata"] == [
+        {"name": "col_1", "type": "unsigned"},
+    ]
+    assert data["rows"] == [[1]]
+
+    data = i1.sql("select 1 + 3 as foo", strip_metadata=False)
+    assert data["metadata"] == [
+        {"name": "foo", "type": "unsigned"},
+    ]
+    assert data["rows"] == [[4]]
+
+    # check subquery with global table
+    data = i1.sql("select (select name from _pico_table where name = 't') as foo")
+    assert data == [["t"]]
+
+    # check subquery with sharded table
+    dml = i2.sql("insert into t values (1), (2), (3), (4)")
+    assert dml["row_count"] == 4
+
+    data = i1.sql("select (select * from t where a = 3) as bar", strip_metadata=False)
+    assert data["metadata"] == [
+        {"name": "bar", "type": "integer"},
+    ]
+    assert data["rows"] == [[3]]
+
+    # check values
+    data = i1.sql("select (values (1))")
+    assert data == [[1]]
+
+    # check recursive
+    data = i1.sql("select (select 1)")
+    assert data == [[1]]
+
+    # check usage as a subquery
+    data = i1.sql("select a from t where a = (select 1)")
+    assert data == [[1]]
+
+    data = i1.sql(
+        "select (select 1 as foo) from t where a = (select 1)", strip_metadata=False
+    )
+    assert data["metadata"] == [
+        {"name": "col_1", "type": "unsigned"},
+    ]
+    assert data["rows"] == [[1]]
+
+    # check usage with union/except
+    data = i1.sql("select 1 union all select 1")
+    assert data == [[1], [1]]
+    data = i1.sql("select 1 union select 1")
+    assert data == [[1]]
+    data = i1.sql("select a from t where a = 1 union select 1")
+    assert data == [[1]]
+    data = i1.sql("select 1 except select 2")
+    assert data == [[1]]
+    data = i1.sql("select a from t where a = 1 except select 1")
+    assert data == []
+
+    # check usage with join
+    data = i1.sql("select * from t join (select 1) on a = col_1")
+    assert data == [[1, 1]]
+
+    data = i1.sql("select * from (select 1) join t on a = col_1")
+    assert data == [[1, 1]]
+
+    # check usage with limit
+    data = i1.sql("select 1 limit 1")
+    assert data == [[1]]
-- 
GitLab