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

morrySnow 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 7a7e86cd9be [improvement](statistics) Accept WITH HOT VALUE on a 
sample analyze (#69225)
7a7e86cd9be is described below

commit 7a7e86cd9beff6242b82408d96e9d8b7467a4cc9
Author: minghong <[email protected]>
AuthorDate: Tue Oct 20 14:23:23 2026 +0800

    [improvement](statistics) Accept WITH HOT VALUE on a sample analyze (#69225)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #69209
    
    Problem Summary:
    
    `ANALYZE ... WITH SAMPLE ... WITH HOT VALUE` was rejected by
    `AnalyzeCommand.validate`. The
    rejection was introduced together with `WITH HOT VALUE`: at that time a
    sample analyze always
    collected hot values, so the flag was redundant and rejecting it early
    kept the command from
    silently doing nothing.
    
    After #69209 the linear estimator no longer collects hot values, but a
    sample analyze still
    collects them for every column the DUJ1 estimator serves - and which
    estimator a column gets is
    decided per column at execution time
    (`OlapAnalysisTask.getSampleCollectInfo`: table row count,
    sampled rows, single distribution / single key column, partition flags),
    so the command layer
    cannot tell whether the flag would be honored for a particular column.
    Rejecting the combination
    is therefore wrong for the columns that do collect hot values (the DUJ1
    ones), while for the
    linear ones the flag never could force collection anyway.
    
    This PR accepts the combination. The flag keeps its only real meaning -
    it selects the hot value
    template of a full analyze - and has no effect on a sample analyze: hot
    values still come from the
    DUJ1 estimator, and a full analyze with `WITH HOT VALUE` remains the way
    to collect them for a
    linear-estimated column.
    
    Before this PR, both command paths that reach the check (`ANALYZE TABLE`
    and `ANALYZE DATABASE`)
    returned:
    
    ```
    ERROR 1105 (HY000): errCode = 2, detailMessage = Sample analyze does not 
support WITH HOT VALUE
    ```
    
    After it, the statement succeeds and the analyze runs with the estimator
    the sample picks.
    
    ### Release note
    
    `ANALYZE TABLE ... WITH SAMPLE ... WITH HOT VALUE` is no longer
    rejected. The flag only affects a
    full analyze; on a sample analyze hot values are still collected only
    for the columns analyzed by
    the DUJ1 estimator.
---
 .../trees/plans/commands/AnalyzeCommand.java       |  9 +++--
 .../plans/commands/AnalyzeTableCommandTest.java    | 44 ++++++++++++++++++++--
 .../statistics/test_full_analyze_hot_value.groovy  | 25 +++++++++---
 3 files changed, 65 insertions(+), 13 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeCommand.java
index 2b612683458..fab038f35d8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeCommand.java
@@ -140,14 +140,17 @@ public abstract class AnalyzeCommand extends Command 
implements ForwardWithSync
 
     /**
      * Validate analyze command properties.
+     *
+     * <p>{@code WITH HOT VALUE} is deliberately not validated against the 
analysis method. The flag
+     * only selects the hot value template of a full analyze; a sample analyze 
collects hot values
+     * depending on the estimator it picks per column at execution time (DUJ1 
does, the linear
+     * estimator does not), which cannot be known here. The combination is 
therefore accepted, and
+     * the flag simply has no effect on a sample analyze.
      */
     public void validate(ConnectContext ctx) throws UserException {
         if (analyzeProperties != null) {
             analyzeProperties.check();
         }
-        if (analyzeProperties.hasCollectHotValue() && getAnalysisMethod() == 
AnalysisInfo.AnalysisMethod.SAMPLE) {
-            throw new AnalysisException("Sample analyze does not support WITH 
HOT VALUE");
-        }
     }
 
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeTableCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeTableCommandTest.java
index 369644bf193..21ae3e05fe4 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeTableCommandTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/AnalyzeTableCommandTest.java
@@ -19,11 +19,15 @@ package org.apache.doris.nereids.trees.plans.commands;
 
 import org.apache.doris.analysis.AnalyzeProperties;
 import org.apache.doris.backup.CatalogMocker;
+import org.apache.doris.catalog.DatabaseIf;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.NameSpaceContext;
+import org.apache.doris.catalog.TableIf;
 import org.apache.doris.catalog.info.PartitionNamesInfo;
 import org.apache.doris.catalog.info.TableNameInfo;
 import org.apache.doris.common.AnalysisException;
+import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.datasource.CatalogMgr;
 import org.apache.doris.datasource.InternalCatalog;
 import org.apache.doris.mysql.privilege.AccessControllerManager;
 import org.apache.doris.mysql.privilege.PrivPredicate;
@@ -151,13 +155,45 @@ public class AnalyzeTableCommandTest {
     }
 
     @Test
-    void testSampleAnalyzeWithHotValueRejected() {
+    void testSampleAnalyzeWithHotValueAccepted() throws Exception {
+        // WITH HOT VALUE is accepted on a sample analyze: the flag only 
selects the hot value
+        // template of a full analyze, while a sample analyze collects hot 
values depending on the
+        // estimator it picks per column at execution time.
+        TableIf table = Mockito.mock(TableIf.class);
+        DatabaseIf database = Mockito.mock(DatabaseIf.class);
+        CatalogIf catalog = Mockito.mock(CatalogIf.class);
+        CatalogMgr catalogMgr = Mockito.mock(CatalogMgr.class);
+        Mockito.when(ctx.getEnv()).thenReturn(env);
+        Mockito.when(env.getCatalogMgr()).thenReturn(catalogMgr);
+        
Mockito.when(catalogMgr.getCatalogOrAnalysisException(internalCtl)).thenReturn(catalog);
+        
Mockito.when(catalog.getDbOrAnalysisException(CatalogMocker.TEST_DB_NAME)).thenReturn(database);
+        
Mockito.when(database.getTableOrAnalysisException(CatalogMocker.TEST_TBL_NAME)).thenReturn(table);
+        
Mockito.when(accessManager.checkTblPriv(Mockito.nullable(ConnectContext.class), 
Mockito.eq(internalCtl),
+                Mockito.eq(CatalogMocker.TEST_DB_NAME), 
Mockito.eq(CatalogMocker.TEST_TBL_NAME),
+                Mockito.eq(PrivPredicate.SELECT))).thenReturn(true);
+
         NereidsParser parser = new NereidsParser();
         AnalyzeTableCommand command = (AnalyzeTableCommand) parser.parseSingle(
                 "ANALYZE TABLE test_db.test_tbl WITH SAMPLE ROWS 100 WITH HOT 
VALUE");
-        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class,
-                () -> command.validate(ctx));
-        Assertions.assertEquals("Sample analyze does not support WITH HOT 
VALUE", exception.getDetailMessage());
+        Assertions.assertDoesNotThrow(() -> command.validate(ctx));
+        
Assertions.assertTrue(command.getAnalyzeProperties().hasCollectHotValue());
+        Assertions.assertEquals(AnalysisInfo.AnalysisMethod.SAMPLE, 
command.getAnalysisMethod());
+    }
+
+    @Test
+    void testSampleAnalyzeDatabaseWithHotValueAccepted() throws Exception {
+        // The rejection lived in the validate shared by both commands, so 
ANALYZE DATABASE accepts
+        // the flag as well.
+        DatabaseIf database = Mockito.mock(DatabaseIf.class);
+        CatalogIf catalog = Mockito.mock(CatalogIf.class);
+        Mockito.when(env.getCurrentCatalog()).thenReturn(catalog);
+        
Mockito.when(catalog.getDbOrAnalysisException(CatalogMocker.TEST_DB_NAME)).thenReturn(database);
+
+        NereidsParser parser = new NereidsParser();
+        AnalyzeDatabaseCommand command = (AnalyzeDatabaseCommand) 
parser.parseSingle(
+                "ANALYZE DATABASE test_db WITH SAMPLE ROWS 100 WITH HOT 
VALUE");
+        Assertions.assertDoesNotThrow(() -> command.validate(ctx));
+        Assertions.assertEquals(AnalysisInfo.AnalysisMethod.SAMPLE, 
command.getAnalysisMethod());
     }
 
     private Map<String, String> defaultAnalyzeProperties() {
diff --git 
a/regression-test/suites/statistics/test_full_analyze_hot_value.groovy 
b/regression-test/suites/statistics/test_full_analyze_hot_value.groovy
index c709f20ed42..14928393224 100644
--- a/regression-test/suites/statistics/test_full_analyze_hot_value.groovy
+++ b/regression-test/suites/statistics/test_full_analyze_hot_value.groovy
@@ -142,12 +142,16 @@ suite("test_full_analyze_hot_value") {
     assertEquals(1, result.size())
     assertEquals("null", String.valueOf(result[0][17]))
 
-    // Test 6: Sample analyze rejects WITH HOT VALUE because it cannot honor 
the flag: inside a
-    // sample analyze hot values only come from the DUJ1 estimator.
-    test {
-        sql """analyze table full_hot_skew with sample rows 400 with sync with 
hot value"""
-        exception "Sample analyze does not support WITH HOT VALUE"
-    }
+    // Test 6: WITH HOT VALUE is accepted on a sample analyze and has no 
effect here: hot values are
+    // collected only for the columns the DUJ1 estimator serves, and 
full_hot_skew is small enough
+    // that its sample analyze falls back to a full scan (rendered with the 
linear template), which
+    // does not collect them.
+    sql """drop stats full_hot_skew"""
+    sql """analyze table full_hot_skew with sample rows 400 with sync with hot 
value"""
+    result = sql """show column stats full_hot_skew(value1)"""
+    logger.info("Test6 result: " + result)
+    assertEquals(1, result.size())
+    assertEquals("null", String.valueOf(result[0][17]))
 
     // Test 7: Explicit full analyze produces the same hot_value as a sample 
analyze that really
     // samples. The column value is uniform, so the sampled rows always carry 
the same
@@ -182,6 +186,15 @@ suite("test_full_analyze_hot_value") {
     // Both full and sample should produce the same hot_value entry.
     assertEquals(fullResult[0][17], sampleResult[0][17])
 
+    // WITH HOT VALUE is accepted on a sample analyze and does not interfere 
with the estimator:
+    // this table really samples, so DUJ1 still collects the hot value.
+    sql """drop stats full_hot_sample_agree"""
+    sql """analyze table full_hot_sample_agree with sample rows 400 with sync 
with hot value"""
+    result = sql """show column stats full_hot_sample_agree(value1)"""
+    logger.info("Test7 sample with hot value result: " + result)
+    assertEquals(1, result.size())
+    assertEquals("' : ;a':1.0", result[0][17])
+
     // Test 8: Explicit full analyze on empty table should produce an empty 
hot_value string.
     sql """drop table if exists full_hot_empty"""
     sql """CREATE TABLE full_hot_empty (


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to