|
72 | 72 | import org.apache.calcite.sql.SqlOperator; |
73 | 73 | import org.apache.calcite.sql.SqlSyntax; |
74 | 74 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; |
| 75 | +import org.apache.calcite.sql.type.ArraySqlType; |
75 | 76 | import org.apache.calcite.sql.type.SqlTypeFamily; |
76 | 77 | import org.apache.calcite.sql.type.SqlTypeName; |
77 | 78 | import org.apache.calcite.util.NlsString; |
@@ -485,6 +486,10 @@ private QueryExpression postfix(RexCall call) { |
485 | 486 | String message = format(Locale.ROOT, "Unsupported operator: [%s]", call); |
486 | 487 | throw new PredicateAnalyzerException(message); |
487 | 488 | } |
| 489 | + |
| 490 | + // OpenSearch DSL does not handle IS_NULL / IS_NOT_NULL on nested fields correctly |
| 491 | + checkForNestedFieldOperands(call); |
| 492 | + |
488 | 493 | Expression a = call.getOperands().get(0).accept(this); |
489 | 494 | // OpenSearch does not want is null/is not null (exists query) |
490 | 495 | // for _id and _index, although it supports for all other metadata column |
@@ -1357,6 +1362,11 @@ public ScriptQueryExpression( |
1357 | 1362 | RelDataType rowType, |
1358 | 1363 | Map<String, ExprType> fieldTypes, |
1359 | 1364 | RelOptCluster cluster) { |
| 1365 | + if (rexNode instanceof RexCall |
| 1366 | + && (rexNode.getKind().equals(SqlKind.IS_NULL) |
| 1367 | + || rexNode.getKind().equals(SqlKind.IS_NOT_NULL))) { |
| 1368 | + checkForNestedFieldOperands((RexCall) rexNode); |
| 1369 | + } |
1360 | 1370 | ReferenceFieldVisitor validator = new ReferenceFieldVisitor(rowType, fieldTypes, true); |
1361 | 1371 | // Dry run visitInputRef to make sure the input reference ExprType is valid for script |
1362 | 1372 | // pushdown |
@@ -1645,4 +1655,29 @@ private static void checkForIncompatibleDateTimeOperands(RexCall call) { |
1645 | 1655 | "Cannot handle " + call.getKind() + " expression for _id field, " + call); |
1646 | 1656 | } |
1647 | 1657 | } |
| 1658 | + |
| 1659 | + /** |
| 1660 | + * Examines the operands of a RexCall to check for nested fields and throws an exception if any |
| 1661 | + * are found. |
| 1662 | + * |
| 1663 | + * <p>This check prevents operations (IS_NULL, IS_NOT_NULL) that would produce incorrect results |
| 1664 | + * in OpenSearch when pushed down as either DSL queries or scripts. |
| 1665 | + * |
| 1666 | + * @param call The RexCall to check for nested field operands |
| 1667 | + * @throws PredicateAnalyzerException if any nested fields are detected in the operands |
| 1668 | + */ |
| 1669 | + private static void checkForNestedFieldOperands(RexCall call) throws PredicateAnalyzerException { |
| 1670 | + boolean conditionContainsNestedField = |
| 1671 | + call.getOperands().stream() |
| 1672 | + .map(RexNode::getType) |
| 1673 | + // Nested fields are of type ArraySqlType |
| 1674 | + .anyMatch(type -> type instanceof ArraySqlType); |
| 1675 | + if (conditionContainsNestedField) { |
| 1676 | + throw new PredicateAnalyzerException( |
| 1677 | + format( |
| 1678 | + Locale.ROOT, |
| 1679 | + "OpenSearch DSL does not handle %s on nested fields correctly", |
| 1680 | + call.getKind())); |
| 1681 | + } |
| 1682 | + } |
1648 | 1683 | } |
0 commit comments