This is an automated email from the ASF dual-hosted git repository.

korlov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 7fc53f029ba IGNITE-27679 Sql. SelectCount plan is affected by 
consequent DML in script (#7524)
7fc53f029ba is described below

commit 7fc53f029ba3dba1018737324c0d3c196b799284
Author: korlov42 <[email protected]>
AuthorDate: Thu Feb 5 12:12:59 2026 +0200

    IGNITE-27679 Sql. SelectCount plan is affected by consequent DML in script 
(#7524)
---
 .../internal/sql/engine/prepare/SelectCountPlan.java     | 16 ++++++++++++++++
 .../internal/sql/engine/exec/QueryTimeoutTest.java       | 12 ++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/SelectCountPlan.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/SelectCountPlan.java
index f8014499598..e25bc847810 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/SelectCountPlan.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/SelectCountPlan.java
@@ -161,6 +161,22 @@ public class SelectCountPlan implements ExplainablePlan, 
ExecutablePlan {
         return 1;
     }
 
+    @Override
+    public boolean lazyCursorPublication() {
+        // Fast SelectCount does not support transactions; therefore, when 
running concurrently with DML
+        // statements, it may return a non-transactionally-consistent result. 
For example, if a table is
+        // empty and two statements are executed in parallel -- one inserting 
1,000 rows and another executing
+        // `SELECT count(*) FROM table` -- SelectCount may return any number 
in the range [0, 1000].
+        // This is not considered a problem when SelectCount runs 
independently (it is considered
+        // eventually consistent), but such behavior contradicts another 
guarantee we want to preserve:
+        // within a script, statements are executed sequentially, one after 
another.
+        //
+        // Lazy publication allows a race in which a subsequent DML statement 
can outrun the execution of
+        // the SelectCount plan, resulting in a situation where the 
SelectCount result includes changes
+        // made by that DML statement.
+        return false;
+    }
+
     private <RowT> Function<Long, Iterator<InternalSqlRow>> 
createResultProjection(ExecutionContext<RowT> ctx) {
         RelDataType getCountType = new 
RelDataTypeFactory.Builder(ctx.getTypeFactory())
                 .add("ROWCOUNT", SqlTypeName.BIGINT)
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/QueryTimeoutTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/QueryTimeoutTest.java
index 8a0c4979f27..11f262bcbaf 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/QueryTimeoutTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/QueryTimeoutTest.java
@@ -134,14 +134,10 @@ public class QueryTimeoutTest extends 
BaseIgniteAbstractTest {
 
     @Test
     void testTimeoutSelectCount() {
-        AsyncSqlCursor<?> cursor = 
gatewayNode.executeQuery(PROPS_WITH_TIMEOUT, "SELECT COUNT(*) FROM my_table");
-
-        assertThat(
-                cursor.requestNextAsync(1),
-                willThrowWithCauseOrSuppressed(
-                        QueryCancelledException.class,
-                        QueryCancelledException.TIMEOUT_MSG
-                )
+        assertThrows(
+                SqlException.class,
+                () -> gatewayNode.executeQuery(PROPS_WITH_TIMEOUT, "SELECT 
COUNT(*) FROM my_table"),
+                QueryCancelledException.TIMEOUT_MSG
         );
     }
 

Reply via email to