You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by pr...@apache.org on 2025/09/09 10:47:04 UTC
(asterixdb) branch master updated: [ASTERIXDB-3632] Fix NPEs in Index Advisor and add error handling for missing samples
This is an automated email from the ASF dual-hosted git repository.
preetham02 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new f9c6fe50c6 [ASTERIXDB-3632] Fix NPEs in Index Advisor and add error handling for missing samples
f9c6fe50c6 is described below
commit f9c6fe50c6c2f87b3ee6d32fdca321095df4233f
Author: preetham0202 <pr...@couchbase.com>
AuthorDate: Fri Aug 29 16:55:46 2025 +0530
[ASTERIXDB-3632] Fix NPEs in Index Advisor and add error handling for missing samples
- user model changes: no
- storage format changes: no
- interface changes: no
Ext-ref: MB-66492
Details:
This patch addresses several corner cases in the Index Advisor:
- Fixes null pointer exceptions that occur in scenarios such as:
- Collections with secondary primary indexes
- Cases where CBO bails out
- Adds proper error handling when an `ADVISE` query is issued without CBO enabled
- Introduces error reporting for collections that have no samples, suggesting the use of ANALYSE SAMPLE before issuing advise
Change-Id: I346728dd634934416d7115a06ab15572b5a98854
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/20282
Reviewed-by: Ali Alsuliman <al...@gmail.com>
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
Reviewed-by: Preetham Poluparthi <pr...@apache.org>
Tested-by: Preetham Poluparthi <pr...@apache.org>
---
.../rules/am/IntroduceJoinAccessMethodRule.java | 2 +-
.../rules/am/IntroduceSelectAccessMethodRule.java | 2 +-
.../optimizer/rules/cbo/EnumerateJoinsRule.java | 50 +++++++++++++--
.../rules/cbo/indexadvisor/AdviseIndexRule.java | 9 +++
.../cbo/indexadvisor/AdvisorConditionParser.java | 72 ++++++++++++----------
.../rules/cbo/indexadvisor/FakeIndexProvider.java | 8 ++-
.../apache/asterix/api/common/APIFramework.java | 8 +++
.../algebricks/core/algebra/base/IndexAdvisor.java | 1 -
.../apache/hyracks/api/exceptions/ErrorCode.java | 3 +-
.../src/main/resources/errormsg/en.properties | 3 +-
10 files changed, 116 insertions(+), 42 deletions(-)
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
index 5bc1e90b5d..09cf843592 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
@@ -123,7 +123,7 @@ public class IntroduceJoinAccessMethodRule extends AbstractIntroduceAccessMethod
throws AlgebricksException {
clear();
boolean adviseIndex = context.getIndexAdvisor().getAdvise();
- if (adviseIndex) {
+ if (adviseIndex && context.getIndexAdvisor().getFakeIndexProvider() != null) {
setMetadataIndexDeclarations(context, (IIndexProvider) context.getIndexAdvisor().getFakeIndexProvider());
} else {
setMetadataIndexDeclarations(context, (IIndexProvider) context.getMetadataProvider());
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
index 56ee034d19..df9fb2cf5a 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
@@ -138,7 +138,7 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
clear();
boolean adviseIndex = context.getIndexAdvisor().getAdvise();
- if (adviseIndex) {
+ if (adviseIndex && context.getIndexAdvisor().getFakeIndexProvider() != null) {
setMetadataIndexDeclarations(context, (IIndexProvider) context.getIndexAdvisor().getFakeIndexProvider());
} else {
setMetadataIndexDeclarations(context, (IIndexProvider) context.getMetadataProvider());
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
index 6e4208d14e..b02301f260 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
@@ -28,6 +28,8 @@ import java.util.Map;
import org.apache.asterix.common.annotations.IndexedNLJoinExpressionAnnotation;
import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
+import org.apache.asterix.common.metadata.DatasetFullyQualifiedName;
+import org.apache.asterix.metadata.declared.DatasetDataSource;
import org.apache.asterix.metadata.declared.IIndexProvider;
import org.apache.asterix.metadata.entities.Index;
import org.apache.asterix.optimizer.rules.cbo.indexadvisor.AdvisorPlanParser;
@@ -71,6 +73,7 @@ import org.apache.hyracks.algebricks.core.algebra.prettyprint.IPlanPrettyPrinter
import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
import org.apache.hyracks.algebricks.core.rewriter.base.PhysicalOptimizationConfig;
+import org.apache.hyracks.api.exceptions.ErrorCode;
import org.apache.hyracks.api.exceptions.IWarningCollector;
import org.apache.hyracks.api.exceptions.Warning;
import org.apache.logging.log4j.LogManager;
@@ -140,8 +143,8 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
throws AlgebricksException {
- boolean cboMode = this.getCBOMode(context);
- boolean cboTestMode = this.getCBOTestMode(context);
+ boolean cboMode = getCBOMode(context);
+ boolean cboTestMode = getCBOTestMode(context);
if (!(cboMode || cboTestMode)) {
return false;
@@ -215,6 +218,9 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
joinEnum.stats = new Stats(context, joinEnum);
if (cboMode) {
if (!doAllDataSourcesHaveSamples(leafInputs, context)) {
+ if (adviseIndex) {
+ errorOutIndexAdvisorSamplesNotFound(leafInputs, context);
+ }
return cleanUp();
}
}
@@ -278,6 +284,9 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
fakeLeafInputsMap, context, indexProvider);
if (cboMode) {
if (!doAllDataSourcesHaveSamples(leafInputs, context)) {
+ if (adviseIndex) {
+ errorOutIndexAdvisorSamplesNotFound(leafInputs, context);
+ }
return cleanUp();
}
}
@@ -768,12 +777,12 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
}
}
- private boolean getCBOMode(IOptimizationContext context) {
+ public static boolean getCBOMode(IOptimizationContext context) {
PhysicalOptimizationConfig physOptConfig = context.getPhysicalOptimizationConfig();
return physOptConfig.getCBOMode();
}
- private boolean getCBOTestMode(IOptimizationContext context) {
+ public static boolean getCBOTestMode(IOptimizationContext context) {
PhysicalOptimizationConfig physOptConfig = context.getPhysicalOptimizationConfig();
return physOptConfig.getCBOTestMode();
}
@@ -1521,4 +1530,37 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
}
return (leafInputs.size() == n);
}
+
+ private void errorOutIndexAdvisorSamplesNotFound(List<ILogicalOperator> leafInputs, IOptimizationContext context)
+ throws AlgebricksException {
+ for (ILogicalOperator li : leafInputs) {
+ DataSourceScanOperator scanOp = joinEnum.findDataSourceScanOperator(li);
+ if (scanOp == null) {
+ // Scan Operator not found
+ continue;
+ }
+ Stats handle = joinEnum.getStatsHandle();
+ if (handle == null) {
+ continue;
+ }
+ Index index = handle.findSampleIndex(scanOp, context);
+ if (index == null) {
+ errorOutIndexAdvisorSampleNotFound(scanOp, context);
+ }
+ }
+ }
+
+ private void errorOutIndexAdvisorSampleNotFound(DataSourceScanOperator scanOperator, IOptimizationContext context) throws AlgebricksException {
+ if (!(scanOperator.getDataSource() instanceof DatasetDataSource dataSource)) {
+ return;
+ }
+ DatasetFullyQualifiedName fullyQualifiedName = dataSource.getDataset().getDatasetFullyQualifiedName();
+ throw new AlgebricksException(ErrorCode.INDEX_ADVISOR_SAMPLE_NOT_FOUND, createSampleStatement(fullyQualifiedName));
+ }
+
+ private static String createSampleStatement(DatasetFullyQualifiedName dqn) {
+ return "ANALYZE COLLECTION `" + dqn.getDatabaseName() + "`.`" + dqn.getDataverseName() + "`.`"
+ + dqn.getDatasetName() + "`;";
+ }
+
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java
index 9a7fbdd53e..7e34d25663 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java
@@ -121,7 +121,16 @@ public class AdviseIndexRule implements IAlgebraicRewriteRule {
DataverseName dataverse = jobGenParams.getDataverseName();
String datasetName = jobGenParams.getDatasetName();
+ if(fakeIndexProvider == null) {
+ // Case when CBO can't parse the plan correctly and the fake index provider is not set.
+ return;
+ }
Index fakeIndex = fakeIndexProvider.getIndex(databaseName, dataverse, datasetName, indexName);
+ if (fakeIndex == null || !(fakeIndex.getIndexDetails() instanceof Index.ValueIndexDetails)) {
+ // skips secondary primary index like
+ // create primary index sec_primary_idx on A;
+ return;
+ }
Index actualIndex = lookupIndex(databaseName, dataverse, datasetName,
((Index.ValueIndexDetails) fakeIndex.getIndexDetails()).getKeyFieldNames(), actualIndexProvider);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java
index a86b1b5df2..05039aa5d2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java
@@ -47,11 +47,27 @@ import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
public class AdvisorConditionParser {
+ private static class ExprRef {
+ private final ILogicalExpression expr;
+ private final ILogicalOperator op;
+
+ public ExprRef(Mutable<ILogicalExpression> expr, ILogicalOperator op) {
+ this.expr = expr.getValue().cloneExpression();
+ this.op = op;
+ }
+
+ public ILogicalExpression getExpr() {
+ return expr;
+ }
+
+ public ILogicalOperator getOp() {
+ return op;
+ }
+ }
+
public static ScanFilter parseScanNode(ILogicalOperator op, IOptimizationContext context)
throws AlgebricksException {
-
- List<Mutable<ILogicalExpression>> filterExprs = new ArrayList<>();
- IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(op, context);
+ List<ExprRef> filterExprRefs = new ArrayList<>();
ILogicalOperator tempOp = op;
do {
if (tempOp.getOperatorTag() == LogicalOperatorTag.SELECT) {
@@ -59,35 +75,33 @@ public class AdvisorConditionParser {
ILogicalExpression condition = selectOp.getCondition().getValue();
List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
if (condition.splitIntoConjuncts(conjs)) {
- filterExprs.addAll(conjs);
+ filterExprRefs.addAll(conjs.stream().map(expr -> new ExprRef(expr, selectOp)).toList());
} else {
- filterExprs.add(selectOp.getCondition());
+ filterExprRefs.add(new ExprRef(selectOp.getCondition(), selectOp));
}
}
tempOp = tempOp.getInputs().getFirst().getValue();
} while (tempOp.hasInputs());
- filterExprs = OperatorManipulationUtil.cloneExpressions(filterExprs);
-
- filterExprs.removeIf(expr -> !(expr.getValue() instanceof AbstractFunctionCallExpression));
+ filterExprRefs.removeIf(exprRef -> !(exprRef.getExpr() instanceof AbstractFunctionCallExpression));
tempOp = op;
do {
if (tempOp.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
- replaceExprsWithAssign((AssignOperator) tempOp, filterExprs);
+ replaceExprsWithAssign((AssignOperator) tempOp, filterExprRefs);
}
tempOp = tempOp.getInputs().getFirst().getValue();
} while (tempOp.hasInputs());
List<ScanFilterCondition> filterConditions = new ArrayList<>();
- for (Mutable<ILogicalExpression> filterExpr : filterExprs) {
- ScanFilterCondition filterCondition = parseCondition(filterExpr.getValue(), typeEnv);
+ for (ExprRef exprRef : filterExprRefs) {
+ IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(exprRef.getOp(), context);
+ ScanFilterCondition filterCondition = parseCondition(exprRef.getExpr(), typeEnv);
if (filterCondition != null) {
filterConditions.add(filterCondition);
}
}
-
return new ScanFilter(filterConditions);
}
@@ -96,9 +110,7 @@ public class AdvisorConditionParser {
if (!(logicalExpression instanceof AbstractFunctionCallExpression expr)) {
return null;
}
-
FunctionIdentifier fi = expr.getFunctionIdentifier();
-
if (!BTreeAccessMethod.INSTANCE.getOptimizableFunctions().contains(new Pair<>(fi, false))) {
return null;
}
@@ -137,6 +149,9 @@ public class AdvisorConditionParser {
expr = functionCallExpr.getArguments().getFirst().getValue();
}
+ if (fieldNames.isEmpty()) {
+ return null;
+ }
VariableReferenceExpression varRef = (VariableReferenceExpression) expr;
LogicalVariable var = varRef.getVariableReference();
@@ -145,23 +160,21 @@ public class AdvisorConditionParser {
public static JoinFilter parseJoinNode(AbstractBinaryJoinOperator joinOp, IOptimizationContext context)
throws AlgebricksException {
- List<Mutable<ILogicalExpression>> joinExprs = new ArrayList<>();
- IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(joinOp, context);
-
+ List<ExprRef> joinExprs = new ArrayList<>();
ILogicalExpression joinExpression = joinOp.getCondition().getValue();
List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
if (joinExpression.splitIntoConjuncts(conjs)) {
- joinExprs.addAll(conjs);
+ joinExprs.addAll(conjs.stream().map(expr -> new ExprRef(expr, joinOp)).toList());
} else {
- joinExprs.add(joinOp.getCondition());
+ joinExprs.add(new ExprRef(joinOp.getCondition(), joinOp));
}
- joinExprs = OperatorManipulationUtil.cloneExpressions(joinExprs);
- traverseAndReplace(joinOp, joinExprs);
+ traverseAndReplace(joinOp, joinExprs);
List<JoinFilterCondition> joinConditions = new ArrayList<>();
- for (Mutable<ILogicalExpression> joinExpr : joinExprs) {
- JoinFilterCondition joinCondition = parseJoinCondition(joinExpr.getValue(), typeEnv);
+ for (ExprRef joinExprRef : joinExprs) {
+ IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(joinExprRef.getOp(), context);
+ JoinFilterCondition joinCondition = parseJoinCondition(joinExprRef.getExpr(), typeEnv);
if (joinCondition != null) {
joinConditions.add(joinCondition);
}
@@ -170,25 +183,22 @@ public class AdvisorConditionParser {
return new JoinFilter(joinConditions);
}
- private static void traverseAndReplace(AbstractLogicalOperator op, List<Mutable<ILogicalExpression>> exprs) {
-
+ private static void traverseAndReplace(AbstractLogicalOperator op, List<ExprRef> exprs) {
if (op.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
replaceExprsWithAssign((AssignOperator) op, exprs);
}
-
for (Mutable<ILogicalOperator> input : op.getInputs()) {
traverseAndReplace((AbstractLogicalOperator) input.getValue(), exprs);
}
-
}
- public static void replaceExprsWithAssign(AssignOperator assignOp, List<Mutable<ILogicalExpression>> exprs) {
- for (Mutable<ILogicalExpression> filterExpr : exprs) {
- if (filterExpr.getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+ private static void replaceExprsWithAssign(AssignOperator assignOp, List<ExprRef> exprRefs) {
+ for (ExprRef exprRef : exprRefs) {
+ if (exprRef.getExpr().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
continue;
}
for (int i = 0; i < assignOp.getVariables().size(); i++) {
- OperatorManipulationUtil.replaceVarWithExpr((AbstractFunctionCallExpression) filterExpr.getValue(),
+ OperatorManipulationUtil.replaceVarWithExpr((AbstractFunctionCallExpression) exprRef.getExpr(),
assignOp.getVariables().get(i), assignOp.getExpressions().get(i).getValue());
}
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java
index ee487dd146..1b33096206 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java
@@ -20,6 +20,7 @@ package org.apache.asterix.optimizer.rules.cbo.indexadvisor;
import static java.util.UUID.randomUUID;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -131,6 +132,9 @@ public class FakeIndexProvider implements IIndexProvider {
for (Map.Entry<DatasetFullyQualifiedName, Set<List<String>>> entry : joinDataSourceFieldNamesMap.entrySet()) {
DatasetFullyQualifiedName qualifiedName = entry.getKey();
Set<List<String>> fieldNames = entry.getValue();
+ if (fieldNames.isEmpty()) {
+ continue;
+ }
joinIndexEnumerator.init(fieldNames);
Iterator<List<List<String>>> itr = joinIndexEnumerator.getIterator();
@@ -169,8 +173,8 @@ public class FakeIndexProvider implements IIndexProvider {
@Override
public List<Index> getDatasetIndexes(String database, DataverseName dataverseName, String datasetName)
throws AlgebricksException {
- return filterIndexesMap.get(new DatasetFullyQualifiedName(database, dataverseName, datasetName)).values()
- .stream().toList();
+ return filterIndexesMap.getOrDefault(new DatasetFullyQualifiedName(database, dataverseName, datasetName),
+ Collections.emptyMap()).values().stream().toList();
}
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 59e6dc2d09..aff78503f0 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -312,6 +312,14 @@ public class APIFramework {
builder.setNormalizedKeyComputerFactoryProvider(format.getNormalizedKeyComputerFactoryProvider());
IndexAdvisor indexAdvisor = new IndexAdvisor(isAdviceOnly);
+
+ if (isAdviceOnly) {
+ if (!physOptConf.getCBOMode() && !physOptConf.getCBOTestMode()) {
+ throw new CompilationException(ErrorCode.COMPILATION_ERROR,
+ "Index advise cannot be used without CBO mode.");
+ }
+ }
+
IRuleSetKind ruleSetKind = isAdviceOnly ? LOGICAL_ADVISOR : QUERY;
ICompiler compiler = compilerFactory.createCompiler(plan, metadataProvider, t.getVarCounter(), ruleSetKind,
indexAdvisor);
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java
index d381afab43..1ba74ca3cf 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java
@@ -130,7 +130,6 @@ public class IndexAdvisor {
}
public String getCreateIndexClause() {
-
return "CREATE INDEX " + indexName + " ON `" + databaseName + "`.`" + dataverseName + "`.`" + datasetName
+ "`" + getKeyFieldNamesClause() + ";";
}
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
index d7abf4c057..d83270f118 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
@@ -174,7 +174,8 @@ public enum ErrorCode implements IError {
INAPPLICABLE_HINT(10006),
CROSS_PRODUCT_JOIN(10007),
GROUP_ALL_DECOR(10008),
- EXPRESSION_CANNOT_BE_CONSTANT(10009);
+ EXPRESSION_CANNOT_BE_CONSTANT(10009),
+ INDEX_ADVISOR_SAMPLE_NOT_FOUND(10010);
private static final String RESOURCE_PATH = "errormsg/en.properties";
public static final String HYRACKS = "HYR";
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
index e9569be66f..8e16262c78 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
@@ -163,4 +163,5 @@
10006 = Could not apply %1$s hint: %2$s
10007 = Encountered a cross product join
10008 = Inappropriate use of group by all with decor variables
-10009 = '%1$s' expression cannot be a constant
\ No newline at end of file
+10009 = '%1$s' expression cannot be a constant
+10010 = No samples found for the collections in this query. Create samples with: '%1$s'
\ No newline at end of file