This is an automated email from the ASF dual-hosted git repository.
sereda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new b727f3b [CALCITE-3056] Elasticsearch adapter. Invalid result with
cast function on raw queries
b727f3b is described below
commit b727f3b04286dd39fddb25a9905819449d27475b
Author: Andrei Sereda <[email protected]>
AuthorDate: Wed May 8 01:52:41 2019 -0400
[CALCITE-3056] Elasticsearch adapter. Invalid result with cast function on
raw queries
Fix queries of type (with elastic adapter):
```sql
select max(cast(_MAP['foo'] as integer)) from elastic
```
---
.../adapter/elasticsearch/ElasticsearchRules.java | 28 ++++++++++++++++++----
.../adapter/elasticsearch/AggregationTest.java | 18 ++++++++++++++
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
index b7c0e34..ed20aa7 100644
---
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
+++
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
@@ -66,7 +66,7 @@ class ElasticsearchRules {
* @param call current relational expression
* @return literal value
*/
- static String isItem(RexCall call) {
+ private static String isItemCall(RexCall call) {
if (call.getOperator() != SqlStdOperatorTable.ITEM) {
return null;
}
@@ -82,15 +82,33 @@ class ElasticsearchRules {
return null;
}
+ /**
+ * Checks if current node represents item access as in {@code _MAP['foo']} or
+ * {@code cast(_MAP['foo'] as integer)}
+ *
+ * @return true if expression is item, false otherwise
+ */
static boolean isItem(RexNode node) {
final Boolean result = node.accept(new RexVisitorImpl<Boolean>(false) {
@Override public Boolean visitCall(final RexCall call) {
- return isItem(call) != null;
+ return isItemCall(uncast(call)) != null;
}
});
return Boolean.TRUE.equals(result);
}
+ /**
+ * Unwraps cast expressions from current call. {@code cast(cast(expr))}
becomes {@code expr}.
+ */
+ private static RexCall uncast(RexCall maybeCast) {
+ if (maybeCast.getKind() == SqlKind.CAST && maybeCast.getOperands().get(0)
instanceof RexCall) {
+ return uncast((RexCall) maybeCast.getOperands().get(0));
+ }
+
+ // not a cast
+ return maybeCast;
+ }
+
static List<String> elasticsearchFieldNames(final RelDataType rowType) {
return SqlValidatorUtil.uniquify(
new AbstractList<String>() {
@@ -144,15 +162,17 @@ class ElasticsearchRules {
}
@Override public String visitCall(RexCall call) {
- final String name = isItem(call);
+ final String name = isItemCall(call);
if (name != null) {
return name;
}
final List<String> strings = visitList(call.operands);
+
if (call.getKind() == SqlKind.CAST) {
- return strings.get(0).startsWith("$") ? strings.get(0).substring(1) :
strings.get(0);
+ return call.getOperands().get(0).accept(this);
}
+
if (call.getOperator() == SqlStdOperatorTable.ITEM) {
final RexNode op1 = call.getOperands().get(1);
if (op1 instanceof RexLiteral && op1.getType().getSqlTypeName() ==
SqlTypeName.INTEGER) {
diff --git
a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java
b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java
index 5b29e71..25fcd17 100644
---
a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java
+++
b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java
@@ -352,6 +352,24 @@ public class AggregationTest {
"cat1=b; EXPR$1=1",
"cat1=null; EXPR$1=1");
}
+
+ /**
+ * {@code select max(cast(_MAP['foo'] as integer)) from tbl}
+ */
+ @Test
+ public void aggregationWithCast() {
+ CalciteAssert.that()
+ .with(newConnectionFactory())
+ .query(
+ String.format(Locale.ROOT, "select max(cast(_MAP['val1'] as
integer)) as v1, "
+ + "min(cast(_MAP['val2'] as integer)) as v2 from elastic.%s",
NAME))
+ .queryContains(
+ ElasticsearchChecker.elasticsearchChecker("_source:false, size:0",
+ "aggregations:{'v1.max.field': 'val1'",
+ "'v2.min.field': 'val2'}"))
+ .returnsUnordered("v1=7; v2=5");
+
+ }
}
// End AggregationTest.java