This is an automated email from the ASF dual-hosted git repository.
hello-stephen 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 7979a5a5e7a [fix](statistics) Use one algorithm decision to pick both
params and template in sample analyze (#66578)
7979a5a5e7a is described below
commit 7979a5a5e7a64dac55e544dabfe0cd6c5273bf9e
Author: yujun <[email protected]>
AuthorDate: Tue Aug 11 11:16:32 2026 +0800
[fix](statistics) Use one algorithm decision to pick both params and
template in sample analyze (#66578)
## Proposed changes
Fix sample analyze generating invalid SQL when the DUJ1 template is
forced on a small table.
`doSample()` previously filled SQL params and selected the SQL template
through two independent decisions: `getSampleParams()` decided based on
`tableRowCount`/`scanFullTable` (filling LINEAR-style params that
reference the raw column name), while the template was picked separately
via `useLinearAnalyzeTemplate()`. When `useDUJ1Template` was forced, the
DUJ1 template was chosen but the params were still filled by the
FULL-scan branch with `${colName}` references. The DUJ1 template's
`cte1` only exposes `col_value`/`count`/`column_length`, so the
generated SQL failed to bind, e.g. `Unknown column 'id' in 'table list'
in PROJECT clause`, which made `test_analyze_long_string` flaky.
This PR makes the algorithm a single decision: `getSampleCollectInfo()`
decides `AnalyzeSampleAlgorithm { FULL, LINEAR, DUJ1 }` once, and both
param filling and template selection derive from the same algorithm, so
params always match the template.
## Key changes
- Add `AnalyzeSampleAlgorithm` enum (FULL/LINEAR/DUJ1) in
`BaseAnalysisTask`.
- Rename `getAnalyzeAlgorithm` to `getSampleCollectInfo`: decide the
algorithm once and return it together with the picked sample tablets.
- Fill params (`getSampleParams`/`setSampleParamsByAlgorithm`) and pick
the SQL template (`doSample`) from the same algorithm decision.
- Remove the `scanFullTable` field, its setter/getter, and all related
checks.
## Unit test
- Updated `OlapAnalysisTaskTest` to cover FULL/LINEAR/DUJ1 param
filling, template selection driven by the algorithm, and the
sample-tablets-not-enough fallback.
---
.../apache/doris/statistics/BaseAnalysisTask.java | 15 ++
.../apache/doris/statistics/OlapAnalysisTask.java | 157 +++++++++++++--------
.../doris/statistics/OlapAnalysisTaskTest.java | 118 +++++++++++++---
3 files changed, 208 insertions(+), 82 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
index 1e68e01210b..6ad286562ce 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
@@ -65,6 +65,21 @@ public abstract class BaseAnalysisTask {
public static final long LIMIT_SIZE = 1024 * 1024 * 1024; // 1GB
public static final double LIMIT_FACTOR = 1.2;
+ /**
+ * The statistics collection algorithm chosen for a sampled analyze task.
It determines
+ * both the SQL template used to collect stats and the params rendered
into it, so callers
+ * must derive the template and the params from the same algorithm
decision.
+ */
+ public enum AnalyzeSampleAlgorithm {
+ // Full table scan without sampling, used when the table is small or
the sample
+ // tablets contain too few rows. Statistics are computed with the
LINEAR template.
+ FULL,
+ // Linear estimator, used for single unique key column / single
distribution column.
+ LINEAR,
+ // DUJ1 estimator, based on the PostgreSQL analyze algorithm.
+ DUJ1
+ }
+
/**
* Marker string embedded in {@code assert_true} inside statistics
collection SQL.
* When any row's string column length exceeds the configured limit, BE
throws an
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
b/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
index 45e84da9177..a7ac364c984 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
@@ -68,7 +68,6 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
private boolean keyColumnSampleTooManyRows = false;
private boolean partitionColumnSampleTooManyRows = false;
- private boolean scanFullTable = false;
private static final long MAXIMUM_SAMPLE_ROWS = 1_000_000_000;
public static final long NO_SKIP_TABLET_ID = -1;
@@ -123,16 +122,17 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
long tableRowCount = info.indexId == -1
? tbl.getRowCount()
: ((OlapTable) tbl).getRowCountForIndex(info.indexId, false);
- getSampleParams(params, tableRowCount);
+ SampleCollectInfo collectInfo = getSampleCollectInfo(tableRowCount);
+ getSampleParams(params, tableRowCount, collectInfo);
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
String sql;
- if (useLinearAnalyzeTemplate()) {
- sql = stringSubstitutor.replace(LINEAR_ANALYZE_TEMPLATE);
- } else {
+ if (collectInfo.algorithm == AnalyzeSampleAlgorithm.DUJ1) {
sql = stringSubstitutor.replace(DUJ1_ANALYZE_TEMPLATE);
+ } else {
+ sql = stringSubstitutor.replace(LINEAR_ANALYZE_TEMPLATE);
}
- LOG.info("Analyze param: scanFullTable {}, partitionColumnTooMany {},
keyColumnTooMany {}",
- scanFullTable, partitionColumnSampleTooManyRows,
keyColumnSampleTooManyRows);
+ LOG.info("Analyze param: algorithm {}, partitionColumnTooMany {},
keyColumnTooMany {}",
+ collectInfo.algorithm, partitionColumnSampleTooManyRows,
keyColumnSampleTooManyRows);
LOG.debug(sql);
runQuery(sql);
}
@@ -234,9 +234,8 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
LOG.info("Add large tablet {} in table {} back, with rows {}",
largeTabletId, tbl.getName(), largeTabletRows);
}
- if (selectedRows < targetSampleRows) {
- scanFullTable = true;
- } else if (forPartitionColumn && selectedRows > MAXIMUM_SAMPLE_ROWS) {
+ // If sampled rows are not enough, the caller falls back to a full
table scan.
+ if (forPartitionColumn && selectedRows > MAXIMUM_SAMPLE_ROWS) {
// If the selected tablets for partition column contain too many
rows, change to linear sample.
partitionColumnSampleTooManyRows = true;
sampleTabletIds.clear();
@@ -251,12 +250,58 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
return Pair.of(sampleTabletIds, selectedRows);
}
+ /**
+ * Result of the sample analyze algorithm decision: the chosen algorithm
and, when
+ * sampling is actually performed, the picked tablets used to build the
{@code TABLET(...)}
+ * hint. Kept local to the sampling flow so the long-lived task holds no
sampling state.
+ */
+ protected static class SampleCollectInfo {
+ public final AnalyzeSampleAlgorithm algorithm;
+ public final Pair<List<Long>, Long> sampleTablets;
+
+ public SampleCollectInfo(AnalyzeSampleAlgorithm algorithm,
Pair<List<Long>, Long> sampleTablets) {
+ this.algorithm = algorithm;
+ this.sampleTablets = sampleTablets;
+ }
+ }
+
+ /**
+ * Decide which statistics algorithm to use and, when sampling is actually
performed,
+ * collect the sample tablets. Call this before
+ * {@link #getSampleParams(Map, long, SampleCollectInfo)} so the params
are always filled
+ * according to the algorithm used to pick the template.
+ */
+ protected SampleCollectInfo getSampleCollectInfo(long tableRowCount) {
+ // Debug point used by tests to force the DUJ1 algorithm. Check it
before the
+ // row-count based FULL decision so tests are not affected by BE row
count report
+ // timing (e.g. a small table whose row count is not fully reported
yet).
+ if (DebugPointUtil.isEnable("OlapAnalysisTask.useDUJ1Template")) {
+ return new SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
getSampleTablets());
+ }
+ long targetSampleRows = getSampleRows();
+ // If table row count is less than the target sample row count, simple
scan the full table.
+ if (tableRowCount <= targetSampleRows) {
+ return new SampleCollectInfo(AnalyzeSampleAlgorithm.FULL, null);
+ }
+ Pair<List<Long>, Long> sampleTablets = getSampleTablets();
+ if (sampleTablets.second < targetSampleRows) {
+ // Sampled tablets contain too few rows, fall back to a full table
scan.
+ return new SampleCollectInfo(AnalyzeSampleAlgorithm.FULL, null);
+ }
+ if (useLinearAnalyzeTemplate()) {
+ return new SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
sampleTablets);
+ }
+ return new SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
sampleTablets);
+ }
+
/**
* Get the sql params for this sample task.
* @param params Sql params to use in analyze task.
* @param tableRowCount BE reported table/index row count.
+ * @param info The analyze algorithm decision from {@link
#getSampleCollectInfo(long)}.
*/
- protected void getSampleParams(Map<String, String> params, long
tableRowCount) {
+ protected void getSampleParams(Map<String, String> params, long
tableRowCount,
+ SampleCollectInfo info) {
long targetSampleRows = getSampleRows();
params.put("rowCount", String.valueOf(tableRowCount));
params.put("type", col.getType().toString());
@@ -270,44 +315,51 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
params.put("preAggHint", "/*+PREAGGOPEN*/");
}
- // If table row count is less than the target sample row count, simple
scan the full table.
- if (tableRowCount <= targetSampleRows) {
+ // If the algorithm is FULL (table too small or sampled rows not
enough), scan the full table.
+ if (info.algorithm == AnalyzeSampleAlgorithm.FULL) {
params.put("scaleFactor", "1");
params.put("sampleHints", "");
- params.put("ndvFunction", "ROUND(NDV(`${colName}`) *
${scaleFactor})");
// For full table scan, use COUNT(1) for table row count.
params.put("rowCount", "COUNT(1)");
- params.put("rowCount2", "(SELECT COUNT(1) FROM cte1 WHERE
`${colName}` IS NOT NULL)");
- scanFullTable = true;
- return;
- }
- Pair<List<Long>, Long> sampleTabletsInfo = getSampleTablets();
- String tabletStr = sampleTabletsInfo.first.stream()
- .map(Object::toString)
- .collect(Collectors.joining(", "));
- String sampleHints = scanFullTable ? "" : String.format("TABLET(%s)",
tabletStr);
- params.put("sampleHints", sampleHints);
- long selectedRows = sampleTabletsInfo.second;
- long finalScanRows = selectedRows;
- double scaleFactor = scanFullTable ? 1 : (double) tableRowCount /
finalScanRows;
- params.put("scaleFactor", String.valueOf(scaleFactor));
-
- // If the tablets to be sampled are too large, use limit to control
the rows to read, and re-calculate
- // the scaleFactor.
- if (needLimit()) {
- finalScanRows = Math.min(targetSampleRows, selectedRows);
- if (col.isKey() && keyColumnSampleTooManyRows) {
- finalScanRows = MAXIMUM_SAMPLE_ROWS;
- }
- // Empty table doesn't need to limit.
- if (finalScanRows > 0) {
- scaleFactor = (double) tableRowCount / finalScanRows;
- params.put("limit", "limit " + finalScanRows);
- params.put("scaleFactor", String.valueOf(scaleFactor));
+ } else {
+ String tabletStr = info.sampleTablets.first.stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", "));
+ params.put("sampleHints", String.format("TABLET(%s)", tabletStr));
+ long selectedRows = info.sampleTablets.second;
+ long finalScanRows = selectedRows;
+ double scaleFactor = (double) tableRowCount / finalScanRows;
+ params.put("scaleFactor", String.valueOf(scaleFactor));
+
+ // If the tablets to be sampled are too large, use limit to
control the rows to read, and re-calculate
+ // the scaleFactor.
+ if (needLimit()) {
+ finalScanRows = Math.min(targetSampleRows, selectedRows);
+ if (col.isKey() && keyColumnSampleTooManyRows) {
+ finalScanRows = MAXIMUM_SAMPLE_ROWS;
+ }
+ // Empty table doesn't need to limit.
+ if (finalScanRows > 0) {
+ scaleFactor = (double) tableRowCount / finalScanRows;
+ params.put("limit", "limit " + finalScanRows);
+ params.put("scaleFactor", String.valueOf(scaleFactor));
+ }
}
}
- // Set algorithm related params.
- if (useLinearAnalyzeTemplate()) {
+ setSampleParamsByAlgorithm(params, tableRowCount, info.algorithm);
+ }
+
+ /**
+ * Set the algorithm related params. Must be called with the same
algorithm that the
+ * caller uses to pick the template, so the params always match the
template.
+ */
+ protected void setSampleParamsByAlgorithm(Map<String, String> params, long
tableRowCount,
+ AnalyzeSampleAlgorithm algorithm) {
+ if (algorithm == AnalyzeSampleAlgorithm.DUJ1) {
+ params.put("ndvFunction",
getNdvFunction(String.valueOf(tableRowCount)));
+ params.put("dataSizeFunction", getDataSizeFunction(col, true));
+ params.put("rowCount2", "(SELECT SUM(`count`) FROM cte1 WHERE
`col_value` IS NOT NULL)");
+ } else {
params.put("rowCount2", "(SELECT COUNT(1) FROM cte1 WHERE
`${colName}` IS NOT NULL)");
// For single unique key, use count as ndv.
if (isSingleUniqueKey()) {
@@ -315,10 +367,6 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
} else {
params.put("ndvFunction", "ROUND(NDV(`${colName}`) *
${scaleFactor})");
}
- } else {
- params.put("ndvFunction",
getNdvFunction(String.valueOf(tableRowCount)));
- params.put("dataSizeFunction", getDataSizeFunction(col, true));
- params.put("rowCount2", "(SELECT SUM(`count`) FROM cte1 WHERE
`col_value` IS NOT NULL)");
}
}
@@ -504,9 +552,6 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
* @return Return true when need to limit.
*/
protected boolean needLimit() {
- if (scanFullTable) {
- return false;
- }
// Key column is sorted, use limit will cause the ndv not accurate
enough, so skip key columns.
if (col.isKey() && !keyColumnSampleTooManyRows) {
return false;
@@ -537,7 +582,7 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
if (DebugPointUtil.isEnable("OlapAnalysisTask.useDUJ1Template")) {
return false;
}
- if (partitionColumnSampleTooManyRows || scanFullTable) {
+ if (partitionColumnSampleTooManyRows) {
return true;
}
if (isSingleUniqueKey()) {
@@ -590,14 +635,4 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
public void setPartitionColumnSampleTooManyRows(boolean value) {
partitionColumnSampleTooManyRows = value;
}
-
- @VisibleForTesting
- public void setScanFullTable(boolean value) {
- scanFullTable = value;
- }
-
- @VisibleForTesting
- public boolean scanFullTable() {
- return scanFullTable;
- }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/statistics/OlapAnalysisTaskTest.java
b/fe/fe-core/src/test/java/org/apache/doris/statistics/OlapAnalysisTaskTest.java
index ae29fc4c9ce..930fa60d36a 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/statistics/OlapAnalysisTaskTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/statistics/OlapAnalysisTaskTest.java
@@ -38,11 +38,13 @@ import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.FeConstants;
import org.apache.doris.common.Pair;
+import org.apache.doris.common.util.DebugPointUtil;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.qe.SessionVariable;
import org.apache.doris.statistics.AnalysisInfo.AnalysisMethod;
import org.apache.doris.statistics.AnalysisInfo.JobType;
+import org.apache.doris.statistics.BaseAnalysisTask.AnalyzeSampleAlgorithm;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.thrift.TStorageMedium;
@@ -141,7 +143,11 @@ public class OlapAnalysisTaskTest {
OlapAnalysisTask olapAnalysisTask = Mockito.spy(new
OlapAnalysisTask());
Mockito.doReturn(new ResultRow(Lists.newArrayList("1",
"2"))).when(olapAnalysisTask).collectMinMax();
-
Mockito.doNothing().when(olapAnalysisTask).getSampleParams(ArgumentMatchers.any(),
ArgumentMatchers.anyLong());
+
Mockito.doNothing().when(olapAnalysisTask).getSampleParams(ArgumentMatchers.any(),
+ ArgumentMatchers.anyLong(), ArgumentMatchers.any());
+ Mockito.doReturn(new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)))
+
.when(olapAnalysisTask).getSampleCollectInfo(ArgumentMatchers.anyLong());
Mockito.doReturn(true).when(olapAnalysisTask).useLinearAnalyzeTemplate();
Mockito.doAnswer(inv -> {
String sql = inv.getArgument(0);
@@ -191,7 +197,10 @@ public class OlapAnalysisTaskTest {
+ "t2) SELECT * FROM cte2 CROSS JOIN cte3", sql);
return null;
}).when(olapAnalysisTask).runQuery(ArgumentMatchers.anyString());
-
Mockito.doReturn(false).when(olapAnalysisTask).useLinearAnalyzeTemplate();
+ // Second run: the DUJ1 algorithm is chosen, so the DUJ1 template must
be used.
+ Mockito.doReturn(new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)))
+
.when(olapAnalysisTask).getSampleCollectInfo(ArgumentMatchers.anyLong());
olapAnalysisTask.doSample();
}
@@ -284,11 +293,6 @@ public class OlapAnalysisTaskTest {
task.setPartitionColumnSampleTooManyRows(true);
Assertions.assertTrue(task.useLinearAnalyzeTemplate());
- task.setPartitionColumnSampleTooManyRows(false);
- task.setScanFullTable(true);
- Assertions.assertTrue(task.useLinearAnalyzeTemplate());
-
- task.setScanFullTable(false);
task.setPartitionColumnSampleTooManyRows(false);
OlapAnalysisTask spyTask = Mockito.spy(task);
Mockito.doReturn(true).when(spyTask).isSingleUniqueKey();
@@ -303,22 +307,45 @@ public class OlapAnalysisTaskTest {
Mockito.doReturn(Pair.of(Lists.newArrayList(1L, 2L),
100L)).when(task).getSampleTablets();
Mockito.doReturn(false).when(task).needLimit();
Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
+ Mockito.doReturn(false).when(task).isSingleUniqueKey();
OlapTable mockTable = Mockito.mock(OlapTable.class);
Mockito.when(mockTable.getKeysType()).thenReturn(KeysType.DUP_KEYS);
task.col = new Column("testColumn", Type.INT, true, null, null, "");
task.setTable(mockTable);
- task.getSampleParams(params, 10);
- Assertions.assertTrue(task.scanFullTable());
+ // FULL algorithm: scan the full table and fill LINEAR-style params
(raw column reference).
+ task.getSampleParams(params, 10,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.FULL, null));
Assertions.assertEquals("1", params.get("scaleFactor"));
Assertions.assertEquals("", params.get("sampleHints"));
+ Assertions.assertEquals("(SELECT COUNT(1) FROM cte1 WHERE `${colName}`
IS NOT NULL)",
+ params.get("rowCount2"));
Assertions.assertEquals("ROUND(NDV(`${colName}`) * ${scaleFactor})",
params.get("ndvFunction"));
Assertions.assertNull(params.get("preAggHint"));
Assertions.assertEquals("COUNT(1)", params.get("rowCount"));
params.clear();
- task.getSampleParams(params, 10000);
- Assertions.assertEquals("10000", params.get("rowCount"));
+ // LINEAR algorithm with sample tablets.
+ task.getSampleParams(params, 10000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
+ Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
+ Assertions.assertEquals("(SELECT COUNT(1) FROM cte1 WHERE `${colName}`
IS NOT NULL)",
+ params.get("rowCount2"));
+ Assertions.assertEquals("ROUND(NDV(`${colName}`) * ${scaleFactor})",
params.get("ndvFunction"));
+ params.clear();
+
+ // DUJ1 algorithm with sample tablets: rowCount2 and ndvFunction must
reference the cte1
+ // output columns (col_value/count), not the raw column name,
otherwise the DUJ1 SQL
+ // fails with "Unknown column".
+ task.getSampleParams(params, 10000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
+ Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
+ Assertions.assertEquals("(SELECT SUM(`count`) FROM cte1 WHERE
`col_value` IS NOT NULL)",
+ params.get("rowCount2"));
+
Assertions.assertTrue(params.get("ndvFunction").contains("`t1`.`col_value`"),
+ "DUJ1 ndvFunction must reference cte1 output, got: " +
params.get("ndvFunction"));
params.clear();
OlapTable mockTable2 = Mockito.mock(OlapTable.class);
@@ -330,7 +357,9 @@ public class OlapAnalysisTaskTest {
Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
task.col = new Column("testColumn", Type.INT, false, null, null, "");
task.setTable(mockTable2);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("10.0", params.get("scaleFactor"));
Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
Assertions.assertEquals("SUM(`t1`.`count`) * COUNT(`t1`.`col_value`) /
(SUM(`t1`.`count`) - SUM(IF(`t1`.`count` = 1 and `t1`.`col_value` is not null,
1, 0)) + SUM(IF(`t1`.`count` = 1 and `t1`.`col_value` is not null, 1, 0)) *
SUM(`t1`.`count`) / 1000)", params.get("ndvFunction"));
@@ -349,7 +378,9 @@ public class OlapAnalysisTaskTest {
Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
task.col = new Column("testColumn", Type.INT, false, null, null, "");
task.setTable(mockTable3);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("/*+PREAGGOPEN*/", params.get("preAggHint"));
params.clear();
@@ -363,7 +394,9 @@ public class OlapAnalysisTaskTest {
Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
task.col = new Column("testColumn", Type.INT, false, null, null, "");
task.setTable(mockTable4);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertNull(params.get("preAggHint"));
params.clear();
@@ -377,7 +410,9 @@ public class OlapAnalysisTaskTest {
Mockito.when(mockTable5.getKeysType()).thenReturn(KeysType.DUP_KEYS);
task.col = new Column("test", PrimitiveType.INT);
task.setTable(mockTable5);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("10.0", params.get("scaleFactor"));
Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
Assertions.assertEquals("ROUND(NDV(`${colName}`) * ${scaleFactor})",
params.get("ndvFunction"));
@@ -393,7 +428,9 @@ public class OlapAnalysisTaskTest {
Mockito.when(mockTable6.getKeysType()).thenReturn(KeysType.DUP_KEYS);
task.col = new Column("test", PrimitiveType.INT);
task.setTable(mockTable6);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("10.0", params.get("scaleFactor"));
Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
Assertions.assertEquals("1000", params.get("ndvFunction"));
@@ -409,7 +446,9 @@ public class OlapAnalysisTaskTest {
Mockito.when(mockTable7.getKeysType()).thenReturn(KeysType.DUP_KEYS);
task.col = new Column("test", PrimitiveType.INT);
task.setTable(mockTable7);
- task.getSampleParams(params, 1000);
+ task.getSampleParams(params, 1000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("20.0", params.get("scaleFactor"));
Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
Assertions.assertEquals("1000", params.get("ndvFunction"));
@@ -428,13 +467,45 @@ public class OlapAnalysisTaskTest {
true, null, null, null);
task.setKeyColumnSampleTooManyRows(true);
task.setTable(mockTable8);
- task.getSampleParams(params, 2000000000);
+ task.getSampleParams(params, 2000000000,
+ new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)));
Assertions.assertEquals("2.0", params.get("scaleFactor"));
Assertions.assertEquals("TABLET(1, 2)", params.get("sampleHints"));
Assertions.assertEquals("2000000000", params.get("ndvFunction"));
Assertions.assertEquals("limit 1000000000", params.get("limit"));
}
+ @Test
+ public void testGetSampleCollectInfo() {
+ // tableRowCount <= sample rows -> full table scan.
+ OlapAnalysisTask task = Mockito.spy(new OlapAnalysisTask());
+ Mockito.doReturn(100L).when(task).getSampleRows();
+ Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
+ OlapAnalysisTask.SampleCollectInfo info =
task.getSampleCollectInfo(10);
+ Assertions.assertEquals(AnalyzeSampleAlgorithm.FULL, info.algorithm);
+
+ // tableRowCount > sample rows -> sample tablets, and
useLinearAnalyzeTemplate decides.
+ Mockito.doReturn(Pair.of(Lists.newArrayList(1L, 2L),
100L)).when(task).getSampleTablets();
+ Mockito.doReturn(true).when(task).useLinearAnalyzeTemplate();
+ info = task.getSampleCollectInfo(10000);
+ Assertions.assertEquals(AnalyzeSampleAlgorithm.LINEAR, info.algorithm);
+
+ // sample tablets contain too few rows -> fall back to full table scan.
+ Mockito.doReturn(Pair.of(Lists.newArrayList(1L, 2L),
10L)).when(task).getSampleTablets();
+ info = task.getSampleCollectInfo(10000);
+ Assertions.assertEquals(AnalyzeSampleAlgorithm.FULL, info.algorithm);
+
+ // Debug point useDUJ1Template forces DUJ1 even when the row count
would normally
+ // fall back to a full table scan, so tests are not affected by BE row
count timing.
+ try (MockedStatic<DebugPointUtil> mocked =
Mockito.mockStatic(DebugPointUtil.class)) {
+ mocked.when(() ->
DebugPointUtil.isEnable("OlapAnalysisTask.useDUJ1Template")).thenReturn(true);
+ Mockito.doReturn(Pair.of(Lists.newArrayList(1L, 2L),
10L)).when(task).getSampleTablets();
+ info = task.getSampleCollectInfo(10000);
+ Assertions.assertEquals(AnalyzeSampleAlgorithm.DUJ1,
info.algorithm);
+ }
+ }
+
@Test
public void testGetSkipPartitionId() throws AnalysisException {
OlapTable tableIf = Mockito.mock(OlapTable.class);
@@ -885,7 +956,8 @@ public class OlapAnalysisTaskTest {
OlapAnalysisTask task = Mockito.spy(new OlapAnalysisTask());
Mockito.doReturn(new ResultRow(Lists.newArrayList("1",
"2"))).when(task).collectMinMax();
- Mockito.doNothing().when(task).getSampleParams(ArgumentMatchers.any(),
ArgumentMatchers.anyLong());
+ Mockito.doNothing().when(task).getSampleParams(ArgumentMatchers.any(),
ArgumentMatchers.anyLong(),
+ ArgumentMatchers.any());
Mockito.doAnswer(invocation -> {
String sql = invocation.getArgument(0);
Assertions.assertTrue(sql.contains("as `hot_value`"), sql);
@@ -908,9 +980,13 @@ public class OlapAnalysisTaskTest {
task.db = databaseIf;
task.tableSample = new TableSample(false, 100L);
- Mockito.doReturn(true).when(task).useLinearAnalyzeTemplate();
+ Mockito.doReturn(new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.LINEAR,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)))
+ .when(task).getSampleCollectInfo(Mockito.anyLong());
task.doSample();
- Mockito.doReturn(false).when(task).useLinearAnalyzeTemplate();
+ Mockito.doReturn(new
OlapAnalysisTask.SampleCollectInfo(AnalyzeSampleAlgorithm.DUJ1,
+ Pair.of(Lists.newArrayList(1L, 2L), 100L)))
+ .when(task).getSampleCollectInfo(Mockito.anyLong());
task.doSample();
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]