Forráskód Böngészése

SQL: Fix exception when using CAST on inexact field (#62943)

* SQL: Fix exception when using CAST on inexact field

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
Marios Trivyzas 5 éve
szülő
commit
7402e8267b

+ 17 - 0
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java

@@ -48,6 +48,7 @@ import org.elasticsearch.xpack.sql.expression.function.aggregate.Max;
 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;
@@ -220,6 +221,7 @@ public final class Verifier {
                 checkForGeoFunctionsOnDocValues(p, localFailures);
                 checkPivot(p, localFailures, attributeRefs);
                 checkMatrixStats(p, localFailures);
+                checkCastOnInexact(p, localFailures);
 
                 // everything checks out
                 // mark the plan as analyzed
@@ -865,4 +867,19 @@ public final class Verifier {
             }
         }, 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);
+    }
 }

+ 9 - 0
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

@@ -1135,4 +1135,13 @@ public class VerifierErrorMessagesTests extends ESTestCase {
         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"));
+    }
 }