This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.1 by this push:
new b779c4a3a35 branch-3.1: [feature](index)Support light index add for
inverted index without parser and ngram bf index #48461 #52251 (#52894)
b779c4a3a35 is described below
commit b779c4a3a35a86e67c7d73b865a2038e61e9da7d
Author: airborne12 <[email protected]>
AuthorDate: Wed Jul 9 13:42:39 2025 +0800
branch-3.1: [feature](index)Support light index add for inverted index
without parser and ngram bf index #48461 #52251 (#52894)
cherry pick from #48461 and #52251
---------
Co-authored-by: qiye <[email protected]>
---
fe/fe-core/src/main/cup/sql_parser.cup | 10 +-
.../apache/doris/alter/SchemaChangeHandler.java | 92 ++-
.../org/apache/doris/alter/SchemaChangeJobV2.java | 15 +-
.../apache/doris/analysis/BuildIndexClause.java | 61 +-
.../java/org/apache/doris/analysis/IndexDef.java | 4 +-
.../main/java/org/apache/doris/catalog/Index.java | 23 +
.../java/org/apache/doris/catalog/OlapTable.java | 14 +
.../cloud/datasource/CloudInternalCatalog.java | 8 +
.../java/org/apache/doris/qe/SessionVariable.java | 17 +
.../org/apache/doris/alter/CloudIndexTest.java | 658 +++++++++++++++++++++
.../org/apache/doris/alter/IndexChangeJobTest.java | 179 +++++-
.../doris/alter/SchemaChangeHandlerTest.java | 5 +-
.../org/apache/doris/catalog/CatalogTestUtil.java | 42 +-
.../test_ngram_bloomfilter_index_change.out | Bin 0 -> 5433 bytes
.../pipeline/cloud_p1/conf/fe_custom.conf | 1 -
.../test_ngram_bloomfilter_index_change.groovy | 325 ++++++++++
16 files changed, 1341 insertions(+), 113 deletions(-)
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index e2f77ba262f..d56090de37c 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -902,7 +902,6 @@ nonterminal Map<String, String> key_value_map,
opt_key_value_map, opt_key_value_
opt_ext_properties, opt_enable_feature_properties, properties;
nonterminal ColumnDef column_definition;
nonterminal IndexDef index_definition;
-nonterminal IndexDef build_index_definition;
nonterminal ArrayList<ColumnDef> column_definition_list;
nonterminal ArrayList<IndexDef> index_definition_list;
nonterminal AggregateType opt_agg_type;
@@ -2206,7 +2205,7 @@ create_stmt ::=
:}
| KW_BUILD KW_INDEX ident:indexName KW_ON table_name:tableName
opt_partition_names:partitionNames
{:
- RESULT = new AlterTableStmt(tableName, Lists.newArrayList(new
BuildIndexClause(tableName, new IndexDef(indexName, partitionNames, true),
false)));
+ RESULT = new AlterTableStmt(tableName, Lists.newArrayList(new
BuildIndexClause(tableName, indexName, partitionNames, false)));
:}
/* stage */
| KW_CREATE KW_STAGE opt_if_not_exists:ifNotExists ident:stageName
KW_PROPERTIES opt_key_value_map:properties
@@ -4133,13 +4132,6 @@ index_definition ::=
:}
;
-build_index_definition ::=
- KW_INDEX ident:indexName opt_partition_names:partitionNames
- {:
- RESULT = new IndexDef(indexName, partitionNames, true);
- :}
- ;
-
opt_nullable_type ::=
{:
RESULT = ColumnNullableType.DEFAULT;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index 7751cbac9f1..37e415afe02 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -1330,7 +1330,7 @@ public class SchemaChangeHandler extends AlterHandler {
throw new DdlException(e.getMessage());
}
- // check bloom filter has change
+ // check bloom filter has been changed
boolean hasBfChange = false;
Set<String> oriBfColumns = olapTable.getCopiedBfColumns();
double oriBfFpp = olapTable.getBfFpp();
@@ -1991,7 +1991,7 @@ public class SchemaChangeHandler extends AlterHandler {
List<Index> newIndexes = olapTable.getCopiedIndexes();
List<Index> alterIndexes = new ArrayList<>();
- Map<Long, Set<String>> invertedIndexOnPartitions = new HashMap<>();
+ Map<Long, Set<String>> indexOnPartitions = new HashMap<>();
boolean isDropIndex = false;
Map<String, String> propertyMap = new HashMap<>();
for (AlterClause alterClause : alterClauses) {
@@ -2145,10 +2145,23 @@ public class SchemaChangeHandler extends AlterHandler {
}
lightSchemaChange = false;
- if (index.isLightIndexChangeSupported() &&
!Config.isCloudMode()) {
+ // Check if the index supports light index change and
session variable is enabled
+ boolean enableAddIndexForNewData = true;
+ try {
+ ConnectContext context = ConnectContext.get();
+ if (context != null && context.getSessionVariable() !=
null) {
+ enableAddIndexForNewData =
context.getSessionVariable().isEnableAddIndexForNewData();
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to get session variable
enable_add_index_for_new_data, "
+ + "using default value: false", e);
+ }
+
+ // ngram_bf index can do light_schema_change in both local
and cloud mode
+ // inverted index can only do light_schema_change in local
mode
+ if
(index.isLightAddIndexSupported(enableAddIndexForNewData)) {
alterIndexes.add(index);
isDropIndex = false;
- // now only support light index change for inverted
index
lightIndexChange = true;
}
} else if (alterClause instanceof BuildIndexClause) {
@@ -2159,54 +2172,14 @@ public class SchemaChangeHandler extends AlterHandler {
throw new DdlException("BUILD INDEX operation failed:
No need to do it in cloud mode.");
}
- if (!olapTable.isPartitionedTable()) {
- List<String> specifiedPartitions =
indexDef.getPartitionNames();
- if (!specifiedPartitions.isEmpty()) {
- throw new DdlException("table " +
olapTable.getName()
- + " is not partitioned, cannot build index
with partitions.");
- }
- }
- List<Index> existedIndexes = olapTable.getIndexes();
- boolean found = false;
- for (Index existedIdx : existedIndexes) {
- if
(existedIdx.getIndexName().equalsIgnoreCase(indexDef.getIndexName())) {
- found = true;
- if (!existedIdx.isLightIndexChangeSupported()) {
- throw new DdlException("BUILD INDEX operation
failed: The index "
- + existedIdx.getIndexName() + " of
type " + existedIdx.getIndexType()
- + " does not support lightweight index
changes.");
- }
- for (Column column : olapTable.getBaseSchema()) {
- if (!column.getType().isVariantType()) {
- continue;
- }
- // variant type column can not support for
building index
- for (String indexColumn :
existedIdx.getColumns()) {
- if
(column.getName().equalsIgnoreCase(indexColumn)) {
- throw new DdlException("BUILD INDEX
operation failed: The "
- + indexDef.getIndexName() + "
index can not be built on the "
- + indexColumn + " column,
because it is a variant type column.");
- }
- }
- }
- index = existedIdx.clone();
- if (indexDef.getPartitionNames().isEmpty()) {
-
invertedIndexOnPartitions.put(index.getIndexId(),
olapTable.getPartitionNames());
- } else {
- invertedIndexOnPartitions.put(
- index.getIndexId(), new
HashSet<>(indexDef.getPartitionNames()));
- }
- break;
- }
- }
- if (!found) {
- throw new DdlException("index " +
indexDef.getIndexName()
- + " not exist, cannot build it with
defferred.");
+ if (indexDef.getPartitionNames().isEmpty()) {
+ indexOnPartitions.put(index.getIndexId(),
olapTable.getPartitionNames());
+ } else {
+ indexOnPartitions.put(
+ index.getIndexId(), new
HashSet<>(indexDef.getPartitionNames()));
}
- if (indexDef.isInvertedIndex()) {
- alterIndexes.add(index);
- }
+ alterIndexes.add(index);
buildIndexChange = true;
lightSchemaChange = false;
} else if (alterClause instanceof DropIndexClause) {
@@ -2224,7 +2197,9 @@ public class SchemaChangeHandler extends AlterHandler {
break;
}
}
- if (found.isLightIndexChangeSupported() &&
!Config.isCloudMode()) {
+ // only inverted index with local mode can do light drop
index change
+ if (found != null && found.getIndexType() ==
IndexDef.IndexType.INVERTED
+ && Config.isNotCloudMode()) {
alterIndexes.add(found);
isDropIndex = true;
lightIndexChange = true;
@@ -2248,13 +2223,16 @@ public class SchemaChangeHandler extends AlterHandler {
null, isDropIndex, jobId, false);
} else if (Config.enable_light_index_change && lightIndexChange) {
long jobId = Env.getCurrentEnv().getNextId();
- //for schema change add/drop inverted index optimize, direct
modify table meta firstly.
+ //for schema change add/drop inverted index and ngram_bf
optimize, direct modify table meta firstly.
modifyTableLightSchemaChange(rawSql, db, olapTable,
indexSchemaMap, newIndexes,
alterIndexes, isDropIndex, jobId,
false);
} else if (buildIndexChange) {
+ if (alterIndexes.isEmpty()) {
+ throw new DdlException("Altered index is empty. please
check your alter stmt.");
+ }
if (Config.enable_light_index_change) {
buildOrDeleteTableInvertedIndices(db, olapTable,
indexSchemaMap,
- alterIndexes,
invertedIndexOnPartitions, false);
+ alterIndexes, indexOnPartitions, false);
}
} else {
createJob(rawSql, db.getId(), olapTable, indexSchemaMap,
propertyMap, newIndexes);
@@ -2747,6 +2725,8 @@ public class SchemaChangeHandler extends AlterHandler {
olapTable.writeUnlock();
}
+ // if this table has ngram_bf index, we must run cancel for schema
change job
+ boolean hasNGramBFIndex = ((OlapTable)
olapTable).hasIndexOfType(IndexDef.IndexType.NGRAM_BF);
// alter job v2's cancel must be called outside the table lock
if (jobList.size() > 0) {
for (IndexChangeJob job : jobList) {
@@ -2761,6 +2741,8 @@ public class SchemaChangeHandler extends AlterHandler {
LOG.info("cancel build index job {} on table {} success",
jobId, tableName);
}
}
+ } else if (hasNGramBFIndex) {
+ cancelColumnJob(cancelAlterTableStmt);
} else {
throw new DdlException("No job to cancel for Table[" + tableName +
"]");
}
@@ -2800,7 +2782,7 @@ public class SchemaChangeHandler extends AlterHandler {
Column column = olapTable.getColumn(col);
if (column != null) {
indexDef.checkColumn(column, olapTable.getKeysType(),
-
olapTable.getTableProperty().getEnableUniqueKeyMergeOnWrite(),
+ olapTable.getEnableUniqueKeyMergeOnWrite(),
olapTable.getInvertedIndexFileStorageFormat());
if
(!InvertedIndexUtil.getInvertedIndexFieldPattern(indexDef.getProperties()).isEmpty())
{
throw new DdlException("Can not create index with field
pattern");
@@ -3040,7 +3022,7 @@ public class SchemaChangeHandler extends AlterHandler {
}
try {
buildOrDeleteTableInvertedIndices(db, olapTable,
indexSchemaMap,
- alterIndexes,
invertedIndexOnPartitions, true);
+ alterIndexes, invertedIndexOnPartitions, true);
} catch (Exception e) {
throw new DdlException(e.getMessage());
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
index 2e4ca2c6a70..ad4a6ef60d1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeJobV2.java
@@ -151,7 +151,7 @@ public class SchemaChangeJobV2 extends AlterJobV2 {
private boolean hasEnableUniqueKeySkipBitmapChanged = false;
// save all schema change tasks
- private AgentBatchTask schemaChangeBatchTask = new AgentBatchTask();
+ AgentBatchTask schemaChangeBatchTask = new AgentBatchTask();
protected SchemaChangeJobV2() {
super(JobType.SCHEMA_CHANGE);
@@ -678,14 +678,13 @@ public class SchemaChangeJobV2 extends AlterJobV2 {
healthyReplicaNum++;
}
}
- if (!FeConstants.runningUnitTest) {
- if (healthyReplicaNum < expectReplicationNum / 2 +
1) {
- LOG.warn("shadow tablet {} has few healthy
replicas: {}, schema change job: {}"
- + " healthyReplicaNum {}
expectReplicationNum {}",
- shadowTablet.getId(), replicas, jobId,
healthyReplicaNum, expectReplicationNum);
- throw new AlterCancelException(
+
+ if ((healthyReplicaNum < expectReplicationNum / 2 + 1)
&& !FeConstants.runningUnitTest) {
+ LOG.warn("shadow tablet {} has few healthy
replicas: {}, schema change job: {}"
+ + " healthyReplicaNum {}
expectReplicationNum {}",
+ shadowTablet.getId(), replicas, jobId,
healthyReplicaNum, expectReplicationNum);
+ throw new AlterCancelException(
"shadow tablet " + shadowTablet.getId() +
" has few healthy replicas");
- }
}
} // end for tablets
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/BuildIndexClause.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/BuildIndexClause.java
index d04d24e86e4..7cd1d915150 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/BuildIndexClause.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/BuildIndexClause.java
@@ -18,12 +18,17 @@
package org.apache.doris.analysis;
import org.apache.doris.alter.AlterOpType;
+import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Index;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.AnalysisException;
import com.google.common.collect.Maps;
+import java.util.List;
import java.util.Map;
public class BuildIndexClause extends AlterTableClause {
@@ -36,11 +41,14 @@ public class BuildIndexClause extends AlterTableClause {
private boolean alter;
// index internal class
private Index index;
+ private String indexName;
+ private PartitionNames partitionNames;
- public BuildIndexClause(TableName tableName, IndexDef indexDef, boolean
alter) {
+ public BuildIndexClause(TableName tableName, String indexName,
PartitionNames partitionNames, boolean alter) {
super(AlterOpType.SCHEMA_CHANGE);
this.tableName = tableName;
- this.indexDef = indexDef;
+ this.indexName = indexName;
+ this.partitionNames = partitionNames;
this.alter = alter;
}
@@ -67,17 +75,52 @@ public class BuildIndexClause extends AlterTableClause {
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
- if (indexDef == null) {
- throw new AnalysisException("index definition expected.");
+ tableName.analyze(analyzer);
+ DatabaseIf<Table> db =
Env.getCurrentEnv().getCatalogMgr().getInternalCatalog()
+ .getDb(tableName.getDb()).orElse(null);
+ if (db == null) {
+ throw new AnalysisException("Database[" + tableName.getDb() + "]
is not exist");
}
- if (indexDef.getIndexType() == IndexDef.IndexType.NGRAM_BF
- || indexDef.getIndexType() == IndexDef.IndexType.BLOOMFILTER) {
+
+ TableIf table = db.getTable(tableName.getTbl()).orElse(null);
+ if (table == null) {
+ throw new AnalysisException("Table[" + tableName.getTbl() + "] is
not exist");
+ }
+ if (!(table instanceof OlapTable)) {
+ throw new AnalysisException("Only olap table support build index");
+ }
+
+ Index existedIdx = null;
+ for (Index index : table.getTableIndexes().getIndexes()) {
+ if (index.getIndexName().equalsIgnoreCase(indexName)) {
+ existedIdx = index;
+ if (!existedIdx.isLightIndexChangeSupported()) {
+ throw new AnalysisException("BUILD INDEX operation failed:
The index "
+ + existedIdx.getIndexName() + " of type " +
existedIdx.getIndexType()
+ + " does not support lightweight index changes.");
+ }
+ break;
+ }
+ }
+ if (existedIdx == null) {
+ throw new AnalysisException("Index[" + indexName + "] is not exist
in table[" + tableName.getTbl() + "]");
+ }
+
+ IndexDef.IndexType indexType = existedIdx.getIndexType();
+ if (indexType == IndexDef.IndexType.NGRAM_BF
+ || indexType == IndexDef.IndexType.BLOOMFILTER) {
throw new AnalysisException("ngram bloomfilter or bloomfilter
index is not needed to build.");
}
+ indexDef = new IndexDef(indexName, partitionNames, indexType, true);
+ if (!table.isPartitionedTable()) {
+ List<String> specifiedPartitions = indexDef.getPartitionNames();
+ if (!specifiedPartitions.isEmpty()) {
+ throw new AnalysisException("table " + table.getName()
+ + " is not partitioned, cannot build index with
partitions.");
+ }
+ }
indexDef.analyze();
- this.index = new Index(Env.getCurrentEnv().getNextId(),
indexDef.getIndexName(),
- indexDef.getColumns(), indexDef.getIndexType(),
- indexDef.getProperties(), indexDef.getComment());
+ this.index = existedIdx.clone();
}
@Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
index 8d1f6a5bfd6..a1df981898e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
@@ -83,9 +83,9 @@ public class IndexDef {
}
}
- public IndexDef(String indexName, PartitionNames partitionNames, boolean
isBuildDeferred) {
+ public IndexDef(String indexName, PartitionNames partitionNames, IndexType
indexType, boolean isBuildDeferred) {
this.indexName = indexName;
- this.indexType = IndexType.INVERTED;
+ this.indexType = indexType;
this.partitionNames = partitionNames;
this.isBuildDeferred = isBuildDeferred;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Index.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Index.java
index 312e3194ed3..54407443a24 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Index.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Index.java
@@ -20,6 +20,7 @@ package org.apache.doris.catalog;
import org.apache.doris.analysis.IndexDef;
import org.apache.doris.analysis.InvertedIndexUtil;
import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Config;
import org.apache.doris.common.io.Text;
import org.apache.doris.common.io.Writable;
import org.apache.doris.common.util.PrintableMap;
@@ -152,6 +153,10 @@ public class Index implements Writable {
return InvertedIndexUtil.getInvertedIndexParser(properties);
}
+ public boolean isInvertedIndexParserNone() {
+ return
InvertedIndexUtil.INVERTED_INDEX_PARSER_NONE.equals(getInvertedIndexParser());
+ }
+
public String getInvertedIndexParserMode() {
return InvertedIndexUtil.getInvertedIndexParserMode(properties);
}
@@ -168,10 +173,28 @@ public class Index implements Writable {
return InvertedIndexUtil.getInvertedIndexParserStopwords(properties);
}
+ // Whether the index can be changed in light mode
public boolean isLightIndexChangeSupported() {
return indexType == IndexDef.IndexType.INVERTED;
}
+ // Whether the index can be added in light mode
+ // cloud mode supports light add for ngram_bf index and non-tokenized
inverted index (parser="none")
+ // local mode supports light add for both inverted index and ngram_bf index
+ // the rest of the index types do not support light add
+ public boolean isLightAddIndexSupported(boolean enableAddIndexForNewData) {
+ if (Config.isCloudMode()) {
+ if (indexType == IndexDef.IndexType.INVERTED) {
+ return isInvertedIndexParserNone() && enableAddIndexForNewData;
+ } else if (indexType == IndexDef.IndexType.NGRAM_BF) {
+ return enableAddIndexForNewData;
+ }
+ return false;
+ }
+ return (indexType == IndexDef.IndexType.NGRAM_BF &&
enableAddIndexForNewData)
+ || (indexType == IndexDef.IndexType.INVERTED);
+ }
+
public String getInvertedIndexCustomAnalyzer() {
return InvertedIndexUtil.getInvertedIndexCustomAnalyzer(properties);
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index d4dca760bfe..e39cc1ec0d0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -24,6 +24,7 @@ import org.apache.doris.analysis.ColumnDef;
import org.apache.doris.analysis.CreateMaterializedViewStmt;
import org.apache.doris.analysis.DataSortInfo;
import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.IndexDef;
import org.apache.doris.analysis.SlotDescriptor;
import org.apache.doris.analysis.SlotRef;
import org.apache.doris.backup.Status;
@@ -356,6 +357,19 @@ public class OlapTable extends Table implements
MTMVRelatedTableIf, GsonPostProc
return indexes.getIndexIds();
}
+ /**
+ * Checks if the table contains at least one index of the specified type.
+ * @param indexType The index type to check for
+ * @return true if the table has at least one index of the specified type,
false otherwise
+ */
+ public boolean hasIndexOfType(IndexDef.IndexType indexType) {
+ if (indexes == null) {
+ return false;
+ }
+ return indexes.getIndexes().stream()
+ .anyMatch(index -> index.getIndexType() == indexType);
+ }
+
@Override
public TableIndexes getTableIndexes() {
return indexes;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
b/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
index bcb17d3c039..f4f3893c87d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/cloud/datasource/CloudInternalCatalog.java
@@ -339,6 +339,14 @@ public class CloudInternalCatalog extends InternalCatalog {
schemaBuilder.setInvertedIndexStorageFormat(OlapFile.InvertedIndexStorageFormatPB.V2);
} else if (invertedIndexFileStorageFormat ==
TInvertedIndexFileStorageFormat.V3) {
schemaBuilder.setInvertedIndexStorageFormat(OlapFile.InvertedIndexStorageFormatPB.V3);
+ } else if (invertedIndexFileStorageFormat ==
TInvertedIndexFileStorageFormat.DEFAULT) {
+ if
(Config.inverted_index_storage_format.equalsIgnoreCase("V1")) {
+
schemaBuilder.setInvertedIndexStorageFormat(OlapFile.InvertedIndexStorageFormatPB.V1);
+ } else if
(Config.inverted_index_storage_format.equalsIgnoreCase("V3")) {
+
schemaBuilder.setInvertedIndexStorageFormat(OlapFile.InvertedIndexStorageFormatPB.V3);
+ } else {
+
schemaBuilder.setInvertedIndexStorageFormat(OlapFile.InvertedIndexStorageFormatPB.V2);
+ }
} else {
throw new DdlException("invalid inverted index storage
format");
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index c268fddee53..7891b735d03 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -732,6 +732,7 @@ public class SessionVariable implements Serializable,
Writable {
public static final String SQL_CONVERTOR_CONFIG = "sql_convertor_config";
public static final String PREFER_UDF_OVER_BUILTIN =
"prefer_udf_over_builtin";
+ public static final String ENABLE_ADD_INDEX_FOR_NEW_DATA =
"enable_add_index_for_new_data";
/**
* If set false, user couldn't submit analyze SQL and FE won't allocate
any related resources.
@@ -2558,6 +2559,14 @@ public class SessionVariable implements Serializable,
Writable {
return enableSortSpill;
}
+ @VariableMgr.VarAttr(name = ENABLE_ADD_INDEX_FOR_NEW_DATA, fuzzy = true,
description = {
+ "是否启用仅对新数据生效的索引添加模式,开启时新建索引只对后续写入的数据生效,关闭时对全部数据重建索引",
+ "Whether to enable add index mode that only affects new data, "
+ + "when enabled new indexes only affect subsequently
written data, "
+ + "when disabled rebuild indexes for all data"
+ })
+ public boolean enableAddIndexForNewData = false;
+
// If this fe is in fuzzy mode, then will use initFuzzyModeVariables to
generate some variables,
// not the default value set in the code.
@SuppressWarnings("checkstyle:Indentation")
@@ -4873,5 +4882,13 @@ public class SessionVariable implements Serializable,
Writable {
public int getGlobalVariantMaxSubcolumnsCount() {
return globalVariantMaxSubcolumnsCount;
}
+
+ public boolean isEnableAddIndexForNewData() {
+ return enableAddIndexForNewData;
+ }
+
+ public void setEnableAddIndexForNewData(boolean enableAddIndexForNewData) {
+ this.enableAddIndexForNewData = enableAddIndexForNewData;
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/alter/CloudIndexTest.java
b/fe/fe-core/src/test/java/org/apache/doris/alter/CloudIndexTest.java
new file mode 100644
index 00000000000..10adecb1676
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/alter/CloudIndexTest.java
@@ -0,0 +1,658 @@
+// 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.
+
+package org.apache.doris.alter;
+
+import org.apache.doris.analysis.AlterClause;
+import org.apache.doris.analysis.Analyzer;
+import org.apache.doris.analysis.CreateIndexClause;
+import org.apache.doris.analysis.DataSortInfo;
+import org.apache.doris.analysis.IndexDef;
+import org.apache.doris.analysis.IndexDef.IndexType;
+import org.apache.doris.analysis.ResourceTypeEnum;
+import org.apache.doris.analysis.TableName;
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.catalog.CatalogTestUtil;
+import org.apache.doris.catalog.Database;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.EnvFactory;
+import org.apache.doris.catalog.FakeEditLog;
+import org.apache.doris.catalog.FakeEnv;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.OlapTable.OlapTableState;
+import org.apache.doris.cloud.catalog.CloudEnv;
+import org.apache.doris.cloud.catalog.CloudEnvFactory;
+import org.apache.doris.cloud.catalog.ComputeGroup;
+import org.apache.doris.cloud.datasource.CloudInternalCatalog;
+import org.apache.doris.cloud.proto.Cloud;
+import org.apache.doris.cloud.proto.Cloud.MetaServiceCode;
+import org.apache.doris.cloud.rpc.MetaServiceProxy;
+import org.apache.doris.cloud.system.CloudSystemInfoService;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.AccessControllerManager;
+import org.apache.doris.mysql.privilege.Auth;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.persist.EditLog;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.system.Backend;
+import org.apache.doris.system.SystemInfoService;
+import org.apache.doris.task.AgentTask;
+import org.apache.doris.task.AgentTaskQueue;
+import org.apache.doris.thrift.TInvertedIndexFileStorageFormat;
+import org.apache.doris.thrift.TSortType;
+import org.apache.doris.thrift.TTaskType;
+import org.apache.doris.utframe.MockedMetaServerFactory;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import mockit.Mock;
+import mockit.MockUp;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class CloudIndexTest {
+ private static final Logger LOG =
LogManager.getLogger(CloudIndexTest.class);
+
+ // Simple ComputeGroupMgr wrapper for CloudSystemInfoService
+ public static class ComputeGroupMgr {
+ private final SystemInfoService systemInfoService;
+
+ public ComputeGroupMgr(SystemInfoService systemInfoService) {
+ this.systemInfoService = systemInfoService;
+ }
+
+ public ComputeGroup getComputeGroupByName(String name) {
+ if (systemInfoService instanceof CloudSystemInfoService) {
+ return ((CloudSystemInfoService)
systemInfoService).getComputeGroupByName(name);
+ }
+ return null;
+ }
+
+ public ComputeGroup getAllBackendComputeGroup() {
+ // Return a default compute group for all backends
+ return new ComputeGroup("default_compute_group",
"default_compute_group",
+ ComputeGroup.ComputeTypeEnum.SQL);
+ }
+ }
+
+ private static String fileName = "./CloudIndexTest";
+
+ private static FakeEditLog fakeEditLog;
+ private static FakeEnv fakeEnv;
+ private static Env masterEnv;
+ private static EditLog testEditLog;
+ private ConnectContext ctx;
+ private static OlapTable olapTable;
+
+ private static Analyzer analyzer;
+ private static Database db;
+ private static CreateIndexClause createIndexClause;
+ private static SchemaChangeHandler schemaChangeHandler;
+
+ @Before
+ public void setUp() throws InstantiationException, IllegalAccessException,
IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException,
SecurityException, UserException {
+ FeConstants.runningUnitTest = true;
+ // Setup for MetaServiceProxy mock
+ new MockUp<MetaServiceProxy>(MetaServiceProxy.class) {
+
+ @Mock
+ public Cloud.BeginTxnResponse beginTxn(Cloud.BeginTxnRequest
request) {
+ Cloud.BeginTxnResponse.Builder beginTxnResponseBuilder =
Cloud.BeginTxnResponse.newBuilder();
+ beginTxnResponseBuilder.setTxnId(1000)
+ .setStatus(
+
Cloud.MetaServiceResponseStatus.newBuilder().setCode(MetaServiceCode.OK).setMsg("OK"));
+ return beginTxnResponseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.CommitTxnResponse commitTxn(Cloud.CommitTxnRequest
request) {
+ Cloud.TxnInfoPB.Builder txnInfoBuilder =
Cloud.TxnInfoPB.newBuilder();
+ txnInfoBuilder.setDbId(CatalogTestUtil.testDbId1);
+
txnInfoBuilder.addAllTableIds(Lists.newArrayList(olapTable.getId()));
+ txnInfoBuilder.setLabel("test_label");
+ txnInfoBuilder.setListenerId(-1);
+ Cloud.CommitTxnResponse.Builder commitTxnResponseBuilder =
Cloud.CommitTxnResponse.newBuilder();
+
commitTxnResponseBuilder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"))
+ .setTxnInfo(txnInfoBuilder.build());
+ return commitTxnResponseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.CheckTxnConflictResponse
checkTxnConflict(Cloud.CheckTxnConflictRequest request) {
+ Cloud.CheckTxnConflictResponse.Builder
checkTxnConflictResponseBuilder =
+ Cloud.CheckTxnConflictResponse.newBuilder();
+
checkTxnConflictResponseBuilder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"))
+ .setFinished(true);
+ return checkTxnConflictResponseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.GetClusterResponse getCluster(Cloud.GetClusterRequest
request) {
+ Cloud.GetClusterResponse.Builder getClusterResponseBuilder =
Cloud.GetClusterResponse.newBuilder();
+ Cloud.ClusterPB.Builder clusterBuilder =
Cloud.ClusterPB.newBuilder();
+
clusterBuilder.setClusterId("test_id").setClusterName("test_group");
+
+ Cloud.NodeInfoPB.Builder node1 = Cloud.NodeInfoPB.newBuilder();
+ node1.setCloudUniqueId("test_cloud")
+ .setName("host1")
+ .setIp("host1")
+ .setHost("host1")
+ .setHeartbeatPort(123)
+ .setEditLogPort(125)
+ .setStatus(Cloud.NodeStatusPB.NODE_STATUS_RUNNING);
+ clusterBuilder.addNodes(node1.build());
+
getClusterResponseBuilder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"))
+ .addCluster(clusterBuilder.build());
+ return getClusterResponseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.CreateTabletsResponse
createTablets(Cloud.CreateTabletsRequest request) {
+ Cloud.CreateTabletsResponse.Builder responseBuilder =
Cloud.CreateTabletsResponse.newBuilder();
+ responseBuilder.setStatus(
+
Cloud.MetaServiceResponseStatus.newBuilder().setCode(MetaServiceCode.OK).setMsg("OK"));
+ return responseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.FinishTabletJobResponse
finishTabletJob(Cloud.FinishTabletJobRequest request) {
+ Cloud.FinishTabletJobResponse.Builder responseBuilder =
Cloud.FinishTabletJobResponse.newBuilder();
+ responseBuilder.setStatus(
+
Cloud.MetaServiceResponseStatus.newBuilder().setCode(MetaServiceCode.OK).setMsg("OK"));
+ return responseBuilder.build();
+ }
+
+ @Mock
+ public Cloud.IndexResponse prepareIndex(Cloud.IndexRequest
request) {
+ Cloud.IndexResponse.Builder builder =
Cloud.IndexResponse.newBuilder();
+ builder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"));
+ return builder.build();
+ }
+
+ @Mock
+ public Cloud.IndexResponse commitIndex(Cloud.IndexRequest request)
{
+ Cloud.IndexResponse.Builder builder =
Cloud.IndexResponse.newBuilder();
+ builder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"));
+ return builder.build();
+ }
+
+ @Mock
+ public Cloud.IndexResponse dropIndex(Cloud.IndexRequest request) {
+ Cloud.IndexResponse.Builder builder =
Cloud.IndexResponse.newBuilder();
+ builder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"));
+ return builder.build();
+ }
+
+ @Mock
+ public Cloud.CheckKVResponse checkKv(Cloud.CheckKVRequest request)
{
+ Cloud.CheckKVResponse.Builder builder =
Cloud.CheckKVResponse.newBuilder();
+ builder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"));
+ return builder.build();
+ }
+
+ @Mock
+ public Cloud.GetCurrentMaxTxnResponse
getCurrentMaxTxnId(Cloud.GetCurrentMaxTxnRequest request) {
+ Cloud.GetCurrentMaxTxnResponse.Builder builder =
Cloud.GetCurrentMaxTxnResponse.newBuilder();
+ builder.setStatus(Cloud.MetaServiceResponseStatus.newBuilder()
+ .setCode(MetaServiceCode.OK).setMsg("OK"))
+ .setCurrentMaxTxnId(1000);
+ return builder.build();
+ }
+ };
+
+ Config.cloud_unique_id = "test_cloud";
+ Config.meta_service_endpoint =
MockedMetaServerFactory.METASERVER_DEFAULT_IP + ":" + 20121;
+
+ EnvFactory envFactory = EnvFactory.getInstance();
+ masterEnv = envFactory.createEnv(false);
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+
+ fakeEditLog = new FakeEditLog();
+ testEditLog = null; // Will be set by MockUp
+ FakeEnv.setEnv(masterEnv);
+
+ ctx = new ConnectContext();
+ ctx.setEnv(masterEnv);
+ ctx.setQualifiedUser("root");
+ UserIdentity rootUser = new UserIdentity("root", "%");
+ rootUser.setIsAnalyzed();
+ ctx.setCurrentUserIdentity(rootUser);
+ ctx.setThreadLocalInfo();
+ ctx.setCloudCluster("test_group");
+ Assert.assertTrue(envFactory instanceof CloudEnvFactory);
+ Assert.assertTrue(masterEnv instanceof CloudEnv);
+ new MockUp<Env>() {
+ @Mock
+ public Env getCurrentEnv() {
+ return masterEnv;
+ }
+
+ @Mock
+ public EditLog getEditLog() {
+ if (testEditLog == null) {
+ // Create a mock EditLog using a no-op approach
+ testEditLog = new EditLog("test") {
+ // Override to avoid initialization issues
+ };
+ }
+ return testEditLog;
+ }
+
+ @Mock
+ public ComputeGroupMgr getComputeGroupMgr() {
+ return new ComputeGroupMgr(Env.getCurrentSystemInfo());
+ }
+
+ @Mock
+ public SchemaChangeHandler getSchemaChangeHandler() {
+ // Create a new independent SchemaChangeHandler for each call
+ return schemaChangeHandler;
+ }
+
+ @Mock
+ public AccessControllerManager getAccessManager() {
+ return new AccessControllerManager(masterEnv.getAuth()) {
+ @Override
+ public boolean checkTblPriv(ConnectContext ctx, String
ctl, String db, String tbl, PrivPredicate wanted) {
+ return true; // Allow all access for test
+ }
+
+ @Override
+ public boolean checkCloudPriv(UserIdentity user, String
cluster, PrivPredicate wanted, ResourceTypeEnum resourceType) {
+ return true; // Allow all cloud privileges for test
+ }
+ };
+ }
+ };
+
+ new MockUp<Auth>() {
+ @Mock
+ public String getDefaultCloudCluster(String user) {
+ return "test_group"; // Return default cluster for test
+ }
+ };
+
+ // Mock cloud environment permissions
+ new MockUp<CloudEnv>() {
+ @Mock
+ public void checkCloudClusterPriv(String cluster) throws Exception
{
+ // Always allow for tests
+ }
+ };
+
+ // Mock ConnectContext to avoid compute group permission check
+ new MockUp<ConnectContext>() {
+ @Mock
+ public String getCloudCluster() {
+ return "test_group";
+ }
+
+ @Mock
+ public UserIdentity getCurrentUserIdentity() {
+ UserIdentity rootUser = new UserIdentity("root", "%");
+ rootUser.setIsAnalyzed();
+ return rootUser;
+ }
+ };
+
+ analyzer = new Analyzer(masterEnv, ctx);
+
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CloudSystemInfoService systemInfo = (CloudSystemInfoService)
Env.getCurrentSystemInfo();
+ ComputeGroup pcg1 = new ComputeGroup("test_id", "test_group",
ComputeGroup.ComputeTypeEnum.COMPUTE);
+ systemInfo.addComputeGroup("test_id", pcg1);
+ Backend backend = new Backend(10001L, "host1", 123);
+ backend.setAlive(true);
+ backend.setBePort(456);
+ backend.setHttpPort(789);
+ backend.setBrpcPort(321);
+ Map<String, String> newTagMap =
org.apache.doris.resource.Tag.DEFAULT_BACKEND_TAG.toMap();
+ newTagMap.put(org.apache.doris.resource.Tag.CLOUD_CLUSTER_STATUS,
"NORMAL");
+ newTagMap.put(org.apache.doris.resource.Tag.CLOUD_CLUSTER_NAME,
"test_cluster");
+ newTagMap.put(org.apache.doris.resource.Tag.CLOUD_CLUSTER_ID,
"test_id");
+
newTagMap.put(org.apache.doris.resource.Tag.CLOUD_CLUSTER_PUBLIC_ENDPOINT, "");
+
newTagMap.put(org.apache.doris.resource.Tag.CLOUD_CLUSTER_PRIVATE_ENDPOINT, "");
+ newTagMap.put(org.apache.doris.resource.Tag.CLOUD_UNIQUE_ID,
"test_cloud");
+ backend.setTagMap(newTagMap);
+ List<Backend> backends = Lists.newArrayList(backend);
+ systemInfo.updateCloudClusterMapNoLock(backends, new ArrayList<>());
+ db = new Database(CatalogTestUtil.testDbId1, CatalogTestUtil.testDb1);
+ masterEnv.unprotectCreateDb(db);
+
+ AgentTaskQueue.clearAllTasks();
+ schemaChangeHandler = masterEnv.getSchemaChangeHandler();
+ }
+
+ @Test
+ public void testCreateNgramBfIndex() throws Exception {
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+ schemaChangeHandler = (SchemaChangeHandler) new
Alter().getSchemaChangeHandler();
+
+ Assert.assertTrue(Env.getCurrentInternalCatalog() instanceof
CloudInternalCatalog);
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CatalogTestUtil.createDupTable(db);
+ olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ DataSortInfo dataSortInfo = new DataSortInfo();
+ dataSortInfo.setSortType(TSortType.LEXICAL);
+ olapTable.setDataSortInfo(dataSortInfo);
+ String indexName = "ngram_bf_index";
+
+ // Add required properties for NGRAM_BF index
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("gram_size", "2");
+ properties.put("bf_size", "256");
+
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(olapTable.getBaseSchema().get(3).getName()),
+ org.apache.doris.analysis.IndexDef.IndexType.NGRAM_BF,
+ properties, "ngram bf index");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ olapTable.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+ ctx.getSessionVariable().setEnableAddIndexForNewData(true);
+ schemaChangeHandler.process(alterClauses, db, olapTable);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(1, olapTable.getIndexes().size());
+ Assert.assertEquals("ngram_bf_index",
olapTable.getIndexes().get(0).getIndexName());
+ Assert.assertEquals(OlapTableState.NORMAL, olapTable.getState());
+
+ long createJobId =
indexChangeJobMap.values().stream().findAny().get().jobId;
+
+ // Finish the create index job first
+ SchemaChangeJobV2 createJobV2 = (SchemaChangeJobV2)
indexChangeJobMap.get(createJobId);
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED,
createJobV2.getJobState());
+ }
+
+ @Test
+ public void testNormalCreateNgramBfIndex() throws Exception {
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+ schemaChangeHandler = (SchemaChangeHandler) new
Alter().getSchemaChangeHandler();
+
+ Assert.assertTrue(Env.getCurrentInternalCatalog() instanceof
CloudInternalCatalog);
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CatalogTestUtil.createDupTable(db);
+ olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ DataSortInfo dataSortInfo = new DataSortInfo();
+ dataSortInfo.setSortType(TSortType.LEXICAL);
+ olapTable.setDataSortInfo(dataSortInfo);
+ String indexName = "ngram_bf_index";
+
+ // Add required properties for NGRAM_BF index
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("gram_size", "2");
+ properties.put("bf_size", "256");
+
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(olapTable.getBaseSchema().get(3).getName()),
+ org.apache.doris.analysis.IndexDef.IndexType.NGRAM_BF,
+ properties, "ngram bf index");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ olapTable.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+ // Set session variable to false (default)
+ ctx.getSessionVariable().setEnableAddIndexForNewData(false);
+ schemaChangeHandler.process(alterClauses, db, olapTable);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(OlapTableState.SCHEMA_CHANGE,
olapTable.getState());
+
+ long createJobId =
indexChangeJobMap.values().stream().findAny().get().jobId;
+
+ // Finish the create index job first
+ SchemaChangeJobV2 createJobV2 = (SchemaChangeJobV2)
indexChangeJobMap.get(createJobId);
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.WAITING_TXN,
createJobV2.getJobState());
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING,
createJobV2.getJobState());
+ Assert.assertEquals(1, createJobV2.schemaChangeBatchTask.getTaskNum());
+
+ List<AgentTask> tasks = AgentTaskQueue.getTask(TTaskType.ALTER);
+ Assert.assertEquals(1, tasks.size());
+ for (AgentTask agentTask : tasks) {
+ agentTask.setFinished(true);
+ }
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED,
createJobV2.getJobState());
+ Assert.assertEquals(OlapTableState.NORMAL, olapTable.getState());
+ Assert.assertEquals(1, olapTable.getIndexes().size());
+ Assert.assertEquals("ngram_bf_index",
olapTable.getIndexes().get(0).getIndexName());
+ }
+
+ @Test
+ public void testCreateInvertedIndex() throws Exception {
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+ schemaChangeHandler = (SchemaChangeHandler) new
Alter().getSchemaChangeHandler();
+
+ Assert.assertTrue(Env.getCurrentInternalCatalog() instanceof
CloudInternalCatalog);
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CatalogTestUtil.createDupTable(db);
+ olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ DataSortInfo dataSortInfo = new DataSortInfo();
+ dataSortInfo.setSortType(TSortType.LEXICAL);
+ olapTable.setDataSortInfo(dataSortInfo);
+ String indexName = "raw_inverted_index";
+ // Explicitly set parser="none" for raw inverted index
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("parser", "none");
+
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(olapTable.getBaseSchema().get(3).getName()),
+ IndexType.INVERTED,
+ properties, "raw inverted index");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ olapTable.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+ ctx.getSessionVariable().setEnableAddIndexForNewData(false);
+ schemaChangeHandler.process(alterClauses, db, olapTable);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+
+ long createJobId =
indexChangeJobMap.values().stream().findAny().get().jobId;
+ Assert.assertEquals(OlapTableState.SCHEMA_CHANGE,
olapTable.getState());
+
+ // Finish the create index job first
+ SchemaChangeJobV2 createJobV2 = (SchemaChangeJobV2)
indexChangeJobMap.get(createJobId);
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.WAITING_TXN,
createJobV2.getJobState());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING,
createJobV2.getJobState());
+ Assert.assertEquals(1, createJobV2.schemaChangeBatchTask.getTaskNum());
+
+ List<AgentTask> tasks = AgentTaskQueue.getTask(TTaskType.ALTER);
+ Assert.assertEquals(1, tasks.size());
+ for (AgentTask agentTask : tasks) {
+ agentTask.setFinished(true);
+ }
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED,
createJobV2.getJobState());
+ Assert.assertEquals(OlapTableState.NORMAL, olapTable.getState());
+ Assert.assertEquals(1, olapTable.getIndexes().size());
+ Assert.assertEquals("raw_inverted_index",
olapTable.getIndexes().get(0).getIndexName());
+ }
+
+ @Test
+ public void testCreateInvertedIndexWithLightweightMode() throws Exception {
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+ schemaChangeHandler = (SchemaChangeHandler) new
Alter().getSchemaChangeHandler();
+
+ Assert.assertTrue(Env.getCurrentInternalCatalog() instanceof
CloudInternalCatalog);
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CatalogTestUtil.createDupTable(db);
+ olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ DataSortInfo dataSortInfo = new DataSortInfo();
+ dataSortInfo.setSortType(TSortType.LEXICAL);
+ olapTable.setDataSortInfo(dataSortInfo);
+ String indexName = "lightweight_raw_inverted_index";
+ // Explicitly set parser="none" for raw inverted index
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("parser", "none");
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(olapTable.getBaseSchema().get(3).getName()),
+ IndexType.INVERTED,
+ properties, "lightweight raw inverted index");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ olapTable.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+ // Test with enable_add_index_for_new_data = true, should use
lightweight mode
+ ctx.getSessionVariable().setEnableAddIndexForNewData(true);
+ schemaChangeHandler.process(alterClauses, db, olapTable);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ // Lightweight mode should not create any schema change jobs
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(1, olapTable.getIndexes().size());
+ Assert.assertEquals("lightweight_raw_inverted_index",
olapTable.getIndexes().get(0).getIndexName());
+ Assert.assertEquals(OlapTableState.NORMAL, olapTable.getState());
+ // Verify the index properties
+ Assert.assertEquals("none",
olapTable.getIndexes().get(0).getProperties().get("parser"));
+ }
+
+ @Test
+ public void testCreateTokenizedInvertedIndex() throws Exception {
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+
+ SystemInfoService cloudSystemInfo = Env.getCurrentSystemInfo();
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+ FakeEnv.setSystemInfo(cloudSystemInfo);
+ schemaChangeHandler = (SchemaChangeHandler) new
Alter().getSchemaChangeHandler();
+
+ Assert.assertTrue(Env.getCurrentInternalCatalog() instanceof
CloudInternalCatalog);
+ Assert.assertTrue(Env.getCurrentSystemInfo() instanceof
CloudSystemInfoService);
+ CatalogTestUtil.createDupTable(db);
+ olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ DataSortInfo dataSortInfo = new DataSortInfo();
+ dataSortInfo.setSortType(TSortType.LEXICAL);
+ olapTable.setDataSortInfo(dataSortInfo);
+
+ // Set inverted index file storage format to V2 for cloud mode
+
olapTable.setInvertedIndexFileStorageFormat(TInvertedIndexFileStorageFormat.V2);
+
+ String indexName = "tokenized_inverted_index";
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("parser", "english");
+ properties.put("support_phrase", "true");
+ properties.put("lower_case", "true");
+
+ // Use VARCHAR column v1 (index 2) for string type support
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(olapTable.getBaseSchema().get(2).getName()),
+ IndexType.INVERTED,
+ properties, "tokenized inverted index with english parser");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ olapTable.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+ schemaChangeHandler.process(alterClauses, db, olapTable);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(OlapTableState.SCHEMA_CHANGE,
olapTable.getState());
+
+ SchemaChangeJobV2 jobV2 = (SchemaChangeJobV2)
indexChangeJobMap.values().stream()
+ .findFirst()
+ .orElse(null);
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ // This should be a heavyweight schema change for tokenized index
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.WAITING_TXN,
jobV2.getJobState());
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING, jobV2.getJobState());
+ Assert.assertEquals(1, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ List<AgentTask> tasks = AgentTaskQueue.getTask(TTaskType.ALTER);
+ Assert.assertEquals(1, tasks.size());
+ for (AgentTask agentTask : tasks) {
+ agentTask.setFinished(true);
+ }
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED, jobV2.getJobState());
+
+ Assert.assertEquals(1, olapTable.getIndexes().size());
+ Assert.assertEquals("tokenized_inverted_index",
olapTable.getIndexes().get(0).getIndexName());
+
+ // Verify that the index has the correct properties
+ Assert.assertEquals("english",
olapTable.getIndexes().get(0).getProperties().get("parser"));
+ Assert.assertEquals("true",
olapTable.getIndexes().get(0).getProperties().get("support_phrase"));
+ Assert.assertEquals("true",
olapTable.getIndexes().get(0).getProperties().get("lower_case"));
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/alter/IndexChangeJobTest.java
b/fe/fe-core/src/test/java/org/apache/doris/alter/IndexChangeJobTest.java
index 6b0943c306f..29aed7e050d 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/alter/IndexChangeJobTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/alter/IndexChangeJobTest.java
@@ -46,6 +46,7 @@ import org.apache.doris.common.FeConstants;
import org.apache.doris.common.FeMetaVersion;
import org.apache.doris.common.UserException;
import org.apache.doris.common.util.PropertyAnalyzer;
+import org.apache.doris.qe.ConnectContext;
import org.apache.doris.task.AgentTask;
import org.apache.doris.task.AgentTaskQueue;
import org.apache.doris.thrift.TStatusCode;
@@ -55,6 +56,8 @@ import org.apache.doris.transaction.GlobalTransactionMgr;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import mockit.Mock;
+import mockit.MockUp;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -85,6 +88,9 @@ public class IndexChangeJobTest {
private static BuildIndexClause buildIndexClause;
private static DropIndexClause dropIndexClause;
private static CancelAlterTableStmt cancelAlterTableStmt;
+ private static TableName tableName;
+ private static String indexName;
+ private static ConnectContext ctx;
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@@ -108,25 +114,40 @@ public class IndexChangeJobTest {
db =
masterEnv.getInternalCatalog().getDbOrDdlException(CatalogTestUtil.testDbId1);
olapTable = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId1);
+ new MockUp<Env>() {
+ @Mock
+ public Env getCurrentEnv() {
+ return masterEnv;
+ }
+ };
+
+ // Initialize ConnectContext
+ ctx = new ConnectContext();
+ new MockUp<ConnectContext>() {
+ @Mock
+ public ConnectContext get() {
+ return ctx;
+ }
+ };
+
// set mow table property
Map<String, String> properties = Maps.newHashMap();
properties.put(PropertyAnalyzer.ENABLE_UNIQUE_KEY_MERGE_ON_WRITE,
"false");
TableProperty tableProperty = new TableProperty(properties);
olapTable.setTableProperty(tableProperty);
- TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ tableName = new TableName(masterEnv.getInternalCatalog().getName(),
db.getName(),
olapTable.getName());
- IndexDef indexDef = new IndexDef("index1", false,
+ indexName = "index1";
+ IndexDef indexDef = new IndexDef(indexName, false,
Lists.newArrayList(olapTable.getBaseSchema().get(1).getName()),
IndexDef.IndexType.INVERTED,
Maps.newHashMap(), "balabala");
+ FakeEnv.setEnv(masterEnv);
createIndexClause = new CreateIndexClause(tableName, indexDef, false);
createIndexClause.analyze(analyzer);
- buildIndexClause = new BuildIndexClause(tableName, indexDef, false);
- buildIndexClause.analyze(analyzer);
-
- dropIndexClause = new DropIndexClause("index1", false, tableName,
false);
+ dropIndexClause = new DropIndexClause(indexName, false, tableName,
false);
dropIndexClause.analyze(analyzer);
cancelAlterTableStmt = new
CancelAlterTableStmt(ShowAlterStmt.AlterType.INDEX, tableName);
@@ -163,6 +184,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -203,6 +226,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -292,6 +317,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -339,6 +366,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -467,6 +496,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -519,6 +550,8 @@ public class IndexChangeJobTest {
Assert.assertEquals(olapTable.getIndexes().size(), 1);
Assert.assertEquals(olapTable.getIndexes().get(0).getIndexName(),
"index1");
alterClauses.clear();
+ buildIndexClause = new BuildIndexClause(tableName, indexName, null,
false);
+ buildIndexClause.analyze(analyzer);
alterClauses.add(buildIndexClause);
schemaChangeHandler.process(alterClauses, db, olapTable);
Map<Long, IndexChangeJob> indexChangeJobMap =
schemaChangeHandler.getIndexChangeJobs();
@@ -526,13 +559,13 @@ public class IndexChangeJobTest {
Assert.assertEquals(OlapTableState.NORMAL, olapTable.getState());
IndexChangeJob indexChangejob =
indexChangeJobMap.values().stream().findAny().get();
-
Assert.assertEquals(indexChangejob.invertedIndexBatchTask.getTaskNum(), 0);
+ Assert.assertEquals(0,
indexChangejob.invertedIndexBatchTask.getTaskNum());
Assert.assertEquals(IndexChangeJob.JobState.WAITING_TXN,
indexChangejob.getJobState());
// run waiting txn job
schemaChangeHandler.runAfterCatalogReady();
Assert.assertEquals(IndexChangeJob.JobState.RUNNING,
indexChangejob.getJobState());
-
Assert.assertEquals(indexChangejob.invertedIndexBatchTask.getTaskNum(), 3);
+ Assert.assertEquals(3,
indexChangejob.invertedIndexBatchTask.getTaskNum());
// run running job
schemaChangeHandler.runAfterCatalogReady();
Assert.assertEquals(IndexChangeJob.JobState.RUNNING,
indexChangejob.getJobState());
@@ -565,17 +598,135 @@ public class IndexChangeJobTest {
fakeEditLog = new FakeEditLog();
FakeEnv.setEnv(masterEnv);
- IndexDef indexDef = new IndexDef("ngram_bf_index", false,
- Lists.newArrayList(olapTable.getBaseSchema().get(1).getName()),
+ OlapTable table = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ String indexName = "ngram_bf_index";
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(table.getBaseSchema().get(3).getName()),
org.apache.doris.analysis.IndexDef.IndexType.NGRAM_BF,
Maps.newHashMap(), "ngram bf index");
TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
- olapTable.getName());
+ table.getName());
createIndexClause = new CreateIndexClause(tableName, indexDef, false);
createIndexClause.analyze(analyzer);
+ SchemaChangeHandler schemaChangeHandler =
Env.getCurrentEnv().getSchemaChangeHandler();
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+
+ // Test with enable_add_index_for_new_data = true
+ ConnectContext context = ConnectContext.get();
+ context.getSessionVariable().setEnableAddIndexForNewData(true);
+ schemaChangeHandler.process(alterClauses, db, table);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(1, table.getIndexes().size());
+ Assert.assertEquals("ngram_bf_index",
table.getIndexes().get(0).getIndexName());
+
+ SchemaChangeJobV2 jobV2 = (SchemaChangeJobV2)
indexChangeJobMap.values().stream()
+ .findFirst()
+ .orElse(null);
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED, jobV2.getJobState());
+
+ // Clean up for next test
+ table.setIndexes(Lists.newArrayList());
+ indexChangeJobMap.clear();
+ AgentTaskQueue.clearAllTasks();
+
+ // Test with enable_add_index_for_new_data = false
+ context.getSessionVariable().setEnableAddIndexForNewData(false);
+ String indexName2 = "ngram_bf_index2";
+ IndexDef indexDef2 = new IndexDef(indexName2, false,
+ Lists.newArrayList(table.getBaseSchema().get(3).getName()),
+ org.apache.doris.analysis.IndexDef.IndexType.NGRAM_BF,
+ Maps.newHashMap(), "ngram bf index2");
+
+ createIndexClause = new CreateIndexClause(tableName, indexDef2, false);
+ createIndexClause.analyze(analyzer);
+ ArrayList<AlterClause> alterClauses2 = new ArrayList<>();
+ alterClauses2.add(createIndexClause);
+ schemaChangeHandler.process(alterClauses2, db, table);
+ indexChangeJobMap = schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(OlapTableState.SCHEMA_CHANGE, table.getState());
+
+ jobV2 = (SchemaChangeJobV2) indexChangeJobMap.values().stream()
+ .findFirst()
+ .orElse(null);
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.WAITING_TXN,
jobV2.getJobState());
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING, jobV2.getJobState());
+ Assert.assertEquals(1, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING, jobV2.getJobState());
+ Assert.assertEquals(1, jobV2.schemaChangeBatchTask.getTaskNum());
- buildIndexClause = new BuildIndexClause(tableName, indexDef, false);
-
org.junit.jupiter.api.Assertions.assertThrows(org.apache.doris.common.AnalysisException.class,
- () -> buildIndexClause.analyze(analyzer));
+ List<AgentTask> tasks = AgentTaskQueue.getTask(TTaskType.ALTER);
+ Assert.assertEquals(1, tasks.size());
+ for (AgentTask agentTask : tasks) {
+ agentTask.setFinished(true);
+ }
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.FINISHED, jobV2.getJobState());
+ Assert.assertEquals(1, table.getIndexes().size());
+ Assert.assertEquals("ngram_bf_index2",
table.getIndexes().get(0).getIndexName());
+ }
+
+ @Test
+ public void testCancelNgramBfBuildIndex() throws UserException {
+ fakeEnv = new FakeEnv();
+ fakeEditLog = new FakeEditLog();
+ FakeEnv.setEnv(masterEnv);
+
+ OlapTable table = (OlapTable)
db.getTableOrDdlException(CatalogTestUtil.testTableId2);
+ String indexName = "ngram_bf_index";
+ IndexDef indexDef = new IndexDef(indexName, false,
+ Lists.newArrayList(table.getBaseSchema().get(3).getName()),
+ org.apache.doris.analysis.IndexDef.IndexType.NGRAM_BF,
+ Maps.newHashMap(), "ngram bf index");
+ TableName tableName = new
TableName(masterEnv.getInternalCatalog().getName(), db.getName(),
+ table.getName());
+ createIndexClause = new CreateIndexClause(tableName, indexDef, false);
+ createIndexClause.analyze(analyzer);
+ SchemaChangeHandler schemaChangeHandler =
Env.getCurrentEnv().getSchemaChangeHandler();
+ ArrayList<AlterClause> alterClauses = new ArrayList<>();
+ alterClauses.add(createIndexClause);
+
+ //cancel test can only with enable_add_index_for_new_data = false
+ ctx.getSessionVariable().setEnableAddIndexForNewData(false);
+ schemaChangeHandler.process(alterClauses, db, table);
+ Map<Long, AlterJobV2> indexChangeJobMap =
schemaChangeHandler.getAlterJobsV2();
+ Assert.assertEquals(1, indexChangeJobMap.size());
+ Assert.assertEquals(OlapTableState.SCHEMA_CHANGE, table.getState());
+
+ SchemaChangeJobV2 jobV2 = (SchemaChangeJobV2)
indexChangeJobMap.values().stream()
+ .findFirst()
+ .orElse(null);
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.WAITING_TXN,
jobV2.getJobState());
+ Assert.assertEquals(0, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING, jobV2.getJobState());
+ Assert.assertEquals(1, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.RUNNING, jobV2.getJobState());
+ Assert.assertEquals(1, jobV2.schemaChangeBatchTask.getTaskNum());
+
+ cancelAlterTableStmt = new
CancelAlterTableStmt(ShowAlterStmt.AlterType.COLUMN, tableName);
+ cancelAlterTableStmt.analyze(analyzer);
+ schemaChangeHandler.cancel(cancelAlterTableStmt);
+
+ schemaChangeHandler.runAfterCatalogReady();
+ Assert.assertEquals(AlterJobV2.JobState.CANCELLED,
jobV2.getJobState());
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
index b87ae340f1b..77693efa6e4 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
@@ -850,9 +850,8 @@ public class SchemaChangeHandlerTest extends
TestWithFeService {
waitAlterJobDone(alterJobs);
String buildNgramBfIndexStmtStr = "BUILD INDEX idx_error_msg on
test.sc_dup ";
- AlterTableStmt buildNgramBfIndexStmt = (AlterTableStmt)
parseAndAnalyzeStmt(buildNgramBfIndexStmtStr);
-
org.junit.jupiter.api.Assertions.assertThrows(org.apache.doris.common.DdlException.class,
- () ->
Env.getCurrentEnv().getAlterInstance().processAlterTable(buildNgramBfIndexStmt));
+
Assertions.assertThrows(org.apache.doris.common.AnalysisException.class,
+ () -> parseAndAnalyzeStmt(buildNgramBfIndexStmtStr));
tbl.readLock();
try {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/CatalogTestUtil.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/CatalogTestUtil.java
index 590b6563e11..b16d3d15cf7 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/CatalogTestUtil.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CatalogTestUtil.java
@@ -22,7 +22,8 @@ import org.apache.doris.analysis.PartitionValue;
import org.apache.doris.analysis.SinglePartitionDesc;
import org.apache.doris.catalog.MaterializedIndex.IndexExtState;
import org.apache.doris.catalog.MaterializedIndex.IndexState;
-import org.apache.doris.catalog.Replica.ReplicaState;
+import org.apache.doris.cloud.catalog.CloudReplica;
+import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.MetaNotFoundException;
import org.apache.doris.persist.EditLog;
@@ -175,13 +176,26 @@ public class CatalogTestUtil {
long version) {
Env.getCurrentInvertedIndex().clear();
- // replica
- Replica replica1 = new Replica(testReplicaId1, testBackendId1,
version, 0, 0L, 0L, 0L,
- ReplicaState.NORMAL, -1, 0);
- Replica replica2 = new Replica(testReplicaId2, testBackendId2,
version, 0, 0L, 0L, 0L,
- ReplicaState.NORMAL, -1, 0);
- Replica replica3 = new Replica(testReplicaId3, testBackendId3,
version, 0, 0L, 0L, 0L,
- ReplicaState.NORMAL, -1, 0);
+ Replica replica1;
+ Replica replica2;
+ Replica replica3;
+ if (Config.isCloudMode()) {
+ // In cloud mode we must create CloudReplica instances to avoid
ClassCastException
+ replica1 = new CloudReplica(testReplicaId1, testBackendId1,
Replica.ReplicaState.NORMAL, version,
+ /*schemaHash*/ 0, dbId, tableId, partitionId, indexId,
/*idx*/ 0);
+ replica2 = new CloudReplica(testReplicaId2, testBackendId2,
Replica.ReplicaState.NORMAL, version,
+ 0, dbId, tableId, partitionId, indexId, 1);
+ replica3 = new CloudReplica(testReplicaId3, testBackendId3,
Replica.ReplicaState.NORMAL, version,
+ 0, dbId, tableId, partitionId, indexId, 2);
+ } else {
+ replica1 = new Replica(testReplicaId1, testBackendId1, version, 0,
0L, 0L, 0L,
+ Replica.ReplicaState.NORMAL, -1, 0);
+ replica2 = new Replica(testReplicaId2, testBackendId2, version, 0,
0L, 0L, 0L,
+ Replica.ReplicaState.NORMAL, -1, 0);
+ replica3 = new Replica(testReplicaId3, testBackendId3, version, 0,
0L, 0L, 0L,
+ Replica.ReplicaState.NORMAL, -1, 0);
+ }
+
// tablet
Tablet tablet = new Tablet(tabletId);
@@ -244,10 +258,14 @@ public class CatalogTestUtil {
}
public static void createDupTable(Database db) {
-
- // replica
- Replica replica = new Replica(testReplicaId4, testBackendId1,
testStartVersion, 0, 0L, 0L, 0L,
- ReplicaState.NORMAL, -1, 0);
+ Replica replica;
+ if (Config.isCloudMode()) {
+ replica = new CloudReplica(testReplicaId4, testBackendId1,
Replica.ReplicaState.NORMAL, testStartVersion,
+ 0, db.getId(), testTableId2, testPartitionId2,
testIndexId2, 0);
+ } else {
+ replica = new Replica(testReplicaId4, testBackendId1,
testStartVersion, 0, 0L, 0L, 0L,
+ Replica.ReplicaState.NORMAL, -1, 0);
+ }
// tablet
Tablet tablet = new Tablet(testTabletId2);
diff --git
a/regression-test/data/index_p0/test_ngram_bloomfilter_index_change.out
b/regression-test/data/index_p0/test_ngram_bloomfilter_index_change.out
new file mode 100644
index 00000000000..49570b96d29
Binary files /dev/null and
b/regression-test/data/index_p0/test_ngram_bloomfilter_index_change.out differ
diff --git a/regression-test/pipeline/cloud_p1/conf/fe_custom.conf
b/regression-test/pipeline/cloud_p1/conf/fe_custom.conf
index 0e43d458f38..5bba5437f73 100644
--- a/regression-test/pipeline/cloud_p1/conf/fe_custom.conf
+++ b/regression-test/pipeline/cloud_p1/conf/fe_custom.conf
@@ -34,6 +34,5 @@ cloud_http_port=18030
meta_service_endpoint=127.0.0.1:5000
arrow_flight_sql_port = 8081
cloud_unique_id=cloud_unique_id_sql_server00
-enable_light_index_change=false
enable_advance_next_id = true
enable_job_schedule_second_for_test = true
\ No newline at end of file
diff --git
a/regression-test/suites/index_p0/test_ngram_bloomfilter_index_change.groovy
b/regression-test/suites/index_p0/test_ngram_bloomfilter_index_change.groovy
new file mode 100644
index 00000000000..402f72ddc36
--- /dev/null
+++ b/regression-test/suites/index_p0/test_ngram_bloomfilter_index_change.groovy
@@ -0,0 +1,325 @@
+// 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.
+import groovy.json.JsonSlurper
+
+suite("test_ngram_bloomfilter_index_change") {
+ def tableName = 'test_ngram_bloomfilter_index_change'
+ def timeout = 60000
+ def delta_time = 1000
+ def alter_res = "null"
+ def useTime = 0
+
+ def wait_for_latest_op_on_table_finish = { table_name, OpTimeout ->
+ for(int t = delta_time; t <= OpTimeout; t += delta_time){
+ alter_res = sql """SHOW ALTER TABLE COLUMN WHERE TableName =
"${table_name}" ORDER BY CreateTime DESC LIMIT 1;"""
+ alter_res = alter_res.toString()
+ if(alter_res.contains("FINISHED")) {
+ sleep(3000) // wait change table state to normal
+ logger.info(table_name + " latest alter job finished, detail:
" + alter_res)
+ break
+ }
+ useTime = t
+ sleep(delta_time)
+ }
+ assertTrue(useTime <= OpTimeout, "wait_for_latest_op_on_table_finish
timeout")
+ }
+
+ // Function to insert test data batch
+ def insertTestData = { ->
+ // insert 10 records
+ sql "INSERT INTO ${tableName} VALUES (1001, '2023-10-06 15:00:00',
'Laptop', 'John Smith', 199.99, 'North');"
+ sql "INSERT INTO ${tableName} VALUES (1002, '2023-10-09 17:05:00',
'Smartphone', 'Emily Johnson', 299.99, 'South');"
+ sql "INSERT INTO ${tableName} VALUES (1003, '2023-10-12 19:10:00',
'Headphones', 'Michael Brown', 399.99, 'East');"
+ sql "INSERT INTO ${tableName} VALUES (1004, '2023-10-15 21:15:00',
'Monitor', 'Jessica Davis', 499.99, 'West');"
+ sql "INSERT INTO ${tableName} VALUES (1005, '2023-10-18 23:20:00',
'Keyboard', 'David Wilson', 89.99, 'North');"
+ sql "INSERT INTO ${tableName} VALUES (1006, '2023-10-21 07:25:00',
'Mouse', 'Sarah Taylor', 699.99, 'South');"
+ sql "INSERT INTO ${tableName} VALUES (1007, '2023-10-24 09:30:00',
'Printer', 'Thomas Anderson', 799.99, 'East');"
+ sql "INSERT INTO ${tableName} VALUES (1008, '2023-10-27 11:35:00',
'Speaker', 'Jennifer Martin', 899.99, 'West');"
+ sql "INSERT INTO ${tableName} VALUES (1009, '2023-10-02 13:40:00',
'External SSD', 'Robert Clark', 999.99, 'North');"
+ sql "INSERT INTO ${tableName} VALUES (1010, '2023-10-05 15:45:00',
'Webcam', 'Amanda Lewis', 89.99, 'South');"
+ sql "sync"
+ }
+
+ // Test settings
+ sql "set enable_function_pushdown=true"
+ sql "set enable_profile=true"
+ sql "set profile_level=2"
+
+ // Define test query
+ def query = "SELECT /*+SET_VAR(enable_function_pushdown = true,
enable_profile = true, profile_level = 2)*/ * FROM ${tableName} WHERE
customer_name LIKE '%xxxx%' ORDER BY sale_id"
+ // Test Case 1: Test with enable_add_index_for_new_data = true
+ logger.info("=== Test Case 1: enable_add_index_for_new_data = true ===")
+ // Create table
+ sql "DROP TABLE IF EXISTS ${tableName}"
+ sql """
+ CREATE TABLE ${tableName} (
+ `sale_id` int NULL,
+ `sale_date` datetime NULL,
+ `product_name` varchar(100) NULL,
+ `customer_name` varchar(100) NULL,
+ `amount` decimal(10,2) NULL,
+ `region` char(50) NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(`sale_id`)
+ PARTITION BY RANGE(`sale_date`) (
+ PARTITION p202310 VALUES [('2023-10-01 00:00:00'), ('2023-11-01
00:00:00')),
+ PARTITION p202311 VALUES [('2023-11-01 00:00:00'), ('2023-12-01
00:00:00')),
+ PARTITION p202312 VALUES [('2023-12-01 00:00:00'), ('2024-01-01 00:00:00'))
+ )
+ DISTRIBUTED BY HASH(`sale_id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "storage_format" = "V2",
+ "light_schema_change" = "true",
+ "disable_auto_compaction" = "true"
+ );
+ """
+
+ // Insert test data
+ insertTestData()
+ // Verify data loaded correctly
+ qt_select_light_mode_init "SELECT * FROM ${tableName} ORDER BY sale_id"
+
+ // Test without NGRAM Bloom Filter index
+ profile("sql_select_like_without_ngram_index_light_mode") {
+ run {
+ sql "/* sql_select_like_without_ngram_index_light_mode */ ${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 0"))
+ }
+ }
+ sql "set enable_add_index_for_new_data = true"
+
+ // Add NGRAM Bloom Filter index (should be immediate in light mode)
+ sql "ALTER TABLE ${tableName} ADD INDEX
idx_ngram_customer_name(customer_name) USING NGRAM_BF PROPERTIES('bf_size' =
'1024', 'gram_size' = '3');"
+
+ // In light mode, the index should be effective immediately, no need to
wait for alter job
+ // But let's give it a moment to ensure metadata is updated
+ sleep(2000)
+
+ // Insert more data after index added
+ insertTestData()
+ // Verify more data loaded correctly
+ qt_select_light_mode_more_data "SELECT * FROM ${tableName} ORDER BY
sale_id"
+
+ // Test with more data (should still filter correctly)
+ profile("sql_select_like_with_ngram_index_light_mode_more_data") {
+ run {
+ sql "/* sql_select_like_with_ngram_index_light_mode_more_data */
${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 10"))
+ }
+ }
+
+ // Drop index
+ sql "DROP INDEX idx_ngram_customer_name ON ${tableName};"
+ wait_for_latest_op_on_table_finish(tableName, timeout)
+
+ // Test after dropping index
+ profile("sql_select_like_with_ngram_index_light_mode_dropped") {
+ run {
+ sql "/* sql_select_like_with_ngram_index_light_mode_dropped */
${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 0"))
+ }
+ }
+
+ // Test Case 2: Test with enable_add_index_for_new_data = false (schema
change mode)
+ logger.info("=== Test Case 2: enable_add_index_for_new_data = false ===")
+ // Set enable_add_index_for_new_data = false
+ sql "set enable_add_index_for_new_data = false"
+ // Create new table
+ sql "DROP TABLE IF EXISTS ${tableName}"
+ sql """
+ CREATE TABLE ${tableName} (
+ `sale_id` int NULL,
+ `sale_date` datetime NULL,
+ `product_name` varchar(100) NULL,
+ `customer_name` varchar(100) NULL,
+ `amount` decimal(10,2) NULL,
+ `region` char(50) NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(`sale_id`)
+ PARTITION BY RANGE(`sale_date`) (
+ PARTITION p202310 VALUES [('2023-10-01 00:00:00'), ('2023-11-01
00:00:00')),
+ PARTITION p202311 VALUES [('2023-11-01 00:00:00'), ('2023-12-01
00:00:00')),
+ PARTITION p202312 VALUES [('2023-12-01 00:00:00'), ('2024-01-01 00:00:00'))
+ )
+ DISTRIBUTED BY HASH(`sale_id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "storage_format" = "V2",
+ "light_schema_change" = "true",
+ "disable_auto_compaction" = "true"
+ );
+ """
+ // Insert test data
+ insertTestData()
+ // Verify data loaded correctly
+ qt_select_schema_change_mode_init "SELECT * FROM ${tableName} ORDER BY
sale_id"
+
+ // Test without NGRAM Bloom Filter index
+ profile("sql_select_like_without_ngram_index_schema_change_mode") {
+ run {
+ sql "/* sql_select_like_without_ngram_index_schema_change_mode */
${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 0"))
+ }
+ }
+
+ // Add NGRAM Bloom Filter index (will trigger schema change in this mode)
+ sql "ALTER TABLE ${tableName} ADD INDEX
idx_ngram_customer_name(customer_name) USING NGRAM_BF PROPERTIES('bf_size' =
'1024', 'gram_size' = '3');"
+ wait_for_latest_op_on_table_finish(tableName, timeout)
+
+ // Test after adding NGRAM Bloom Filter index (should filter existing data)
+ profile("sql_select_like_with_ngram_index_schema_change_mode_added") {
+ run {
+ sql "/* sql_select_like_with_ngram_index_schema_change_mode_added
*/ ${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 10"))
+ }
+ }
+
+ // Insert more data after index is built
+ insertTestData()
+ // Verify more data loaded correctly
+ qt_select_schema_change_mode_more_data "SELECT * FROM ${tableName} ORDER
BY sale_id"
+
+ // Test with more data (should filter all data)
+ profile("sql_select_like_with_ngram_index_schema_change_mode_more_data") {
+ run {
+ sql "/*
sql_select_like_with_ngram_index_schema_change_mode_more_data */ ${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 20"))
+ }
+ }
+
+ // Drop index
+ sql "DROP INDEX idx_ngram_customer_name ON ${tableName};"
+ wait_for_latest_op_on_table_finish(tableName, timeout)
+
+ // Test after dropping index
+ profile("sql_select_like_with_ngram_index_schema_change_mode_dropped") {
+ run {
+ sql "/*
sql_select_like_with_ngram_index_schema_change_mode_dropped */ ${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 0"))
+ }
+ }
+
+ // Test Case 3: Test different scenarios for index lifecycle
+ logger.info("=== Test Case 3: Index lifecycle with
enable_add_index_for_new_data = true ===")
+ // Set enable_add_index_for_new_data = true
+ sql "set enable_add_index_for_new_data = true"
+ // Create table and add index before inserting data
+ sql "DROP TABLE IF EXISTS ${tableName}"
+ sql """
+ CREATE TABLE ${tableName} (
+ `sale_id` int NULL,
+ `sale_date` datetime NULL,
+ `product_name` varchar(100) NULL,
+ `customer_name` varchar(100) NULL,
+ `amount` decimal(10,2) NULL,
+ `region` char(50) NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(`sale_id`)
+ PARTITION BY RANGE(`sale_date`) (
+ PARTITION p202310 VALUES [('2023-10-01 00:00:00'), ('2023-11-01
00:00:00')),
+ PARTITION p202311 VALUES [('2023-11-01 00:00:00'), ('2023-12-01
00:00:00')),
+ PARTITION p202312 VALUES [('2023-12-01 00:00:00'), ('2024-01-01 00:00:00'))
+ )
+ DISTRIBUTED BY HASH(`sale_id`) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "storage_format" = "V2",
+ "light_schema_change" = "true",
+ "disable_auto_compaction" = "true"
+ );
+ """
+
+ // Add ngram bf index before data insertion
+ sql "ALTER TABLE ${tableName} ADD INDEX
idx_ngram_customer_name(customer_name) USING NGRAM_BF PROPERTIES('bf_size' =
'1024', 'gram_size' = '3');"
+ wait_for_latest_op_on_table_finish(tableName, timeout)
+
+ // Insert data after index creation
+ insertTestData()
+ // Verify data loaded correctly
+ qt_select_lifecycle_after_data "SELECT * FROM ${tableName} ORDER BY
sale_id"
+
+ // Test filtering with index added before data insertion
+ profile("sql_select_like_with_ngram_index_lifecycle_test") {
+ run {
+ sql "/* sql_select_like_with_ngram_index_lifecycle_test */
${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 10"))
+ }
+ }
+
+ // Insert more data
+ insertTestData()
+ // Verify more data loaded correctly
+ qt_select_lifecycle_final "SELECT * FROM ${tableName} ORDER BY sale_id"
+
+ // Test filtering with more data
+ profile("sql_select_like_with_ngram_index_lifecycle_final") {
+ run {
+ sql "/* sql_select_like_with_ngram_index_lifecycle_final */
${query}"
+ sleep(1000)
+ }
+
+ check { profileString, exception ->
+ log.info(profileString)
+ assertTrue(profileString.contains("RowsBloomFilterFiltered: 20"))
+ }
+ }
+
+ // Final cleanup
+ sql "DROP INDEX idx_ngram_customer_name ON ${tableName};"
+ sleep(2000)
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]