SQL: Enhance checks for inexact fields (#39427)
For functions: move checks for `text` fields without underlying `keyword` fields or with many of them (ambiguity) to the type resolution stage. For Order By/Group By: move checks to the `Verifier` to catch early before `QueryTranslator` or execution. Closes: #38501 Fixes: #35203
This commit is contained in:
parent
1a50af7dd4
commit
9fb2f670dc
|
@ -2348,25 +2348,25 @@ Arumugam
|
|||
////////////
|
||||
limitationSubSelect
|
||||
// tag::limitationSubSelect
|
||||
SELECT * FROM (SELECT first_name, last_name FROM emp WHERE last_name NOT LIKE '%a%') WHERE first_name LIKE 'A%';
|
||||
SELECT * FROM (SELECT first_name, last_name FROM emp WHERE last_name NOT LIKE '%a%') WHERE first_name LIKE 'A%' ORDER BY 1;
|
||||
|
||||
first_name | last_name
|
||||
---------------+---------------
|
||||
Anneke |Preusig
|
||||
Alejandro |McAlpine
|
||||
Anoosh |Peyn
|
||||
Arumugam |Ossenbruggen
|
||||
Alejandro |McAlpine
|
||||
Anneke |Preusig
|
||||
Anoosh |Peyn
|
||||
Arumugam |Ossenbruggen
|
||||
// end::limitationSubSelect
|
||||
;
|
||||
|
||||
limitationSubSelect
|
||||
limitationSubSelectRewritten
|
||||
// tag::limitationSubSelectRewritten
|
||||
SELECT first_name, last_name FROM emp WHERE last_name NOT LIKE '%a%' AND first_name LIKE 'A%';
|
||||
SELECT first_name, last_name FROM emp WHERE last_name NOT LIKE '%a%' AND first_name LIKE 'A%' ORDER BY 1;
|
||||
// end::limitationSubSelectRewritten
|
||||
first_name | last_name
|
||||
---------------+---------------
|
||||
Anneke |Preusig
|
||||
Alejandro |McAlpine
|
||||
Anoosh |Peyn
|
||||
Arumugam |Ossenbruggen
|
||||
Alejandro |McAlpine
|
||||
Anneke |Preusig
|
||||
Anoosh |Peyn
|
||||
Arumugam |Ossenbruggen
|
||||
;
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.elasticsearch.xpack.sql.stats.FeatureMetric;
|
|||
import org.elasticsearch.xpack.sql.stats.Metrics;
|
||||
import org.elasticsearch.xpack.sql.tree.Node;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.EsField;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -294,7 +295,8 @@ public final class Verifier {
|
|||
*/
|
||||
private static boolean checkGroupBy(LogicalPlan p, Set<Failure> localFailures,
|
||||
Map<String, Function> resolvedFunctions, Set<LogicalPlan> groupingFailures) {
|
||||
return checkGroupByAgg(p, localFailures, resolvedFunctions)
|
||||
return checkGroupByInexactField(p, localFailures)
|
||||
&& checkGroupByAgg(p, localFailures, resolvedFunctions)
|
||||
&& checkGroupByOrder(p, localFailures, groupingFailures)
|
||||
&& checkGroupByHaving(p, localFailures, groupingFailures, resolvedFunctions);
|
||||
}
|
||||
|
@ -463,6 +465,21 @@ public final class Verifier {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static boolean checkGroupByInexactField(LogicalPlan p, Set<Failure> localFailures) {
|
||||
if (p instanceof Aggregate) {
|
||||
Aggregate a = (Aggregate) p;
|
||||
|
||||
// The grouping can not be an aggregate function or an inexact field (e.g. text without a keyword)
|
||||
a.groupings().forEach(e -> e.forEachUp(c -> {
|
||||
EsField.Exact exact = c.getExactInfo();
|
||||
if (exact.hasExact() == false) {
|
||||
localFailures.add(fail(c, "Field of data type [" + c.dataType().typeName + "] cannot be used for grouping; " +
|
||||
exact.errorMsg()));
|
||||
}
|
||||
}, FieldAttribute.class));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check whether plain columns specified in an agg are mentioned in the group-by
|
||||
private static boolean checkGroupByAgg(LogicalPlan p, Set<Failure> localFailures, Map<String, Function> functions) {
|
||||
|
|
|
@ -33,6 +33,8 @@ import static org.elasticsearch.search.sort.SortBuilders.scriptSort;
|
|||
|
||||
public abstract class SourceGenerator {
|
||||
|
||||
private SourceGenerator() {}
|
||||
|
||||
private static final List<String> NO_STORED_FIELD = singletonList(StoredFieldsContext._NONE_);
|
||||
|
||||
public static SearchSourceBuilder sourceBuilder(QueryContainer container, QueryBuilder filter, Integer size) {
|
||||
|
@ -107,8 +109,7 @@ public abstract class SourceGenerator {
|
|||
|
||||
// sorting only works on not-analyzed fields - look for a multi-field replacement
|
||||
if (attr instanceof FieldAttribute) {
|
||||
FieldAttribute fa = (FieldAttribute) attr;
|
||||
fa = fa.isInexact() ? fa.exactAttribute() : fa;
|
||||
FieldAttribute fa = ((FieldAttribute) attr).exactAttribute();
|
||||
|
||||
sortBuilder = fieldSort(fa.name())
|
||||
.missing(as.missing().position())
|
||||
|
@ -125,7 +126,8 @@ public abstract class SourceGenerator {
|
|||
if (nestedSort == null) {
|
||||
fieldSort.setNestedSort(newSort);
|
||||
} else {
|
||||
for (; nestedSort.getNestedSort() != null; nestedSort = nestedSort.getNestedSort()) {
|
||||
while (nestedSort.getNestedSort() != null) {
|
||||
nestedSort = nestedSort.getNestedSort();
|
||||
}
|
||||
nestedSort.setNestedSort(newSort);
|
||||
}
|
||||
|
|
|
@ -6,22 +6,16 @@
|
|||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression.TypeResolution;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||
import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN;
|
||||
|
||||
public final class Expressions {
|
||||
|
||||
|
@ -154,55 +148,4 @@ public final class Expressions {
|
|||
}
|
||||
return pipes;
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeBoolean(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, dt -> dt == BOOLEAN, operationName, paramOrd, "boolean");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeInteger(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, DataType::isInteger, operationName, paramOrd, "integer");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeNumeric(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, DataType::isNumeric, operationName, paramOrd, "numeric");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeString(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, DataType::isString, operationName, paramOrd, "string");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeDate(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, DataType::isDateBased, operationName, paramOrd, "date", "datetime");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBeNumericOrDate(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return typeMustBe(e, dt -> dt.isNumeric() || dt.isDateBased(), operationName, paramOrd, "date", "datetime", "numeric");
|
||||
}
|
||||
|
||||
public static TypeResolution typeMustBe(Expression e,
|
||||
Predicate<DataType> predicate,
|
||||
String operationName,
|
||||
ParamOrdinal paramOrd,
|
||||
String... acceptedTypes) {
|
||||
return predicate.test(e.dataType()) || DataTypes.isNull(e.dataType())?
|
||||
TypeResolution.TYPE_RESOLVED :
|
||||
new TypeResolution(format(null, "[{}]{} argument must be [{}], found value [{}] type [{}]",
|
||||
operationName,
|
||||
paramOrd == null || paramOrd == ParamOrdinal.DEFAULT ? "" : " " + paramOrd.name().toLowerCase(Locale.ROOT),
|
||||
acceptedTypesForErrorMsg(acceptedTypes),
|
||||
Expressions.name(e),
|
||||
e.dataType().typeName));
|
||||
}
|
||||
|
||||
private static String acceptedTypesForErrorMsg(String... acceptedTypes) {
|
||||
StringJoiner sj = new StringJoiner(", ");
|
||||
for (int i = 0; i < acceptedTypes.length - 1; i++) {
|
||||
sj.add(acceptedTypes[i]);
|
||||
}
|
||||
if (acceptedTypes.length > 1) {
|
||||
return sj.toString() + " or " + acceptedTypes[acceptedTypes.length - 1];
|
||||
} else {
|
||||
return acceptedTypes[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,12 +81,13 @@ public class FieldAttribute extends TypedAttribute {
|
|||
return nestedParent;
|
||||
}
|
||||
|
||||
public boolean isInexact() {
|
||||
return field.isExact() == false;
|
||||
public EsField.Exact getExactInfo() {
|
||||
return field.getExactInfo();
|
||||
}
|
||||
|
||||
public FieldAttribute exactAttribute() {
|
||||
if (field.isExact() == false) {
|
||||
EsField exactField = field.getExactField();
|
||||
if (exactField.equals(field) == false) {
|
||||
return innerField(field.getExactField());
|
||||
}
|
||||
return this;
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isExact;
|
||||
|
||||
public class Order extends Expression {
|
||||
|
||||
|
@ -45,6 +46,11 @@ public class Order extends Expression {
|
|||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return isExact(child, "ORDER BY cannot be applied to field of data type [{}]: {}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return child.dataType();
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
import org.elasticsearch.xpack.sql.type.EsField;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.Expression.TypeResolution;
|
||||
import static org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import static org.elasticsearch.xpack.sql.expression.Expressions.name;
|
||||
import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN;
|
||||
|
||||
public final class TypeResolutions {
|
||||
|
||||
private TypeResolutions() {}
|
||||
|
||||
public static TypeResolution isBoolean(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, dt -> dt == BOOLEAN, operationName, paramOrd, "boolean");
|
||||
}
|
||||
|
||||
public static TypeResolution isInteger(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, DataType::isInteger, operationName, paramOrd, "integer");
|
||||
}
|
||||
|
||||
public static TypeResolution isNumeric(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, DataType::isNumeric, operationName, paramOrd, "numeric");
|
||||
}
|
||||
|
||||
public static TypeResolution isString(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, DataType::isString, operationName, paramOrd, "string");
|
||||
}
|
||||
|
||||
public static TypeResolution isDate(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, DataType::isDateBased, operationName, paramOrd, "date", "datetime");
|
||||
}
|
||||
|
||||
public static TypeResolution isNumericOrDate(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
return isType(e, dt -> dt.isNumeric() || dt.isDateBased(), operationName, paramOrd, "date", "datetime", "numeric");
|
||||
}
|
||||
|
||||
public static TypeResolution isExact(Expression e, String message) {
|
||||
if (e instanceof FieldAttribute) {
|
||||
EsField.Exact exact = ((FieldAttribute) e).getExactInfo();
|
||||
if (exact.hasExact() == false) {
|
||||
return new TypeResolution(format(null, message, e.dataType().typeName, exact.errorMsg()));
|
||||
}
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
public static TypeResolution isExact(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
if (e instanceof FieldAttribute) {
|
||||
EsField.Exact exact = ((FieldAttribute) e).getExactInfo();
|
||||
if (exact.hasExact() == false) {
|
||||
return new TypeResolution(format(null, "[{}] cannot operate on {}field of data type [{}]: {}",
|
||||
operationName,
|
||||
paramOrd == null || paramOrd == ParamOrdinal.DEFAULT ?
|
||||
"" : paramOrd.name().toLowerCase(Locale.ROOT) + " argument ",
|
||||
e.dataType().typeName, exact.errorMsg()));
|
||||
}
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
public static TypeResolution isStringAndExact(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
TypeResolution resolution = isString(e, operationName, paramOrd);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return isExact(e, operationName, paramOrd);
|
||||
}
|
||||
|
||||
public static TypeResolution isFoldable(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
if (!e.foldable()) {
|
||||
return new TypeResolution(format(null, "{}argument of [{}] must be a constant, received [{}]",
|
||||
paramOrd == null || paramOrd == ParamOrdinal.DEFAULT ? "" : paramOrd.name().toLowerCase(Locale.ROOT) + " ",
|
||||
operationName,
|
||||
Expressions.name(e)));
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
public static TypeResolution isNotFoldable(Expression e, String operationName, ParamOrdinal paramOrd) {
|
||||
if (e.foldable()) {
|
||||
return new TypeResolution(format(null, "{}argument of [{}] must be a table column, found constant [{}]",
|
||||
paramOrd == null || paramOrd == ParamOrdinal.DEFAULT ? "" : paramOrd.name().toLowerCase(Locale.ROOT) + " ",
|
||||
operationName,
|
||||
Expressions.name(e)));
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
public static TypeResolution isType(Expression e,
|
||||
Predicate<DataType> predicate,
|
||||
String operationName,
|
||||
ParamOrdinal paramOrd,
|
||||
String... acceptedTypes) {
|
||||
return predicate.test(e.dataType()) || DataTypes.isNull(e.dataType())?
|
||||
TypeResolution.TYPE_RESOLVED :
|
||||
new TypeResolution(format(null, "{}argument of [{}] must be [{}], found value [{}] type [{}]",
|
||||
paramOrd == null || paramOrd == ParamOrdinal.DEFAULT ? "" : paramOrd.name().toLowerCase(Locale.ROOT) + " ",
|
||||
operationName,
|
||||
acceptedTypesForErrorMsg(acceptedTypes),
|
||||
name(e),
|
||||
e.dataType().typeName));
|
||||
}
|
||||
|
||||
private static String acceptedTypesForErrorMsg(String... acceptedTypes) {
|
||||
StringJoiner sj = new StringJoiner(", ");
|
||||
for (int i = 0; i < acceptedTypes.length - 1; i++) {
|
||||
sj.add(acceptedTypes[i]);
|
||||
}
|
||||
if (acceptedTypes.length > 1) {
|
||||
return sj.toString() + " or " + acceptedTypes[acceptedTypes.length - 1];
|
||||
} else {
|
||||
return acceptedTypes[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
|||
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.TypeResolutions;
|
||||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.AggNameInput;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
|
@ -78,8 +80,13 @@ public abstract class AggregateFunction extends Function {
|
|||
&& Objects.equals(other.parameters(), parameters());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return TypeResolutions.isExact(field, sourceText(), Expressions.ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field(), parameters());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
@ -14,6 +13,8 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumericOrDate;
|
||||
|
||||
/**
|
||||
* Find the maximum value in matching documents.
|
||||
*/
|
||||
|
@ -48,7 +49,7 @@ public class Max extends NumericAggregate implements EnclosedAgg {
|
|||
if (field().dataType().isString()) {
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
} else {
|
||||
return Expressions.typeMustBeNumericOrDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isNumericOrDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
@ -14,6 +13,8 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumericOrDate;
|
||||
|
||||
/**
|
||||
* Find the minimum value in matched documents.
|
||||
*/
|
||||
|
@ -51,7 +52,7 @@ public class Min extends NumericAggregate implements EnclosedAgg {
|
|||
if (field().dataType().isString()) {
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
} else {
|
||||
return Expressions.typeMustBeNumericOrDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isNumericOrDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
abstract class NumericAggregate extends AggregateFunction {
|
||||
|
||||
NumericAggregate(Source source, Expression field, List<Expression> parameters) {
|
||||
|
@ -25,7 +26,7 @@ abstract class NumericAggregate extends AggregateFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return Expressions.typeMustBeNumeric(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isNumeric(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
@ -16,7 +15,8 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isFoldable;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
public class Percentile extends NumericAggregate implements EnclosedAgg {
|
||||
|
||||
|
@ -42,17 +42,17 @@ public class Percentile extends NumericAggregate implements EnclosedAgg {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!percent.foldable()) {
|
||||
return new TypeResolution(format(null, "Second argument of PERCENTILE must be a constant, received [{}]",
|
||||
Expressions.name(percent)));
|
||||
}
|
||||
|
||||
TypeResolution resolution = super.resolveType();
|
||||
TypeResolution resolution = isFoldable(percent, sourceText(), ParamOrdinal.SECOND);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeNumeric(percent, sourceText(), ParamOrdinal.DEFAULT);
|
||||
resolution = super.resolveType();
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return isNumeric(percent, sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
public Expression percent() {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
@ -16,7 +15,8 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isFoldable;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
public class PercentileRank extends AggregateFunction implements EnclosedAgg {
|
||||
|
||||
|
@ -42,17 +42,17 @@ public class PercentileRank extends AggregateFunction implements EnclosedAgg {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!value.foldable()) {
|
||||
return new TypeResolution(format(null, "Second argument of PERCENTILE_RANK must be a constant, received [{}]",
|
||||
Expressions.name(value)));
|
||||
}
|
||||
|
||||
TypeResolution resolution = super.resolveType();
|
||||
TypeResolution resolution = isFoldable(value, sourceText(), ParamOrdinal.SECOND);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeNumeric(value, sourceText(), ParamOrdinal.DEFAULT);
|
||||
resolution = super.resolveType();
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return isNumeric(value, sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
public Expression value() {
|
||||
|
|
|
@ -5,16 +5,15 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
||||
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.TypeResolutions;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNotFoldable;
|
||||
|
||||
/**
|
||||
* Super class of Aggregation functions on field types other than numeric, that need to be
|
||||
|
@ -37,29 +36,25 @@ public abstract class TopHits extends AggregateFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (field().foldable()) {
|
||||
return new TypeResolution(format(null, "First argument of [{}] must be a table column, found constant [{}]",
|
||||
functionName(),
|
||||
Expressions.name(field())));
|
||||
TypeResolution resolution = isNotFoldable(field(), sourceText(), ParamOrdinal.FIRST);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
try {
|
||||
((FieldAttribute) field()).exactAttribute();
|
||||
} catch (MappingException ex) {
|
||||
return new TypeResolution(format(null, "[{}] cannot operate on first argument field of data type [{}]",
|
||||
functionName(), field().dataType().typeName));
|
||||
|
||||
resolution = TypeResolutions.isExact(field(), sourceText(), ParamOrdinal.FIRST);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
if (orderField() != null) {
|
||||
if (orderField().foldable()) {
|
||||
return new TypeResolution(format(null, "Second argument of [{}] must be a table column, found constant [{}]",
|
||||
functionName(),
|
||||
Expressions.name(orderField())));
|
||||
resolution = isNotFoldable(orderField(), sourceText(), ParamOrdinal.SECOND);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
try {
|
||||
((FieldAttribute) orderField()).exactAttribute();
|
||||
} catch (MappingException ex) {
|
||||
return new TypeResolution(format(null, "[{}] cannot operate on second argument field of data type [{}]",
|
||||
functionName(), orderField().dataType().typeName));
|
||||
|
||||
resolution = TypeResolutions.isExact(orderField(), sourceText(), ParamOrdinal.SECOND);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.grouping;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
@ -20,6 +19,10 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumericOrDate;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isType;
|
||||
|
||||
public class Histogram extends GroupingFunction {
|
||||
|
||||
private final Literal interval;
|
||||
|
@ -41,13 +44,13 @@ public class Histogram extends GroupingFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
TypeResolution resolution = Expressions.typeMustBeNumericOrDate(field(), "HISTOGRAM", ParamOrdinal.FIRST);
|
||||
TypeResolution resolution = isNumericOrDate(field(), "HISTOGRAM", ParamOrdinal.FIRST);
|
||||
if (resolution == TypeResolution.TYPE_RESOLVED) {
|
||||
// interval must be Literal interval
|
||||
if (field().dataType().isDateBased()) {
|
||||
resolution = Expressions.typeMustBe(interval, DataTypes::isInterval, "(Date) HISTOGRAM", ParamOrdinal.SECOND, "interval");
|
||||
resolution = isType(interval, DataTypes::isInterval, "(Date) HISTOGRAM", ParamOrdinal.SECOND, "interval");
|
||||
} else {
|
||||
resolution = Expressions.typeMustBeNumeric(interval, "(Numeric) HISTOGRAM", ParamOrdinal.SECOND);
|
||||
resolution = isNumeric(interval, "(Numeric) HISTOGRAM", ParamOrdinal.SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
@ -17,6 +16,8 @@ import java.time.ZoneId;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isDate;
|
||||
|
||||
abstract class BaseDateTimeFunction extends UnaryScalarFunction {
|
||||
|
||||
private final ZoneId zoneId;
|
||||
|
@ -35,7 +36,7 @@ abstract class BaseDateTimeFunction extends UnaryScalarFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return Expressions.typeMustBeDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isDate(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
public ZoneId zoneId() {
|
||||
|
|
|
@ -16,6 +16,8 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
public abstract class BinaryNumericFunction extends BinaryScalarFunction {
|
||||
|
||||
private final BinaryMathOperation operation;
|
||||
|
@ -36,12 +38,12 @@ public abstract class BinaryNumericFunction extends BinaryScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution resolution = Expressions.typeMustBeNumeric(left(), sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution resolution = isNumeric(left(), sourceText(), ParamOrdinal.FIRST);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
|
||||
}
|
||||
return Expressions.typeMustBeNumeric(right(), sourceText(), ParamOrdinal.SECOND);
|
||||
return isNumeric(right(), sourceText(), ParamOrdinal.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
|
||||
|
@ -18,6 +17,7 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
public abstract class MathFunction extends UnaryScalarFunction {
|
||||
|
||||
|
@ -56,7 +56,7 @@ public abstract class MathFunction extends UnaryScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeNumeric(field(), operation().toString(), ParamOrdinal.DEFAULT);
|
||||
return isNumeric(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,7 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.Expressions.typeMustBeString;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ public abstract class BinaryStringFunction<T,R> extends BinaryScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution resolution = typeMustBeString(left(), sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution resolution = isStringAndExact(left(), sourceText(), ParamOrdinal.FIRST);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public abstract class BinaryStringFunction<T,R> extends BinaryScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
/**
|
||||
* A binary string function with a numeric second parameter and a string result
|
||||
*/
|
||||
|
@ -26,7 +28,7 @@ public abstract class BinaryStringNumericFunction extends BinaryStringFunction<N
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveSecondParameterInputType(Expression e) {
|
||||
return Expressions.typeMustBeNumeric(e, sourceText(), Expressions.ParamOrdinal.SECOND);
|
||||
return isNumeric(e, sourceText(), Expressions.ParamOrdinal.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,6 +10,8 @@ import org.elasticsearch.xpack.sql.expression.Expressions;
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
|
||||
/**
|
||||
* A binary string function with two string parameters and a numeric result
|
||||
*/
|
||||
|
@ -21,7 +23,7 @@ public abstract class BinaryStringStringFunction extends BinaryStringFunction<St
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveSecondParameterInputType(Expression e) {
|
||||
return Expressions.typeMustBeString(e, sourceText(), Expressions.ParamOrdinal.SECOND);
|
||||
return isStringAndExact(e, sourceText(), Expressions.ParamOrdinal.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor.process;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
|
@ -37,12 +38,12 @@ public class Concat extends BinaryScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution sourceResolution = Expressions.typeMustBeString(left(), sourceText(), ParamOrdinal.FIRST);
|
||||
if (sourceResolution.unresolved()) {
|
||||
return sourceResolution;
|
||||
TypeResolution resolution = isStringAndExact(left(), functionName(), ParamOrdinal.FIRST);
|
||||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeString(right(), sourceText(), ParamOrdinal.SECOND);
|
||||
return isStringAndExact(right(), functionName(), ParamOrdinal.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,7 +79,7 @@ public class Concat extends BinaryScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.InsertFunctionProcessor.doProcess;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
|
@ -46,22 +48,22 @@ public class Insert extends ScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution sourceResolution = Expressions.typeMustBeString(source, sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution sourceResolution = isStringAndExact(source, sourceText(), ParamOrdinal.FIRST);
|
||||
if (sourceResolution.unresolved()) {
|
||||
return sourceResolution;
|
||||
}
|
||||
|
||||
TypeResolution startResolution = Expressions.typeMustBeNumeric(start, sourceText(), ParamOrdinal.SECOND);
|
||||
|
||||
TypeResolution startResolution = isNumeric(start, sourceText(), ParamOrdinal.SECOND);
|
||||
if (startResolution.unresolved()) {
|
||||
return startResolution;
|
||||
}
|
||||
|
||||
TypeResolution lengthResolution = Expressions.typeMustBeNumeric(length, sourceText(), ParamOrdinal.THIRD);
|
||||
TypeResolution lengthResolution = isNumeric(length, sourceText(), ParamOrdinal.THIRD);
|
||||
if (lengthResolution.unresolved()) {
|
||||
return lengthResolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeString(replacement, sourceText(), ParamOrdinal.FOURTH);
|
||||
return isStringAndExact(replacement, sourceText(), ParamOrdinal.FOURTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -119,7 +121,7 @@ public class Insert extends ScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.LocateFunctionProcessor.doProcess;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
|
@ -48,19 +50,17 @@ public class Locate extends ScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution patternResolution = Expressions.typeMustBeString(pattern, sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution patternResolution = isStringAndExact(pattern, sourceText(), ParamOrdinal.FIRST);
|
||||
if (patternResolution.unresolved()) {
|
||||
return patternResolution;
|
||||
}
|
||||
|
||||
TypeResolution sourceResolution = Expressions.typeMustBeString(source, sourceText(), ParamOrdinal.SECOND);
|
||||
TypeResolution sourceResolution = isStringAndExact(source, sourceText(), ParamOrdinal.SECOND);
|
||||
if (sourceResolution.unresolved()) {
|
||||
return sourceResolution;
|
||||
}
|
||||
|
||||
return start == null ?
|
||||
TypeResolution.TYPE_RESOLVED :
|
||||
Expressions.typeMustBeNumeric(start, sourceText(), ParamOrdinal.THIRD);
|
||||
|
||||
return start == null ? TypeResolution.TYPE_RESOLVED : isNumeric(start, sourceText(), ParamOrdinal.THIRD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,7 +80,7 @@ public class Locate extends ScalarFunction {
|
|||
public boolean foldable() {
|
||||
return pattern.foldable()
|
||||
&& source.foldable()
|
||||
&& (start == null? true : start.foldable());
|
||||
&& (start == null || start.foldable());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,7 +122,7 @@ public class Locate extends ScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.ReplaceFunctionProcessor.doProcess;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
|
@ -44,17 +45,17 @@ public class Replace extends ScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution sourceResolution = Expressions.typeMustBeString(source, sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution sourceResolution = isStringAndExact(source, sourceText(), ParamOrdinal.FIRST);
|
||||
if (sourceResolution.unresolved()) {
|
||||
return sourceResolution;
|
||||
}
|
||||
|
||||
TypeResolution patternResolution = Expressions.typeMustBeString(pattern, sourceText(), ParamOrdinal.SECOND);
|
||||
TypeResolution patternResolution = isStringAndExact(pattern, sourceText(), ParamOrdinal.SECOND);
|
||||
if (patternResolution.unresolved()) {
|
||||
return patternResolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeString(replacement, sourceText(), ParamOrdinal.THIRD);
|
||||
return isStringAndExact(replacement, sourceText(), ParamOrdinal.THIRD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,7 +108,7 @@ public class Replace extends ScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
@ -124,4 +125,4 @@ public class Replace extends ScalarFunction {
|
|||
|
||||
return new Replace(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isInteger;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor.doProcess;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
|
@ -45,17 +47,17 @@ public class Substring extends ScalarFunction {
|
|||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution sourceResolution = Expressions.typeMustBeString(source, sourceText(), ParamOrdinal.FIRST);
|
||||
TypeResolution sourceResolution = isStringAndExact(source, sourceText(), ParamOrdinal.FIRST);
|
||||
if (sourceResolution.unresolved()) {
|
||||
return sourceResolution;
|
||||
}
|
||||
|
||||
TypeResolution startResolution = Expressions.typeMustBeNumeric(start, sourceText(), ParamOrdinal.SECOND);
|
||||
TypeResolution startResolution = isInteger(start, sourceText(), ParamOrdinal.SECOND);
|
||||
if (startResolution.unresolved()) {
|
||||
return startResolution;
|
||||
}
|
||||
|
||||
return Expressions.typeMustBeNumeric(length, sourceText(), ParamOrdinal.THIRD);
|
||||
return isInteger(length, sourceText(), ParamOrdinal.THIRD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,7 +109,7 @@ public class Substring extends ScalarFunction {
|
|||
@Override
|
||||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
@ -124,4 +126,4 @@ public class Substring extends ScalarFunction {
|
|||
|
||||
return new Substring(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar.string;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
|
@ -20,6 +19,7 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
public abstract class UnaryStringFunction extends UnaryScalarFunction {
|
||||
|
@ -43,7 +43,7 @@ public abstract class UnaryStringFunction extends UnaryScalarFunction {
|
|||
if (!childrenResolved()) {
|
||||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
return Expressions.typeMustBeString(field(), operation().toString(), ParamOrdinal.DEFAULT);
|
||||
return isStringAndExact(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,7 +57,7 @@ public abstract class UnaryStringFunction extends UnaryScalarFunction {
|
|||
public ScriptTemplate scriptWithField(FieldAttribute field) {
|
||||
//TODO change this to use _source instead of the exact form (aka field.keyword for text fields)
|
||||
return new ScriptTemplate(processScript("doc[{}].value"),
|
||||
paramsBuilder().variable(field.isInexact() ? field.exactAttribute().name() : field.name()).build(),
|
||||
paramsBuilder().variable(field.exactAttribute().name()).build(),
|
||||
dataType());
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar.string;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
|
@ -19,6 +18,7 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isInteger;
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,7 @@ public abstract class UnaryStringIntFunction extends UnaryScalarFunction {
|
|||
if (!childrenResolved()) {
|
||||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
return Expressions.typeMustBeInteger(field(), operation().toString(), ParamOrdinal.DEFAULT);
|
||||
return isInteger(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProce
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isBoolean;
|
||||
|
||||
public abstract class BinaryLogic extends BinaryOperator<Boolean, Boolean, Boolean, BinaryLogicOperation> {
|
||||
|
||||
protected BinaryLogic(Source source, Expression left, Expression right, BinaryLogicOperation operation) {
|
||||
|
@ -27,7 +29,7 @@ public abstract class BinaryLogic extends BinaryOperator<Boolean, Boolean, Boole
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveInputType(Expression e, Expressions.ParamOrdinal paramOrdinal) {
|
||||
return Expressions.typeMustBeBoolean(e, sourceText(), paramOrdinal);
|
||||
return isBoolean(e, sourceText(), paramOrdinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.logical;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
@ -16,6 +15,8 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isBoolean;
|
||||
|
||||
public class Not extends UnaryScalarFunction {
|
||||
|
||||
public Not(Source source, Expression child) {
|
||||
|
@ -37,7 +38,7 @@ public class Not extends UnaryScalarFunction {
|
|||
if (DataType.BOOLEAN == field().dataType()) {
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
return Expressions.typeMustBeBoolean(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isBoolean(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.elasticsearch.xpack.sql.tree.Source;
|
|||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
public abstract class ArithmeticOperation extends BinaryOperator<Object, Object, Object, BinaryArithmeticOperation> {
|
||||
|
||||
private DataType dataType;
|
||||
|
@ -24,7 +26,7 @@ public abstract class ArithmeticOperation extends BinaryOperator<Object, Object,
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveInputType(Expression e, Expressions.ParamOrdinal paramOrdinal) {
|
||||
return Expressions.typeMustBeNumeric(e, symbol(), paramOrdinal);
|
||||
return isNumeric(e, sourceText(), paramOrdinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,4 +46,4 @@ public abstract class ArithmeticOperation extends BinaryOperator<Object, Object,
|
|||
protected Pipe makePipe() {
|
||||
return new BinaryArithmeticPipe(source(), this, Expressions.pipe(left()), Expressions.pipe(right()), function());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
@ -16,6 +15,8 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isNumeric;
|
||||
|
||||
/**
|
||||
* Negation function (@{code -x}).
|
||||
*/
|
||||
|
@ -37,7 +38,7 @@ public class Neg extends UnaryScalarFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return Expressions.typeMustBeNumeric(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
return isNumeric(field(), sourceText(), ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
|||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.TypeResolutions;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
|
@ -22,7 +23,7 @@ public abstract class BinaryComparison extends BinaryOperator<Object, Object, Bo
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveInputType(Expression e, Expressions.ParamOrdinal paramOrdinal) {
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
return TypeResolutions.isExact(e, sourceText(), paramOrdinal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,4 +44,4 @@ public abstract class BinaryComparison extends BinaryOperator<Object, Object, Bo
|
|||
public static Integer compare(Object left, Object right) {
|
||||
return Comparisons.compare(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.TypeResolutions;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
|
@ -109,13 +108,9 @@ public class In extends ScalarFunction {
|
|||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (value instanceof FieldAttribute) {
|
||||
try {
|
||||
((FieldAttribute) value).exactAttribute();
|
||||
} catch (MappingException e) {
|
||||
return new TypeResolution(format(null, "[{}] cannot operate on field of data type [{}]: {}",
|
||||
functionName(), value().dataType().esType, e.getMessage()));
|
||||
}
|
||||
TypeResolution resolution = TypeResolutions.isExact(value, functionName(), Expressions.ParamOrdinal.DEFAULT);
|
||||
if (resolution != TypeResolution.TYPE_RESOLVED) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
for (Expression ex : list) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.regex;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
@ -14,6 +15,8 @@ import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.Reg
|
|||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.TypeResolutions.isStringAndExact;
|
||||
|
||||
public abstract class RegexMatch extends UnaryScalarFunction {
|
||||
|
||||
private final String pattern;
|
||||
|
@ -36,6 +39,11 @@ public abstract class RegexMatch extends UnaryScalarFunction {
|
|||
return field().nullable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
return isStringAndExact(field(), sourceText(), Expressions.ParamOrdinal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean foldable() {
|
||||
// right() is not directly foldable in any context but Like can fold it.
|
||||
|
|
|
@ -257,10 +257,7 @@ final class QueryTranslator {
|
|||
|
||||
// change analyzed to non non-analyzed attributes
|
||||
if (exp instanceof FieldAttribute) {
|
||||
FieldAttribute fa = (FieldAttribute) exp;
|
||||
if (fa.isInexact()) {
|
||||
ne = fa.exactAttribute();
|
||||
}
|
||||
ne = ((FieldAttribute) exp).exactAttribute();
|
||||
}
|
||||
|
||||
// handle functions differently
|
||||
|
@ -448,7 +445,7 @@ final class QueryTranslator {
|
|||
// COUNT(DISTINCT) uses cardinality aggregation which works on exact values (not changed by analyzers or normalizers)
|
||||
if (af instanceof Count && ((Count) af).distinct()) {
|
||||
// use the `keyword` version of the field, if there is one
|
||||
return field.isInexact() ? field.exactAttribute().name() : field.name();
|
||||
return field.exactAttribute().name();
|
||||
}
|
||||
return field.name();
|
||||
}
|
||||
|
@ -481,9 +478,7 @@ final class QueryTranslator {
|
|||
String target = null;
|
||||
|
||||
if (e.field() instanceof FieldAttribute) {
|
||||
FieldAttribute fa = (FieldAttribute) e.field();
|
||||
inexact = fa.isInexact();
|
||||
target = nameOf(inexact ? fa.exactAttribute() : fa);
|
||||
target = nameOf(((FieldAttribute) e.field()).exactAttribute());
|
||||
} else {
|
||||
throw new SqlIllegalArgumentException("Scalar function ({}) not allowed (yet) as arguments for LIKE",
|
||||
Expressions.name(e.field()));
|
||||
|
@ -683,12 +678,9 @@ final class QueryTranslator {
|
|||
}
|
||||
if (bc instanceof Equals || bc instanceof NullEquals || bc instanceof NotEquals) {
|
||||
if (bc.left() instanceof FieldAttribute) {
|
||||
FieldAttribute fa = (FieldAttribute) bc.left();
|
||||
// equality should always be against an exact match
|
||||
// (which is important for strings)
|
||||
if (fa.isInexact()) {
|
||||
name = fa.exactAttribute().name();
|
||||
}
|
||||
name = ((FieldAttribute) bc.left()).exactAttribute().name();
|
||||
}
|
||||
Query query = new TermQuery(source, name, value);
|
||||
if (bc instanceof NotEquals) {
|
||||
|
@ -726,7 +718,7 @@ final class QueryTranslator {
|
|||
if (in.value() instanceof FieldAttribute) {
|
||||
FieldAttribute fa = (FieldAttribute) in.value();
|
||||
// equality should always be against an exact match (which is important for strings)
|
||||
q = new TermsQuery(in.source(), fa.isInexact() ? fa.exactAttribute().name() : fa.name(), in.list());
|
||||
q = new TermsQuery(in.source(), fa.exactAttribute().name(), in.list());
|
||||
} else {
|
||||
q = new ScriptQuery(in.source(), in.asScript());
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Objects;
|
|||
* SQL-related information about an index field
|
||||
*/
|
||||
public class EsField {
|
||||
|
||||
private final DataType esDataType;
|
||||
private final boolean aggregatable;
|
||||
private final Map<String, EsField> properties;
|
||||
|
@ -58,7 +59,9 @@ public class EsField {
|
|||
|
||||
/**
|
||||
* Returns the path to the keyword version of this field if this field is text and it has a subfield that is
|
||||
* indexed as keyword, null if such field is not found or the field name itself in all other cases
|
||||
* indexed as keyword, throws an exception if such field is not found or the field name itself in all other cases.
|
||||
* To avoid the exception {@link EsField#getExactInfo()} should be used beforehand, to check if an exact field exists
|
||||
* and if not get the errorMessage which explains why is that.
|
||||
*/
|
||||
public EsField getExactField() {
|
||||
return this;
|
||||
|
@ -76,13 +79,14 @@ public class EsField {
|
|||
}
|
||||
|
||||
/**
|
||||
* True if this field name can be used in sorting, aggregations and term queries as is
|
||||
* <p>
|
||||
* This will be true for most fields except analyzed text fields that cannot be used directly and should be
|
||||
* replaced with the field returned by {@link EsField#getExactField()} instead.
|
||||
* Returns and {@link Exact} object with all the necessary info about the field:
|
||||
* <ul>
|
||||
* <li>If it has an exact underlying field or not</li>
|
||||
* <li>and if not an error message why it doesn't</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean isExact() {
|
||||
return true;
|
||||
public Exact getExactInfo() {
|
||||
return Exact.EXACT_FIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,4 +112,25 @@ public class EsField {
|
|||
public int hashCode() {
|
||||
return Objects.hash(esDataType, aggregatable, properties, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Exact {
|
||||
|
||||
private static Exact EXACT_FIELD = new Exact(true, null);
|
||||
|
||||
private boolean hasExact;
|
||||
private String errorMsg;
|
||||
|
||||
public Exact(boolean hasExact, String errorMsg) {
|
||||
this.hasExact = hasExact;
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
public boolean hasExact() {
|
||||
return hasExact;
|
||||
}
|
||||
|
||||
public String errorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package org.elasticsearch.xpack.sql.type;
|
||||
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -46,12 +46,12 @@ public class InvalidMappedField extends EsField {
|
|||
|
||||
@Override
|
||||
public EsField getExactField() {
|
||||
throw new MappingException("Field [" + getName() + "] is invalid, cannot access it");
|
||||
throw new SqlIllegalArgumentException("Field [" + getName() + "] is invalid, cannot access it");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExact() {
|
||||
return false;
|
||||
public Exact getExactInfo() {
|
||||
return new Exact(false, "Field [" + getName() + "] is invalid, cannot access it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ public class KeywordEsField extends EsField {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isExact() {
|
||||
return normalized == false;
|
||||
public Exact getExactInfo() {
|
||||
return new Exact(normalized == false, "Normalized keyword field cannot be used for exact match operations");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,4 +52,4 @@ public class KeywordEsField extends EsField {
|
|||
|
||||
return Objects.hash(super.hashCode(), precision, normalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.type;
|
||||
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* SQL-related information about an index field with text type
|
||||
|
@ -20,25 +22,41 @@ public class TextEsField extends EsField {
|
|||
|
||||
@Override
|
||||
public EsField getExactField() {
|
||||
Tuple<EsField, String> findExact = findExact();
|
||||
if (findExact.v1() == null) {
|
||||
throw new SqlIllegalArgumentException(findExact.v2());
|
||||
}
|
||||
return findExact.v1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exact getExactInfo() {
|
||||
return PROCESS_EXACT_FIELD.apply(findExact());
|
||||
}
|
||||
|
||||
private Tuple<EsField, String> findExact() {
|
||||
EsField field = null;
|
||||
for (EsField property : getProperties().values()) {
|
||||
if (property.getDataType() == DataType.KEYWORD && property.isExact()) {
|
||||
if (property.getDataType() == DataType.KEYWORD && property.getExactInfo().hasExact()) {
|
||||
if (field != null) {
|
||||
throw new MappingException("Multiple exact keyword candidates available for [" + getName() +
|
||||
"]; specify which one to use");
|
||||
return new Tuple<>(null, "Multiple exact keyword candidates available for [" + getName() +
|
||||
"]; specify which one to use");
|
||||
}
|
||||
field = property;
|
||||
}
|
||||
}
|
||||
if (field == null) {
|
||||
throw new MappingException("No keyword/multi-field defined exact matches for [" + getName() +
|
||||
"]; define one or use MATCH/QUERY instead");
|
||||
return new Tuple<>(null, "No keyword/multi-field defined exact matches for [" + getName() +
|
||||
"]; define one or use MATCH/QUERY instead");
|
||||
}
|
||||
return field;
|
||||
return new Tuple<>(field, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExact() {
|
||||
return false;
|
||||
}
|
||||
private Function<Tuple<EsField, String>, Exact> PROCESS_EXACT_FIELD = tuple -> {
|
||||
if (tuple.v1() == null) {
|
||||
return new Exact(false, tuple.v2());
|
||||
} else {
|
||||
return new Exact(true, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,16 +26,21 @@ public class UnsupportedEsField extends EsField {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
UnsupportedEsField that = (UnsupportedEsField) o;
|
||||
return Objects.equals(originalType, that.originalType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(super.hashCode(), originalType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
package org.elasticsearch.xpack.sql.analysis.analyzer;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.TestUtils;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
|
@ -113,9 +113,9 @@ public class FieldAttributeTests extends ESTestCase {
|
|||
assertThat(attr.path(), is("some"));
|
||||
assertThat(attr.name(), is("some.string"));
|
||||
assertThat(attr.dataType(), is(DataType.TEXT));
|
||||
assertThat(attr.isInexact(), is(true));
|
||||
assertTrue(attr.getExactInfo().hasExact());
|
||||
FieldAttribute exact = attr.exactAttribute();
|
||||
assertThat(exact.isInexact(), is(false));
|
||||
assertTrue(exact.getExactInfo().hasExact());
|
||||
assertThat(exact.name(), is("some.string.typical"));
|
||||
assertThat(exact.dataType(), is(KEYWORD));
|
||||
}
|
||||
|
@ -125,9 +125,11 @@ public class FieldAttributeTests extends ESTestCase {
|
|||
assertThat(attr.path(), is("some"));
|
||||
assertThat(attr.name(), is("some.ambiguous"));
|
||||
assertThat(attr.dataType(), is(DataType.TEXT));
|
||||
assertThat(attr.isInexact(), is(true));
|
||||
MappingException me = expectThrows(MappingException.class, () -> attr.exactAttribute());
|
||||
assertThat(me.getMessage(),
|
||||
assertFalse(attr.getExactInfo().hasExact());
|
||||
assertThat(attr.getExactInfo().errorMsg(),
|
||||
is("Multiple exact keyword candidates available for [ambiguous]; specify which one to use"));
|
||||
SqlIllegalArgumentException e = expectThrows(SqlIllegalArgumentException.class, () -> attr.exactAttribute());
|
||||
assertThat(e.getMessage(),
|
||||
is("Multiple exact keyword candidates available for [ambiguous]; specify which one to use"));
|
||||
}
|
||||
|
||||
|
@ -136,7 +138,7 @@ public class FieldAttributeTests extends ESTestCase {
|
|||
assertThat(attr.path(), is("some.string"));
|
||||
assertThat(attr.name(), is("some.string.normalized"));
|
||||
assertThat(attr.dataType(), is(KEYWORD));
|
||||
assertThat(attr.isInexact(), is(true));
|
||||
assertFalse(attr.getExactInfo().hasExact());
|
||||
}
|
||||
|
||||
public void testDottedFieldPath() {
|
||||
|
@ -197,4 +199,4 @@ public class FieldAttributeTests extends ESTestCase {
|
|||
assertThat(attribute.qualifier(), is("test"));
|
||||
assertThat(attribute.name(), is("test.test"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testGroupByOrderByAliasedInSelectAllowed() {
|
||||
LogicalPlan lp = accept("SELECT text t FROM test GROUP BY text ORDER BY t");
|
||||
LogicalPlan lp = accept("SELECT int i FROM test GROUP BY int ORDER BY i");
|
||||
assertNotNull(lp);
|
||||
}
|
||||
|
||||
|
@ -292,6 +292,12 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
assertNotNull(accept("SELECT dep.* FROM test"));
|
||||
}
|
||||
|
||||
public void testGroupByOnInexact() {
|
||||
assertEquals("1:36: Field of data type [text] cannot be used for grouping; " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT COUNT(*) FROM test GROUP BY text"));
|
||||
}
|
||||
|
||||
public void testGroupByOnNested() {
|
||||
assertEquals("1:38: Grouping isn't (yet) compatible with nested fields [dep.dep_id]",
|
||||
error("SELECT dep.dep_id FROM test GROUP BY dep.dep_id"));
|
||||
|
@ -322,6 +328,18 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
error("SELECT * FROM test WHERE unsupported > 1"));
|
||||
}
|
||||
|
||||
public void testTermEqualitOnInexact() {
|
||||
assertEquals("1:26: [text = 'value'] cannot operate on first argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT * FROM test WHERE text = 'value'"));
|
||||
}
|
||||
|
||||
public void testTermEqualityOnAmbiguous() {
|
||||
assertEquals("1:26: [some.ambiguous = 'value'] cannot operate on first argument field of data type [text]: " +
|
||||
"Multiple exact keyword candidates available for [ambiguous]; specify which one to use",
|
||||
error("SELECT * FROM test WHERE some.ambiguous = 'value'"));
|
||||
}
|
||||
|
||||
public void testUnsupportedTypeInFunction() {
|
||||
assertEquals("1:12: Cannot use field [unsupported] type [ip_range] as is unsupported",
|
||||
error("SELECT ABS(unsupported) FROM test"));
|
||||
|
@ -332,6 +350,12 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
error("SELECT * FROM test ORDER BY unsupported"));
|
||||
}
|
||||
|
||||
public void testInexactFieldInOrder() {
|
||||
assertEquals("1:29: ORDER BY cannot be applied to field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT * FROM test ORDER BY text"));
|
||||
}
|
||||
|
||||
public void testGroupByOrderByAggregate() {
|
||||
accept("SELECT AVG(int) a FROM test GROUP BY bool ORDER BY a");
|
||||
}
|
||||
|
@ -416,65 +440,106 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testNotSupportedAggregateOnDate() {
|
||||
assertEquals("1:8: [AVG(date)] argument must be [numeric], found value [date] type [datetime]",
|
||||
assertEquals("1:8: argument of [AVG(date)] must be [numeric], found value [date] type [datetime]",
|
||||
error("SELECT AVG(date) FROM test"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForStringFunction_WithOneArg() {
|
||||
assertEquals("1:8: [LENGTH] argument must be [string], found value [1] type [integer]",
|
||||
public void testInvalidTypeForStringFunction_WithOneArgString() {
|
||||
assertEquals("1:8: argument of [LENGTH(1)] must be [string], found value [1] type [integer]",
|
||||
error("SELECT LENGTH(1)"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForStringFunction_WithOneArgNumeric() {
|
||||
assertEquals("1:8: argument of [CHAR('foo')] must be [integer], found value ['foo'] type [keyword]",
|
||||
error("SELECT CHAR('foo')"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForNestedStringFunctions_WithOneArg() {
|
||||
assertEquals("1:14: argument of [CHAR('foo')] must be [integer], found value ['foo'] type [keyword]",
|
||||
error("SELECT ASCII(CHAR('foo'))"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForNumericFunction_WithOneArg() {
|
||||
assertEquals("1:8: [COS] argument must be [numeric], found value ['foo'] type [keyword]",
|
||||
assertEquals("1:8: argument of [COS('foo')] must be [numeric], found value ['foo'] type [keyword]",
|
||||
error("SELECT COS('foo')"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForBooleanFunction_WithOneArg() {
|
||||
assertEquals("1:8: [NOT 'foo'] argument must be [boolean], found value ['foo'] type [keyword]",
|
||||
assertEquals("1:8: argument of [NOT 'foo'] must be [boolean], found value ['foo'] type [keyword]",
|
||||
error("SELECT NOT 'foo'"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForStringFunction_WithTwoArgs() {
|
||||
assertEquals("1:8: [CONCAT(1, 'bar')] first argument must be [string], found value [1] type [integer]",
|
||||
assertEquals("1:8: first argument of [CONCAT] must be [string], found value [1] type [integer]",
|
||||
error("SELECT CONCAT(1, 'bar')"));
|
||||
assertEquals("1:8: [CONCAT('foo', 2)] second argument must be [string], found value [2] type [integer]",
|
||||
assertEquals("1:8: second argument of [CONCAT] must be [string], found value [2] type [integer]",
|
||||
error("SELECT CONCAT('foo', 2)"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForNumericFunction_WithTwoArgs() {
|
||||
assertEquals("1:8: [TRUNCATE('foo', 2)] first argument must be [numeric], found value ['foo'] type [keyword]",
|
||||
assertEquals("1:8: first argument of [TRUNCATE('foo', 2)] must be [numeric], found value ['foo'] type [keyword]",
|
||||
error("SELECT TRUNCATE('foo', 2)"));
|
||||
assertEquals("1:8: [TRUNCATE(1.2, 'bar')] second argument must be [numeric], found value ['bar'] type [keyword]",
|
||||
assertEquals("1:8: second argument of [TRUNCATE(1.2, 'bar')] must be [numeric], found value ['bar'] type [keyword]",
|
||||
error("SELECT TRUNCATE(1.2, 'bar')"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForBooleanFuntion_WithTwoArgs() {
|
||||
assertEquals("1:8: [1 OR true] first argument must be [boolean], found value [1] type [integer]",
|
||||
assertEquals("1:8: first argument of [1 OR true] must be [boolean], found value [1] type [integer]",
|
||||
error("SELECT 1 OR true"));
|
||||
assertEquals("1:8: [true OR 2] second argument must be [boolean], found value [2] type [integer]",
|
||||
assertEquals("1:8: second argument of [true OR 2] must be [boolean], found value [2] type [integer]",
|
||||
error("SELECT true OR 2"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForFunction_WithThreeArgs() {
|
||||
assertEquals("1:8: [REPLACE(1, 'foo', 'bar')] first argument must be [string], found value [1] type [integer]",
|
||||
public void testInvalidTypeForReplace() {
|
||||
assertEquals("1:8: first argument of [REPLACE(1, 'foo', 'bar')] must be [string], found value [1] type [integer]",
|
||||
error("SELECT REPLACE(1, 'foo', 'bar')"));
|
||||
assertEquals("1:8: [REPLACE('text', 2, 'bar')] second argument must be [string], found value [2] type [integer]",
|
||||
error("SELECT REPLACE('text', 2, 'bar')"));
|
||||
assertEquals("1:8: [REPLACE('text', 'foo', 3)] third argument must be [string], found value [3] type [integer]",
|
||||
error("SELECT REPLACE('text', 'foo', 3)"));
|
||||
assertEquals("1:8: [REPLACE(text, 'foo', 'bar')] cannot operate on first argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT REPLACE(text, 'foo', 'bar') FROM test"));
|
||||
|
||||
assertEquals("1:8: second argument of [REPLACE('foo', 2, 'bar')] must be [string], found value [2] type [integer]",
|
||||
error("SELECT REPLACE('foo', 2, 'bar')"));
|
||||
assertEquals("1:8: [REPLACE('foo', text, 'bar')] cannot operate on second argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT REPLACE('foo', text, 'bar') FROM test"));
|
||||
|
||||
assertEquals("1:8: third argument of [REPLACE('foo', 'bar', 3)] must be [string], found value [3] type [integer]",
|
||||
error("SELECT REPLACE('foo', 'bar', 3)"));
|
||||
assertEquals("1:8: [REPLACE('foo', 'bar', text)] cannot operate on third argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT REPLACE('foo', 'bar', text) FROM test"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForSubString() {
|
||||
assertEquals("1:8: first argument of [SUBSTRING(1, 2, 3)] must be [string], found value [1] type [integer]",
|
||||
error("SELECT SUBSTRING(1, 2, 3)"));
|
||||
assertEquals("1:8: [SUBSTRING(text, 2, 3)] cannot operate on first argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT SUBSTRING(text, 2, 3) FROM test"));
|
||||
|
||||
assertEquals("1:8: second argument of [SUBSTRING('foo', 'bar', 3)] must be [integer], found value ['bar'] type [keyword]",
|
||||
error("SELECT SUBSTRING('foo', 'bar', 3)"));
|
||||
|
||||
assertEquals("1:8: third argument of [SUBSTRING('foo', 2, 'bar')] must be [integer], found value ['bar'] type [keyword]",
|
||||
error("SELECT SUBSTRING('foo', 2, 'bar')"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForFunction_WithFourArgs() {
|
||||
assertEquals("1:8: [INSERT(1, 1, 2, 'new')] first argument must be [string], found value [1] type [integer]",
|
||||
assertEquals("1:8: first argument of [INSERT(1, 1, 2, 'new')] must be [string], found value [1] type [integer]",
|
||||
error("SELECT INSERT(1, 1, 2, 'new')"));
|
||||
assertEquals("1:8: [INSERT('text', 'foo', 2, 'new')] second argument must be [numeric], found value ['foo'] type [keyword]",
|
||||
assertEquals("1:8: second argument of [INSERT('text', 'foo', 2, 'new')] must be [numeric], found value ['foo'] type [keyword]",
|
||||
error("SELECT INSERT('text', 'foo', 2, 'new')"));
|
||||
assertEquals("1:8: [INSERT('text', 1, 'bar', 'new')] third argument must be [numeric], found value ['bar'] type [keyword]",
|
||||
assertEquals("1:8: third argument of [INSERT('text', 1, 'bar', 'new')] must be [numeric], found value ['bar'] type [keyword]",
|
||||
error("SELECT INSERT('text', 1, 'bar', 'new')"));
|
||||
assertEquals("1:8: [INSERT('text', 1, 2, 3)] fourth argument must be [string], found value [3] type [integer]",
|
||||
assertEquals("1:8: fourth argument of [INSERT('text', 1, 2, 3)] must be [string], found value [3] type [integer]",
|
||||
error("SELECT INSERT('text', 1, 2, 3)"));
|
||||
}
|
||||
|
||||
public void testInvalidTypeForRegexMatch() {
|
||||
assertEquals("1:26: [text LIKE 'foo'] cannot operate on field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT * FROM test WHERE text LIKE 'foo'"));
|
||||
}
|
||||
|
||||
public void testAllowCorrectFieldsInIncompatibleMappings() {
|
||||
assertNotNull(incompatibleAccept("SELECT languages FROM \"*\""));
|
||||
|
@ -616,32 +681,34 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testErrorMessageForPercentileWithSecondArgBasedOnAField() {
|
||||
assertEquals("1:8: Second argument of PERCENTILE must be a constant, received [ABS(int)]",
|
||||
assertEquals("1:8: second argument of [PERCENTILE(int, ABS(int))] must be a constant, received [ABS(int)]",
|
||||
error("SELECT PERCENTILE(int, ABS(int)) FROM test"));
|
||||
}
|
||||
|
||||
public void testErrorMessageForPercentileRankWithSecondArgBasedOnAField() {
|
||||
assertEquals("1:8: Second argument of PERCENTILE_RANK must be a constant, received [ABS(int)]",
|
||||
assertEquals("1:8: second argument of [PERCENTILE_RANK(int, ABS(int))] must be a constant, received [ABS(int)]",
|
||||
error("SELECT PERCENTILE_RANK(int, ABS(int)) FROM test"));
|
||||
}
|
||||
|
||||
public void testTopHitsFirstArgConstant() {
|
||||
assertEquals("1:8: First argument of [FIRST] must be a table column, found constant ['foo']",
|
||||
assertEquals("1:8: first argument of [FIRST('foo', int)] must be a table column, found constant ['foo']",
|
||||
error("SELECT FIRST('foo', int) FROM test"));
|
||||
}
|
||||
|
||||
public void testTopHitsSecondArgConstant() {
|
||||
assertEquals("1:8: Second argument of [LAST] must be a table column, found constant [10]",
|
||||
assertEquals("1:8: second argument of [LAST(int, 10)] must be a table column, found constant [10]",
|
||||
error("SELECT LAST(int, 10) FROM test"));
|
||||
}
|
||||
|
||||
public void testTopHitsFirstArgTextWithNoKeyword() {
|
||||
assertEquals("1:8: [FIRST] cannot operate on first argument field of data type [text]",
|
||||
assertEquals("1:8: [FIRST(text)] cannot operate on first argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT FIRST(text) FROM test"));
|
||||
}
|
||||
|
||||
public void testTopHitsSecondArgTextWithNoKeyword() {
|
||||
assertEquals("1:8: [LAST] cannot operate on second argument field of data type [text]",
|
||||
assertEquals("1:8: [LAST(keyword, text)] cannot operate on second argument field of data type [text]: " +
|
||||
"No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead",
|
||||
error("SELECT LAST(keyword, text) FROM test"));
|
||||
}
|
||||
|
||||
|
@ -671,4 +738,4 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
public void testProjectUnresolvedAliasInFilter() {
|
||||
assertEquals("1:8: Unknown column [tni]", error("SELECT tni AS i FROM test WHERE i > 10 GROUP BY i"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
|
|||
import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||
|
@ -110,16 +109,6 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
assertEquals("value", tq.value());
|
||||
}
|
||||
|
||||
public void testTermEqualityAnalyzerAmbiguous() {
|
||||
LogicalPlan p = plan("SELECT some.string FROM test WHERE some.ambiguous = 'value'");
|
||||
assertTrue(p instanceof Project);
|
||||
p = ((Project) p).child();
|
||||
assertTrue(p instanceof Filter);
|
||||
Expression condition = ((Filter) p).condition();
|
||||
// the message is checked elsewhere (in FieldAttributeTests)
|
||||
expectThrows(MappingException.class, () -> QueryTranslator.toQuery(condition, false));
|
||||
}
|
||||
|
||||
public void testTermEqualityNotAnalyzed() {
|
||||
LogicalPlan p = plan("SELECT some.string FROM test WHERE int = 5");
|
||||
assertTrue(p instanceof Project);
|
||||
|
@ -640,7 +629,7 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
EsQueryExec eqe = (EsQueryExec) p;
|
||||
assertEquals(1, eqe.output().size());
|
||||
assertEquals("FIRST(keyword)", eqe.output().get(0).qualifiedName());
|
||||
assertTrue(eqe.output().get(0).dataType() == DataType.KEYWORD);
|
||||
assertEquals(DataType.KEYWORD, eqe.output().get(0).dataType());
|
||||
assertThat(eqe.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""),
|
||||
endsWith("\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false," +
|
||||
"\"explain\":false,\"docvalue_fields\":[{\"field\":\"keyword\"}]," +
|
||||
|
@ -652,7 +641,7 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
EsQueryExec eqe = (EsQueryExec) p;
|
||||
assertEquals(1, eqe.output().size());
|
||||
assertEquals("LAST(date)", eqe.output().get(0).qualifiedName());
|
||||
assertTrue(eqe.output().get(0).dataType() == DataType.DATETIME);
|
||||
assertEquals(DataType.DATETIME, eqe.output().get(0).dataType());
|
||||
assertThat(eqe.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""),
|
||||
endsWith("\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false," +
|
||||
"\"explain\":false,\"docvalue_fields\":[{\"field\":\"date\",\"format\":\"epoch_millis\"}]," +
|
||||
|
@ -667,7 +656,7 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
EsQueryExec eqe = (EsQueryExec) p;
|
||||
assertEquals(1, eqe.output().size());
|
||||
assertEquals("FIRST(keyword, int)", eqe.output().get(0).qualifiedName());
|
||||
assertTrue(eqe.output().get(0).dataType() == DataType.KEYWORD);
|
||||
assertEquals(DataType.KEYWORD, eqe.output().get(0).dataType());
|
||||
assertThat(eqe.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""),
|
||||
endsWith("\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false," +
|
||||
"\"explain\":false,\"docvalue_fields\":[{\"field\":\"keyword\"}]," +
|
||||
|
@ -681,7 +670,7 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
EsQueryExec eqe = (EsQueryExec) p;
|
||||
assertEquals(1, eqe.output().size());
|
||||
assertEquals("LAST(date, int)", eqe.output().get(0).qualifiedName());
|
||||
assertTrue(eqe.output().get(0).dataType() == DataType.DATETIME);
|
||||
assertEquals(DataType.DATETIME, eqe.output().get(0).dataType());
|
||||
assertThat(eqe.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""),
|
||||
endsWith("\"top_hits\":{\"from\":0,\"size\":1,\"version\":false,\"seq_no_primary_term\":false," +
|
||||
"\"explain\":false,\"docvalue_fields\":[{\"field\":\"date\",\"format\":\"epoch_millis\"}]," +
|
||||
|
|
Loading…
Reference in New Issue