diff --git a/config-model/src/main/java/com/yahoo/schema/processing/IndexingValidation.java b/config-model/src/main/java/com/yahoo/schema/processing/IndexingValidation.java index eceffb1bc025..1011fb2617f2 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/IndexingValidation.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/IndexingValidation.java @@ -52,7 +52,7 @@ public void process(boolean validate, boolean documentsOnly) { converter.convert(exp); // TODO: stop doing this explicitly when visiting a script does not branch } } catch (VerificationException e) { - fail(schema, field, "For expression '" + e.getExpression() + "': " + Exceptions.toMessageString(e)); + fail(schema, field, Exceptions.toMessageString(e)); } } } @@ -71,17 +71,17 @@ public ExpressionConverter branch() { } @Override - protected boolean shouldConvert(Expression exp) { - if (exp instanceof OutputExpression) { - String fieldName = ((OutputExpression)exp).getFieldName(); + protected boolean shouldConvert(Expression expression) { + if (expression instanceof OutputExpression) { + String fieldName = ((OutputExpression)expression).getFieldName(); if (outputs.contains(fieldName) && !prevNames.contains(fieldName)) { - throw new VerificationException(exp, "Attempting to assign conflicting values to field '" + - fieldName + "'"); + throw new VerificationException(expression, "Attempting to assign conflicting values to field '" + + fieldName + "'"); } outputs.add(fieldName); prevNames.add(fieldName); } - if (exp.createdOutputType() != null) { + if (expression.createdOutputType() != null) { prevNames.clear(); } return false; diff --git a/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java b/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java index e920672646f6..d77722bdd31b 100644 --- a/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java @@ -1,7 +1,10 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.schema; +import com.yahoo.config.model.deploy.DeployState; import com.yahoo.document.Document; +import com.yahoo.schema.derived.DerivedConfiguration; +import com.yahoo.schema.derived.SchemaInfo; import com.yahoo.schema.document.Stemming; import com.yahoo.schema.parser.ParseException; import com.yahoo.schema.processing.ImportedFieldsResolver; @@ -479,6 +482,25 @@ void testInheritingMultipleRankProfilesWithOverlappingConstructsIsDisallowed2() } } + @Test + void testDeriving() throws Exception { + String schema = + """ + schema test { + field my_hash type long { + indexing: input my_string | hash | attribute + } + document test { + field my_string type string { + } + } + }"""; + ApplicationBuilder builder = new ApplicationBuilder(new DeployLoggerStub()); + builder.addSchema(schema); + var application = builder.build(false); // validate=false to test config deriving without validation + new DerivedConfiguration(application.schemas().get("test"), application.rankProfileRegistry()); + } + private void assertInheritedFromParent(Schema schema, RankProfileRegistry rankProfileRegistry) { assertEquals("pf1", schema.fieldSets().userFieldSets().get("parent_set").getFieldNames().stream().findFirst().get()); assertEquals(Stemming.NONE, schema.getStemming()); diff --git a/config-model/src/test/java/com/yahoo/schema/processing/IndexingInputsTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/IndexingInputsTestCase.java index 597b83908ecb..f0e86e8b5826 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/IndexingInputsTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/IndexingInputsTestCase.java @@ -57,7 +57,7 @@ void requireThatExtraFieldInputImplicitThrows() throws ParseException { } catch (IllegalArgumentException e) { assertEquals("For schema 'indexing_extra_field_input_implicit', field 'foo': " + - "For expression '{ tokenize normalize stem:\"BEST\" | index foo; }': Expected string input, but no input is specified", + "Invalid expression '{ tokenize normalize stem:\"BEST\" | index foo; }': Expected string input, but no input is specified", Exceptions.toMessageString(e)); } } @@ -156,7 +156,7 @@ void testNoInputInDerivedField() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'test', field 'derived1': For expression '{ attribute derived1; }': " + + assertEquals("For schema 'test', field 'derived1': Invalid expression '{ attribute derived1; }': " + "Expected any input, but no input is specified", Exceptions.toMessageString(e)); } diff --git a/config-model/src/test/java/com/yahoo/schema/processing/IndexingOutputsTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/IndexingOutputsTestCase.java index ebaa0ef37b0e..53db3d7e512b 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/IndexingOutputsTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/IndexingOutputsTestCase.java @@ -56,7 +56,7 @@ void requireThatOutputConflictThrows() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'indexing_output_confict', field 'bar': For expression 'index bar': Attempting " + + assertEquals("For schema 'indexing_output_confict', field 'bar': Invalid expression 'index bar': Attempting " + "to assign conflicting values to field 'bar'", Exceptions.toMessageString(e)); } diff --git a/config-model/src/test/java/com/yahoo/schema/processing/IndexingValidationTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/IndexingValidationTestCase.java index 898f3f5b23d7..c638c55a0314 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/IndexingValidationTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/IndexingValidationTestCase.java @@ -35,7 +35,7 @@ void testAttributeChanged() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'indexing_attribute_changed', field 'foo': For expression 'attribute foo': " + + assertEquals("For schema 'indexing_attribute_changed', field 'foo': Invalid expression 'attribute foo': " + "Attempting to assign conflicting values to field 'foo'", Exceptions.toMessageString(e)); } @@ -79,7 +79,7 @@ void testIndexChanged() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'indexing_index_changed', field 'foo': For expression 'index foo': " + + assertEquals("For schema 'indexing_index_changed', field 'foo': Invalid expression 'index foo': " + "Attempting to assign conflicting values to field 'foo'", Exceptions.toMessageString(e)); } @@ -123,7 +123,7 @@ void testSummaryChanged() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'indexing_summary_fail', field 'foo': For expression 'summary foo': Attempting " + + assertEquals("For schema 'indexing_summary_fail', field 'foo': Invalid expression 'summary foo': Attempting " + "to assign conflicting values to field 'foo'", Exceptions.toMessageString(e)); } @@ -185,7 +185,7 @@ void requireThatMultilineOutputConflictThrows() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'indexing_multiline_output_confict', field 'cox': For expression 'index cox': " + + assertEquals("For schema 'indexing_multiline_output_confict', field 'cox': Invalid expression 'index cox': " + "Attempting to assign conflicting values to field 'cox'", Exceptions.toMessageString(e)); } diff --git a/document/abi-spec.json b/document/abi-spec.json index ba2313dcf0b1..51cbb6d6475d 100644 --- a/document/abi-spec.json +++ b/document/abi-spec.json @@ -182,7 +182,8 @@ "public com.yahoo.document.datatypes.FieldValue createFieldValue(java.lang.Object)", "public abstract java.lang.Class getValueClass()", "public abstract boolean isValueCompatible(com.yahoo.document.datatypes.FieldValue)", - "public final boolean isAssignableFrom(com.yahoo.document.DataType)", + "public boolean isAssignableFrom(com.yahoo.document.DataType)", + "public boolean isAssignableTo(com.yahoo.document.DataType)", "public static com.yahoo.document.ArrayDataType getArray(com.yahoo.document.DataType)", "public static com.yahoo.document.MapDataType getMap(com.yahoo.document.DataType, com.yahoo.document.DataType)", "public static com.yahoo.document.WeightedSetDataType getWeightedSet(com.yahoo.document.DataType)", diff --git a/document/src/main/java/com/yahoo/document/DataType.java b/document/src/main/java/com/yahoo/document/DataType.java index 7a49938334fa..3b1277a39e75 100644 --- a/document/src/main/java/com/yahoo/document/DataType.java +++ b/document/src/main/java/com/yahoo/document/DataType.java @@ -159,11 +159,16 @@ public FieldValue createFieldValue(Object arg) { public abstract boolean isValueCompatible(FieldValue value); - public final boolean isAssignableFrom(DataType dataType) { + public boolean isAssignableFrom(DataType dataType) { // TODO: Reverse this so that isValueCompatible() uses this instead. return isValueCompatible(dataType.createFieldValue()); } + /** The reverse of isAssignableFrom */ + public boolean isAssignableTo(DataType dataType) { + return dataType.isAssignableFrom(this); + } + /** * Returns an array datatype, where the array elements are of the given type * diff --git a/document/src/main/java/com/yahoo/document/NumericDataType.java b/document/src/main/java/com/yahoo/document/NumericDataType.java index 828cf024045a..1f27ceeba37b 100644 --- a/document/src/main/java/com/yahoo/document/NumericDataType.java +++ b/document/src/main/java/com/yahoo/document/NumericDataType.java @@ -11,6 +11,7 @@ public class NumericDataType extends PrimitiveDataType { // The global class identifier shared with C++. public static int classId = registerClass(Ids.document + 52, NumericDataType.class); + /** * Creates a datatype * @@ -18,7 +19,7 @@ public class NumericDataType extends PrimitiveDataType { * @param code the code (id) of the type * @param type the field value used for this type */ - protected NumericDataType(java.lang.String name, int code, Class type, Factory factory) { + protected NumericDataType(String name, int code, Class type, Factory factory) { super(name, code, type, factory); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AnyDataType.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AnyDataType.java index 3b401ae40851..e3996b228e1f 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AnyDataType.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/AnyDataType.java @@ -19,12 +19,21 @@ private AnyDataType() { } @Override - public FieldValue createFieldValue() { throw new UnsupportedOperationException(); } + public boolean isAssignableFrom(DataType other) { return true; } @Override - public Class getValueClass() { throw new UnsupportedOperationException(); } + public boolean isAssignableTo(DataType other) { + return other instanceof AnyDataType; + } @Override public boolean isValueCompatible(FieldValue value) { return true; } + @Override + public FieldValue createFieldValue() { throw new UnsupportedOperationException(); } + + @Override + public Class getValueClass() { throw new UnsupportedOperationException(); } + + } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java index 1a5bda47b8be..d7bc6afff39c 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticExpression.java @@ -66,11 +66,48 @@ public ArithmeticExpression convertChildren(ExpressionConverter converter) { return new ArithmeticExpression(converter.convert(left), op, converter.convert(right)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + DataType leftOutput = left.setInputType(inputType, context); + DataType rightOutput = right.setInputType(inputType, context); + return resultingType(leftOutput, rightOutput); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + DataType leftInput = left.setOutputType(outputType, context); + DataType rightInput = right.setOutputType(outputType, context); + if (leftInput == rightInput) // TODO: Generalize + return leftInput; + else + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); - context.setCurrentType(evaluate(context.setCurrentType(input).verify(left).getCurrentType(), - context.setCurrentType(input).verify(right).getCurrentType())); + context.setCurrentType(resultingType(context.setCurrentType(input).verify(left).getCurrentType(), + context.setCurrentType(input).verify(right).getCurrentType())); + } + + private DataType resultingType(DataType left, DataType right) { + if (left == null || right == null) + return null; + if (!(left instanceof NumericDataType)) + throw new VerificationException(this, "The first argument must be a number, but has type " + left.getName()); + if (!(right instanceof NumericDataType)) + throw new VerificationException(this, "The second argument must be a number, but has type " + right.getName()); + + if (left == DataType.FLOAT || left == DataType.DOUBLE || right == DataType.FLOAT || right == DataType.DOUBLE) { + if (left == DataType.DOUBLE || right == DataType.DOUBLE) + return DataType.DOUBLE; + return DataType.FLOAT; + } + if (left == DataType.LONG || right == DataType.LONG) + return DataType.LONG; + return DataType.INT; } @Override @@ -80,15 +117,15 @@ protected void doExecute(ExecutionContext context) { context.setCurrentValue(input).execute(right).getCurrentValue())); } - private static DataType requiredInputType(Expression lhs, Expression rhs) { - DataType lhsType = lhs.requiredInputType(); - DataType rhsType = rhs.requiredInputType(); - if (lhsType == null) return rhsType; - if (rhsType == null) return lhsType; - if (!lhsType.equals(rhsType)) + private static DataType requiredInputType(Expression left, Expression right) { + DataType leftType = left.requiredInputType(); + DataType rightType = right.requiredInputType(); + if (leftType == null) return rightType; + if (rightType == null) return leftType; + if (!leftType.equals(rightType)) throw new VerificationException(ArithmeticExpression.class, "Operands require conflicting input types, " + - lhsType.getName() + " vs " + rhsType.getName()); - return lhsType; + leftType.getName() + " vs " + rightType.getName()); + return leftType; } @Override @@ -116,23 +153,6 @@ public int hashCode() { return getClass().hashCode() + left.hashCode() + op.hashCode() + right.hashCode(); } - private DataType evaluate(DataType lhs, DataType rhs) { - if (lhs == null || rhs == null) - throw new VerificationException(this, "Attempting to perform arithmetic on a null value"); - if (!(lhs instanceof NumericDataType) || !(rhs instanceof NumericDataType)) - throw new VerificationException(this, "Attempting to perform unsupported arithmetic: [" + - lhs.getName() + "] " + op + " [" + rhs.getName() + "]"); - - if (lhs == DataType.FLOAT || lhs == DataType.DOUBLE || rhs == DataType.FLOAT || rhs == DataType.DOUBLE) { - if (lhs == DataType.DOUBLE || rhs == DataType.DOUBLE) - return DataType.DOUBLE; - return DataType.FLOAT; - } - if (lhs == DataType.LONG || rhs == DataType.LONG) - return DataType.LONG; - return DataType.INT; - } - private FieldValue evaluate(FieldValue lhs, FieldValue rhs) { if (lhs == null || rhs == null) return null; if (!(lhs instanceof NumericFieldValue) || !(rhs instanceof NumericFieldValue)) diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java index 2ad8a2bac573..4ffd3befed52 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeExpression.java @@ -24,7 +24,7 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(outputType, DataType.LONG, context); + super.setOutputType(DataType.LONG, outputType, null, context); return DataType.STRING; } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java index e430d5ce8fb5..9516833892be 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeExpression.java @@ -24,7 +24,7 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(outputType, DataType.STRING, context); + super.setOutputType(DataType.STRING, outputType, null, context); return DataType.LONG; } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BinarizeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BinarizeExpression.java index 8c28f53be8ed..22d2b2d883a0 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BinarizeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BinarizeExpression.java @@ -38,14 +38,14 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - return super.setOutputType(outputType, TensorDataType.any(), context); + return super.setOutputType(null, outputType, TensorDataType.any(), context); } @Override protected void doVerify(VerificationContext context) { type = context.getCurrentType(); if (! (type instanceof TensorDataType)) - throw new IllegalArgumentException("The 'binarize' function requires a tensor, but got " + type); + throw new VerificationException(this, "Require a tensor, but got " + type); } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BusyWaitExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BusyWaitExpression.java index 61fa94402032..a8d20866555a 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BusyWaitExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/BusyWaitExpression.java @@ -41,4 +41,5 @@ private static double nihlakanta(int i) { @Override public String toString() { return "busy_wait"; } @Override public boolean equals(Object obj) { return obj instanceof BusyWaitExpression; } @Override public int hashCode() { return getClass().hashCode(); } + } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java index 1fbf3d5412c3..b8df92b9c1b6 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CatExpression.java @@ -4,6 +4,7 @@ import com.yahoo.document.ArrayDataType; import com.yahoo.document.CollectionDataType; import com.yahoo.document.DataType; +import com.yahoo.document.TensorDataType; import com.yahoo.document.WeightedSetDataType; import com.yahoo.document.datatypes.Array; import com.yahoo.document.datatypes.FieldValue; @@ -12,10 +13,12 @@ import com.yahoo.vespa.indexinglanguage.ExpressionConverter; import java.util.*; +import java.util.List; /** * @author Simon Thoresen Hult */ +// TODO: Support Map in addition to Array and Wighted Set (doc just says "collection type") public final class CatExpression extends ExpressionList { public CatExpression(Expression... expressions) { @@ -31,6 +34,32 @@ public CatExpression convertChildren(ExpressionConverter converter) { return new CatExpression(convertChildList(converter)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + + List outputTypes = new ArrayList<>(expressions().size()); + for (var expression : expressions()) + outputTypes.add(expression.setInputType(inputType, context)); + DataType outputType = resolveOutputType(outputTypes); + return outputType != null ? outputType : getOutputType(context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + if (outputType != DataType.STRING && ! (outputType instanceof CollectionDataType)) + throw new VerificationException(this, "Required to produce " + outputType.getName() + + ", but this produces a string or collection"); + super.setOutputType(outputType, context); + for (var expression : expressions()) + expression.setOutputType(AnyDataType.instance, context); // Any output is handled by converting to string + + if (outputType instanceof CollectionDataType) + return outputType; + else + return getInputType(context); // Cannot infer input type since we take the string value + } + @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); @@ -52,8 +81,8 @@ protected void doExecute(ExecutionContext context) { context.fillVariableTypes(verificationContext); List values = new LinkedList<>(); List types = new LinkedList<>(); - for (Expression exp : this) { - FieldValue val = context.setCurrentValue(input).execute(exp).getCurrentValue(); + for (Expression expression : this) { + FieldValue val = context.setCurrentValue(input).execute(expression).getCurrentValue(); values.add(val); DataType type; @@ -110,6 +139,7 @@ public boolean equals(Object obj) { private static DataType resolveOutputType(List types) { DataType resolved = null; for (DataType type : types) { + if (type == null) return null; if (!(type instanceof CollectionDataType)) return DataType.STRING; if (resolved == null) diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ChoiceExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ChoiceExpression.java index 02443ced8112..ef362b527d6d 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ChoiceExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ChoiceExpression.java @@ -1,10 +1,12 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.indexinglanguage.expressions; +import com.yahoo.document.CollectionDataType; import com.yahoo.document.DataType; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.vespa.indexinglanguage.ExpressionConverter; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -36,6 +38,34 @@ public ChoiceExpression convertChildren(ExpressionConverter converter) { return new ChoiceExpression(asList().stream().map(choice -> converter.branch().convert(choice)).toList()); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + + DataType resolvedType = null; + boolean resolvedTypeNeverAssigned = true; + for (var expression : expressions()) { + DataType outputType = expression.setInputType(inputType, context); + resolvedType = resolvedTypeNeverAssigned ? outputType : mostGeneralOf(resolvedType, outputType); + resolvedTypeNeverAssigned = false; + } + return resolvedType != null ? resolvedType : getOutputType(context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + + DataType resolvedType = null; + boolean resolvedTypeNeverAssigned = true; + for (var expression : expressions()) { + DataType inputType = expression.setOutputType(outputType, context); + resolvedType = resolvedTypeNeverAssigned ? inputType : mostGeneralOf(resolvedType, inputType); + resolvedTypeNeverAssigned = false; + } + return resolvedType != null ? resolvedType : getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ConstantExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ConstantExpression.java index 986556a343f4..6576648ea5f2 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ConstantExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ConstantExpression.java @@ -33,8 +33,8 @@ public DataType setInputType(DataType inputType, VerificationContext context) { public DataType setOutputType(DataType outputType, VerificationContext context) { // TODO: //if (outputType != value.getDataType()) - // throw new IllegalArgumentException(this + " produces a " + value.getDataType() + ", but a " + - // outputType + " is required"); + // throw new VerificationException(this, "Produces type " + value.getDataType() + ", but type " + + // outputType + " is required"); return super.setOutputType(outputType, context); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EmbedExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EmbedExpression.java index a51469a71f45..87e07444765b 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EmbedExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EmbedExpression.java @@ -70,13 +70,13 @@ public DataType setInputType(DataType type, VerificationContext context) { // TODO: Activate type checking // if ( ! (type == DataType.STRING) // && ! (type instanceof ArrayDataType array && array.getNestedType() == DataType.STRING)) - // throw new IllegalArgumentException("embed request either a string or array input type, but got " + type); - return null; // embed cannot determine the output type from the input + // throw new VerificationException(this, "This requires either a string or array input type, but got " + type); + return getOutputType(context); // embed cannot determine the output type from the input } @Override public DataType setOutputType(DataType type, VerificationContext context) { - super.setOutputType(type, TensorDataType.any(), context); + super.setOutputType(null, type, TensorDataType.any(), context); return getInputType(context); // the input (string vs. array of string) cannot be determined from the output } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java index 259a89e98032..c0eaf8e5ede2 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExactExpression.java @@ -38,6 +38,16 @@ public ExactExpression(int maxTokenLength) { this(OptionalInt.of(maxTokenLength)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } + @Override protected void doExecute(ExecutionContext context) { StringFieldValue input = (StringFieldValue) context.getCurrentValue(); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java index 99d5e51c9eda..42c2a3c538e8 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/Expression.java @@ -6,7 +6,6 @@ import com.yahoo.document.DocumentType; import com.yahoo.document.DocumentUpdate; import com.yahoo.document.Field; -import com.yahoo.document.TensorDataType; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.language.Linguistics; import com.yahoo.language.process.Embedder; @@ -66,10 +65,9 @@ public void setStatementOutput(DocumentType documentType, Field field) {} */ protected final DataType setInputType(DataType inputType, DataType requiredType, VerificationContext context) { // TODO: Activate type checking - // if ( ! (inputType instanceof TensorDataType)) - // throw new IllegalArgumentException(this + " requires a " + requiredType + ", but gets " + inputType); - this.inputType = inputType; - return inputType; + // if ( ! (inputType.isAssignableTo(requiredType)) + // throw new VerificationException(this, "This requires type " + requiredType.getName() + ", but gets " + inputType.getName()); + return assignInputType(inputType); } /** @@ -78,8 +76,14 @@ protected final DataType setInputType(DataType inputType, DataType requiredType, * Subtypes may implement this by calling setInputType(inputType, requiredType, VerificationContext context). */ public DataType setInputType(DataType inputType, VerificationContext context) { - this.inputType = inputType; - return inputType; + return assignInputType(inputType); + } + + private DataType assignInputType(DataType inputType) { + // Since we assign in both directions, in both orders, we may already know + if (this.inputType == null) + this.inputType = inputType; + return this.inputType; } /** @@ -88,23 +92,39 @@ public DataType setInputType(DataType inputType, VerificationContext context) { */ public DataType getOutputType(VerificationContext context) { return outputType; } + /** Returns the already assigned (during verification) output type, or null if no type is assigned. */ + public DataType getOutputType() { return outputType; } + + /** Returns the already assigned (during verification) output type, or throws an exception if no type is assigned. */ + public DataType requireOutputType() { + if (outputType == null) + throw new IllegalStateException("The output type of " + this + " is unresolved"); + return outputType; + } + /** * Sets the output type of this and returns the resulting input type, or null if it cannot be - * uniquely determined. + * uniquely determined, with additional arguments for convenience type checking. * This implementation returns the same type, which is appropriate for all statements * that do not change the type. * - * @param outputType the type to set as the output type of this, or null if it cannot be determined - * @param requiredType the type the output type must be assignable to + * @param actualOutput the type actually produced by this, must be assignable to the requiredOutput, + * or null if not known + * @param requiredOutput the type required by the next expression, which actualOutput must be assignable to, + * or null if it cannot be uniquely determined. + * @param requiredType a type the required output must be assignable to, or null to not verify this * @param context the context of this - * @throws IllegalArgumentException if outputType isn't assignable to requiredType + * @return the actualOutput if set, requiredOutput otherwise + * @throws IllegalArgumentException if actualOutput */ - protected final DataType setOutputType(DataType outputType, DataType requiredType, VerificationContext context) { + protected final DataType setOutputType(DataType actualOutput, DataType requiredOutput, DataType requiredType, + VerificationContext context) { // TODO: Activate type checking - // if (outputType != null && ! requiredType.isAssignableFrom(outputType)) - // throw new IllegalArgumentException(this + " produces a " + outputType + " but " + requiredType + " is required"); - this.outputType = outputType; - return outputType; + // if (actualOutput != null && requiredOutput != null && ! actualOutput.isAssignableTo(requiredOutput)) + // throw new VerificationException(this, "This produces type " + actualOutput.getName() + " but " + requiredOutput.getName() + " is required"); + // if (requiredType != null && requiredOutput != null && ! requiredOutputOutput.isAssignableTo(requiredType)) + // throw new VerificationException(this, "This is required to produce type " + requiredOutput.getName() + " but is produces " + requiredType.getName());; + return assignOutputType(actualOutput != null ? actualOutput : requiredOutput); // Use the more precise type when known } /** @@ -113,8 +133,14 @@ protected final DataType setOutputType(DataType outputType, DataType requiredTyp * Subtypes implement this by calling setOutputType(outputType, requiredType, VerificationContext context). */ public DataType setOutputType(DataType outputType, VerificationContext context) { - this.outputType = outputType; - return outputType; + return assignOutputType(outputType); + } + + private DataType assignOutputType(DataType outputType) { + // Since we assign in both directions, in both orders, we may already know + if (this.outputType == null) + this.outputType = outputType; + return this.outputType; } public abstract DataType createdOutputType(); @@ -304,4 +330,14 @@ public final FieldValue execute() { return execute(new ExecutionContext()); } + protected DataType mostGeneralOf(DataType left, DataType right) { + if (left == null || right == null) return null; + return left.isAssignableTo(right) ? right : left; + } + + protected DataType leastGeneralOf(DataType left, DataType right) { + if (left == null || right == null) return null; + return left.isAssignableTo(right) ? left : right; + } + } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java index 6b44e4ef3cc3..255f35b926e3 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenExpression.java @@ -28,6 +28,15 @@ public FlattenExpression() { super(DataType.STRING); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java index 6805b0445f97..06fff2fe0d5b 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachExpression.java @@ -42,20 +42,75 @@ public void setStatementOutput(DocumentType documentType, Field field) { @Override public DataType setInputType(DataType inputType, VerificationContext context) { - // TODO: Activate type checking - // if ( ! inputType.isMultivalue()) - // throw new IllegalArgumentException("for_each requires a multivalue type, but gets " + inputType); - expression.setInputType(inputType.getNestedType(), context); - return super.setInputType(inputType, context); + super.setInputType(inputType, context); + + if (inputType instanceof ArrayDataType || inputType instanceof WeightedSetDataType) { + // Value type outside block becomes the collection type having the block output type as argument + return withInnerType(expression.setInputType(inputType.getNestedType(), context), inputType); + } + else if (inputType instanceof StructDataType struct) { + return verifyStructFields(struct, context); + } + if (inputType instanceof MapDataType) { + // Inner value will be MapEntryFieldValue which has the same type as the map + DataType outputType = expression.setInputType(inputType, context); + if (outputType == null) return getOutputType(context); + return DataType.getArray(outputType); + } + else { + throw new VerificationException(this, "Expected Array, Struct, WeightedSet or Map input, got " + + inputType.getName()); + } } @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - // TODO: Activate type checking - // if ( ! outputType.isMultivalue()) - // throw new IllegalArgumentException("for_each produces a multivalue type, but " + outputType + " is required"); - expression.setOutputType(outputType.getNestedType(), context); - return super.setOutputType(outputType, context); + super.setOutputType(outputType, context); + + if (outputType instanceof ArrayDataType || outputType instanceof WeightedSetDataType) { + DataType innerInputType = expression.setOutputType(outputType.getNestedType(), context); + if (innerInputType instanceof MapDataType mapDataType) // A map converted to an array of entries + return mapDataType; + else + return withInnerType(innerInputType, outputType); + } + else if (outputType instanceof StructDataType struct) { + return verifyStructFields(struct, context); + } + else { + throw new VerificationException(this, "Expected Array, Struct, WeightedSet or Map input, got " + + outputType.getName()); + } + } + + private DataType withInnerType(DataType innerType, DataType collectionType) { + if (innerType == null) return null; + if (collectionType instanceof WeightedSetDataType wset) + return DataType.getWeightedSet(innerType, wset.createIfNonExistent(), wset.removeIfZero()); + else + return DataType.getArray(innerType); + } + + /** + * Verifies that each struct field is compatible with the expression. + * This is symmetric in both verification directions since the expression just need to be compatible with + * all the struct fields. + */ + private DataType verifyStructFields(StructDataType struct, VerificationContext context) { + for (Field field : struct.getFields()) { + DataType fieldType = field.getDataType(); + DataType fieldOutputType = expression.setInputType(fieldType, context); + if (fieldOutputType != null && ! fieldOutputType.isAssignableTo(fieldType)) + throw new VerificationException(this, "Struct field " + field.getName() + " has type " + fieldType.getName() + + " but expression produces " + fieldOutputType); + DataType fieldInputType = expression.setOutputType(fieldType, context); + if (fieldOutputType != null && ! fieldType.isAssignableTo(fieldInputType)) + throw new VerificationException(this, "Struct field " + field.getName() + " has type " + fieldType.getName() + + " but expression requires " + fieldInputType); + if (fieldOutputType == null && fieldInputType == null) + return null; // Neither direction could be inferred + } + return struct; } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java index 425eb90d6f8d..e84a19e29239 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java @@ -31,22 +31,21 @@ public GetFieldExpression(String structFieldName) { @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - return getFieldType(context); + return getFieldType(inputType, context); } @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(getFieldType(context), outputType, context); - return AnyDataType.instance; + super.setOutputType(outputType, context); + return getInputType(context); } @Override protected void doVerify(VerificationContext context) { - context.setCurrentType(getFieldType(context)); + context.setCurrentType(getFieldType(context.getCurrentType(), context)); } - private DataType getFieldType(VerificationContext context) { - DataType input = context.getCurrentType(); + private DataType getFieldType(DataType input, VerificationContext context) { if (input instanceof MapDataType entryInput) { if (structFieldName.equals(keyName)) return entryInput.getKeyType(); @@ -58,7 +57,7 @@ else if (entryInput.getValueType() instanceof StructuredDataType structInput) else if (input instanceof StructuredDataType structInput) { return getStructFieldType(structInput); } - throw new VerificationException(this, "Expected a struct or map, bit got an " + input.getName()); + throw new VerificationException(this, "Expected a struct or map, but got an " + input.getName()); } private DataType getStructFieldType(StructuredDataType structInput) { diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java index 1f5e1c4f5b9d..2c0ca758601f 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarExpression.java @@ -25,16 +25,15 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(context.getVariable(variableName), outputType, context); + super.setOutputType(context.getVariable(variableName), outputType, null, context); return AnyDataType.instance; } @Override protected void doVerify(VerificationContext context) { DataType input = context.getVariable(variableName); - if (input == null) { + if (input == null) throw new VerificationException(this, "Variable '" + variableName + "' not found"); - } context.setCurrentType(input); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HashExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HashExpression.java index bdfcd6155803..99e395da1bd2 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HashExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HashExpression.java @@ -3,7 +3,6 @@ import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; -import com.yahoo.document.ArrayDataType; import com.yahoo.document.DataType; import com.yahoo.document.DocumentType; import com.yahoo.document.Field; @@ -22,43 +21,36 @@ public class HashExpression extends Expression { private final HashFunction hasher = Hashing.sipHash24(); - /** The target *primitive* type we are hashing into. */ - private DataType targetType; - public HashExpression() { super(DataType.STRING); } @Override - public void setStatementOutput(DocumentType documentType, Field field) { - if ( ! canStoreHash(field.getDataType())) - throw new IllegalArgumentException("Cannot use the hash function on an indexing statement for " + - field.getName() + - ": The hash function can only be used when the target field " + - "is int or long or an array of int or long, not " + field.getDataType()); - targetType = field.getDataType().getPrimitiveType(); + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, DataType.STRING, context); + return getOutputType(context); // Can not infer int or long + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + if ( ! isHashCompatible(outputType)) + throw new VerificationException(this, "An " + outputType.getName() + + " output is required, but this produces int or long"); + return DataType.STRING; } @Override protected void doVerify(VerificationContext context) { - String outputField = context.getOutputField(); - if (outputField == null) - throw new VerificationException(this, "No output field in this statement: " + - "Don't know what value to hash to"); - DataType outputFieldType = context.getFieldType(this); - if ( ! canStoreHash(outputFieldType)) - throw new VerificationException(this, "The type of the output field " + outputField + - " is not int or long but " + outputFieldType); - targetType = outputFieldType.getPrimitiveType(); context.setCurrentType(createdOutputType()); } @Override protected void doExecute(ExecutionContext context) { StringFieldValue input = (StringFieldValue) context.getCurrentValue(); - if (targetType.equals(DataType.INT)) + if (requireOutputType().equals(DataType.INT)) context.setCurrentValue(new IntegerFieldValue(hashToInt(input.getString()))); - else if (targetType.equals(DataType.LONG)) + else if (requireOutputType().equals(DataType.LONG)) context.setCurrentValue(new LongFieldValue(hashToLong(input.getString()))); else throw new IllegalStateException(); // won't happen @@ -72,15 +64,16 @@ private long hashToLong(String value) { return hasher.hashString(value, StandardCharsets.UTF_8).asLong(); } - private boolean canStoreHash(DataType type) { + private boolean isHashCompatible(DataType type) { if (type.equals(DataType.INT)) return true; if (type.equals(DataType.LONG)) return true; - if (type instanceof ArrayDataType) return canStoreHash(((ArrayDataType)type).getNestedType()); return false; } @Override - public DataType createdOutputType() { return targetType; } + public DataType createdOutputType() { + return getOutputType(); + } @Override public String toString() { return "hash"; } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java index a7d314ff45a4..16d9487690f2 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeExpression.java @@ -17,6 +17,18 @@ public HexDecodeExpression() { super(DataType.STRING); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, DataType.STRING, context); + return DataType.LONG; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.LONG, outputType, null, context); + return DataType.STRING; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java index c32b08e12484..bb89ca144124 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeExpression.java @@ -14,6 +14,18 @@ public HexEncodeExpression() { super(DataType.LONG); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, DataType.LONG, context); + return DataType.STRING; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.STRING, outputType, null, context); + return DataType.LONG; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java index 550355c9d242..7cd4241fdb4b 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/HostNameExpression.java @@ -14,6 +14,18 @@ public HostNameExpression() { super(null); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + return DataType.STRING; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.STRING, outputType, null, context); + return AnyDataType.instance; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java index eeaee3ff1355..75754b51fb79 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenExpression.java @@ -66,6 +66,28 @@ public IfThenExpression convertChildren(ExpressionConverter converter) { converter.branch().convert(ifFalse)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + left.setInputType(inputType, context); + right.setInputType(inputType, context); + var trueOutputType = ifTrue.setInputType(inputType, context); + var falseOutputType = ifFalse.setInputType(inputType, context); + DataType output = mostGeneralOf(trueOutputType, falseOutputType); + return output != null ? output : getOutputType(context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + left.setOutputType(AnyDataType.instance, context); + right.setOutputType(AnyDataType.instance, context); + var trueInputType = ifTrue.setOutputType(outputType, context); + var falseInputType = ifFalse.setOutputType(outputType, context); + DataType input = leastGeneralOf(trueInputType, falseInputType); + return input != null ? input : getInputType(context); + } + @Override public void setStatementOutput(DocumentType documentType, Field field) { left.setStatementOutput(documentType, field); @@ -91,9 +113,7 @@ protected void doVerify(VerificationContext context) { context.setCurrentType(input).verify(right); var trueValue = context.setCurrentType(input).verify(ifTrue); var falseValue = context.setCurrentType(input).verify(ifFalse); - var valueType = trueValue.getCurrentType().isAssignableFrom(falseValue.getCurrentType()) ? - trueValue.getCurrentType() : falseValue.getCurrentType(); - context.setCurrentType(valueType); + context.setCurrentType(mostGeneralOf(trueValue.getCurrentType(), falseValue.getCurrentType())); } @Override @@ -160,24 +180,12 @@ public String toString() { @Override public boolean equals(Object obj) { - if (!(obj instanceof IfThenExpression exp)) { - return false; - } - if (!left.equals(exp.left)) { - return false; - } - if (!comparator.equals(exp.comparator)) { - return false; - } - if (!right.equals(exp.right)) { - return false; - } - if (!ifTrue.equals(exp.ifTrue)) { - return false; - } - if (!equals(ifFalse, exp.ifFalse)) { - return false; - } + if ( ! (obj instanceof IfThenExpression exp)) return false; + if ( ! left.equals(exp.left)) return false; + if ( ! comparator.equals(exp.comparator)) return false; + if ( ! right.equals(exp.right)) return false; + if ( ! ifTrue.equals(exp.ifTrue)) return false; + if ( ! equals(ifFalse, exp.ifFalse)) return false; return true; } @@ -204,16 +212,16 @@ private static DataType resolveRequiredInputType(DataType prev, DataType next) { return prev; } - private static boolean isTrue(FieldValue lhs, Comparator cmp, FieldValue rhs) { + private static boolean isTrue(FieldValue left, Comparator comparator, FieldValue right) { int res; - if (lhs instanceof NumericFieldValue && rhs instanceof NumericFieldValue) { - BigDecimal lhsVal = ArithmeticExpression.asBigDecimal((NumericFieldValue)lhs); - BigDecimal rhsVal = ArithmeticExpression.asBigDecimal((NumericFieldValue)rhs); + if (left instanceof NumericFieldValue && right instanceof NumericFieldValue) { + BigDecimal lhsVal = ArithmeticExpression.asBigDecimal((NumericFieldValue)left); + BigDecimal rhsVal = ArithmeticExpression.asBigDecimal((NumericFieldValue)right); res = lhsVal.compareTo(rhsVal); } else { - res = lhs.compareTo(rhs); + res = left.compareTo(right); } - return switch (cmp) { + return switch (comparator) { case EQ -> res == 0; case NE -> res != 0; case GT -> res > 0; diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java index 48cf6ac237c3..bfadc5d174b0 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/InputExpression.java @@ -28,11 +28,27 @@ public InputExpression(String fieldName) { public String getFieldName() { return fieldName; } @Override - protected void doVerify(VerificationContext context) { - DataType val = context.getFieldType(fieldName, this); - if (val == null) + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + return requireFieldType(context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(requireFieldType(context), outputType, null, context); + return AnyDataType.instance; + } + + private DataType requireFieldType(VerificationContext context) { + DataType fieldType = context.getFieldType(fieldName, this); + if (fieldType == null) throw new VerificationException(this, "Field '" + fieldName + "' not found"); - context.setCurrentType(val); + return fieldType; + } + + @Override + protected void doVerify(VerificationContext context) { + context.setCurrentType(requireFieldType(context)); } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java index d4640086a311..7d3e8bd46198 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/JoinExpression.java @@ -24,11 +24,25 @@ public JoinExpression(String delimiter) { public String getDelimiter() { return delimiter; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + if ( ! (inputType instanceof ArrayDataType)) + throw new VerificationException(this, "Expected Array input, got type " + inputType.getName()); + return DataType.STRING; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.STRING, outputType, null, context); + return getInputType(context); // Cannot deduce since any array type is accepted + } + @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); if (!(input instanceof ArrayDataType)) { - throw new VerificationException(this, "Expected Array input, got " + input.getName()); + throw new VerificationException(this, "Expected Array input, got type " + input.getName()); } context.setCurrentType(createdOutputType()); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LiteralBoolExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LiteralBoolExpression.java index 12fa802d59b1..c33241152edb 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LiteralBoolExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LiteralBoolExpression.java @@ -1,6 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.indexinglanguage.expressions; +import com.yahoo.document.ArrayDataType; import com.yahoo.document.DataType; import com.yahoo.document.datatypes.BoolFieldValue; @@ -18,6 +19,18 @@ public LiteralBoolExpression(boolean value) { this.value = value; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + return DataType.BOOL; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.BOOL, outputType, null, context); + return AnyDataType.instance; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java index 8cf304043bed..a7d77c5342d4 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseExpression.java @@ -15,6 +15,16 @@ public LowerCaseExpression() { super(DataType.STRING); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MapEntryFieldValue.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MapEntryFieldValue.java index 9ed1480aec23..150f9a23641c 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MapEntryFieldValue.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/MapEntryFieldValue.java @@ -21,10 +21,6 @@ class MapEntryFieldValue extends FieldValue { private FieldValue key = null; private FieldValue value = null; - MapEntryFieldValue(MapDataType type) { - this.type = type; - } - MapEntryFieldValue(FieldValue key, FieldValue value) { this.type = new MapDataType(key.getDataType(), value.getDataType()); this.key = key; diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NGramExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NGramExpression.java index 8954951ce462..9abcc400d29c 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NGramExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NGramExpression.java @@ -43,6 +43,16 @@ public NGramExpression(Linguistics linguistics, int gramSize) { public int getGramSize() { return gramSize; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } + @Override protected void doVerify(VerificationContext context) { // empty diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java index d269fa1fd4a9..f866f9142301 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeExpression.java @@ -23,7 +23,17 @@ public NormalizeExpression(Linguistics linguistics) { } public Linguistics getLinguistics() { return linguistics; } - + + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java index b00b38454628..c01dedee426e 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/NowExpression.java @@ -22,6 +22,17 @@ public NowExpression(Timer timer) { public Timer getTimer() { return timer; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.LONG, outputType, null, context); + return AnyDataType.instance; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java index 56531b3aae94..bb4494e43a9f 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateExpression.java @@ -29,6 +29,16 @@ public OptimizePredicateExpression() { this.optimizer = optimizer; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.PREDICATE, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.PREDICATE, outputType, null, context); + } + @Override protected void doVerify(VerificationContext context) { checkVariable(context, "arity", DataType.INT, true); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java index a050eee7413f..19c8c451d657 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/OutputExpression.java @@ -24,6 +24,17 @@ public OutputExpression(String image, String fieldName) { public String getFieldName() { return fieldName; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, context.getFieldType(fieldName, this), context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + return context.getFieldType(fieldName, this); + } + @Override protected void doVerify(VerificationContext context) { context.tryOutputType(fieldName, context.getCurrentType(), this); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/PackBitsExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/PackBitsExpression.java index 078639a8071b..9415cd2a1898 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/PackBitsExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/PackBitsExpression.java @@ -30,8 +30,7 @@ public PackBitsExpression() { public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); if ( ! validType(inputType)) - throw new IllegalArgumentException("pack_bits requires a tensor with one dense dimension, " + - "but got " + inputType); + throw new VerificationException(this, "Require a tensor with one dense dimension, but got " + inputType.getName()); outputTensorType = outputType(((TensorDataType)inputType).getTensorType()); return new TensorDataType(outputTensorType); } @@ -40,8 +39,8 @@ public DataType setInputType(DataType inputType, VerificationContext context) { public DataType setOutputType(DataType outputType, VerificationContext context) { super.setOutputType(outputType, context); if ( ! validType(outputType)) - throw new IllegalArgumentException("pack_bits produces a tensor with one dense dimension, " + - "but need " + outputType); + throw new VerificationException(this, "Required to produce " + outputType.getName() + + " but this produces a tensor with one dense dimension"); outputTensorType = ((TensorDataType)outputType).getTensorType(); return new TensorDataType(inputType(outputTensorType)); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java index ade7cc72d149..1c24ecf02369 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisExpression.java @@ -27,6 +27,18 @@ public ParenthesisExpression convertChildren(ExpressionConverter converter) { return new ParenthesisExpression(converter.convert(innerExp)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + return innerExp.setInputType(inputType, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + return innerExp.setInputType(outputType, context); + } + @Override public void setStatementOutput(DocumentType documentType, Field field) { innerExp.setStatementOutput(documentType, field); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java index 31a44172a180..08d4e0823746 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/RandomExpression.java @@ -25,6 +25,18 @@ public RandomExpression(Integer max) { public Integer getMaxValue() { return max; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + return DataType.INT; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(DataType.INT, outputType, null, context); + return AnyDataType.instance; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java index bf241cf61784..1c4e097b1f8a 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptExpression.java @@ -43,6 +43,24 @@ public ScriptExpression convertChildren(ExpressionConverter converter) { .toList()); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + DataType currentOutput = null; + for (var expression : expressions()) + currentOutput = expression.setInputType(inputType, context); + return currentOutput != null ? currentOutput : getOutputType(context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + DataType currentInput = null; + for (var expression : expressions()) + currentInput = expression.setOutputType(outputType, context); + return currentInput != null ? currentInput : getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java index d465d0aed66f..3239be6daa35 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputExpression.java @@ -38,6 +38,38 @@ public SelectInputExpression convertChildren(ExpressionConverter converter) { .toList()); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, context); + + DataType outputType = null; + boolean outputNeverAssigned = true; // Needed to separate this null case from the "cannot be inferred" case + for (Pair entry : cases) { + DataType fieldType = context.getFieldType(entry.getFirst(), this); + if (fieldType == null) + throw new VerificationException(this, "Field '" + entry.getFirst() + "' not found"); + var entryOutputType = entry.getSecond().setInputType(fieldType, context); + outputType = outputNeverAssigned ? entryOutputType : mostGeneralOf(outputType, entryOutputType); + outputNeverAssigned = false; + } + return outputType; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + + for (Pair entry : cases) { + DataType fieldType = context.getFieldType(entry.getFirst(), this); + if (fieldType == null) + throw new VerificationException(this, "Field '" + entry.getFirst() + "' not found"); + DataType inputType = entry.getSecond().setOutputType(outputType, context); + if ( ! fieldType.isAssignableTo(inputType)) + throw new VerificationException(this, "Field '" + entry.getFirst() + "' not found"); + } + return AnyDataType.instance; + } + @Override public void setStatementOutput(DocumentType documentType, Field field) { for (var casePair : cases) diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java index 8dbbc0c974d6..48095eacead0 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageExpression.java @@ -15,6 +15,16 @@ public SetLanguageExpression() { super(DataType.STRING); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(outputType, context); + } + @Override protected void doVerify(VerificationContext context) { // empty diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java index b4c92fc695b1..06342cbd03ed 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarExpression.java @@ -17,15 +17,30 @@ public SetVarExpression(String varName) { public String getVariableName() { return varName; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + setVariableType(inputType, context); + return super.setInputType(inputType, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + setVariableType(outputType, context); + return super.setOutputType(outputType, context); + } + @Override protected void doVerify(VerificationContext context) { - DataType next = context.getCurrentType(); - DataType prev = context.getVariable(varName); - if (prev != null && !prev.equals(next)) { - throw new VerificationException(this, "Attempting to assign conflicting types to variable '" + varName + - "', " + prev.getName() + " vs " + next.getName()); + setVariableType(context.getCurrentType(), context); + } + + private void setVariableType(DataType newType, VerificationContext context) { + DataType existingType = context.getVariable(varName); + if (existingType != null && ! newType.equals(existingType)) { + throw new VerificationException(this, "Cannot set variable '" + varName + "' to type " + newType.getName() + + ": It is already set to type " + existingType.getName()); } - context.setVariable(varName, next); + context.setVariable(varName, newType); } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java index 7d36455ebce6..7884e7632ddd 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SplitExpression.java @@ -25,10 +25,7 @@ public SplitExpression(String splitString) { @Override public DataType setInputType(DataType input, VerificationContext context) { - super.setInputType(input, context); - // TODO: Activate type checking - // if (input != DataType.STRING) - // throw new IllegalArgumentException("split requires a string input, but got " + input); + super.setInputType(input, DataType.STRING, context); return new ArrayDataType(DataType.STRING); } @@ -37,7 +34,7 @@ public DataType setOutputType(DataType output, VerificationContext context) { super.setOutputType(output, context); // TODO: Activate type checking // if ( ! (output instanceof ArrayDataType) && output.getNestedType() == DataType.STRING) - // throw new IllegalArgumentException("split produces a string array, but needs " + output); + // throw new VerificationException(this, "This produces a string array, but needs type " + output.getName()); return DataType.STRING; } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java index e8ed41df7b9c..1e16f6e52646 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java @@ -28,9 +28,6 @@ public final class StatementExpression extends ExpressionList { /** The names of the fields consumed by this. */ private final List inputFields; - /** The name of the (last) output field this statement will write to, or null if none */ - private String outputField; - public StatementExpression(Expression... list) { this(Arrays.asList(list)); // TODO: Can contain null - necessary ? } @@ -56,31 +53,57 @@ public StatementExpression convertChildren(ExpressionConverter converter) { } @Override - protected void doVerify(VerificationContext context) { - if (expressions().isEmpty()) return; + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + resolveBackwards(context); + return resolveForwards(context); + } - outputField = outputFieldName(); - if (outputField != null) - context.setOutputField(outputField); + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(output, context); + resolveForwards(context); + return resolveBackwards(context); + } - // Result input and output types: - // Some expressions can only determine their input from their output, and others only their output from - // their input. Therefore, we try resolving in both directions, which should always meet up to produce - // uniquely determined inputs and outputs of all expressions. - // forward: + // Result input and output types: + // Some expressions can only determine their input from their output, and others only their output from + // their input. Therefore, we try resolving in both directions, which should always meet up to produce + // uniquely determined inputs and outputs of all expressions. + // forward: + + /** Resolves types forward and returns the final output, or null if resolution could not progress to the end. */ + private DataType resolveForwards(VerificationContext context) { int i = 0; var inputType = getInputType(context); // A nested statement; input imposed from above if (inputType == null) // otherwise the first expression will be an input deciding the type inputType = expressions().get(i++).getOutputType(context); while (i < expressions().size() && inputType != null) inputType = expressions().get(i++).setInputType(inputType, context); - // reverse: - int j = expressions().size(); + return inputType; + } + + /** Resolves types backwards and returns the required input, or null if resolution could not progress to the start. */ + private DataType resolveBackwards(VerificationContext context) { + int i = expressions().size(); var outputType = getOutputType(context); // A nested statement; output imposed from above if (outputType == null) // otherwise the last expression will be an output deciding the type - outputType = expressions().get(--j).getInputType(context); - while (--j >= 0 && outputType != null) - outputType = expressions().get(j).setOutputType(outputType, context); + outputType = expressions().get(--i).getInputType(context); + while (--i >= 0 && outputType != null) + outputType = expressions().get(i).setOutputType(outputType, context); + return outputType; + } + + @Override + protected void doVerify(VerificationContext context) { + if (expressions().isEmpty()) return; + + String outputField = outputFieldName(); + if (outputField != null) + context.setOutputField(outputField); + + resolveForwards(context); + resolveBackwards(context); for (Expression expression : expressions()) context.verify(expression); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java index 00765e0b263c..36a34c4f887e 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringExpression.java @@ -26,6 +26,16 @@ public SubstringExpression(int from, int to) { public int getTo() { return to; } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + return super.setInputType(inputType, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + return super.setOutputType(DataType.STRING, outputType, null, context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java index 120f08847f5e..8bfd11681a16 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchExpression.java @@ -21,6 +21,8 @@ public final class SwitchExpression extends CompositeExpression { private final Map cases = new LinkedHashMap<>(); + + /** The default expression, or null if none */ private final Expression defaultExp; public SwitchExpression(Map cases) { @@ -54,6 +56,36 @@ public SwitchExpression convertChildren(ExpressionConverter converter) { return new SwitchExpression(convertedCases, converter.branch().convert(defaultExp)); } + @Override + public DataType setInputType(DataType inputType, VerificationContext context) { + super.setInputType(inputType, DataType.STRING, context); + + DataType outputType = defaultExp == null ? null : defaultExp.setInputType(inputType, context); + boolean outputNeverAssigned = true; // Needed to separate this null case from the "cannot be inferred" case + for (Expression expression : cases.values()) { + DataType expressionOutputType = expression.setInputType(inputType, context); + outputType = outputNeverAssigned ? expressionOutputType : mostGeneralOf(outputType, expressionOutputType); + outputNeverAssigned = false; + } + return outputType; + } + + @Override + public DataType setOutputType(DataType outputType, VerificationContext context) { + super.setOutputType(outputType, context); + + setOutputType(outputType, defaultExp, context); + for (Expression expression : cases.values()) + setOutputType(outputType, expression, context); + return DataType.STRING; + } + + private void setOutputType(DataType outputType, Expression expression, VerificationContext context) { + DataType inputType = expression.setOutputType(outputType, context); + if (inputType != null && ! DataType.STRING.isAssignableTo(inputType)) + throw new VerificationException(this, "This inputs a string, but '" + expression + "' requires type " + inputType); + } + @Override public void setStatementOutput(DocumentType documentType, Field field) { defaultExp.setStatementOutput(documentType, field); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java index 303458a19ff7..0b677ad23d69 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayExpression.java @@ -15,6 +15,20 @@ public ToArrayExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return new ArrayDataType(input); + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(output, context); + if ( ! (output instanceof ArrayDataType arrayType)) + throw new VerificationException(this, "Produces an array, but " + output + " is required"); + return arrayType.getNestedType(); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(DataType.getArray(context.getCurrentType())); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolExpression.java index 3e0523995322..fcb7b5311544 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolExpression.java @@ -1,7 +1,9 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.indexinglanguage.expressions; +import com.yahoo.document.ArrayDataType; import com.yahoo.document.DataType; +import com.yahoo.document.NumericDataType; import com.yahoo.document.datatypes.BoolFieldValue; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.NumericFieldValue; @@ -16,6 +18,20 @@ public ToBoolExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + if ( ! (input.isAssignableTo(DataType.STRING) && ! (input instanceof NumericDataType))) + throw new VerificationException(this, "Input must be a string or number, but got " + input.getName()); + return DataType.BOOL; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.BOOL, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java index 1606a9a70f62..a3326e9189fe 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteExpression.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; +import com.yahoo.document.NumericDataType; import com.yahoo.document.datatypes.ByteFieldValue; /** @@ -13,6 +14,18 @@ public ToByteExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.BYTE; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.BYTE, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java index 4f0e75c3f770..11a7c8d49db7 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleExpression.java @@ -13,6 +13,18 @@ public ToDoubleExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.DOUBLE; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.DOUBLE, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpression.java index 37032852b92e..88628879626a 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpression.java @@ -16,6 +16,18 @@ public ToEpochSecondExpression() { super(DataType.STRING); //only accept string input } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, DataType.STRING, context); + return DataType.LONG; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.LONG, output, null, context); + return DataType.STRING; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java index 91b51e8b7e34..11cf1a447b31 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatExpression.java @@ -13,6 +13,18 @@ public ToFloatExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.FLOAT; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.FLOAT, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java index cbf1ff949ce8..53fa9210711c 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerExpression.java @@ -13,6 +13,18 @@ public ToIntegerExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.INT; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.INT, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java index dc601a9e2dd3..fd611047ab98 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongExpression.java @@ -13,6 +13,18 @@ public ToLongExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.LONG; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.LONG, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java index 0d4bafaf8ed5..776714db6eab 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionExpression.java @@ -3,6 +3,7 @@ import com.yahoo.document.DataType; import com.yahoo.document.PositionDataType; +import com.yahoo.document.PrimitiveDataType; /** * @author Simon Thoresen Hult @@ -13,6 +14,18 @@ public ToPositionExpression() { super(DataType.STRING); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, DataType.STRING, context); + return PositionDataType.INSTANCE; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(PositionDataType.INSTANCE, output, null, context); + return DataType.STRING; + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java index d1209e59710b..e290680e32d4 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringExpression.java @@ -13,6 +13,18 @@ public ToStringExpression() { super(UnresolvedDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return DataType.STRING; + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.STRING, output, null, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java index 6ed50759e253..b165d51c7208 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetExpression.java @@ -24,24 +24,38 @@ public ToWsetExpression(boolean createIfNonExistent, boolean removeIfZero) { public boolean getRemoveIfZero() { return removeIfZero; } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + super.setInputType(input, context); + return outputType(input); + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + if ( ! (output instanceof WeightedSetDataType)) + throw new VerificationException(this, "This creates a WeightedSet, but type " + output.getName() + " is needed"); + super.setOutputType(output, context); + return getInputType(context); + } + @Override protected void doVerify(VerificationContext context) { - context.setCurrentType(DataType.getWeightedSet(context.getCurrentType(), createIfNonExistent, removeIfZero)); + context.setCurrentType(outputType(context.getCurrentType())); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected void doExecute(ExecutionContext context) { FieldValue input = context.getCurrentValue(); - DataType inputType = input.getDataType(); - - WeightedSetDataType outputType = DataType.getWeightedSet(inputType, createIfNonExistent, removeIfZero); - WeightedSet output = outputType.createFieldValue(); + WeightedSet output = outputType(input.getDataType()).createFieldValue(); output.add(input); - context.setCurrentValue(output); } + private WeightedSetDataType outputType(DataType inputType) { + return DataType.getWeightedSet(inputType, createIfNonExistent, removeIfZero); + } + @Override public DataType createdOutputType() { return UnresolvedDataType.INSTANCE; } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java index 64d6c0a5a199..c993dff65974 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeExpression.java @@ -27,6 +27,16 @@ public TokenizeExpression(Linguistics linguistics, AnnotatorConfig config) { public AnnotatorConfig getConfig() { return config; } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + return super.setInputType(input, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + return super.setOutputType(DataType.STRING, output, null, context); + } + @Override protected void doVerify(VerificationContext context) { // empty diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java index 78f89fabe460..b22fcefda471 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/TrimExpression.java @@ -18,6 +18,16 @@ protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + return super.setInputType(input, DataType.STRING, context); + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + return super.setOutputType(DataType.STRING, output, null, context); + } + @Override protected void doExecute(ExecutionContext context) { context.setCurrentValue(new StringFieldValue(String.valueOf(context.getCurrentValue()).trim())); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java index cfc7273d0901..fb69a1a13ef5 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationException.java @@ -4,35 +4,35 @@ /** * @author Simon Thoresen Hult */ -public class VerificationException extends RuntimeException { +public class VerificationException extends IllegalArgumentException { private final Class type; - private final String exp; + private final String expression; - public VerificationException(Expression exp, String msg) { - super(msg); - if (exp != null) { - this.type = exp.getClass(); - this.exp = exp.toString(); + public VerificationException(Expression expression, String message) { + super("Invalid expression '" + expression + "': " + message); + if (expression != null) { + this.type = expression.getClass(); + this.expression = expression.toString(); } else { this.type = null; - this.exp = "null"; + this.expression = "null"; } } - public VerificationException(Class exp, String msg) { - super(msg); - this.type = exp; - this.exp = exp.getName(); + public VerificationException(Class expression, String message) { + super("Invalid expression of type '" + expression.getSimpleName() + "': " + message); + this.type = expression; + this.expression = expression.getName(); } - public String getExpression() { return exp; } + public String getExpression() { return expression; } public Class getExpressionType() { return type; } @Override public String toString() { - return getClass().getName() + ": For expression '" + exp + "': " + getMessage(); + return getClass().getName() + ": " + getMessage(); } } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java index 9d6826ba2535..e9fb5ace622e 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveExpression.java @@ -3,6 +3,7 @@ import com.yahoo.document.DataType; import com.yahoo.document.PositionDataType; +import com.yahoo.document.StructDataType; import com.yahoo.document.datatypes.IntegerFieldValue; import com.yahoo.document.datatypes.LongFieldValue; import com.yahoo.document.datatypes.Struct; @@ -17,6 +18,30 @@ public ZCurveExpression() { super(PositionDataType.INSTANCE); } + @Override + public DataType setInputType(DataType input, VerificationContext context) { + if ( ! (input instanceof StructDataType struct)) + throw new VerificationException(this, "This requires a struct as input, but got " + input.getName()); + requireIntegerField(PositionDataType.FIELD_X, struct); + requireIntegerField(PositionDataType.FIELD_Y, struct); + + super.setInputType(input, context); + return DataType.LONG; + } + + private void requireIntegerField(String fieldName, StructDataType struct) { + var field = struct.getField(fieldName); + if (field == null || field.getDataType() != DataType.INT) + throw new VerificationException(this, "The struct '" + struct.getName() + + "' does not have an integer field named '" + fieldName + "'"); + } + + @Override + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(DataType.LONG, output, null, context); + return null; // There's no 'any' struct + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/EmbeddingScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/EmbeddingScriptTestCase.java index b46232f0f2e1..03afa347aa26 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/EmbeddingScriptTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/EmbeddingScriptTestCase.java @@ -246,7 +246,7 @@ public void testArrayEmbedTo3dMixedTensor_missingDimensionArgument() { fail("Expected exception"); } catch (VerificationException e) { - assertEquals("When the embedding target field is a 3d tensor the name of the tensor dimension that corresponds to the input array elements must be given as a second argument to embed, e.g: ... | embed colbert paragraph | ...", + assertEquals("Invalid expression 'embed emb1': When the embedding target field is a 3d tensor the name of the tensor dimension that corresponds to the input array elements must be given as a second argument to embed, e.g: ... | embed colbert paragraph | ...", e.getMessage()); } } @@ -268,7 +268,7 @@ public void testArrayEmbedTo3dMixedTensor_wrongDimensionArgument() { fail("Expected exception"); } catch (VerificationException e) { - assertEquals("The dimension 'd' given to embed is not a sparse dimension of the target type tensor(d[3],passage{},token{})", + assertEquals("Invalid expression 'embed emb1 d': The dimension 'd' given to embed is not a sparse dimension of the target type tensor(d[3],passage{},token{})", e.getMessage()); } } @@ -323,7 +323,7 @@ public void testArrayEmbedTo2dMappedTensor_wrongDimensionArgument() { fail("Expected exception"); } catch (VerificationException e) { - assertEquals("The dimension 'doh' given to embed is not a sparse dimension of the target type tensor(passage{},token{})", + assertEquals("Invalid expression 'embed emb1 doh': The dimension 'doh' given to embed is not a sparse dimension of the target type tensor(passage{},token{})", e.getMessage()); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java index 2a145ae79e3f..5afa432863f7 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java @@ -9,6 +9,7 @@ import com.yahoo.document.datatypes.Array; import com.yahoo.document.datatypes.BoolFieldValue; import com.yahoo.document.datatypes.IntegerFieldValue; +import com.yahoo.document.datatypes.LongFieldValue; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.vespa.indexinglanguage.expressions.*; import com.yahoo.vespa.indexinglanguage.parser.ParseException; @@ -60,7 +61,7 @@ public void requireThatEachStatementHasEmptyInput() { fail(); } catch (VerificationException e) { assertEquals(e.getExpressionType(), ScriptExpression.class); - assertEquals("Expected any input, but no input is specified", e.getMessage()); + assertEquals("Invalid expression '{ input in-1 | attribute out-1; attribute out-2; }': Expected any input, but no input is specified", e.getMessage()); } } @@ -102,14 +103,11 @@ public void testIntHash() throws ParseException { var intField = new Field("myInt", DataType.INT); adapter.createField(intField); adapter.setValue("myText", new StringFieldValue("input text")); - expression.setStatementOutput(new DocumentType("myDocument"), intField); - // Necessary to resolve output type VerificationContext verificationContext = new VerificationContext(adapter); assertEquals(DataType.INT, expression.verify(verificationContext)); ExecutionContext context = new ExecutionContext(adapter); - context.setCurrentValue(new StringFieldValue("input text")); expression.execute(context); assertTrue(adapter.values.containsKey("myInt")); assertEquals(-1425622096, adapter.values.get("myInt").getWrappedValue()); @@ -128,14 +126,11 @@ public void testIntArrayHash() throws ParseException { array.add(new StringFieldValue("first")); array.add(new StringFieldValue("second")); adapter.setValue("myTextArray", array); - expression.setStatementOutput(new DocumentType("myDocument"), intField); - // Necessary to resolve output type VerificationContext verificationContext = new VerificationContext(adapter); assertEquals(new ArrayDataType(DataType.INT), expression.verify(verificationContext)); ExecutionContext context = new ExecutionContext(adapter); - context.setCurrentValue(array); expression.execute(context); assertTrue(adapter.values.containsKey("myIntArray")); var intArray = (Array)adapter.values.get("myIntArray"); @@ -152,17 +147,39 @@ public void testLongHash() throws ParseException { var intField = new Field("myLong", DataType.LONG); adapter.createField(intField); adapter.setValue("myText", new StringFieldValue("input text")); - expression.setStatementOutput(new DocumentType("myDocument"), intField); - // Necessary to resolve output type VerificationContext verificationContext = new VerificationContext(adapter); assertEquals(DataType.LONG, expression.verify(verificationContext)); ExecutionContext context = new ExecutionContext(adapter); - context.setCurrentValue(new StringFieldValue("input text")); expression.execute(context); assertTrue(adapter.values.containsKey("myLong")); assertEquals(7678158186624760752L, adapter.values.get("myLong").getWrappedValue()); } + @SuppressWarnings("unchecked") + @Test + public void testZCurveArray() throws ParseException { + var expression = Expression.fromString("input location_str | for_each { to_pos } | for_each { zcurve } | attribute location_zcurve"); + + SimpleTestAdapter adapter = new SimpleTestAdapter(); + adapter.createField(new Field("location_str", DataType.getArray(DataType.STRING))); + var zcurveField = new Field("location_zcurve", DataType.getArray(DataType.LONG)); + adapter.createField(zcurveField); + var array = new Array(new ArrayDataType(DataType.STRING)); + array.add(new StringFieldValue("30;40")); + array.add(new StringFieldValue("50;60")); + adapter.setValue("location_str", array); + + VerificationContext verificationContext = new VerificationContext(adapter); + assertEquals(DataType.getArray(DataType.LONG), expression.verify(verificationContext)); + + ExecutionContext context = new ExecutionContext(adapter); + expression.execute(context); + assertTrue(adapter.values.containsKey("location_zcurve")); + var longArray = (Array)adapter.values.get("location_zcurve"); + assertEquals( 2516, longArray.get(0).getLong()); + assertEquals(4004, longArray.get(1).getLong()); + } + } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java index 8fe1fd59d093..158b5721fe7c 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleDocumentAdapterTestCase.java @@ -48,7 +48,7 @@ public void requireThatUnknownFieldsReturnNull() { adapter.getInputType(null, "foo"); fail(); } catch (VerificationException e) { - assertEquals("Input field 'foo' not found", e.getMessage()); + assertEquals("Invalid expression 'null': Input field 'foo' not found", e.getMessage()); } assertNull(adapter.getInputValue("foo")); } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java index bee0add22c07..54f888b42e80 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ArithmeticTestCase.java @@ -11,6 +11,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ArithmeticExpression.Operator; import static org.junit.Assert.*; +import static org.mockito.Mockito.verify; /** * @author Simon Thoresen Hult @@ -64,19 +65,19 @@ public void requireThatVerifyCallsAreForwarded() { SimpleExpression.newOutput(DataType.INT), null); assertVerifyThrows(SimpleExpression.newOutput(null), Operator.ADD, SimpleExpression.newOutput(DataType.INT), null, - "Attempting to perform arithmetic on a null value"); + "Expected any output, but no output is specified"); assertVerifyThrows(SimpleExpression.newOutput(DataType.INT), Operator.ADD, SimpleExpression.newOutput(null), null, - "Attempting to perform arithmetic on a null value"); + "Expected any output, but no output is specified"); assertVerifyThrows(SimpleExpression.newOutput(null), Operator.ADD, SimpleExpression.newOutput(null), null, - "Attempting to perform arithmetic on a null value"); + "Expected any output, but no output is specified"); assertVerifyThrows(SimpleExpression.newOutput(DataType.INT), Operator.ADD, SimpleExpression.newOutput(DataType.STRING), null, - "Attempting to perform unsupported arithmetic: [int] + [string]"); + "The second argument must be a number, but has type string"); assertVerifyThrows(SimpleExpression.newOutput(DataType.STRING), Operator.ADD, SimpleExpression.newOutput(DataType.STRING), null, - "Attempting to perform unsupported arithmetic: [string] + [string]"); + "The first argument must be a number, but has type string"); } @Test @@ -101,13 +102,13 @@ public void requireThatOperandsAreInputCompatible() { @Test public void requireThatResultIsCalculated() { for (int i = 0; i < 50; ++i) { - LongFieldValue lhs = new LongFieldValue(i); - LongFieldValue rhs = new LongFieldValue(100 - i); - assertResult(lhs, Operator.ADD, rhs, new LongFieldValue(lhs.getLong() + rhs.getLong())); - assertResult(lhs, Operator.SUB, rhs, new LongFieldValue(lhs.getLong() - rhs.getLong())); - assertResult(lhs, Operator.DIV, rhs, new LongFieldValue(lhs.getLong() / rhs.getLong())); - assertResult(lhs, Operator.MOD, rhs, new LongFieldValue(lhs.getLong() % rhs.getLong())); - assertResult(lhs, Operator.MUL, rhs, new LongFieldValue(lhs.getLong() * rhs.getLong())); + LongFieldValue left = new LongFieldValue(i); + LongFieldValue right = new LongFieldValue(100 - i); + assertResult(left, Operator.ADD, right, new LongFieldValue(left.getLong() + right.getLong())); + assertResult(left, Operator.SUB, right, new LongFieldValue(left.getLong() - right.getLong())); + assertResult(left, Operator.DIV, right, new LongFieldValue(left.getLong() / right.getLong())); + assertResult(left, Operator.MOD, right, new LongFieldValue(left.getLong() % right.getLong())); + assertResult(left, Operator.MUL, right, new LongFieldValue(left.getLong() * right.getLong())); } } @@ -195,11 +196,15 @@ private static void assertVerify(Expression lhs, Operator op, Expression rhs, Da private static void assertVerifyThrows(Expression lhs, Operator op, Expression rhs, DataType val, String expectedException) { + ArithmeticExpression expression = null; try { - new ArithmeticExpression(lhs, op, rhs).verify(val); - fail(); + expression = new ArithmeticExpression(lhs, op, rhs); + expression.verify(val); + fail("Expected exception"); } catch (VerificationException e) { - assertEquals(expectedException, e.getMessage()); + String expressionString = expression == null ? "of type '" + ArithmeticExpression.class.getSimpleName() + "'" + : "'" + expression + "'"; + assertEquals("Invalid expression " + expressionString + ": " + expectedException, e.getMessage()); } } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java index 680c27181c1b..6f33d66965c9 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/AttributeExpressionTestCase.java @@ -38,4 +38,5 @@ public void requireThatExpressionCanBeVerified() { public void requireThatExpressionCanBeExecuted() { assertExecute(new AttributeExpression("foo")); } + } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java index 2d4f7afc6dc2..01eed5fdc8bf 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64DecodeTestCase.java @@ -66,7 +66,7 @@ public void requireThatIllegalInputThrows() { public void requireThatExpressionCanBeVerified() { Expression exp = new Base64DecodeExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.LONG, exp, "Expected string input, got long"); + assertVerifyThrows("Invalid expression 'base64decode': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'base64decode': Expected string input, got long", DataType.LONG, exp); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java index 2f73c07d3c8e..4cc017113b85 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/Base64EncodeTestCase.java @@ -40,7 +40,7 @@ public void requireThatInputIsEncoded() { public void requireThatExpressionCanBeVerified() { Expression exp = new Base64EncodeExpression(); assertVerify(DataType.LONG, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected long input, but no input is specified"); - assertVerifyThrows(DataType.STRING, exp, "Expected long input, got string"); + assertVerifyThrows("Invalid expression 'base64encode': Expected long input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'base64encode': Expected long input, got string", DataType.STRING, exp); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java index 5a9634e4c3b3..4de31753695b 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/CatTestCase.java @@ -51,16 +51,16 @@ public void requireThatExpressionCanBeVerified() { new SimpleExpression(DataType.STRING), DataType.STRING); assertVerifyThrows(new SimpleExpression().setCreatedOutput(null), new SimpleExpression().setCreatedOutput(DataType.STRING), null, - "In SimpleExpression: Attempting to concatenate a null value"); + "Invalid expression 'SimpleExpression . SimpleExpression': In SimpleExpression: Attempting to concatenate a null value"); assertVerifyThrows(new SimpleExpression(DataType.STRING), new SimpleExpression(DataType.INT), null, - "Operands require conflicting input types, string vs int"); + "Invalid expression of type 'CatExpression': Operands require conflicting input types, string vs int"); assertVerifyThrows(new SimpleExpression(DataType.STRING), new SimpleExpression(DataType.STRING), null, - "Expected string input, but no input is specified"); + "Invalid expression 'SimpleExpression . SimpleExpression': Expected string input, but no input is specified"); assertVerifyThrows(new SimpleExpression(DataType.STRING), new SimpleExpression(DataType.STRING), DataType.INT, - "Expected string input, got int"); + "Invalid expression 'SimpleExpression . SimpleExpression': Expected string input, got int"); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java index 9bce89b62330..4d197527ec11 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/EchoTestCase.java @@ -53,6 +53,6 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new EchoExpression(); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'echo': Expected any input, but no input is specified", null, exp); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java index bff8b81d4fd1..7a861a8e5904 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExactTestCase.java @@ -84,8 +84,8 @@ public void requireThatEmptyStringsAreNotAnnotated() { public void requireThatExpressionCanBeVerified() { Expression exp = new ExactExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'exact': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'exact': Expected string input, got int", DataType.INT, exp); } private static void assertAnnotation(int expectedFrom, int expectedLen, StringFieldValue expectedVal, diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java index 755e47d64086..b20c737d6565 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssert.java @@ -13,44 +13,41 @@ */ class ExpressionAssert { - public static void assertVerifyCtx(VerificationContext ctx, Expression exp, DataType expectedValueAfter) { - assertEquals(expectedValueAfter, exp.verify(ctx)); + public static void assertVerifyCtx(Expression expression, DataType expectedValueAfter, VerificationContext context) { + assertEquals(expectedValueAfter, expression.verify(context)); } - public static void assertVerify(DataType valueBefore, Expression exp, DataType expectedValueAfter) { - assertVerifyCtx(new VerificationContext().setCurrentType(valueBefore), exp, expectedValueAfter); + public static void assertVerify(DataType valueBefore, Expression expression, DataType expectedValueAfter) { + assertVerifyCtx(expression, expectedValueAfter, new VerificationContext().setCurrentType(valueBefore)); } - public static void assertVerifyThrows(DataType valueBefore, Expression exp, String expectedException) { - assertVerifyCtxThrows(new VerificationContext().setCurrentType(valueBefore), exp, expectedException); + public static void assertVerifyThrows(String expectedMessage, DataType valueBefore, Expression expression) { + assertVerifyThrows(expectedMessage, expression, new VerificationContext().setCurrentType(valueBefore)); } interface CreateExpression { Expression create(); } - public static void assertVerifyThrows(DataType valueBefore, CreateExpression createExp, String expectedException) { - assertVerifyCtxThrows(new VerificationContext().setCurrentType(valueBefore), createExp, expectedException); + public static void assertVerifyThrows(String expectedMessage, DataType valueBefore, CreateExpression createExpression) { + assertVerifyThrows(expectedMessage, createExpression, new VerificationContext().setCurrentType(valueBefore)); } - public static void assertVerifyCtxThrows(VerificationContext ctx, CreateExpression createExp, String expectedException) { + public static void assertVerifyThrows(String expectedMessage, CreateExpression createExp, VerificationContext context) { try { Expression exp = createExp.create(); - exp.verify(ctx); - fail(); + exp.verify(context); + fail("Expected exception"); } catch (VerificationException e) { - if (!Pattern.matches(expectedException, e.getMessage())) { - assertEquals(expectedException, e.getMessage()); - } + assertEquals(expectedMessage, e.getMessage()); } } - public static void assertVerifyCtxThrows(VerificationContext ctx, Expression exp, String expectedException) { + public static void assertVerifyThrows(String expectedMessage, Expression expression, VerificationContext context) { try { - exp.verify(ctx); - fail(); + expression.verify(context); + fail("Expected exception"); } catch (VerificationException e) { - if (!Pattern.matches(expectedException, e.getMessage())) { - assertEquals(expectedException, e.getMessage()); - } + assertEquals(expectedMessage, e.getMessage()); } } + } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java index 4cfda8cefe7c..28de6e6a1b98 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionAssertTestCase.java @@ -25,8 +25,8 @@ public void requireThatAssertVerifyMethodThrowsWhenAppropriate() { thrown = null; try { - assertVerifyThrows(DataType.INT, new SimpleExpression(), - "unchecked expected exception message"); + assertVerifyThrows("unchecked expected exception message", DataType.INT, new SimpleExpression() + ); } catch (Throwable t) { thrown = t; } @@ -34,8 +34,8 @@ public void requireThatAssertVerifyMethodThrowsWhenAppropriate() { thrown = null; try { - assertVerifyThrows(DataType.INT, SimpleExpression.newRequired(DataType.STRING), - "wrong expected exception message"); + assertVerifyThrows("wrong expected exception message", DataType.INT, SimpleExpression.newRequired(DataType.STRING) + ); } catch (Throwable t) { thrown = t; } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java index 92f8fbbfd80d..9bc54e26645b 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionTestCase.java @@ -34,22 +34,22 @@ public void requireThatOutputTypeIsCheckedAfterExecute() { public void requireThatInputTypeIsCheckedBeforeVerify() { assertVerify(newRequiredInput(DataType.INT), DataType.INT); assertVerifyThrows(newRequiredInput(DataType.INT), null, - "Expected int input, but no input is specified"); + "Invalid expression 'SimpleExpression': Expected int input, but no input is specified"); assertVerifyThrows(newRequiredInput(DataType.INT), UnresolvedDataType.INSTANCE, - "Failed to resolve input type"); + "Invalid expression 'SimpleExpression': Failed to resolve input type"); assertVerifyThrows(newRequiredInput(DataType.INT), DataType.STRING, - "Expected int input, got string"); + "Invalid expression 'SimpleExpression': Expected int input, got string"); } @Test public void requireThatOutputTypeIsCheckedAfterVerify() { assertVerify(newCreatedOutput(DataType.INT, DataType.INT), null); assertVerifyThrows(newCreatedOutput(DataType.INT, (DataType)null), null, - "Expected int output, but no output is specified"); + "Invalid expression 'SimpleExpression': Expected int output, but no output is specified"); assertVerifyThrows(newCreatedOutput(DataType.INT, UnresolvedDataType.INSTANCE), null, - "Failed to resolve output type"); + "Invalid expression 'SimpleExpression': Failed to resolve output type"); assertVerifyThrows(newCreatedOutput(DataType.INT, DataType.STRING), null, - "Expected int output, got string"); + "Invalid expression 'SimpleExpression': Expected int output, got string"); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java index 4295917fcccf..5f0cc0818140 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/FlattenTestCase.java @@ -87,7 +87,7 @@ public void requireThatAnnotationsWithoutFieldValueUseOriginalSpan() { public void requireThatExpressionCanBeVerified() { Expression exp = new FlattenExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'flatten': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'flatten': Expected string input, got int", DataType.INT, exp); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java index 0ab1c90b7c55..e5f5a737b9dd 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ForEachTestCase.java @@ -53,9 +53,9 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ForEachExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING)); assertVerify(DataType.getArray(DataType.INT), exp, DataType.getArray(DataType.STRING)); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected Array, Struct, WeightedSet or Map input, got int"); - assertVerifyThrows(DataType.getArray(DataType.STRING), exp, "Expected int input, got string"); + assertVerifyThrows("Invalid expression 'for_each { SimpleExpression }': Expected any input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'for_each { SimpleExpression }': Expected Array, Struct, WeightedSet or Map input, got int", DataType.INT, exp); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.getArray(DataType.STRING), exp); } @Test @@ -63,10 +63,8 @@ public void requireThatStructFieldCompatibilityIsVerified() { StructDataType type = new StructDataType("my_struct"); type.addField(new Field("foo", DataType.INT)); assertVerify(type, new ForEachExpression(new SimpleExpression()), type); - assertVerifyThrows(type, new ForEachExpression(SimpleExpression.newConversion(DataType.STRING, DataType.INT)), - "Expected string input, got int"); - assertVerifyThrows(type, new ForEachExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING)), - "Expected int output, got string"); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected string input, got int", type, new ForEachExpression(SimpleExpression.newConversion(DataType.STRING, DataType.INT))); + assertVerifyThrows("Invalid expression 'for_each { SimpleExpression }': Expected int output, got string", type, new ForEachExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING))); } @Test @@ -215,7 +213,7 @@ public void requireThatIncompatibleStructFieldsFailToValidate() { new ForEachExpression(new ToArrayExpression()).verify(ctx); fail(); } catch (VerificationException e) { - assertEquals("Expected int output, got Array", e.getMessage()); + assertEquals("Invalid expression 'for_each { to_array }': Expected int output, got Array", e.getMessage()); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java index b3530cca1fe4..a95fa7646220 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldTestCase.java @@ -38,9 +38,9 @@ public void requireThatExpressionCanBeVerified() { type.addField(new Field("foo", DataType.STRING)); Expression exp = new GetFieldExpression("foo"); assertVerify(type, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected a struct or map, bit got an int"); - assertVerifyThrows(type, new GetFieldExpression("bar"), "Field 'bar' not found in struct type 'my_struct'"); + assertVerifyThrows("Invalid expression 'get_field foo': Expected any input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'get_field foo': Expected a struct or map, but got an int", DataType.INT, exp); + assertVerifyThrows("Invalid expression 'get_field bar': Field 'bar' not found in struct type 'my_struct'", type, new GetFieldExpression("bar")); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java index 20fdf562ff1c..f7f665947096 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GetVarTestCase.java @@ -41,7 +41,7 @@ public void requireThatExpressionCanBeVerified() { new GetVarExpression("bar").verify(ctx); fail(); } catch (VerificationException e) { - assertEquals("Variable 'bar' not found", e.getMessage()); + assertEquals("Invalid expression 'get_var bar': Variable 'bar' not found", e.getMessage()); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java index b8f73c14156c..19f0ef96a489 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/GuardTestCase.java @@ -46,8 +46,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new GuardExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING)); assertVerify(DataType.INT, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected int input, but no input is specified"); - assertVerifyThrows(DataType.STRING, exp, "Expected int input, got string"); + assertVerifyThrows("Invalid expression 'guard { SimpleExpression; }': Expected int input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'guard { SimpleExpression; }': Expected int input, got string", DataType.STRING, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java index 9cb8977afd7b..3d90e49722a0 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexDecodeTestCase.java @@ -29,8 +29,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new HexDecodeExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.LONG, exp, "Expected string input, got long"); + assertVerifyThrows("Invalid expression 'hexdecode': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'hexdecode': Expected string input, got long", DataType.LONG, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java index 670172319101..416d5cff37a7 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/HexEncodeTestCase.java @@ -29,8 +29,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new HexEncodeExpression(); assertVerify(DataType.LONG, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected long input, but no input is specified"); - assertVerifyThrows(DataType.STRING, exp, "Expected long input, got string"); + assertVerifyThrows("Invalid expression 'hexencode': Expected long input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'hexencode': Expected long input, got string", DataType.STRING, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java index 932652e65717..14e1fdbe268f 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IfThenTestCase.java @@ -41,29 +41,30 @@ public void requireThatRequiredInputTypeCompatibilityIsVerified() { Expression exp = newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, DataType.STRING, DataType.STRING); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); - assertVerifyThrows(null, () -> newRequiredInput(DataType.INT, Comparator.EQ, DataType.STRING, - DataType.STRING, DataType.STRING), - "Operands require conflicting input types, int vs string"); - assertVerifyThrows(null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.INT, - DataType.STRING, DataType.STRING), - "Operands require conflicting input types, string vs int"); - assertVerifyThrows(null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, - DataType.INT, DataType.STRING), - "Operands require conflicting input types, string vs int"); - assertVerifyThrows(null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, - DataType.STRING, DataType.INT), - "Operands require conflicting input types, string vs int"); + String prefix = "Invalid expression 'if (SimpleExpression == SimpleExpression) { SimpleExpression; } else { SimpleExpression; }': "; + assertVerifyThrows(prefix + "Expected string input, but no input is specified", null, exp); + assertVerifyThrows(prefix + "Expected string input, got int", DataType.INT, exp); + assertVerifyThrows("Invalid expression of type 'IfThenExpression': Operands require conflicting input types, int vs string", null, () -> newRequiredInput(DataType.INT, Comparator.EQ, DataType.STRING, + DataType.STRING, DataType.STRING) + ); + assertVerifyThrows("Invalid expression of type 'IfThenExpression': Operands require conflicting input types, string vs int", null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.INT, + DataType.STRING, DataType.STRING) + ); + assertVerifyThrows("Invalid expression of type 'IfThenExpression': Operands require conflicting input types, string vs int", null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, + DataType.INT, DataType.STRING) + ); + assertVerifyThrows("Invalid expression of type 'IfThenExpression': Operands require conflicting input types, string vs int", null, () -> newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, + DataType.STRING, DataType.INT) + ); } @Test public void requireThatExpressionCanBeVerified() { assertVerify(DataType.STRING, new FlattenExpression(), DataType.STRING); - assertVerifyThrows(null, new FlattenExpression(), - "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, new FlattenExpression(), - "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'flatten': Expected string input, but no input is specified", null, new FlattenExpression() + ); + assertVerifyThrows("Invalid expression 'flatten': Expected string input, got int", DataType.INT, new FlattenExpression() + ); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java index 28f8941064db..6f013fdccd92 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java @@ -40,7 +40,7 @@ public void requireThatExpressionCanBeVerified() { new InputExpression("bar").verify(adapter); fail(); } catch (VerificationException e) { - assertEquals("Input field 'bar' not found.", e.getMessage()); + assertEquals("Invalid expression 'input bar': Input field 'bar' not found.", e.getMessage()); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java index 7c997ecebf26..d2ca020d05ae 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/JoinTestCase.java @@ -36,8 +36,8 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new JoinExpression(";"); assertVerify(DataType.getArray(DataType.INT), exp, DataType.STRING); assertVerify(DataType.getArray(DataType.STRING), exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected Array input, got int"); + assertVerifyThrows("Invalid expression 'join \";\"': Expected any input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'join \";\"': Expected Array input, got type int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java index 49085ec7da18..b3ed2f5a5a31 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/LowerCaseTestCase.java @@ -28,8 +28,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new LowerCaseExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'lowercase': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'lowercase': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NGramTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NGramTestCase.java index a3ce6a204b24..d5db120b6356 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NGramTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NGramTestCase.java @@ -46,8 +46,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new NGramExpression(new SimpleLinguistics(), 69); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'ngram 69': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'ngram 69': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java index 2c8e2c029e2a..f89b838464bb 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/NormalizeTestCase.java @@ -43,8 +43,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new NormalizeExpression(new SimpleLinguistics()); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'normalize': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'normalize': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java index 750e3114b624..040308f52b48 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OptimizePredicateTestCase.java @@ -11,7 +11,7 @@ import org.mockito.Mockito; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyCtx; -import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyCtxThrows; +import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static org.junit.Assert.*; @@ -76,22 +76,24 @@ public void requireThatHashCodeAndEqualsAreImplemented() { @Test public void requireThatExpressionCanBeVerified() { Expression exp = new OptimizePredicateExpression(); - assertVerifyThrows(null, exp, "Expected predicate input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected predicate input, got int"); - assertVerifyThrows(DataType.PREDICATE, exp, "Variable 'arity' must be set"); + String prefix = "Invalid expression 'optimize_predicate': "; + assertVerifyThrows(prefix + "Expected predicate input, but no input is specified", null, exp); + assertVerifyThrows(prefix + "Expected predicate input, got int", DataType.INT, exp); + assertVerifyThrows(prefix + "Variable 'arity' must be set", DataType.PREDICATE, exp); VerificationContext context = new VerificationContext().setCurrentType(DataType.PREDICATE); context.setVariable("arity", DataType.STRING); - assertVerifyCtxThrows(context, exp, "Variable 'arity' must have type int"); + ExpressionAssert.assertVerifyThrows(prefix + "Variable 'arity' must have type int", exp, context); context.setVariable("arity", DataType.INT); - assertVerifyCtx(context, exp, DataType.PREDICATE); + assertVerifyCtx(exp, DataType.PREDICATE, context); context.setVariable("lower_bound", DataType.INT); - assertVerifyCtxThrows(context, exp, "Variable 'lower_bound' must have type long"); + ExpressionAssert.assertVerifyThrows(prefix + "Variable 'lower_bound' must have type long", exp, context); context.setVariable("lower_bound", DataType.LONG); - assertVerifyCtx(context, exp, DataType.PREDICATE); + assertVerifyCtx(exp, DataType.PREDICATE, context); context.setVariable("upper_bound", DataType.INT); - assertVerifyCtxThrows(context, exp, "Variable 'upper_bound' must have type long"); + ExpressionAssert.assertVerifyThrows(prefix + "Variable 'upper_bound' must have type long", exp, context); context.setVariable("upper_bound", DataType.LONG); - assertVerifyCtx(context, exp, DataType.PREDICATE); + assertVerifyCtx(exp, DataType.PREDICATE, context); } + } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java index 23cbc0ab9760..a4b19163dcd8 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/OutputAssert.java @@ -27,8 +27,8 @@ public static void assertExecute(OutputExpression exp) { public static void assertVerify(OutputExpression exp) { assertVerify(new MyAdapter(null), DataType.INT, exp); assertVerify(new MyAdapter(null), DataType.STRING, exp); - assertVerifyThrows(new MyAdapter(null), null, exp, "Expected any input, but no input is specified"); - assertVerifyThrows(new MyAdapter(new VerificationException((Expression) null, "foo")), DataType.INT, exp, "foo"); + assertVerifyThrows(new MyAdapter(null), null, exp, "Invalid expression '" + exp + "': Expected any input, but no input is specified"); + assertVerifyThrows(new MyAdapter(new VerificationException((Expression) null, "foo")), DataType.INT, exp, "Invalid expression 'null': foo"); } public static void assertVerify(FieldTypeAdapter adapter, DataType value, Expression exp) { diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java index 54aaba282742..6c997356bbfd 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ParenthesisTestCase.java @@ -37,8 +37,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ParenthesisExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING)); assertVerify(DataType.INT, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected int input, but no input is specified"); - assertVerifyThrows(DataType.STRING, exp, "Expected int input, got string"); + assertVerifyThrows("Invalid expression '(SimpleExpression)': Expected int input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression '(SimpleExpression)': Expected int input, got string", DataType.STRING, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java index 8491736c763f..14435b14c331 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java @@ -6,7 +6,6 @@ import com.yahoo.document.Field; import com.yahoo.document.StructDataType; import com.yahoo.document.datatypes.Array; -import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.IntegerFieldValue; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.document.datatypes.Struct; @@ -62,12 +61,12 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = newScript(newStatement(SimpleExpression.newConversion(DataType.INT, DataType.STRING))); assertVerify(DataType.INT, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected int input, but no input is specified"); - assertVerifyThrows(DataType.STRING, exp, "Expected int input, got string"); + assertVerifyThrows("Invalid expression '{ SimpleExpression; }': Expected int input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression '{ SimpleExpression; }': Expected int input, got string", DataType.STRING, exp); - assertVerifyThrows(null, () -> newScript(newStatement(SimpleExpression.newConversion(DataType.INT, DataType.STRING)), - newStatement(SimpleExpression.newConversion(DataType.STRING, DataType.INT))), - "Statements require conflicting input types, int vs string"); + assertVerifyThrows("Invalid expression of type 'ScriptExpression': Statements require conflicting input types, int vs string", null, () -> newScript(newStatement(SimpleExpression.newConversion(DataType.INT, DataType.STRING)), + newStatement(SimpleExpression.newConversion(DataType.STRING, DataType.INT))) + ); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java index 22bb6085c254..1820eea893dd 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java @@ -58,9 +58,9 @@ public void requireThatExpressionCanBeVerified() { assertVerify(adapter, DataType.STRING, exp); assertVerifyThrows(adapter, newSelectInput(new AttributeExpression("my_int"), "my_str"), - "Can not assign string to field 'my_int' which is int."); + "Invalid expression 'attribute my_int': Can not assign string to field 'my_int' which is int."); assertVerifyThrows(adapter, newSelectInput(new AttributeExpression("my_int"), "my_unknown"), - "Input field 'my_unknown' not found."); + "Invalid expression 'select_input { my_unknown: attribute my_int; }': Input field 'my_unknown' not found."); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java index fae3b0c25d4e..5878f89e463b 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetLanguageTestCase.java @@ -29,8 +29,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SetLanguageExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'set_language': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'set_language': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java index 4364865ba24e..261ecd797d3a 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SetVarTestCase.java @@ -36,13 +36,13 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new SetVarExpression("foo"); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'set_var foo': Expected any input, but no input is specified", null, exp); try { new VerificationContext().setVariable("foo", DataType.INT).setCurrentType(DataType.STRING).verify(exp); fail(); } catch (VerificationException e) { - assertEquals("Attempting to assign conflicting types to variable 'foo', int vs string", e.getMessage()); + assertEquals("Invalid expression 'set_var foo': Cannot set variable 'foo' to type string: It is already set to type int", e.getMessage()); } } @@ -69,7 +69,7 @@ public void requireThatVariableTypeCanNotChange() { fail(); } catch (VerificationException e) { assertTrue(e.getExpressionType().equals(SetVarExpression.class)); - assertEquals("Attempting to assign conflicting types to variable 'out', int vs string", e.getMessage()); + assertEquals("Invalid expression 'set_var out': Cannot set variable 'out' to type string: It is already set to type int", e.getMessage()); } } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java index e7df02f9be25..79dd93985488 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SplitTestCase.java @@ -37,8 +37,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SplitExpression(";"); assertVerify(DataType.STRING, exp, DataType.getArray(DataType.STRING)); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'split \";\"': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'split \";\"': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java index f134191c4a3d..351df9af3fcf 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/StatementTestCase.java @@ -91,9 +91,9 @@ public void requireThatCreatedOutputIsDeterminedByLastNonNullCreatedOutput() { public void requireThatInternalVerificationIsPerformed() { Expression exp = newStatement(SimpleExpression.newOutput(DataType.STRING), SimpleExpression.newConversion(DataType.INT, DataType.STRING)); - assertVerifyThrows(null, exp, "Expected int input, got string"); - assertVerifyThrows(DataType.INT, exp, "Expected int input, got string"); - assertVerifyThrows(DataType.STRING, exp, "Expected int input, got string"); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", null, exp); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.INT, exp); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.STRING, exp); exp = newStatement(SimpleExpression.newOutput(DataType.INT), SimpleExpression.newConversion(DataType.INT, DataType.STRING)); diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java index f05e3117ba70..2c3a2ee4df88 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SubstringTestCase.java @@ -37,8 +37,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SubstringExpression(6, 9); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'substring 6 9': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'substring 6 9': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java index dddc8762b553..05f597ed563d 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SwitchTestCase.java @@ -57,18 +57,18 @@ public void requireThatExpressionCanBeVerified() { Expression foo = SimpleExpression.newConversion(DataType.STRING, DataType.INT); Expression exp = new SwitchExpression(Map.of("foo", foo)); assertVerify(DataType.STRING, exp, DataType.STRING); // does not touch output - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'switch { case \"foo\": SimpleExpression; }': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'switch { case \"foo\": SimpleExpression; }': Expected string input, got int", DataType.INT, exp); } @Test public void requireThatCasesAreVerified() { Map cases = new HashMap<>(); cases.put("foo", SimpleExpression.newRequired(DataType.INT)); - assertVerifyThrows(DataType.STRING, new SwitchExpression(cases), - "Expected int input, got string"); - assertVerifyThrows(DataType.STRING, new SwitchExpression(Map.of(), SimpleExpression.newRequired(DataType.INT)), - "Expected int input, got string"); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.STRING, new SwitchExpression(cases) + ); + assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.STRING, new SwitchExpression(Map.of(), SimpleExpression.newRequired(DataType.INT)) + ); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java index 21c399b8c0cf..02e9335e90e5 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ThisTestCase.java @@ -29,7 +29,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ThisExpression(); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'this': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java index d1ac4152ee36..e249b0e8b6de 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToArrayTestCase.java @@ -33,7 +33,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToArrayExpression(); assertVerify(DataType.INT, exp, DataType.getArray(DataType.INT)); assertVerify(DataType.STRING, exp, DataType.getArray(DataType.STRING)); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_array': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolTestCase.java index 6ac0a27c11be..fc1d148b502b 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToBoolTestCase.java @@ -33,7 +33,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToBoolExpression(); assertVerify(DataType.INT, exp, DataType.BOOL); assertVerify(DataType.STRING, exp, DataType.BOOL); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_bool': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java index 43f53c45e09c..5bb083bfa835 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToByteTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToByteExpression(); assertVerify(DataType.INT, exp, DataType.BYTE); assertVerify(DataType.STRING, exp, DataType.BYTE); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_byte': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java index 60d3f83c77a1..f83f1b41b627 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToDoubleTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToDoubleExpression(); assertVerify(DataType.INT, exp, DataType.DOUBLE); assertVerify(DataType.STRING, exp, DataType.DOUBLE); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_double': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpressionTestCase.java index 0a98ef845996..3a5ee328c172 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToEpochSecondExpressionTestCase.java @@ -27,8 +27,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ToEpochSecondExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_epoch_second': Expected string input, got int", DataType.INT, exp); + assertVerifyThrows("Invalid expression 'to_epoch_second': Expected string input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java index 32677aea11f5..c1c8d7601468 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToFloatTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToFloatExpression(); assertVerify(DataType.INT, exp, DataType.FLOAT); assertVerify(DataType.STRING, exp, DataType.FLOAT); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_float': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java index 6812848e0100..c611c4780fcd 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToIntegerTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToIntegerExpression(); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.INT); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_int': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java index d88d1c6691d7..79b3036e1f9e 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToLongTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToLongExpression(); assertVerify(DataType.INT, exp, DataType.LONG); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_long': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java index bd7fe2fe78bc..6c0a5259e205 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToPositionTestCase.java @@ -31,8 +31,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ToPositionExpression(); assertVerify(DataType.STRING, exp, PositionDataType.INSTANCE); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'to_pos': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'to_pos': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java index 70ef2302e099..ed41629ae9c3 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToStringTestCase.java @@ -30,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToStringExpression(); assertVerify(DataType.INT, exp, DataType.STRING); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression 'to_string': Expected any input, but no input is specified", null, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java index 0c86fc462209..a6f04d1e841c 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ToWsetTestCase.java @@ -55,22 +55,23 @@ public void requireThatValueIsConverted() { } private static void assertVerify(boolean createIfNonExistent, boolean removeIfZero) { - Expression exp = new ToWsetExpression(createIfNonExistent, removeIfZero); - ExpressionAssert.assertVerify(DataType.INT, exp, + Expression expression = new ToWsetExpression(createIfNonExistent, removeIfZero); + ExpressionAssert.assertVerify(DataType.INT, expression, DataType.getWeightedSet(DataType.INT, createIfNonExistent, removeIfZero)); - ExpressionAssert.assertVerify(DataType.STRING, exp, + ExpressionAssert.assertVerify(DataType.STRING, expression, DataType.getWeightedSet(DataType.STRING, createIfNonExistent, removeIfZero)); - assertVerifyThrows(null, exp, "Expected any input, but no input is specified"); + assertVerifyThrows("Invalid expression '" + expression + "': " + + "Expected any input, but no input is specified", null, expression); } private static void assertConvert(boolean createIfNonExistent, boolean removeIfZero) { - ExecutionContext ctx = new ExecutionContext(new SimpleTestAdapter()); - ctx.setCurrentValue(new StringFieldValue("69")).execute(new ToWsetExpression(createIfNonExistent, removeIfZero)); + ExecutionContext context = new ExecutionContext(new SimpleTestAdapter()); + context.setCurrentValue(new StringFieldValue("69")).execute(new ToWsetExpression(createIfNonExistent, removeIfZero)); - FieldValue val = ctx.getCurrentValue(); - assertEquals(WeightedSet.class, val.getClass()); + FieldValue value = context.getCurrentValue(); + assertEquals(WeightedSet.class, value.getClass()); - WeightedSet wset = (WeightedSet)val; + WeightedSet wset = (WeightedSet)value; WeightedSetDataType type = wset.getDataType(); assertEquals(DataType.STRING, type.getNestedType()); assertEquals(createIfNonExistent, type.createIfNonExistent()); @@ -81,8 +82,9 @@ private static void assertConvert(boolean createIfNonExistent, boolean removeIfZ } private static void assertAccessors(boolean createIfNonExistent, boolean removeIfZero) { - ToWsetExpression exp = new ToWsetExpression(createIfNonExistent, removeIfZero); - assertEquals(createIfNonExistent, exp.getCreateIfNonExistent()); - assertEquals(removeIfZero, exp.getRemoveIfZero()); + ToWsetExpression expression = new ToWsetExpression(createIfNonExistent, removeIfZero); + assertEquals(createIfNonExistent, expression.getCreateIfNonExistent()); + assertEquals(removeIfZero, expression.getRemoveIfZero()); } + } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java index 9c0b17dd1220..3d09d19295d5 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TokenizeTestCase.java @@ -48,8 +48,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new TokenizeExpression(new SimpleLinguistics(), new AnnotatorConfig()); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'tokenize': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'tokenize': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java index c2f9d4b126f4..024e2f17b382 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/TrimTestCase.java @@ -28,8 +28,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new TrimExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows(null, exp, "Expected string input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected string input, got int"); + assertVerifyThrows("Invalid expression 'trim': Expected string input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'trim': Expected string input, got int", DataType.INT, exp); } @Test diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java index ad94c4592900..1a82d9cb9749 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationExceptionTestCase.java @@ -15,7 +15,7 @@ public void requireThatAccessorsWork() { Expression exp = new SimpleExpression(); VerificationException e = new VerificationException(exp, "foo"); assertEquals(exp.toString(), e.getExpression()); - assertEquals("foo", e.getMessage()); + assertEquals("Invalid expression 'SimpleExpression': foo", e.getMessage()); assertTrue(e.toString().contains(exp.toString())); assertTrue(e.toString().contains(e.getMessage())); } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java index 4babd49dbc29..33c255d3f1da 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ZCurveTestCase.java @@ -30,8 +30,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ZCurveExpression(); assertVerify(PositionDataType.INSTANCE, exp, DataType.LONG); - assertVerifyThrows(null, exp, "Expected position input, but no input is specified"); - assertVerifyThrows(DataType.INT, exp, "Expected position input, got int"); + assertVerifyThrows("Invalid expression 'zcurve': Expected position input, but no input is specified", null, exp); + assertVerifyThrows("Invalid expression 'zcurve': Expected position input, got int", DataType.INT, exp); } @Test