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
);
}