Skip to content

Commit fa9e656

Browse files
committed
Experiment blacklist certain coercion by extending TypeCoercionImpl
Signed-off-by: Yuanchun Shen <yuanchu@amazon.com>
1 parent 844664d commit fa9e656

8 files changed

Lines changed: 127 additions & 40 deletions

File tree

core/src/main/java/org/opensearch/sql/calcite/utils/CalciteToolsHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
import org.opensearch.sql.calcite.CalcitePlanContext;
9393
import org.opensearch.sql.calcite.plan.Scannable;
9494
import org.opensearch.sql.calcite.udf.udaf.NullableSqlAvgAggFunction;
95-
import org.opensearch.sql.calcite.validate.PPLOpTable;
95+
import org.opensearch.sql.calcite.validate.PplOpTable;
9696

9797
/**
9898
* Calcite Tools Helper. This class is used to create customized: 1. Connection 2. JavaTypeFactory
@@ -343,7 +343,7 @@ public Type getElementType() {
343343
protected SqlValidator createSqlValidator(CalciteCatalogReader catalogReader) {
344344
return SqlValidatorUtil.newValidator(
345345
// this is different from the original implementation
346-
PPLOpTable.getInstance(),
346+
PplOpTable.getInstance(),
347347
catalogReader,
348348
context.getTypeFactory(),
349349
// this may be customized in the future

core/src/main/java/org/opensearch/sql/calcite/validate/OpenSearchTypeCoercionRule.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

core/src/main/java/org/opensearch/sql/calcite/validate/PPLOpTable.java renamed to core/src/main/java/org/opensearch/sql/calcite/validate/PplOpTable.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* PPLOpTable is a custom implementation of {@link SqlOperatorTable} that provides a way to register
2828
* and look up PPL operators.
2929
*/
30-
public class PPLOpTable implements SqlOperatorTable {
30+
public class PplOpTable implements SqlOperatorTable {
3131
// Implementation notes:
3232
// - Did not extend ListSqlOperatorTable because it does not support registering multiple
3333
// SqlOperator to one name.
@@ -38,13 +38,13 @@ public class PPLOpTable implements SqlOperatorTable {
3838

3939
protected Map<BuiltinFunctionName, ArrayList<SqlOperator>> operators;
4040

41-
private static final PPLOpTable INSTANCE = new PPLOpTable();
41+
private static final PplOpTable INSTANCE = new PplOpTable();
4242

43-
public static PPLOpTable getInstance() {
43+
public static PplOpTable getInstance() {
4444
return INSTANCE;
4545
}
4646

47-
private PPLOpTable() {
47+
private PplOpTable() {
4848
this.operators = new HashMap<>();
4949
}
5050

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.calcite.validate;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
import java.util.Set;
11+
import java.util.stream.IntStream;
12+
import org.apache.calcite.rel.type.RelDataType;
13+
import org.apache.calcite.rel.type.RelDataTypeFactory;
14+
import org.apache.calcite.sql.SqlCallBinding;
15+
import org.apache.calcite.sql.type.SqlTypeFamily;
16+
import org.apache.calcite.sql.validate.SqlValidator;
17+
import org.apache.calcite.sql.validate.implicit.TypeCoercionImpl;
18+
19+
public class PplTypeCoercion extends TypeCoercionImpl {
20+
// A blacklist of coercions that are not allowed in PPL.
21+
// key cannot be cast from values
22+
private static final Map<SqlTypeFamily, Set<SqlTypeFamily>> BLACKLISTED_COERCIONS;
23+
24+
static {
25+
// Initialize the blacklist for coercions that are not allowed in PPL.
26+
BLACKLISTED_COERCIONS =
27+
Map.of(
28+
SqlTypeFamily.CHARACTER,
29+
Set.of(SqlTypeFamily.NUMERIC),
30+
SqlTypeFamily.STRING,
31+
Set.of(SqlTypeFamily.NUMERIC),
32+
SqlTypeFamily.NUMERIC,
33+
Set.of(SqlTypeFamily.CHARACTER, SqlTypeFamily.STRING));
34+
}
35+
36+
public PplTypeCoercion(RelDataTypeFactory typeFactory, SqlValidator validator) {
37+
super(typeFactory, validator);
38+
}
39+
40+
@Override
41+
public boolean builtinFunctionCoercion(
42+
SqlCallBinding binding,
43+
List<RelDataType> operandTypes,
44+
List<SqlTypeFamily> expectedFamilies) {
45+
assert binding.getOperandCount() == operandTypes.size();
46+
if (IntStream.range(0, operandTypes.size())
47+
.anyMatch(i -> isBlacklistedCoercion(operandTypes.get(i), expectedFamilies.get(i)))) {
48+
return false;
49+
}
50+
return super.builtinFunctionCoercion(binding, operandTypes, expectedFamilies);
51+
}
52+
53+
// This method tries to blacklist coercions that are not allowed in PPL.
54+
private boolean isBlacklistedCoercion(RelDataType operandType, SqlTypeFamily expectedFamily) {
55+
if (BLACKLISTED_COERCIONS.containsKey(expectedFamily)) {
56+
Set<SqlTypeFamily> blacklistedFamilies = BLACKLISTED_COERCIONS.get(expectedFamily);
57+
if (blacklistedFamilies.contains(operandType.getSqlTypeName().getFamily())) {
58+
return true;
59+
}
60+
}
61+
return false;
62+
}
63+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.calcite.validate;
7+
8+
import org.apache.calcite.rel.type.RelDataTypeFactory;
9+
import org.apache.calcite.sql.SqlOperatorTable;
10+
import org.apache.calcite.sql.validate.SqlValidatorCatalogReader;
11+
import org.apache.calcite.sql.validate.SqlValidatorImpl;
12+
13+
public class PplValidator extends SqlValidatorImpl {
14+
/**
15+
* Creates a validator.
16+
*
17+
* @param opTab Operator table
18+
* @param catalogReader Catalog reader
19+
* @param typeFactory Type factory
20+
* @param config Config
21+
*/
22+
protected PplValidator(
23+
SqlOperatorTable opTab,
24+
SqlValidatorCatalogReader catalogReader,
25+
RelDataTypeFactory typeFactory,
26+
Config config) {
27+
super(opTab, catalogReader, typeFactory, config);
28+
}
29+
}

core/src/main/java/org/opensearch/sql/calcite/validate/TypeChecker.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
package org.opensearch.sql.calcite.validate;
77

8-
import java.util.Map;
98
import org.apache.calcite.jdbc.CalcitePrepare;
109
import org.apache.calcite.jdbc.CalciteSchema;
1110
import org.apache.calcite.prepare.CalciteCatalogReader;
11+
import org.apache.calcite.rel.type.RelDataTypeFactory;
1212
import org.apache.calcite.schema.SchemaPlus;
1313
import org.apache.calcite.server.CalciteServerStatement;
1414
import org.apache.calcite.sql.type.SqlTypeCoercionRule;
1515
import org.apache.calcite.sql.validate.SqlValidator;
16-
import org.apache.calcite.sql.validate.SqlValidatorUtil;
16+
import org.apache.calcite.sql.validate.implicit.TypeCoercion;
1717
import org.apache.calcite.tools.FrameworkConfig;
1818
import org.opensearch.sql.calcite.utils.OpenSearchTypeFactory;
1919

@@ -32,16 +32,25 @@ public static SqlValidator getValidator(
3232
OpenSearchTypeFactory.TYPE_FACTORY,
3333
prepareContext.config());
3434
SqlValidator.Config validatorConfig =
35-
SqlValidator.Config.DEFAULT.withTypeCoercionRules(getTypeCoercionRule());
36-
return SqlValidatorUtil.newValidator(
37-
PPLOpTable.getInstance(),
35+
SqlValidator.Config.DEFAULT
36+
.withTypeCoercionRules(getTypeCoercionRule())
37+
.withTypeCoercionFactory(TypeChecker::createTypeCoercion);
38+
return new PplValidator(
39+
PplOpTable.getInstance(),
3840
catalogReader,
3941
OpenSearchTypeFactory.TYPE_FACTORY,
4042
validatorConfig);
4143
}
4244

4345
public static SqlTypeCoercionRule getTypeCoercionRule() {
4446
var defaultMapping = SqlTypeCoercionRule.instance().getTypeMapping();
45-
return SqlTypeCoercionRule.instance(Map.of());
47+
// try deleting all coercion rules
48+
return SqlTypeCoercionRule.instance(defaultMapping);
49+
}
50+
51+
/** Creates a custom TypeCoercion instance for PPL. This can be used as a TypeCoercionFactory. */
52+
public static TypeCoercion createTypeCoercion(
53+
RelDataTypeFactory typeFactory, SqlValidator validator) {
54+
return new PplTypeCoercion(typeFactory, validator);
4655
}
4756
}

core/src/main/java/org/opensearch/sql/executor/QueryService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public void explainWithCalcite(
153153
CalcitePlanContext.create(
154154
buildFrameworkConfig(), getQuerySizeLimit(), queryType);
155155
RelNode relNode = analyze(plan, context);
156+
RelNode validated = validate(relNode, context);
156157
RelNode optimized = optimize(relNode);
157158
RelNode calcitePlan = convertToCalcitePlan(optimized);
158159
executionEngine.explain(calcitePlan, format, context, listener);
@@ -284,15 +285,24 @@ public SqlNode visit(SqlIdentifier id) {
284285
}
285286
});
286287
SqlValidator validator = context.getValidator();
288+
SqlNode validated;
287289
if (rewritten != null) {
288290
try {
289-
SqlNode validated = validator.validate(rewritten);
290-
log.debug("After validation [{}]", validated);
291+
String before = rewritten.toString();
292+
// rewritten will be modified in-place
293+
validator.validate(rewritten);
294+
log.debug("After validation [{}]", rewritten);
295+
String after = rewritten.toString();
296+
if (before.equals(after)) {
297+
// If the rewritten SQL node is not modified, we can return the original RelNode as is
298+
return relNode;
299+
}
291300
} catch (CalciteContextException e) {
292301
throw new ExpressionEvaluationException(e.getMessage(), e);
293302
}
294303
} else {
295304
log.debug("Failed to rewrite the SQL node before validation: {}", root);
305+
return relNode;
296306
}
297307

298308
// Convert the validated SqlNode to RelNode

core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@
255255
import org.opensearch.sql.calcite.utils.OpenSearchTypeFactory;
256256
import org.opensearch.sql.calcite.utils.PlanUtils;
257257
import org.opensearch.sql.calcite.utils.UserDefinedFunctionUtils;
258-
import org.opensearch.sql.calcite.validate.PPLOpTable;
258+
import org.opensearch.sql.calcite.validate.PplOpTable;
259259
import org.opensearch.sql.exception.ExpressionEvaluationException;
260260
import org.opensearch.sql.executor.QueryType;
261261

@@ -521,7 +521,7 @@ void registerOperator(BuiltinFunctionName functionName, SqlOperator operator) {
521521
}
522522

523523
// Currently, only functions registered via registerOperator is added to PPLOpTable
524-
PPLOpTable.getInstance().add(functionName, operator);
524+
PplOpTable.getInstance().add(functionName, operator);
525525
}
526526

527527
private static SqlOperandTypeChecker extractTypeCheckerFromUDF(

0 commit comments

Comments
 (0)