This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new de98e73ca93 [test](fe) Port tests that branch-4.x fixes added but
master lacks (#68801)
de98e73ca93 is described below
commit de98e73ca9310c57567f46b0e3c2d9a0536a2c19
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Sat Oct 10 10:36:05 2026 +0800
[test](fe) Port tests that branch-4.x fixes added but master lacks (#68801)
### What problem does this PR solve?
Issue Number: None
Related PR: #64757, #67704, #67153, #68250
Original authors: @yujun777 (#68250: the regression suite and the MTMV
assertion are ported from it), @Gabriel39 (#67153, #67704). The commit
carries Co-authored-by for both.
Problem Summary:
**In short.** These four branch-4.x fixes reached master through
master's own PRs, so master's production code already behaves the same.
Some of the tests the branch-4.x PRs added did not come along, mostly
because the code they test has moved on master. This PR adds that
coverage in master's structure: four unit-test cases and one regression
suite. It does not change production code.
**Background**
| branch-4.x PR | Master PR | Where the code lives on master |
|---|---|---|
| #64757: SQL Server / Oracle booleans pushed down as 1/0 | #62183,
#64756 | `fe-connector-jdbc` (`JdbcQueryBuilder.formatBooleanLiteral`) |
| #67704: fractional seconds in Iceberg `FOR TIME AS OF` | #67705 |
`fe-connector-iceberg` (`IcebergTimeUtils`,
`IcebergConnectorMetadata.resolveTimeTravel`) |
| #67153: fuzzy mode keeps FileScannerV2 on | #67574 |
`SessionVariable.initFuzzyModeVariables` |
| #68250: a base partition without a snapshot does not force a full MV
rebuild | #62606 | `MTMV.hasRefreshSnapshot`, `MTMVTask` refresh
planning |
**The problem, and what it cost**
| Behavior | branch-4.x tests | Master before this PR |
|---|---|---|
| SQL Server `bit_value IN (TRUE, FALSE)` → `[bit_value] IN (1, 0)` |
`JdbcScanNodeTest` | not tested; the IN-list path has no unit test (only
`= FALSE` is covered) |
| SQL Server `= TRUE` → `= 1` | `JdbcScanNodeTest` | not tested |
| `FOR TIME AS OF '... .SSS'` picks the snapshot committed at that
millisecond | `IcebergUtilsTest` | only the parser is tested; nothing
checks that the fraction reaches snapshot selection |
| `enable_file_scanner_v2` defaults to true | `SessionVariablesTest` |
only the fuzzy-mode override is tested |
| An emptied partition snapshot map reads as "no baseline" | `MTMVTest`
| only the non-empty case is tested |
| A new base partition makes REFRESH AUTO refresh only that MV partition
| `test_base_table_add_partition_mtmv` | no suite; the existing ones
only check that the partition exists, refresh it by name, or go through
IVM |
A regression in any of these would reach master's CI unnoticed.
**How this PR fixes it**
- `JdbcQueryBuilderTest`: `testSqlserverBooleanTrueLiteral` and
`testSqlserverBooleanInList`, written against `JdbcQueryBuilder` with
SPI expressions (`ConnectorComparison`, `ConnectorIn`). These replace
the deleted `JdbcScanNodeTest`.
-
`IcebergConnectorMetadataMvccTest.resolveTimestampDatetimeStringKeepsFractionalSeconds`.
The 4.x test mocked two snapshots in the same second, but this module
has no Mockito and its fixture is a real `InMemoryCatalog`. So the new
test uses the fixture's real S2 commit time instead:
- S2's exact millisecond, written with 3 or 6 fractional digits, must
resolve to S2;
- one millisecond earlier must not.
A parser that rejects the fraction throws, and one that truncates it
lands before S2. The comment of the existing whole-second test still
described a truncating parser and is corrected.
- `SessionVariablesTest`: assert the constructor default before the
fuzzy-mode case.
- `MTMVTest`: after the incomplete-map case, an emptied map must make
`hasRefreshSnapshot()` false.
-
`regression-test/suites/mtmv_p0/test_base_table_add_partition_mtmv.groovy`:
ported from #68250, with the table names hard-coded and the tables
dropped before use rather than after. The `.out` was generated on
master.
**Results**
| Test | Result |
|---|---|
| `JdbcQueryBuilderTest` | 34 run, 0 failures |
| `IcebergConnectorMetadataMvccTest` | 32 run, 0 failures |
| `MTMVTest` | 49 run, 0 failures |
| `SessionVariablesTest` | 19 run, 0 failures |
| `mtmv_p0/test_base_table_add_partition_mtmv` | passes; the generated
`.out` is byte-identical to branch-4.x's (`["p_3"]`, `PARTIAL`, then
`NOT_REFRESH`) |
Co-authored-by: yujun <[email protected]>
Co-authored-by: Gabriel <[email protected]>
---
.../iceberg/IcebergConnectorMetadataMvccTest.java | 29 ++++++++-
.../doris/connector/jdbc/JdbcQueryBuilderTest.java | 34 +++++++++++
.../test/java/org/apache/doris/mtmv/MTMVTest.java | 5 ++
.../org/apache/doris/qe/SessionVariablesTest.java | 1 +
.../mtmv_p0/test_base_table_add_partition_mtmv.out | 17 ++++++
.../test_base_table_add_partition_mtmv.groovy | 70 ++++++++++++++++++++++
6 files changed, 154 insertions(+), 2 deletions(-)
diff --git
a/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataMvccTest.java
b/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataMvccTest.java
index b0dd3baaeca..7aa7bef38e2 100644
---
a/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataMvccTest.java
+++
b/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataMvccTest.java
@@ -431,8 +431,8 @@ public class IcebergConnectorMetadataMvccTest {
// (isDigital == false) must route through
IcebergTimeUtils.datetimeToMillis(session zone) ->
// SnapshotUtil.snapshotIdAsOfTime, distinct from the digital
epoch-millis parseLong path the existing
// test drives. A null session resolves to UTC (resolveSessionZone);
zone-correctness itself is pinned by
- // IcebergTimeUtilsTest. Format one second AFTER S2 in UTC so the
second-precision parse (which truncates
- // sub-second millis) still lands at-or-after S2's commit -> resolves
to S2. MUTATION: routing the
+ // IcebergTimeUtilsTest. Format one second AFTER S2 in UTC, at
whole-second precision, so the string lands
+ // after S2's commit -> resolves to S2 (fractional seconds: see the
test below). MUTATION: routing the
// datetime string through the digital parseLong branch ->
NumberFormatException -> red; never wiring the
// datetime branch through resolveTimeTravel -> empty/wrong snapshot
-> red.
String datetime = java.time.Instant.ofEpochMilli(f.tsS2 + 1000)
@@ -445,6 +445,31 @@ public class IcebergConnectorMetadataMvccTest {
Assertions.assertEquals(f.schemaIdS2, snap.get().getSchemaId());
}
+ @Test
+ public void resolveTimestampDatetimeStringKeepsFractionalSeconds() {
+ Fixture f = fixture();
+ // Snapshots committed within the same second are told apart only by
the fraction, which a client
+ // gets from e.g. date_format(committed_at, '%Y-%m-%d %H:%i:%s.%f').
S2's exact commit millis, written
+ // with 3 or 6 fractional digits, resolves to S2; one millisecond
earlier no longer reaches S2 (it is
+ // S1 or nothing, depending on when S1 committed). A parser that
rejects the fraction throws, and one
+ // that truncates it to the second lands before S2 unless S2 happened
to commit on a whole second.
+ java.time.ZonedDateTime commit =
java.time.Instant.ofEpochMilli(f.tsS2).atZone(java.time.ZoneOffset.UTC);
+ for (String pattern : new String[] {"yyyy-MM-dd HH:mm:ss.SSS",
"yyyy-MM-dd HH:mm:ss.SSSSSS"}) {
+ String datetime =
commit.format(java.time.format.DateTimeFormatter.ofPattern(pattern));
+ Optional<ConnectorMvccSnapshot> snap = metadataFor(f.table, new
RecordingIcebergCatalogOps())
+ .resolveTimeTravel(null, handle(),
ConnectorTimeTravelSpec.timestamp(datetime, false));
+ Assertions.assertTrue(snap.isPresent(), "S2's exact commit time
must resolve: " + datetime);
+ Assertions.assertEquals(f.s2, snap.get().getSnapshotId(),
datetime);
+ Assertions.assertEquals(f.schemaIdS2, snap.get().getSchemaId(),
datetime);
+ }
+ String justBefore = java.time.Instant.ofEpochMilli(f.tsS2 -
1).atZone(java.time.ZoneOffset.UTC)
+
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd
HH:mm:ss.SSS"));
+ Optional<ConnectorMvccSnapshot> before = metadataFor(f.table, new
RecordingIcebergCatalogOps())
+ .resolveTimeTravel(null, handle(),
ConnectorTimeTravelSpec.timestamp(justBefore, false));
+ Assertions.assertFalse(before.isPresent() &&
before.get().getSnapshotId() == f.s2,
+ "one millisecond before S2's commit must not resolve to S2: "
+ justBefore);
+ }
+
// ---------------------------------------------------------------------
// B-2: getMvccPartitionView / listPartitionNames (connector level: auth
wrap + not-exist degrade)
// The math/merge/gate parity is exhaustively covered by
IcebergPartitionUtilsTest; these tests pin the
diff --git
a/fe/fe-connector/fe-connector-jdbc/src/test/java/org/apache/doris/connector/jdbc/JdbcQueryBuilderTest.java
b/fe/fe-connector/fe-connector-jdbc/src/test/java/org/apache/doris/connector/jdbc/JdbcQueryBuilderTest.java
index 30cb31a6793..640a7198920 100644
---
a/fe/fe-connector/fe-connector-jdbc/src/test/java/org/apache/doris/connector/jdbc/JdbcQueryBuilderTest.java
+++
b/fe/fe-connector/fe-connector-jdbc/src/test/java/org/apache/doris/connector/jdbc/JdbcQueryBuilderTest.java
@@ -25,6 +25,7 @@ import
org.apache.doris.connector.spi.pushdown.ConnectorColumnRef;
import org.apache.doris.connector.spi.pushdown.ConnectorComparison;
import org.apache.doris.connector.spi.pushdown.ConnectorExpression;
import org.apache.doris.connector.spi.pushdown.ConnectorFunctionCall;
+import org.apache.doris.connector.spi.pushdown.ConnectorIn;
import org.apache.doris.connector.spi.pushdown.ConnectorLiteral;
import org.apache.doris.connector.spi.pushdown.ConnectorNot;
import org.apache.doris.connector.spi.pushdown.ConnectorOr;
@@ -538,6 +539,39 @@ class JdbcQueryBuilderTest {
"SQL Server must not render FALSE keyword. SQL: " + sql);
}
+ @Test
+ void testSqlserverBooleanTrueLiteral() {
+ // SQL Server has no boolean literal and reads TRUE as a column name
(issue #64464).
+ JdbcQueryBuilder builder = sqlserverBuilder();
+ ConnectorExpression filter = new ConnectorComparison(
+ ConnectorComparison.Operator.EQ,
+ new ConnectorColumnRef("bit_value",
ConnectorType.of("BOOLEAN")),
+ ConnectorLiteral.ofBoolean(true));
+ String sql = builder.buildQuery(DB, TABLE, columns("bit_value"),
+ Optional.of(filter), -1);
+ Assertions.assertTrue(sql.contains("[bit_value] = 1"),
+ "SQL Server booleans must use 1/0 integers. SQL: " + sql);
+ Assertions.assertFalse(sql.contains("TRUE"),
+ "SQL Server must not render TRUE keyword. SQL: " + sql);
+ }
+
+ @Test
+ void testSqlserverBooleanInList() {
+ // `bit_value IN ('1', '0')` reaches the connector as an IN list of
boolean literals; every
+ // item goes through the same 1/0 rendering as a comparison.
+ JdbcQueryBuilder builder = sqlserverBuilder();
+ ConnectorExpression filter = new ConnectorIn(
+ new ConnectorColumnRef("bit_value",
ConnectorType.of("BOOLEAN")),
+ Arrays.asList(ConnectorLiteral.ofBoolean(true),
ConnectorLiteral.ofBoolean(false)),
+ false);
+ String sql = builder.buildQuery(DB, TABLE, columns("bit_value"),
+ Optional.of(filter), -1);
+ Assertions.assertTrue(sql.contains("[bit_value] IN (1, 0)"),
+ "SQL Server boolean IN lists must use 1/0 integers. SQL: " +
sql);
+ Assertions.assertFalse(sql.contains("TRUE") || sql.contains("FALSE"),
+ "SQL Server must not render TRUE/FALSE keywords. SQL: " + sql);
+ }
+
@Test
void testOceanbaseOracleBooleanLiteral() {
JdbcQueryBuilder builder = oceanBaseOracleBuilder();
diff --git a/fe/fe-core/src/test/java/org/apache/doris/mtmv/MTMVTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mtmv/MTMVTest.java
index fcdcb028410..1636d09517f 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mtmv/MTMVTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mtmv/MTMVTest.java
@@ -361,6 +361,11 @@ public class MTMVTest {
mtmv.setRefreshSnapshot(refreshSnapshot);
Assertions.assertTrue(mtmv.hasRefreshSnapshot());
+
+ // An emptied partition map means the baseline is gone; MTMVTask then
plans a COMPLETE refresh for an
+ // AUTO refresh of a non-IVM MV.
+ mtmv.setRefreshSnapshot(new MTMVRefreshSnapshot());
+ Assertions.assertFalse(mtmv.hasRefreshSnapshot());
}
@Test
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
index 306c6570f02..b978122e2f8 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
@@ -231,6 +231,7 @@ public class SessionVariablesTest extends TestWithFeService
{
@Test
public void testFileScannerV2RemainsEnabledInFuzzyMode() throws Exception {
SessionVariable sessionVar = new SessionVariable();
+ Assertions.assertTrue(sessionVar.enableFileScannerV2);
sessionVar.enableFileScannerV2 = false;
sessionVar.initFuzzyModeVariables();
Assertions.assertTrue(sessionVar.enableFileScannerV2);
diff --git
a/regression-test/data/mtmv_p0/test_base_table_add_partition_mtmv.out
b/regression-test/data/mtmv_p0/test_base_table_add_partition_mtmv.out
new file mode 100644
index 00000000000..f3f3ec2051c
--- /dev/null
+++ b/regression-test/data/mtmv_p0/test_base_table_add_partition_mtmv.out
@@ -0,0 +1,17 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !baseline --
+1 1
+
+-- !need_refresh_partitions --
+["p_3"]
+
+-- !refresh_mode --
+PARTIAL
+
+-- !after_add_partition --
+1 1
+3 3
+
+-- !refresh_mode_no_change --
+NOT_REFRESH
+
diff --git
a/regression-test/suites/mtmv_p0/test_base_table_add_partition_mtmv.groovy
b/regression-test/suites/mtmv_p0/test_base_table_add_partition_mtmv.groovy
new file mode 100644
index 00000000000..4af75ec0377
--- /dev/null
+++ b/regression-test/suites/mtmv_p0/test_base_table_add_partition_mtmv.groovy
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_base_table_add_partition_mtmv", "mtmv") {
+ sql """drop materialized view if exists
test_base_table_add_partition_mtmv_mv"""
+ sql """drop table if exists test_base_table_add_partition_mtmv_table"""
+
+ sql """
+ CREATE TABLE test_base_table_add_partition_mtmv_table (
+ k1 int,
+ k2 int
+ )
+ PARTITION BY LIST(`k1`)
+ (
+ PARTITION `p1` VALUES IN ('1'),
+ PARTITION `p2` VALUES IN ('2')
+ )
+ DISTRIBUTED BY HASH(k1) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1"
+ );
+ """
+ sql """insert into test_base_table_add_partition_mtmv_table values(1,1);"""
+
+ sql """
+ CREATE MATERIALIZED VIEW test_base_table_add_partition_mtmv_mv
+ BUILD DEFERRED REFRESH AUTO ON MANUAL
+ partition by(k1)
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES ('replication_num' = '1')
+ AS
+ SELECT * FROM test_base_table_add_partition_mtmv_table;
+ """
+ sql """REFRESH MATERIALIZED VIEW test_base_table_add_partition_mtmv_mv
AUTO"""
+ waitingMTMVTaskFinishedByMvName("test_base_table_add_partition_mtmv_mv")
+ order_qt_baseline "select * from test_base_table_add_partition_mtmv_mv"
+
+ // A new base partition makes the refresh add the matching MV partition,
which has no snapshot yet.
+ // Only that partition may be refreshed: reading it as a lost refresh
baseline would rebuild every
+ // partition of the MV, including the partitions that are already in sync.
+ sql """alter table test_base_table_add_partition_mtmv_table add partition
p3 values in ('3');"""
+ sql """insert into test_base_table_add_partition_mtmv_table values(3,3);"""
+ sql """REFRESH MATERIALIZED VIEW test_base_table_add_partition_mtmv_mv
AUTO"""
+ waitingMTMVTaskFinishedByMvName("test_base_table_add_partition_mtmv_mv")
+ order_qt_need_refresh_partitions """select NeedRefreshPartitions from
tasks('type'='mv')
+ where MvName='test_base_table_add_partition_mtmv_mv' order by
CreateTime desc limit 1"""
+ order_qt_refresh_mode """select RefreshMode from tasks('type'='mv')
+ where MvName='test_base_table_add_partition_mtmv_mv' order by
CreateTime desc limit 1"""
+ order_qt_after_add_partition "select * from
test_base_table_add_partition_mtmv_mv"
+
+ // The added partition is in sync now, so a refresh without base table
changes has nothing to do.
+ sql """REFRESH MATERIALIZED VIEW test_base_table_add_partition_mtmv_mv
AUTO"""
+ waitingMTMVTaskFinishedByMvName("test_base_table_add_partition_mtmv_mv")
+ order_qt_refresh_mode_no_change """select RefreshMode from
tasks('type'='mv')
+ where MvName='test_base_table_add_partition_mtmv_mv' order by
CreateTime desc limit 1"""
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]