Skip to content

Commit

Permalink
SQL: Fix exception when using CAST on inexact field (#62943) (#63186)
Browse files Browse the repository at this point in the history
Currently, CAST will use the first keyword subfield of a text field for
an expression in WHERE clause that gets translated to a painless script
which will lead to an exception thrown:
```
"root_cause": [
      {
        "type": "script_exception",
        "reason": "runtime error",
        "script_stack": [
          "org.elasticsearch.index.mapper.TextFieldMapper$TextFieldType.fielddataBuilder(TextFieldMapper.java:759)",
          "org.elasticsearch.index.fielddata.IndexFieldDataService.getForField(IndexFieldDataService.java:116)",
          "org.elasticsearch.index.query.QueryShardContext.lambda$lookup$0(QueryShardContext.java:308)",
          "org.elasticsearch.search.lookup.LeafDocLookup$1.run(LeafDocLookup.java:101)",
          "org.elasticsearch.search.lookup.LeafDocLookup$1.run(LeafDocLookup.java:98)",
          "java.security.AccessController.doPrivileged(Native Method)",
          "org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:98)",
          "org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:41)",
          "org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils.docValue(InternalSqlScriptUtils.java:79)",
          "InternalSqlScriptUtils.cast(InternalSqlScriptUtils.docValue(doc,params.v0),params.v1)",
          "                                                                      ^---- HERE"
        ],
        "script": "InternalSqlScriptUtils.cast(InternalSqlScriptUtils.docValue(doc,params.v0),params.v1)",
        "lang": "painless"
      }
    ],
```

Instead of allowing a painless translation using the first underlying
keyword silently, which can be confusing, we detect such usage and throw\
an error early.

Relates to #60178

(cherry picked from commit 7402e82)
  • Loading branch information
matriv committed Oct 2, 2020
1 parent 1b423b1 commit 6af8cee
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.elasticsearch.xpack.sql.expression.function.aggregate.Min;
import org.elasticsearch.xpack.sql.expression.function.aggregate.Skewness;
import org.elasticsearch.xpack.sql.expression.function.aggregate.TopHits;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.plan.logical.Distinct;
import org.elasticsearch.xpack.sql.plan.logical.LocalRelation;
import org.elasticsearch.xpack.sql.plan.logical.Pivot;
Expand Down Expand Up @@ -220,6 +221,7 @@ Collection<Failure> verify(LogicalPlan plan) {
checkForGeoFunctionsOnDocValues(p, localFailures);
checkPivot(p, localFailures, attributeRefs);
checkMatrixStats(p, localFailures);
checkCastOnInexact(p, localFailures);

// everything checks out
// mark the plan as analyzed
Expand Down Expand Up @@ -865,4 +867,19 @@ private static void checkMatrixStats(LogicalPlan p, Set<Failure> localFailures)
}
}, Skewness.class));
}

private static void checkCastOnInexact(LogicalPlan p, Set<Failure> localFailures) {
p.forEachDown(f -> f.forEachExpressionsUp(e -> e.forEachUp((Cast c) -> {
if (c.field() instanceof FieldAttribute) {
EsField.Exact exactInfo = ((FieldAttribute) c.field()).getExactInfo();
if (exactInfo.hasExact() == false
|| ((FieldAttribute) c.field()).exactAttribute().equals(c.field()) == false) {
localFailures.add(fail(c.field(),
"[{}] of data type [{}] cannot be used for [{}()] inside the WHERE clause",
c.field().sourceText(), c.field().dataType().typeName(), c.functionName()));
}

}
}, Cast.class)), Filter.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1113,4 +1113,13 @@ public void testErrorMessageForMatrixStatsWithScalars() {
assertEquals("1:17: [SKEWNESS()] cannot be used on top of operators or scalars",
error("SELECT SKEWNESS(ABS(int * 10.123)) FROM test"));
}

public void testCastOnInexact() {
// inexact with underlying keyword
assertEquals("1:36: [some.string] of data type [text] cannot be used for [CAST()] inside the WHERE clause",
error("SELECT * FROM test WHERE NOT (CAST(some.string AS string) = 'foo') OR true"));
// inexact without underlying keyword (text only)
assertEquals("1:36: [text] of data type [text] cannot be used for [CAST()] inside the WHERE clause",
error("SELECT * FROM test WHERE NOT (CAST(text AS string) = 'foo') OR true"));
}
}

0 comments on commit 6af8cee

Please sign in to comment.