diff --git a/config-model/src/main/java/com/yahoo/schema/processing/IndexingInputs.java b/config-model/src/main/java/com/yahoo/schema/processing/IndexingInputs.java index b29f76393fca..a5ba67d69769 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/IndexingInputs.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/IndexingInputs.java @@ -75,7 +75,7 @@ protected boolean shouldConvert(Expression exp) { @Override protected Expression doConvert(Expression exp) { - if (exp.requiresInput()) { + if (exp.requiredInputType() != null) { return new StatementExpression(new InputExpression(field.getName()), exp); } else { return exp; 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 dcb54f3ebb5b..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': " + - "Invalid expression 'tokenize normalize stem:\"BEST\"': Expected string input, but no input is provided", + "Invalid expression '{ tokenize normalize stem:\"BEST\" | index foo; }': Expected string input, but no input is specified", Exceptions.toMessageString(e)); } } @@ -156,8 +156,8 @@ void testNoInputInDerivedField() throws ParseException { fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("For schema 'test', field 'derived1': Invalid expression 'attribute derived1': " + - "Expected int input, but no input is provided", + assertEquals("For schema 'test', field 'derived1': Invalid expression '{ attribute derived1; }': " + + "Expected any input, but no input is specified", Exceptions.toMessageString(e)); } } 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 8016d3d0bf60..dd7ccd360937 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 @@ -3,13 +3,7 @@ import com.yahoo.document.DataType; import com.yahoo.document.NumericDataType; -import com.yahoo.document.datatypes.ByteFieldValue; -import com.yahoo.document.datatypes.DoubleFieldValue; -import com.yahoo.document.datatypes.FieldValue; -import com.yahoo.document.datatypes.FloatFieldValue; -import com.yahoo.document.datatypes.IntegerFieldValue; -import com.yahoo.document.datatypes.LongFieldValue; -import com.yahoo.document.datatypes.NumericFieldValue; +import com.yahoo.document.datatypes.*; import com.yahoo.vespa.indexinglanguage.ExpressionConverter; import com.yahoo.vespa.objects.ObjectOperation; import com.yahoo.vespa.objects.ObjectPredicate; @@ -54,14 +48,12 @@ public String toString() { private final Expression right; public ArithmeticExpression(Expression left, Operator op, Expression right) { + super(requiredInputType(left, right)); this.left = Objects.requireNonNull(left); this.op = Objects.requireNonNull(op); this.right = Objects.requireNonNull(right); } - @Override - public boolean requiresInput() { return false; } - public Expression getLeftHandSide() { return left; } public Operator getOperator() { return op; } @@ -83,21 +75,15 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType == null) return null; super.setOutputType(outputType, context); DataType leftInput = left.setOutputType(AnyNumericDataType.instance, context); DataType rightInput = right.setOutputType(AnyNumericDataType.instance, context); - - if (leftInput == null) return getInputType(context); - if (rightInput == null) return getInputType(context); if (leftInput.isAssignableTo(rightInput)) return rightInput; else if (rightInput.isAssignableTo(leftInput)) return leftInput; else - throw new VerificationException(this, "The left argument requires " + leftInput.getName() + - ", while the right argument requires " + rightInput.getName() + - ": These are incompatible"); + return getInputType(context); } @Override @@ -132,6 +118,17 @@ protected void doExecute(ExecutionContext context) { context.setCurrentValue(input).execute(right).getCurrentValue())); } + 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, " + + leftType.getName() + " vs " + rightType.getName()); + return leftType; + } + @Override public DataType createdOutputType() { return UnresolvedDataType.INSTANCE; 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 ebe61bcdf390..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 @@ -2,6 +2,7 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; +import com.yahoo.document.TensorDataType; import com.yahoo.document.datatypes.LongFieldValue; import java.util.Base64; @@ -11,6 +12,10 @@ */ public final class Base64DecodeExpression extends Expression { + public Base64DecodeExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, DataType.STRING, context); 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 e8a4621de9c2..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 @@ -12,6 +12,10 @@ */ public final class Base64EncodeExpression extends Expression { + public Base64EncodeExpression() { + super(DataType.LONG); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, DataType.LONG, context); 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 a6401701afc2..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 @@ -27,6 +27,7 @@ public class BinarizeExpression extends Expression { * @param threshold the value which the tensor cell value must be larger than to be set to 1 and not 0. */ public BinarizeExpression(double threshold) { + super(TensorDataType.any()); this.threshold = threshold; } 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 6483dd62f238..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 @@ -12,6 +12,10 @@ */ public final class BusyWaitExpression extends Expression { + public BusyWaitExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override protected void doExecute(ExecutionContext context) { FieldValue value = context.getCurrentValue(); 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 c9fee28efa6b..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; @@ -11,16 +12,13 @@ import com.yahoo.document.datatypes.WeightedSet; import com.yahoo.vespa.indexinglanguage.ExpressionConverter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.*; import java.util.List; /** * @author Simon Thoresen Hult */ -// TODO: Support Map in addition to Array and Weghted Set (doc just says "collection type") +// 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) { @@ -28,12 +26,9 @@ public CatExpression(Expression... expressions) { } public CatExpression(Collection expressions) { - super(expressions); + super(expressions, resolveInputType(expressions)); } - @Override - public boolean requiresInput() { return false; } - @Override public CatExpression convertChildren(ExpressionConverter converter) { return new CatExpression(convertChildList(converter)); @@ -47,14 +42,11 @@ public DataType setInputType(DataType inputType, VerificationContext context) { for (var expression : expressions()) outputTypes.add(expression.setInputType(inputType, context)); DataType outputType = resolveOutputType(outputTypes); - if (outputType == null) outputType = getOutputType(context); // TODO: Remove this line - super.setOutputType(outputType, context); - return outputType; + return outputType != null ? outputType : getOutputType(context); } @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType == null) return null; if (outputType != DataType.STRING && ! (outputType instanceof CollectionDataType)) throw new VerificationException(this, "Required to produce " + outputType.getName() + ", but this produces a string or collection"); @@ -84,15 +76,43 @@ protected void doVerify(VerificationContext context) { @Override protected void doExecute(ExecutionContext context) { FieldValue input = context.getCurrentValue(); + DataType inputType = input != null ? input.getDataType() : null; + VerificationContext verificationContext = new VerificationContext(context.getFieldValue()); + context.fillVariableTypes(verificationContext); List values = new LinkedList<>(); - for (Expression expression : this) - values.add(context.setCurrentValue(input).execute(expression).getCurrentValue()); - DataType type = getOutputType(); - if (type == null) - throw new RuntimeException("Output type is not resolved in " + this); + List types = new LinkedList<>(); + for (Expression expression : this) { + FieldValue val = context.setCurrentValue(input).execute(expression).getCurrentValue(); + values.add(val); + + DataType type; + if (val != null) { + type = val.getDataType(); + } else { + type = verificationContext.setCurrentType(inputType).verify(this).getCurrentType(); + } + types.add(type); + } + DataType type = resolveOutputType(types); context.setCurrentValue(type == DataType.STRING ? asString(values) : asCollection(type, values)); } + private static DataType resolveInputType(Collection list) { + DataType prev = null; + for (Expression exp : list) { + DataType next = exp.requiredInputType(); + if (next == null) { + // ignore + } else if (prev == null) { + prev = next; + } else if (!prev.isAssignableFrom(next)) { + throw new VerificationException(CatExpression.class, "Operands require conflicting input types, " + + prev.getName() + " vs " + next.getName()); + } + } + return prev; + } + @Override public DataType createdOutputType() { return UnresolvedDataType.INSTANCE; 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 7a2f15d829af..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; @@ -28,7 +30,7 @@ public ChoiceExpression(Expression... choices) { } public ChoiceExpression(Collection choices) { - super(choices); + super(choices, resolveInputType(choices)); } @Override @@ -82,6 +84,27 @@ protected void doExecute(ExecutionContext context) { } } + private static DataType resolveInputType(Collection list) { + DataType previousInput = null; + DataType previousOutput = null; + for (Expression choice : list) { + DataType thisInput = choice.requiredInputType(); + if (previousInput == null) + previousInput = thisInput; + else if (thisInput != null && !previousInput.isAssignableFrom(thisInput)) + throw new VerificationException(ScriptExpression.class, "Choice expression require conflicting input types, " + + previousInput.getName() + " vs " + thisInput.getName()); + + DataType thisOutput = choice.createdOutputType(); + if (previousOutput == null) + previousOutput = thisOutput; + else if (thisOutput != null && !previousOutput.isAssignableFrom(thisOutput)) + throw new VerificationException(ScriptExpression.class, "Choice expression produce conflicting output types, " + + previousOutput.getName() + " vs " + thisOutput.getName()); + } + return previousInput; + } + @Override public DataType createdOutputType() { return UnresolvedDataType.INSTANCE; diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java index 051888daa1ee..48b69525df52 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateExpression.java @@ -8,8 +8,9 @@ */ public final class ClearStateExpression extends Expression { - @Override - public boolean requiresInput() { return false; } + public ClearStateExpression() { + super(null); + } @Override protected void doVerify(VerificationContext context) { diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java index ec4bb409e241..df1ec9e39b44 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/CompositeExpression.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.DataType; import com.yahoo.vespa.indexinglanguage.ExpressionConverter; /** @@ -11,6 +12,10 @@ public abstract class CompositeExpression extends Expression { @Override public abstract CompositeExpression convertChildren(ExpressionConverter converter); + protected CompositeExpression(DataType inputType) { + super(inputType); + } + protected static String toScriptBlock(Expression exp) { if (exp instanceof ScriptExpression) return exp.toString(); 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 1220576fe77a..d7af8669b3a4 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 @@ -17,12 +17,10 @@ public final class ConstantExpression extends Expression { private final FieldValue value; public ConstantExpression(FieldValue value) { + super(null); this.value = Objects.requireNonNull(value); } - @Override - public boolean requiresInput() { return false; } - public FieldValue getValue() { return value; } @Override @@ -33,11 +31,10 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType != null && ! value.getDataType().isAssignableTo(outputType)) + if ( ! value.getDataType().isAssignableTo(outputType)) throw new VerificationException(this, "Produces type " + value.getDataType().getName() + ", but type " + outputType.getName() + " is required"); - super.setOutputType(outputType, context); - return null; + return super.setOutputType(outputType, context); } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java index 1f22989b21cf..173f086d9231 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/EchoExpression.java @@ -17,6 +17,7 @@ public EchoExpression() { } public EchoExpression(PrintStream out) { + super(UnresolvedDataType.INSTANCE); this.out = out; } 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 e62aac1e52d0..71e471b03a66 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 @@ -39,6 +39,7 @@ public class EmbedExpression extends Expression { private TensorType targetType; public EmbedExpression(Linguistics linguistics, Map embedders, String embedderId, List embedderArguments) { + super(null); this.linguistics = linguistics; this.embedderId = embedderId; this.embedderArguments = List.copyOf(embedderArguments); @@ -64,13 +65,12 @@ else if ( ! embedders.containsKey(embedderId)) { } @Override - public DataType setInputType(DataType inputType, VerificationContext context) { - super.setInputType(inputType, context); - if ( inputType != null && - ! (inputType == DataType.STRING) && - ! (inputType instanceof ArrayDataType array && array.getNestedType() == DataType.STRING)) + public DataType setInputType(DataType type, VerificationContext context) { + super.setInputType(type, context); + if ( ! (type == DataType.STRING) && + ! (type instanceof ArrayDataType array && array.getNestedType() == DataType.STRING)) throw new VerificationException(this, "This requires either a string or array input type, but got " + - inputType.getName()); + type.getName()); return getOutputType(context); // embed cannot determine the output type from the input } 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 cb1df30983d7..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 @@ -26,6 +26,7 @@ public final class ExactExpression extends Expression { private final int maxTokenLength; private ExactExpression(OptionalInt maxTokenLength) { + super(DataType.STRING); this.maxTokenLength = maxTokenLength.isPresent() ? maxTokenLength.getAsInt() : AnnotatorConfig.getDefaultMaxTokenLength(); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionValueExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionValueExpression.java index 0292b638f8f3..363f08ddb07e 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionValueExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExecutionValueExpression.java @@ -2,6 +2,14 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; +import com.yahoo.document.DocumentType; +import com.yahoo.document.FieldPath; +import com.yahoo.vespa.objects.ObjectOperation; +import com.yahoo.vespa.objects.ObjectPredicate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * Returns the current execution value, that is the value passed to this expression. @@ -12,6 +20,10 @@ */ public final class ExecutionValueExpression extends Expression { + public ExecutionValueExpression() { + super(null); + } + @Override protected void doExecute(ExecutionContext context) { // Noop: Set the output execution value to the current execution value 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 1320776cf8c9..c1c2b5fc68fd 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 @@ -10,14 +10,7 @@ import com.yahoo.language.Linguistics; import com.yahoo.language.process.Embedder; import com.yahoo.language.simple.SimpleLinguistics; -import com.yahoo.vespa.indexinglanguage.AdapterFactory; -import com.yahoo.vespa.indexinglanguage.DocumentAdapter; -import com.yahoo.vespa.indexinglanguage.DocumentTypeAdapter; -import com.yahoo.vespa.indexinglanguage.ExpressionConverter; -import com.yahoo.vespa.indexinglanguage.ScriptParser; -import com.yahoo.vespa.indexinglanguage.ScriptParserContext; -import com.yahoo.vespa.indexinglanguage.SimpleAdapterFactory; -import com.yahoo.vespa.indexinglanguage.UpdateAdapter; +import com.yahoo.vespa.indexinglanguage.*; import com.yahoo.vespa.indexinglanguage.parser.IndexingInput; import com.yahoo.vespa.indexinglanguage.parser.ParseException; import com.yahoo.vespa.objects.Selectable; @@ -25,23 +18,26 @@ import java.util.Map; /** - * Superclass of expressions. - * - * Rules: - * - All expressions produce an output. - * - Expressions that does not require an input overrides requiresInput() to return false - * * @author Simon Thoresen Hult - * @author bratseth */ public abstract class Expression extends Selectable { + private final DataType requiredInputType; + // Input and output types resolved during verification private DataType inputType; private DataType outputType; - /** Returns whether this expression requires an input value. */ - public boolean requiresInput() { return true; } + /** + * Creates an expression + * + * @param requiredInputType the type of the input this expression can work with. + * UnresolvedDataType.INSTANCE if it works with any type, + * and null if it does not consume any input. + */ + protected Expression(DataType requiredInputType) { + this.requiredInputType = requiredInputType; + } /** * Returns an expression where the children of this has been converted using the given converter. @@ -52,6 +48,8 @@ public abstract class Expression extends Selectable { /** Sets the document type and field the statement this expression is part of will write to */ public void setStatementOutput(DocumentType documentType, Field field) {} + public final DataType requiredInputType() { return requiredInputType; } + public DataType getInputType(VerificationContext context) { return inputType; } /** @@ -66,10 +64,8 @@ public void setStatementOutput(DocumentType documentType, Field field) {} * @throws IllegalArgumentException if inputType isn't assignable to requiredType */ protected final DataType setInputType(DataType inputType, DataType requiredType, VerificationContext context) { - if (requiredType != null && inputType == null) - throw new VerificationException(this, "Expected " + requiredType.getName() + " input, but no input is provided"); - if (requiredType != null && ! (inputType.isAssignableTo(requiredType))) - throw new VerificationException(this, "Expected " + requiredType.getName() + " input, got " + inputType.getName()); + if ( ! (inputType.isAssignableTo(requiredType))) + throw new VerificationException(this, "This requires type " + requiredType.getName() + ", but gets " + inputType.getName()); return assignInputType(inputType); } @@ -147,10 +143,21 @@ private DataType assignOutputType(DataType outputType) { public abstract DataType createdOutputType(); + /** Implementations that don't change the type should implement this to do verification. */ + protected void doVerify(VerificationContext context) {} + + public final DataType verify() { + return verify(new VerificationContext()); + } + public final void verify(DocumentType type) { verify(new DocumentTypeAdapter(type)); } + public final DataType verify(DataType val) { + return verify(new VerificationContext().setCurrentType(val)); + } + public final Document verify(Document doc) { return verify(new SimpleAdapterFactory(), doc); } @@ -193,12 +200,36 @@ public final DataType verify(FieldTypeAdapter adapter) { } public final DataType verify(VerificationContext context) { + if (requiredInputType != null) { + DataType input = context.getCurrentType(); + if (input == null) { + throw new VerificationException(this, "Expected " + requiredInputType.getName() + " input, but no input is specified"); + } + if (input.getPrimitiveType() == UnresolvedDataType.INSTANCE) { + throw new VerificationException(this, "Failed to resolve input type"); + } + if (!requiredInputType.isAssignableFrom(input)) { + throw new VerificationException(this, "Expected " + requiredInputType.getName() + " input, got " + + input.getName()); + } + } doVerify(context); + DataType outputType = createdOutputType(); + if (outputType != null) { + DataType output = context.getCurrentType(); + if (output == null) { + throw new VerificationException(this, "Expected " + outputType.getName() + " output, but no output is specified"); + } + if (output.getPrimitiveType() == UnresolvedDataType.INSTANCE) { + throw new VerificationException(this, "Failed to resolve output type"); + } + if (!outputType.isAssignableFrom(output)) { + throw new VerificationException(this, "Expected " + outputType.getName() + " output, got " + output.getName()); + } + } return context.getCurrentType(); } - protected void doVerify(VerificationContext context) {} - public final FieldValue execute(FieldValue val) { return execute(new ExecutionContext().setCurrentValue(val)); } @@ -240,9 +271,15 @@ public final FieldValue execute(FieldValueAdapter adapter) { } public final FieldValue execute(ExecutionContext context) { - if (requiresInput()) { + DataType inputType = requiredInputType(); + if (inputType != null) { FieldValue input = context.getCurrentValue(); if (input == null) return null; + + if (!inputType.isValueCompatible(input)) { + throw new IllegalArgumentException("Expression '" + this + "' expected " + inputType.getName() + + " input, got " + input.getDataType().getName()); + } } doExecute(context); DataType outputType = createdOutputType(); @@ -284,7 +321,6 @@ protected static boolean equals(Object lhs, Object rhs) { // Convenience For testing public static Document execute(Expression expression, Document doc) { - expression.verify(doc); return expression.execute(new SimpleAdapterFactory(), doc); } @@ -298,18 +334,12 @@ public final FieldValue execute() { protected DataType mostGeneralOf(DataType left, DataType right) { if (left == null || right == null) return null; - if (left.isAssignableTo(right)) return right; - if (right.isAssignableTo(left)) return left; - throw new VerificationException(this, "Argument types are incompatible. " + - "First " + left.getName() + ", second: " + right.getName()); + return left.isAssignableTo(right) ? right : left; } protected DataType leastGeneralOf(DataType left, DataType right) { if (left == null || right == null) return null; - if (left.isAssignableTo(right)) return left; - if (right.isAssignableTo(left)) return right; - throw new VerificationException(this, "Argument types are incompatible. " + - "First " + left.getName() + ", second: " + right.getName()); + return left.isAssignableTo(right) ? left : right; } } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java index d6298edbdad4..a31e09e59e1e 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ExpressionList.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.DataType; import com.yahoo.document.DocumentType; import com.yahoo.document.Field; import com.yahoo.vespa.indexinglanguage.ExpressionConverter; @@ -20,14 +21,13 @@ public abstract class ExpressionList extends CompositeExpr private final List expressions = new ArrayList(); - protected ExpressionList(Iterable expressions) { - for (T exp : expressions) + protected ExpressionList(Iterable expressions, DataType inputType) { + super(inputType); + for (T exp : expressions) { this.expressions.add(exp); + } } - @Override - public boolean requiresInput() { return !expressions.isEmpty() && expressions.get(0).requiresInput(); } - public List expressions() { return expressions; } protected List convertChildList(ExpressionConverter converter) { 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 320f3abdf1d3..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 @@ -24,6 +24,10 @@ // TODO: Remove on Vespa 9 public final class FlattenExpression extends Expression { + public FlattenExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { return super.setInputType(inputType, DataType.STRING, context); 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 8e23745e67be..f0fabf052992 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 @@ -1,13 +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.DocumentType; -import com.yahoo.document.Field; -import com.yahoo.document.MapDataType; -import com.yahoo.document.StructDataType; -import com.yahoo.document.WeightedSetDataType; +import com.yahoo.document.*; import com.yahoo.document.datatypes.Array; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.MapFieldValue; @@ -29,6 +23,7 @@ public final class ForEachExpression extends CompositeExpression { private final Expression expression; public ForEachExpression(Expression expression) { + super(UnresolvedDataType.INSTANCE); this.expression = Objects.requireNonNull(expression); } @@ -48,7 +43,6 @@ public void setStatementOutput(DocumentType documentType, Field field) { @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - if (inputType == null) return null; if (inputType instanceof ArrayDataType || inputType instanceof WeightedSetDataType) { // Value type outside block becomes the collection type having the block output type as argument @@ -71,7 +65,6 @@ else if (inputType instanceof StructDataType struct) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType == null) return null; super.setOutputType(outputType, context); if (outputType instanceof ArrayDataType || outputType instanceof WeightedSetDataType) { @@ -111,12 +104,12 @@ private DataType verifyStructFields(StructDataType struct, VerificationContext c 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.getName()); + 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.getName()); + 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 } @@ -158,7 +151,7 @@ else if (valueType instanceof MapDataType) { } else { throw new VerificationException(this, "Expected Array, Struct, WeightedSet or Map input, got " + - (valueType == null ? "no value" : valueType.getName())); + valueType.getName()); } } 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 28fd1ac59b03..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 @@ -22,6 +22,7 @@ public final class GetFieldExpression extends Expression { private final String structFieldName; public GetFieldExpression(String structFieldName) { + super(UnresolvedDataType.INSTANCE); this.structFieldName = structFieldName; } @@ -30,7 +31,6 @@ public GetFieldExpression(String structFieldName) { @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - if (inputType == null) return null; return getFieldType(inputType, context); } @@ -57,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, but got " + (input == null ? "no value": 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 4e9f52f5c87a..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 @@ -11,12 +11,10 @@ public final class GetVarExpression extends Expression { private final String variableName; public GetVarExpression(String variableName) { + super(null); this.variableName = variableName; } - @Override - public boolean requiresInput() { return false; } - public String getVariableName() { return variableName; } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java index e92dc4278af1..58037e3927d5 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GuardExpression.java @@ -15,39 +15,37 @@ */ public final class GuardExpression extends CompositeExpression { - private final Expression innerExpression; + private final Expression expression; private final boolean shouldExecute; - public GuardExpression(Expression innerExpression) { - this.innerExpression = innerExpression; - shouldExecute = shouldExecute(innerExpression); + public GuardExpression(Expression expression) { + super(expression.requiredInputType()); + this.expression = expression; + shouldExecute = shouldExecute(expression); } - @Override - public boolean requiresInput() { return innerExpression.requiresInput(); } - - public Expression getInnerExpression() { return innerExpression; } + public Expression getInnerExpression() { return expression; } @Override public GuardExpression convertChildren(ExpressionConverter converter) { - return new GuardExpression(converter.convert(innerExpression)); + return new GuardExpression(converter.convert(expression)); } @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - return innerExpression.setInputType(inputType, context); + return expression.setInputType(inputType, context); } @Override public DataType setOutputType(DataType outputType, VerificationContext context) { super.setOutputType(outputType, context); - return innerExpression.setOutputType(outputType, context); + return expression.setOutputType(outputType, context); } @Override protected void doVerify(VerificationContext context) { - innerExpression.verify(context); + expression.verify(context); } @Override @@ -55,40 +53,40 @@ protected void doExecute(ExecutionContext context) { if (!shouldExecute && context.getFieldValue() instanceof UpdateAdapter) { context.setCurrentValue(null); } else { - innerExpression.execute(context); + expression.execute(context); } } @Override public void setStatementOutput(DocumentType documentType, Field field) { - innerExpression.setStatementOutput(documentType, field); + expression.setStatementOutput(documentType, field); } @Override public DataType createdOutputType() { - return innerExpression.createdOutputType(); + return expression.createdOutputType(); } @Override public String toString() { - return "guard " + toScriptBlock(innerExpression); + return "guard " + toScriptBlock(expression); } @Override public void selectMembers(ObjectPredicate predicate, ObjectOperation operation) { - select(innerExpression, predicate, operation); + select(expression, predicate, operation); } @Override public boolean equals(Object obj) { if (!(obj instanceof GuardExpression rhs)) return false; - if (!innerExpression.equals(rhs.innerExpression)) return false; + if (!expression.equals(rhs.expression)) return false; return true; } @Override public int hashCode() { - return getClass().hashCode() + innerExpression.hashCode(); + return getClass().hashCode() + expression.hashCode(); } private static boolean shouldExecute(Expression exp) { 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 0734f99f4810..fdebde6c4945 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 @@ -23,6 +23,10 @@ public class HashExpression extends Expression { private DataType targetType; + public HashExpression() { + super(DataType.STRING); + } + @Override public void setStatementOutput(DocumentType documentType, Field field) { targetType = field.getDataType(); @@ -37,7 +41,7 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { super.setOutputType(outputType, context); - if (outputType != null && ! isHashCompatible(outputType)) + if ( ! isHashCompatible(outputType)) throw new VerificationException(this, "An " + outputType.getName() + " output is required, but this produces int or long"); return DataType.STRING; 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 9092c4594211..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 @@ -13,6 +13,10 @@ public final class HexDecodeExpression extends Expression { private static final BigInteger ULONG_MAX = new BigInteger("18446744073709551616"); + public HexDecodeExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, DataType.STRING, context); 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 5754efab20c4..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 @@ -10,6 +10,10 @@ */ public final class HexEncodeExpression extends Expression { + public HexEncodeExpression() { + super(DataType.LONG); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, DataType.LONG, context); 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 53d650f60e22..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 @@ -3,7 +3,6 @@ import com.yahoo.document.DataType; import com.yahoo.document.datatypes.StringFieldValue; - import static com.yahoo.vespa.defaults.Defaults.getDefaults; /** @@ -11,8 +10,9 @@ */ public final class HostNameExpression extends Expression { - @Override - public boolean requiresInput() { return false; } + public HostNameExpression() { + super(null); + } @Override public DataType setInputType(DataType inputType, VerificationContext context) { 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 a2af300a0de0..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 @@ -49,6 +49,7 @@ public IfThenExpression(Expression lhs, Comparator cmp, Expression right, Expres } public IfThenExpression(Expression lhs, Comparator cmp, Expression right, Expression ifTrue, Expression ifFalse) { + super(resolveInputType(lhs, right, ifTrue, ifFalse)); this.left = lhs; this.comparator = cmp; this.right = right; @@ -56,11 +57,6 @@ public IfThenExpression(Expression lhs, Comparator cmp, Expression right, Expres this.ifFalse = ifFalse; } - @Override - public boolean requiresInput() { - return left.requiresInput() || right.requiresInput() || ifTrue.requiresInput() || (ifFalse != null && ifFalse.requiresInput()); - } - @Override public IfThenExpression convertChildren(ExpressionConverter converter) { return new IfThenExpression(converter.branch().convert(left), @@ -75,21 +71,21 @@ public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); left.setInputType(inputType, context); right.setInputType(inputType, context); - var outputType = ifTrue.setInputType(inputType, context); - if (ifFalse != null) - outputType = mostGeneralOf(outputType, ifFalse.setInputType(inputType, context)); - return outputType != null ? outputType : getOutputType(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); - var inputType = left.setOutputType(AnyDataType.instance, context); - inputType = leastGeneralOf(inputType, right.setOutputType(AnyDataType.instance, context)); - inputType = leastGeneralOf(inputType, ifTrue.setOutputType(outputType, context)); - if (ifFalse != null) - inputType = leastGeneralOf(inputType, ifFalse.setOutputType(outputType, context)); - return inputType != null ? inputType : getInputType(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 @@ -149,6 +145,17 @@ public void selectMembers(ObjectPredicate predicate, ObjectOperation operation) select(ifFalse, predicate, operation); } + private static DataType resolveInputType(Expression lhs, Expression rhs, Expression ifTrue, Expression ifFalse) { + DataType input = null; + input = resolveRequiredInputType(input, lhs.requiredInputType()); + input = resolveRequiredInputType(input, rhs.requiredInputType()); + input = resolveRequiredInputType(input, ifTrue.requiredInputType()); + if (ifFalse != null) { + input = resolveRequiredInputType(input, ifFalse.requiredInputType()); + } + return input; + } + @Override public DataType createdOutputType() { DataType ifTrueType = ifTrue.createdOutputType(); @@ -191,6 +198,20 @@ public int hashCode() { return ret; } + private static DataType resolveRequiredInputType(DataType prev, DataType next) { + if (next == null) { + return prev; + } + if (prev == null) { + return next; + } + if (!prev.equals(next)) { + throw new VerificationException(IfThenExpression.class, "Operands require conflicting input types, " + + prev.getName() + " vs " + next.getName()); + } + return prev; + } + private static boolean isTrue(FieldValue left, Comparator comparator, FieldValue right) { int res; if (left instanceof NumericFieldValue && right instanceof NumericFieldValue) { 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 81d262808a4d..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 @@ -19,14 +19,12 @@ public final class InputExpression extends Expression { private FieldPath fieldPath; public InputExpression(String fieldName) { + super(null); if (fieldName == null) throw new IllegalArgumentException("'input' must be given a field name as argument"); this.fieldName = fieldName; } - @Override - public boolean requiresInput() { return false; } - public String getFieldName() { return fieldName; } @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 a5e5e5479918..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 @@ -18,6 +18,7 @@ public final class JoinExpression extends Expression { private final String delimiter; public JoinExpression(String delimiter) { + super(UnresolvedDataType.INSTANCE); this.delimiter = delimiter; } @@ -26,7 +27,6 @@ public JoinExpression(String delimiter) { @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - if (inputType == null) return null; if ( ! (inputType instanceof ArrayDataType)) throw new VerificationException(this, "Expected Array input, got type " + inputType.getName()); return DataType.STRING; @@ -41,8 +41,9 @@ public DataType setOutputType(DataType outputType, VerificationContext context) @Override protected void doVerify(VerificationContext context) { DataType input = context.getCurrentType(); - if (!(input instanceof ArrayDataType)) - throw new VerificationException(this, "Expected Array input, got " + (input == null ? "no value" : input.getName())); + if (!(input instanceof ArrayDataType)) { + 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 7fa1443aad67..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; @@ -14,12 +15,10 @@ public class LiteralBoolExpression extends Expression { private final boolean value; public LiteralBoolExpression(boolean value) { + super(null); this.value = value; } - @Override - public boolean requiresInput() { return false; } - @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); 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 2a94f785a404..66a9ac57038e 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 @@ -11,6 +11,10 @@ */ public final class LowerCaseExpression extends Expression { + public LowerCaseExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { return super.setInputType(inputType, DataType.STRING, context); 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 7621906cfee5..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 @@ -34,6 +34,7 @@ public final class NGramExpression extends Expression { * @param gramSize the gram size */ public NGramExpression(Linguistics linguistics, int gramSize) { + super(DataType.STRING); this.linguistics = linguistics; this.gramSize = gramSize; } 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 2c6b5d1953ff..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 @@ -6,8 +6,8 @@ import com.yahoo.language.Linguistics; import com.yahoo.language.process.Transformer; -import java.util.logging.Level; import java.util.logging.Logger; +import java.util.logging.Level; /** * @author Simon Thoresen Hult @@ -18,6 +18,7 @@ public final class NormalizeExpression extends Expression { private static final Logger logger = Logger.getLogger(NormalizeExpression.class.getName()); public NormalizeExpression(Linguistics linguistics) { + super(DataType.STRING); this.linguistics = linguistics; } 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 50ade192e4b9..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 @@ -16,12 +16,10 @@ public NowExpression() { } public NowExpression(Timer timer) { + super(null); this.timer = timer; } - @Override - public boolean requiresInput() { return false; } - public Timer getTimer() { return timer; } @Override 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 4d1e60cc5a84..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 @@ -25,6 +25,7 @@ public OptimizePredicateExpression() { } OptimizePredicateExpression(PredicateProcessor optimizer) { + super(DataType.PREDICATE); this.optimizer = optimizer; } 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 e6682045366c..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 @@ -17,6 +17,7 @@ public abstract class OutputExpression extends Expression { private final String fieldName; public OutputExpression(String image, String fieldName) { + super(UnresolvedDataType.INSTANCE); this.image = image; this.fieldName = fieldName; } 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 66602c403554..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 @@ -21,9 +21,13 @@ public class PackBitsExpression extends Expression { private TensorType outputTensorType; + /** Creates a pack_bits expression. */ + public PackBitsExpression() { + super(TensorDataType.any()); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { - if (inputType == null) return null; super.setInputType(inputType, context); if ( ! validType(inputType)) throw new VerificationException(this, "Require a tensor with one dense dimension, but got " + inputType.getName()); @@ -34,7 +38,7 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { super.setOutputType(outputType, context); - if (outputType != null && ! validType(outputType)) + if ( ! validType(outputType)) throw new VerificationException(this, "Required to produce " + outputType.getName() + " but this produces a tensor with one dense dimension"); outputTensorType = ((TensorDataType)outputType).getTensorType(); 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 19b53074af04..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 @@ -13,74 +13,72 @@ */ public class ParenthesisExpression extends CompositeExpression { - private final Expression innerExpression; + private final Expression innerExp; - public ParenthesisExpression(Expression innerExpression) { - this.innerExpression = innerExpression; + public ParenthesisExpression(Expression innerExp) { + super(innerExp.requiredInputType()); + this.innerExp = innerExp; } - @Override - public boolean requiresInput() { return innerExpression.requiresInput(); } - - public Expression getInnerExpression() { return innerExpression; } + public Expression getInnerExpression() { return innerExp; } @Override public ParenthesisExpression convertChildren(ExpressionConverter converter) { - return new ParenthesisExpression(converter.convert(innerExpression)); + return new ParenthesisExpression(converter.convert(innerExp)); } @Override public DataType setInputType(DataType inputType, VerificationContext context) { super.setInputType(inputType, context); - return innerExpression.setInputType(inputType, context); + return innerExp.setInputType(inputType, context); } @Override public DataType setOutputType(DataType outputType, VerificationContext context) { super.setOutputType(outputType, context); - return innerExpression.setInputType(outputType, context); + return innerExp.setInputType(outputType, context); } @Override public void setStatementOutput(DocumentType documentType, Field field) { - innerExpression.setStatementOutput(documentType, field); + innerExp.setStatementOutput(documentType, field); } @Override protected void doVerify(VerificationContext context) { - innerExpression.verify(context); + innerExp.verify(context); } @Override protected void doExecute(ExecutionContext context) { - innerExpression.execute(context); + innerExp.execute(context); } @Override public DataType createdOutputType() { - return innerExpression.createdOutputType(); + return innerExp.createdOutputType(); } @Override public String toString() { - return "(" + innerExpression + ")"; + return "(" + innerExp + ")"; } @Override public boolean equals(Object obj) { if (!(obj instanceof ParenthesisExpression rhs)) return false; - if (!innerExpression.equals(rhs.innerExpression)) return false; + if (!innerExp.equals(rhs.innerExp)) return false; return true; } @Override public int hashCode() { - return getClass().hashCode() + innerExpression.hashCode(); + return getClass().hashCode() + innerExp.hashCode(); } @Override public void selectMembers(ObjectPredicate predicate, ObjectOperation operation) { - select(innerExpression, predicate, operation); + select(innerExp, predicate, operation); } } 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 ee60a16a0e7e..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 @@ -19,12 +19,10 @@ public RandomExpression() { } public RandomExpression(Integer max) { + super(null); this.max = max; } - @Override - public boolean requiresInput() { return false; } - public Integer getMaxValue() { return max; } @Override 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 4d18287dfd69..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 @@ -32,7 +32,7 @@ public ScriptExpression(StatementExpression... statements) { } public ScriptExpression(Collection statements) { - super(statements); + super(statements, resolveInputType(statements)); } @Override @@ -88,6 +88,20 @@ private boolean containsAtLeastOneInputFrom(List inputFields, ExecutionC return false; } + private static DataType resolveInputType(Collection list) { + DataType prev = null; + for (Expression exp : list) { + DataType next = exp.requiredInputType(); + if (prev == null) { + prev = next; + } else if (next != null && !prev.isAssignableFrom(next)) { + throw new VerificationException(ScriptExpression.class, "Statements require conflicting input types, " + + prev.getName() + " vs " + next.getName()); + } + } + return prev; + } + @Override public DataType createdOutputType() { var expressions = asList(); 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 d370ece96031..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 @@ -27,12 +27,10 @@ public SelectInputExpression(Pair... cases) { } public SelectInputExpression(List> cases) { + super(null); this.cases = cases; } - @Override - public boolean requiresInput() { return false; } - @Override public SelectInputExpression convertChildren(ExpressionConverter converter) { return new SelectInputExpression(cases.stream() 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 2e419db14348..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 @@ -11,6 +11,10 @@ */ public final class SetLanguageExpression extends Expression { + public SetLanguageExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType inputType, VerificationContext context) { return super.setInputType(inputType, DataType.STRING, context); 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 38356d0ef518..3169563818cb 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 @@ -11,6 +11,7 @@ public final class SetVarExpression extends Expression { private final String varName; public SetVarExpression(String varName) { + super(UnresolvedDataType.INSTANCE); this.varName = varName; } @@ -24,15 +25,12 @@ public DataType setInputType(DataType inputType, VerificationContext context) { @Override public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType == null) return null; setVariableType(outputType, context); return super.setOutputType(outputType, context); } @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); setVariableType(context.getCurrentType(), context); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SleepExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SleepExpression.java index 54174868ba99..cc43506539a1 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SleepExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/SleepExpression.java @@ -12,6 +12,10 @@ */ public final class SleepExpression extends Expression { + public SleepExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override protected void doVerify(VerificationContext context) { } 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 6bd3be97e7ea..3f2fe7d7b8e3 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 @@ -17,6 +17,7 @@ public final class SplitExpression extends Expression { private final Pattern splitPattern; public SplitExpression(String splitString) { + super(DataType.STRING); this.splitPattern = Pattern.compile(splitString); } @@ -29,10 +30,10 @@ public DataType setInputType(DataType input, VerificationContext context) { } @Override - public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(outputType, context); - if (outputType != null && ! (outputType instanceof ArrayDataType) && outputType.getNestedType() == DataType.STRING) - throw new VerificationException(this, "This produces a string array, but " + outputType.getName() + " is required"); + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(output, context); + if ( ! (output instanceof ArrayDataType) && output.getNestedType() == DataType.STRING) + throw new VerificationException(this, "This produces a string array, but " + output.getName() + " is required"); 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 d18b42f3e255..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 @@ -37,7 +37,7 @@ public StatementExpression(Iterable list) { } private StatementExpression(Iterable list, Object unused) { - super(list); + super(list, resolveInputType(list)); inputFields = List.copyOf(InputExpression.InputFieldNameExtractor.runOn(this)); } @@ -74,11 +74,12 @@ public DataType setOutputType(DataType output, VerificationContext context) { /** Resolves types forward and returns the final output, or null if resolution could not progress to the end. */ private DataType resolveForwards(VerificationContext context) { - var inputType = getInputType(context); - for (var expression : expressions()) { - inputType = expression.setInputType(inputType, context); - if (inputType == null) break; - } + 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); return inputType; } @@ -88,7 +89,7 @@ private DataType resolveBackwards(VerificationContext context) { 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(--i).getInputType(context); - while (--i >= 0) + while (--i >= 0 && outputType != null) outputType = expressions().get(i).setOutputType(outputType, context); return outputType; } @@ -123,6 +124,17 @@ private String outputFieldName() { return null; } + private static DataType resolveInputType(Iterable expressions) { + for (Expression expression : expressions) { + DataType type = expression.requiredInputType(); + if (type != null) return type; + + type = expression.createdOutputType(); + if (type != null) return null; + } + return null; + } + @Override public DataType createdOutputType() { for (int i = size(); --i >= 0; ) { 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 fa239a13a9c8..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 @@ -14,6 +14,7 @@ public final class SubstringExpression extends Expression { private final int to; public SubstringExpression(int from, int to) { + super(DataType.STRING); if (from < 0 || to < 0 || to < from) { throw new IndexOutOfBoundsException(); } 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 38a7c720b4e8..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 @@ -30,6 +30,7 @@ public SwitchExpression(Map cases) { } public SwitchExpression(Map cases, Expression defaultExp) { + super(null); this.defaultExp = defaultExp; this.cases.putAll(cases); } diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java index 31a0d862b9c5..b3e5e5c23f82 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/ThisExpression.java @@ -8,10 +8,13 @@ */ public final class ThisExpression extends Expression { + public ThisExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); + // empty } @Override 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 d2d730fec1d7..351cf450046f 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 @@ -11,29 +11,29 @@ */ public final class ToArrayExpression extends Expression { + public ToArrayExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); - if (input == null) return null; return new ArrayDataType(input); } @Override - public DataType setOutputType(DataType outputType, VerificationContext context) { - if (outputType == null) return null; - super.setOutputType(outputType, context); - if (outputType instanceof ArrayDataType arrayType) + public DataType setOutputType(DataType output, VerificationContext context) { + super.setOutputType(output, context); + if (output instanceof ArrayDataType arrayType) return arrayType.getNestedType(); - if (outputType instanceof AnyDataType) + if (output instanceof AnyDataType) return AnyDataType.instance; else - throw new VerificationException(this, "Produces an array, but " + outputType + " is required"); + throw new VerificationException(this, "Produces an array, but " + output + " is required"); } @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 2d388453ab8a..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,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.NumericDataType; import com.yahoo.document.datatypes.BoolFieldValue; @@ -13,10 +14,13 @@ */ public final class ToBoolExpression extends Expression { + public ToBoolExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); - if (input == null) return null; 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; @@ -30,8 +34,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 d50be545101c..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; /** @@ -9,6 +10,10 @@ */ public final class ToByteExpression extends Expression { + public ToByteExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +28,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 627d6b3ca2f5..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 @@ -9,6 +9,10 @@ */ public final class ToDoubleExpression extends Expression { + public ToDoubleExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +27,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 03b00890407a..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 @@ -3,7 +3,6 @@ import com.yahoo.document.DataType; import com.yahoo.document.datatypes.LongFieldValue; - import java.time.Instant; /** @@ -13,6 +12,10 @@ */ public class ToEpochSecondExpression extends Expression { + public ToEpochSecondExpression() { + super(DataType.STRING); //only accept string input + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, DataType.STRING, context); 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 6b7ce8f5edec..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 @@ -9,6 +9,10 @@ */ public final class ToFloatExpression extends Expression { + public ToFloatExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +27,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 11a4f433d09d..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 @@ -9,6 +9,10 @@ */ public final class ToIntegerExpression extends Expression { + public ToIntegerExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +27,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 9acd89dec529..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 @@ -9,6 +9,10 @@ */ public final class ToLongExpression extends Expression { + public ToLongExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +27,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 60fa8bfb38c8..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,12 +3,17 @@ import com.yahoo.document.DataType; import com.yahoo.document.PositionDataType; +import com.yahoo.document.PrimitiveDataType; /** * @author Simon Thoresen Hult */ public final class ToPositionExpression extends Expression { + public ToPositionExpression() { + super(DataType.STRING); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, DataType.STRING, context); 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 60871a770aba..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 @@ -9,6 +9,10 @@ */ public final class ToStringExpression extends Expression { + public ToStringExpression() { + super(UnresolvedDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); @@ -23,8 +27,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); 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 66a1abf6aceb..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 @@ -15,6 +15,7 @@ public final class ToWsetExpression extends Expression { private final Boolean removeIfZero; public ToWsetExpression(boolean createIfNonExistent, boolean removeIfZero) { + super(UnresolvedDataType.INSTANCE); this.createIfNonExistent = createIfNonExistent; this.removeIfZero = removeIfZero; } @@ -26,7 +27,6 @@ public ToWsetExpression(boolean createIfNonExistent, boolean removeIfZero) { @Override public DataType setInputType(DataType input, VerificationContext context) { super.setInputType(input, context); - if (input == null) return null; return outputType(input); } @@ -40,8 +40,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); context.setCurrentType(outputType(context.getCurrentType())); } 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 f7e14887a508..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 @@ -18,6 +18,7 @@ public final class TokenizeExpression extends Expression { private final AnnotatorConfig config; public TokenizeExpression(Linguistics linguistics, AnnotatorConfig config) { + super(DataType.STRING); this.linguistics = linguistics; this.config = config; } 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 2fb8857ac478..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 @@ -9,6 +9,10 @@ */ public final class TrimExpression extends Expression { + public TrimExpression() { + super(DataType.STRING); + } + @Override protected void doVerify(VerificationContext context) { context.setCurrentType(createdOutputType()); diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java index b3b880cfb0cd..0fd475b689f4 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/UnresolvedDataType.java @@ -12,7 +12,7 @@ final class UnresolvedDataType extends PrimitiveDataType { public static final UnresolvedDataType INSTANCE = new UnresolvedDataType(); private UnresolvedDataType() { - super("unresolved", -69, UnresolvedFieldValue.class, UnresolvedFieldValue.getFactory()); + super("any", -69, UnresolvedFieldValue.class, UnresolvedFieldValue.getFactory()); } @Override diff --git a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java index 64ea7c0af434..6de7a2c3ede9 100644 --- a/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java +++ b/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContext.java @@ -5,7 +5,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.Objects; /** * @author Simon Thoresen Hult @@ -17,8 +16,12 @@ public class VerificationContext { private DataType currentType; private String outputField; + public VerificationContext() { + this(null); + } + public VerificationContext(FieldTypeAdapter fieldTypes) { - this.fieldTypes = Objects.requireNonNull(fieldTypes); + this.fieldTypes = fieldTypes; } public VerificationContext verify(Expression expression) { 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 375b38aa0bdd..2eff85e49d34 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 @@ -14,9 +14,12 @@ */ public final class ZCurveExpression extends Expression { + public ZCurveExpression() { + super(PositionDataType.INSTANCE); + } + @Override public DataType setInputType(DataType input, VerificationContext context) { - if (input == null) return null; if ( ! (input instanceof StructDataType struct)) throw new VerificationException(this, "This requires a struct as input, but got " + input.getName()); requireIntegerField(PositionDataType.FIELD_X, struct); @@ -41,8 +44,6 @@ public DataType setOutputType(DataType output, VerificationContext context) { @Override protected void doVerify(VerificationContext context) { - if (context.getCurrentType() == null) - throw new VerificationException(this, "Expected input, but no input is provided"); context.setCurrentType(createdOutputType()); } 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 c529b7d4fc1f..c9b9e0c01fb3 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/ScriptTestCase.java @@ -11,22 +11,14 @@ import com.yahoo.document.datatypes.FloatFieldValue; import com.yahoo.document.datatypes.IntegerFieldValue; import com.yahoo.document.datatypes.LongFieldValue; +import com.yahoo.document.datatypes.MapFieldValue; import com.yahoo.document.datatypes.StringFieldValue; -import com.yahoo.vespa.indexinglanguage.expressions.AttributeExpression; -import com.yahoo.vespa.indexinglanguage.expressions.ExecutionContext; -import com.yahoo.vespa.indexinglanguage.expressions.Expression; -import com.yahoo.vespa.indexinglanguage.expressions.InputExpression; -import com.yahoo.vespa.indexinglanguage.expressions.ScriptExpression; -import com.yahoo.vespa.indexinglanguage.expressions.StatementExpression; -import com.yahoo.vespa.indexinglanguage.expressions.VerificationContext; -import com.yahoo.vespa.indexinglanguage.expressions.VerificationException; +import com.yahoo.document.datatypes.Struct; +import com.yahoo.vespa.indexinglanguage.expressions.*; import com.yahoo.vespa.indexinglanguage.parser.ParseException; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -60,7 +52,7 @@ public void requireThatScriptExecutesStatements() { } @Test - public void failsWhenOneStatementIsMissingInput() { + public void requireThatEachStatementHasEmptyInput() { Document input = new Document(type, "id:scheme:mytype::"); input.setFieldValue(input.getField("in-1"), new StringFieldValue("69")); @@ -69,38 +61,13 @@ public void failsWhenOneStatementIsMissingInput() { new StatementExpression(new AttributeExpression("out-2"))); try { exp.verify(input); - fail("Expected exception"); + fail(); } catch (VerificationException e) { - assertEquals("Invalid expression 'attribute out-2': Expected string input, but no input is provided", e.getMessage()); + assertEquals(e.getExpressionType(), ScriptExpression.class); + assertEquals("Invalid expression '{ input in-1 | attribute out-1; attribute out-2; }': Expected any input, but no input is specified", e.getMessage()); } } - @Test - public void failsWhenAllStatementIsMissingInput() { - Document input = new Document(type, "id:scheme:mytype::"); - input.setFieldValue(input.getField("in-1"), new StringFieldValue("69")); - - Expression exp = new ScriptExpression( - new StatementExpression(new AttributeExpression("out-2"))); - try { - exp.verify(input); - fail("Expected exception"); - } catch (VerificationException e) { - assertEquals(AttributeExpression.class, e.getExpressionType()); - assertEquals("Invalid expression 'attribute out-2': Expected string input, but no input is provided", e.getMessage()); - } - } - - @Test - public void succeedsWhenAllStatementsHaveInput() { - Document input = new Document(type, "id:scheme:mytype::"); - input.setFieldValue(input.getField("in-1"), new StringFieldValue("69")); - - Expression exp = new ScriptExpression( - new StatementExpression(new InputExpression("in-1"), new AttributeExpression("out-1"))); - exp.verify(input); - } - @Test public void requireThatFactoryMethodWorks() throws ParseException { Document input = new Document(type, "id:scheme:mytype::"); 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 a2b2d960463c..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 @@ -10,10 +10,8 @@ import org.junit.Test; import static com.yahoo.vespa.indexinglanguage.expressions.ArithmeticExpression.Operator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; +import static org.mockito.Mockito.verify; /** * @author Simon Thoresen Hult @@ -65,6 +63,15 @@ public void requireThatConstructorDoesNotAcceptNull() { public void requireThatVerifyCallsAreForwarded() { assertVerify(SimpleExpression.newOutput(DataType.INT), Operator.ADD, SimpleExpression.newOutput(DataType.INT), null); + assertVerifyThrows(SimpleExpression.newOutput(null), Operator.ADD, + SimpleExpression.newOutput(DataType.INT), null, + "Expected any output, but no output is specified"); + assertVerifyThrows(SimpleExpression.newOutput(DataType.INT), Operator.ADD, + SimpleExpression.newOutput(null), null, + "Expected any output, but no output is specified"); + assertVerifyThrows(SimpleExpression.newOutput(null), Operator.ADD, + SimpleExpression.newOutput(null), null, + "Expected any output, but no output is specified"); assertVerifyThrows(SimpleExpression.newOutput(DataType.INT), Operator.ADD, SimpleExpression.newOutput(DataType.STRING), null, "The second argument must be a number, but has type string"); @@ -73,6 +80,25 @@ public void requireThatVerifyCallsAreForwarded() { "The first argument must be a number, but has type string"); } + @Test + public void requireThatOperandInputCanBeNull() { + SimpleExpression reqNull = new SimpleExpression(); + SimpleExpression reqInt = new SimpleExpression(DataType.INT); + assertNull(newArithmetic(reqNull, Operator.ADD, reqNull).requiredInputType()); + assertEquals(DataType.INT, newArithmetic(reqInt, Operator.ADD, reqNull).requiredInputType()); + assertEquals(DataType.INT, newArithmetic(reqInt, Operator.ADD, reqInt).requiredInputType()); + assertEquals(DataType.INT, newArithmetic(reqNull, Operator.ADD, reqInt).requiredInputType()); + } + + @Test + public void requireThatOperandsAreInputCompatible() { + assertVerify(new SimpleExpression(DataType.INT), Operator.ADD, + new SimpleExpression(DataType.INT), DataType.INT); + assertVerifyThrows(new SimpleExpression(DataType.INT), Operator.ADD, + new SimpleExpression(DataType.STRING), null, + "Operands require conflicting input types, int vs string"); + } + @Test public void requireThatResultIsCalculated() { for (int i = 0; i < 50; ++i) { @@ -137,7 +163,7 @@ private void assertResult(FieldValue lhs, Operator op, FieldValue rhs, FieldValu private void assertType(DataType lhs, Operator op, DataType rhs, DataType expected) { assertEquals(expected, newArithmetic(SimpleExpression.newOutput(lhs), op, - SimpleExpression.newOutput(rhs)).verify(new VerificationContext(new SimpleTestAdapter()))); + SimpleExpression.newOutput(rhs)).verify()); assertEquals(expected, newArithmetic(lhs.createFieldValue(6), op, rhs.createFieldValue(9)).execute().getDataType()); } @@ -157,14 +183,7 @@ private static ArithmeticExpression newArithmetic(FieldValue lhs, Operator op, F } private static ArithmeticExpression newArithmetic(Expression lhs, Operator op, Expression rhs) { - return newArithmetic(lhs, op, rhs, null); - } - - private static ArithmeticExpression newArithmetic(Expression lhs, Operator op, Expression rhs, VerificationContext context) { - var expression = new ArithmeticExpression(lhs, op, rhs); - if (context != null) - expression.verify(context); - return expression; + return new ArithmeticExpression(lhs, op, rhs); } private static ConstantExpression newLong(long val) { @@ -172,7 +191,7 @@ private static ConstantExpression newLong(long val) { } private static void assertVerify(Expression lhs, Operator op, Expression rhs, DataType val) { - new ArithmeticExpression(lhs, op, rhs).verify(new VerificationContext(new SimpleTestAdapter()).setCurrentType(val)); + new ArithmeticExpression(lhs, op, rhs).verify(val); } private static void assertVerifyThrows(Expression lhs, Operator op, Expression rhs, DataType val, @@ -180,8 +199,7 @@ private static void assertVerifyThrows(Expression lhs, Operator op, Expression r ArithmeticExpression expression = null; try { expression = new ArithmeticExpression(lhs, op, rhs); - expression.setInputType(null, new VerificationContext(new SimpleTestAdapter())); - expression.verify(new VerificationContext(new SimpleTestAdapter()).setCurrentType(val)); + expression.verify(val); fail("Expected exception"); } catch (VerificationException e) { String expressionString = expression == null ? "of type '" + ArithmeticExpression.class.getSimpleName() + "'" 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 f6323bd7e127..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 @@ -3,6 +3,8 @@ import org.junit.Test; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertExecute; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertVerify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -27,4 +29,14 @@ public void requireThatHashCodeAndEqualsAreImplemented() { assertEquals(exp.hashCode(), new AttributeExpression("foo").hashCode()); } + @Test + public void requireThatExpressionCanBeVerified() { + assertVerify(new AttributeExpression("foo")); + } + + @Test + 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 c4cfe1815028..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 @@ -10,10 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -69,7 +66,7 @@ public void requireThatIllegalInputThrows() { public void requireThatExpressionCanBeVerified() { Expression exp = new Base64DecodeExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows("Invalid expression 'base64decode': Expected string input, but no input is provided", null, exp); + 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 723677f64b86..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -42,7 +40,7 @@ public void requireThatInputIsEncoded() { public void requireThatExpressionCanBeVerified() { Expression exp = new Base64EncodeExpression(); assertVerify(DataType.LONG, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'base64encode': Expected long input, but no input is provided", null, exp); + 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 a656bd5f756d..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 @@ -3,22 +3,14 @@ import com.yahoo.document.DataType; import com.yahoo.document.Field; -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.WeightedSet; +import com.yahoo.document.datatypes.*; import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; + import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -52,18 +44,23 @@ public void requireThatHashCodeAndEqualsAreImplemented() { } @Test - public void expressionCanBeVerified() { + public void requireThatExpressionCanBeVerified() { assertVerify(new ConstantExpression(new StringFieldValue("foo")), new ConstantExpression(new StringFieldValue("bar")), null); + assertVerify(new SimpleExpression(DataType.STRING), + new SimpleExpression(DataType.STRING), DataType.STRING); assertVerifyThrows(new SimpleExpression().setCreatedOutput(null), new SimpleExpression().setCreatedOutput(DataType.STRING), null, "Invalid expression 'SimpleExpression . SimpleExpression': In SimpleExpression: Attempting to concatenate a null value"); + assertVerifyThrows(new SimpleExpression(DataType.STRING), + new SimpleExpression(DataType.INT), null, + "Invalid expression of type 'CatExpression': Operands require conflicting input types, string vs int"); assertVerifyThrows(new SimpleExpression(DataType.STRING), new SimpleExpression(DataType.STRING), null, - "Invalid expression 'SimpleExpression': Expected string input, but no input is provided"); + "Invalid expression 'SimpleExpression . SimpleExpression': Expected string input, but no input is specified"); assertVerifyThrows(new SimpleExpression(DataType.STRING), new SimpleExpression(DataType.STRING), DataType.INT, - "Invalid expression 'SimpleExpression': Expected string input, got int"); + "Invalid expression 'SimpleExpression . SimpleExpression': Expected string input, got int"); } @Test @@ -91,23 +88,22 @@ public void requireThatPrimitiveVerificationWorks() { } @Test - public void inputValueIsAvailableToAllInnerExpressions() { - var expression = new StatementExpression(new ConstantExpression(new StringFieldValue("foo")), - new CatExpression(new ThisExpression(), - new ConstantExpression(new StringFieldValue("bar")), - new ThisExpression())); - expression.verify(new SimpleTestAdapter()); - assertEquals(new StringFieldValue("foobarfoo"), expression.execute()); + public void requireThatInputValueIsAvailableToAllInnerExpressions() { + assertEquals(new StringFieldValue("foobarfoo"), + new StatementExpression(new ConstantExpression(new StringFieldValue("foo")), + new CatExpression(new ThisExpression(), + new ConstantExpression(new StringFieldValue("bar")), + new ThisExpression())).execute()); } @Test - public void requiredInputTypeAllowsNull() { + public void requiredThatRequiredInputTypeAllowsNull() { assertVerify(new ConstantExpression(new StringFieldValue("foo")), new TrimExpression(), DataType.STRING); assertVerify(new TrimExpression(), new ConstantExpression(new StringFieldValue("foo")), DataType.STRING); } @Test - public void arraysAreConcatenated() { + public void requireThatArraysAreConcatenated() { Array lhs = new Array<>(DataType.getArray(DataType.STRING)); lhs.add(new StringFieldValue("6")); Array rhs = new Array<>(DataType.getArray(DataType.STRING)); @@ -217,16 +213,13 @@ public void requireThatCollectionValuesMustBeCompatible() { } private static void assertVerify(Expression expA, Expression expB, DataType val) { - new CatExpression(expA, expB).verify(new VerificationContext(new SimpleTestAdapter()).setCurrentType(val)); + new CatExpression(expA, expB).verify(val); } private static void assertVerifyThrows(Expression expA, Expression expB, DataType val, String expectedException) { try { - var expression = new CatExpression(expA, expB); - var context = new VerificationContext(new SimpleTestAdapter()).setCurrentType(val); - expression.setInputType(val, context); - expression.verify(context); - fail("Expected exception"); + new CatExpression(expA, expB).verify(val); + fail(); } catch (VerificationException e) { if (!e.getMessage().startsWith(expectedException)) { assertEquals(expectedException, e.getMessage()); @@ -239,15 +232,12 @@ private static FieldValue evaluate(FieldValue valA, FieldValue valB) { } private static FieldValue evaluate(DataType typeA, FieldValue valA, DataType typeB, FieldValue valB) { - var adapter = new SimpleTestAdapter(new Field("a", typeA), - new Field("b", typeB)); - var expression = new CatExpression(new InputExpression("a"), new InputExpression("b")); - expression.setInputType(null, new VerificationContext(adapter)); - ExecutionContext context = new ExecutionContext(adapter); - context.setFieldValue("a", valA, null); - context.setFieldValue("b", valB, null); - expression.execute(context); - return context.getCurrentValue(); + ExecutionContext ctx = new ExecutionContext(new SimpleTestAdapter(new Field("a", typeA), + new Field("b", typeB))); + ctx.setFieldValue("a", valA, null); + ctx.setFieldValue("b", valB, null); + new CatExpression(new InputExpression("a"), new InputExpression("b")).execute(ctx); + return ctx.getCurrentValue(); } private static DataType evaluate(DataType typeA, DataType typeB) { diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java index ee09313f8e86..cb480a36bf4d 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ClearStateTestCase.java @@ -2,13 +2,10 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -60,10 +57,6 @@ private static class MyVerification extends VerificationContext { boolean cleared = false; - MyVerification() { - super(new SimpleTestAdapter()); - } - @Override public VerificationContext clear() { cleared = true; 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 a210def9b2cc..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 @@ -10,9 +10,8 @@ import java.io.PrintStream; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; +import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -54,6 +53,6 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new EchoExpression(); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.STRING); + 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 a23e8705cf0e..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 @@ -2,11 +2,7 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.document.annotation.Annotation; -import com.yahoo.document.annotation.SpanList; -import com.yahoo.document.annotation.SpanNode; -import com.yahoo.document.annotation.SpanTree; -import com.yahoo.document.annotation.SpanTrees; +import com.yahoo.document.annotation.*; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; @@ -15,11 +11,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -92,7 +84,7 @@ public void requireThatEmptyStringsAreNotAnnotated() { public void requireThatExpressionCanBeVerified() { Expression exp = new ExactExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'exact': Expected string input, but no input is provided", null, exp); + 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); } 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 f75765567bea..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 @@ -2,8 +2,8 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.document.datatypes.StringFieldValue; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; + +import java.util.regex.Pattern; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -18,24 +18,23 @@ public static void assertVerifyCtx(Expression expression, DataType expectedValue } public static void assertVerify(DataType valueBefore, Expression expression, DataType expectedValueAfter) { - assertVerifyCtx(expression, expectedValueAfter, new VerificationContext(new SimpleTestAdapter()).setCurrentType(valueBefore)); + assertVerifyCtx(expression, expectedValueAfter, new VerificationContext().setCurrentType(valueBefore)); } public static void assertVerifyThrows(String expectedMessage, DataType valueBefore, Expression expression) { - assertVerifyThrows(expectedMessage, expression, new VerificationContext(new SimpleTestAdapter()).setCurrentType(valueBefore)); + assertVerifyThrows(expectedMessage, expression, new VerificationContext().setCurrentType(valueBefore)); } interface CreateExpression { Expression create(); } public static void assertVerifyThrows(String expectedMessage, DataType valueBefore, CreateExpression createExpression) { - assertVerifyThrows(expectedMessage, createExpression, new VerificationContext(new SimpleTestAdapter()).setCurrentType(valueBefore)); + assertVerifyThrows(expectedMessage, createExpression, new VerificationContext().setCurrentType(valueBefore)); } public static void assertVerifyThrows(String expectedMessage, CreateExpression createExp, VerificationContext context) { try { Expression exp = createExp.create(); - exp = new StatementExpression(new ConstantExpression(new StringFieldValue("test")), exp); exp.verify(context); fail("Expected exception"); } catch (VerificationException e) { @@ -44,7 +43,6 @@ public static void assertVerifyThrows(String expectedMessage, CreateExpression c } public static void assertVerifyThrows(String expectedMessage, Expression expression, VerificationContext context) { try { - expression.setInputType(context.getCurrentType(), context); expression.verify(context); fail("Expected exception"); } catch (VerificationException e) { 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 ed862931c3e6..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 @@ -5,19 +5,23 @@ import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.IntegerFieldValue; import com.yahoo.document.datatypes.StringFieldValue; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult */ public class ExpressionTestCase { + @Test + public void requireThatInputTypeIsCheckedBeforeExecute() { + assertExecute(newRequiredInput(DataType.INT), null); + assertExecute(newRequiredInput(DataType.INT), new IntegerFieldValue(69)); + assertExecuteThrows(newRequiredInput(DataType.INT), new StringFieldValue("foo"), + new IllegalArgumentException("expected int input, got string")); + } + @Test public void requireThatOutputTypeIsCheckedAfterExecute() { assertExecute(newCreatedOutput(DataType.INT, (FieldValue)null), null); @@ -30,13 +34,24 @@ public void requireThatOutputTypeIsCheckedAfterExecute() { public void requireThatInputTypeIsCheckedBeforeVerify() { assertVerify(newRequiredInput(DataType.INT), DataType.INT); assertVerifyThrows(newRequiredInput(DataType.INT), null, - "Invalid expression 'SimpleExpression': Expected int input, but no input is provided"); + "Invalid expression 'SimpleExpression': Expected int input, but no input is specified"); assertVerifyThrows(newRequiredInput(DataType.INT), UnresolvedDataType.INSTANCE, - "Invalid expression 'SimpleExpression': Expected int input, got unresolved"); + "Invalid expression 'SimpleExpression': Failed to resolve input type"); assertVerifyThrows(newRequiredInput(DataType.INT), DataType.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, + "Invalid expression 'SimpleExpression': Expected int output, but no output is specified"); + assertVerifyThrows(newCreatedOutput(DataType.INT, UnresolvedDataType.INSTANCE), null, + "Invalid expression 'SimpleExpression': Failed to resolve output type"); + assertVerifyThrows(newCreatedOutput(DataType.INT, DataType.STRING), null, + "Invalid expression 'SimpleExpression': Expected int output, got string"); + } + @Test public void requireThatEqualsMethodWorks() { assertTrue(Expression.equals(null, null)); @@ -73,15 +88,13 @@ private static void assertExecuteThrows(Expression exp, FieldValue val, Exceptio } private static void assertVerify(Expression exp, DataType val) { - var context = new VerificationContext(new SimpleTestAdapter()).setCurrentType(val); - exp.setInputType(val, context); - exp.verify(context); + exp.verify(val); } private static void assertVerifyThrows(Expression exp, DataType val, String expectedException) { try { - assertVerify(exp, val); - fail("Expected exception"); + exp.verify(val); + fail(); } catch (VerificationException e) { assertEquals(expectedException, e.getMessage()); } 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 f5399f2cd212..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 @@ -2,11 +2,7 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.document.annotation.Annotation; -import com.yahoo.document.annotation.AnnotationTypes; -import com.yahoo.document.annotation.Span; -import com.yahoo.document.annotation.SpanTree; -import com.yahoo.document.annotation.SpanTrees; +import com.yahoo.document.annotation.*; import com.yahoo.document.datatypes.StringFieldValue; import org.junit.Test; @@ -91,7 +87,7 @@ public void requireThatAnnotationsWithoutFieldValueUseOriginalSpan() { public void requireThatExpressionCanBeVerified() { Expression exp = new FlattenExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'flatten': Expected string input, but no input is provided", null, exp); + 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 79f8c81a8c9b..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,7 +53,7 @@ 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("Invalid expression 'for_each { SimpleExpression }': Expected Array, Struct, WeightedSet or Map input, got no value", null, exp); + 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); } @@ -64,7 +64,7 @@ public void requireThatStructFieldCompatibilityIsVerified() { type.addField(new Field("foo", DataType.INT)); assertVerify(type, new ForEachExpression(new SimpleExpression()), type); assertVerifyThrows("Invalid expression 'SimpleExpression': Expected string input, got int", type, new ForEachExpression(SimpleExpression.newConversion(DataType.STRING, DataType.INT))); - assertVerifyThrows("Invalid expression 'for_each { SimpleExpression }': Struct field 'foo' has type int but expression produces string", type, new ForEachExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING))); + assertVerifyThrows("Invalid expression 'for_each { SimpleExpression }': Expected int output, got string", type, new ForEachExpression(SimpleExpression.newConversion(DataType.INT, DataType.STRING))); } @Test @@ -240,6 +240,9 @@ private static class MyCollector extends Expression { List lst = new LinkedList<>(); + MyCollector() { + super(null); + } @Override protected void doExecute(ExecutionContext context) { lst.add(context.getCurrentValue()); 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 c715b08e59b9..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 @@ -1,11 +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.DataType; -import com.yahoo.document.Document; -import com.yahoo.document.DocumentType; -import com.yahoo.document.Field; -import com.yahoo.document.StructDataType; +import com.yahoo.document.*; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.document.datatypes.Struct; @@ -14,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -44,8 +38,8 @@ public void requireThatExpressionCanBeVerified() { type.addField(new Field("foo", DataType.STRING)); Expression exp = new GetFieldExpression("foo"); assertVerify(type, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'get_field foo': Expected a struct or map, but got no value", null, exp); - assertVerifyThrows("Invalid expression 'get_field foo': Expected a struct or map, but got int", DataType.INT, exp); + 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")); } 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 89be7daaeef7..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 @@ -9,10 +9,7 @@ import com.yahoo.vespa.indexinglanguage.parser.ParseException; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -36,7 +33,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { @Test public void requireThatExpressionCanBeVerified() { - VerificationContext ctx = new VerificationContext(new SimpleTestAdapter()); + VerificationContext ctx = new VerificationContext(); ctx.setVariable("foo", DataType.STRING); assertEquals(DataType.STRING, new GetVarExpression("foo").verify(ctx)); 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 b772c4a0157c..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 @@ -1,11 +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.DataType; -import com.yahoo.document.Document; -import com.yahoo.document.DocumentType; -import com.yahoo.document.DocumentUpdate; -import com.yahoo.document.Field; +import com.yahoo.document.*; import com.yahoo.document.datatypes.LongFieldValue; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.document.update.AssignValueUpdate; @@ -21,12 +17,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -55,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("Invalid expression 'SimpleExpression': Expected int input, but no input is provided", null, exp); - assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.STRING, exp); + 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 @@ -67,7 +58,7 @@ public void requireThatInputFieldsAreIncludedByDocument() throws ParseException Document doc = new Document(docType, "id:scheme:my_input::"); doc.setFieldValue("my_str", new StringFieldValue("69")); - assertNotNull(doc = Expression.execute(Expression.fromString("guard { input my_str | to_long | attribute my_lng }"), doc)); + assertNotNull(doc = Expression.execute(Expression.fromString("guard { input my_str | to_int | attribute my_lng }"), doc)); assertEquals(new LongFieldValue(69), doc.getFieldValue("my_lng")); } @@ -79,7 +70,7 @@ public void requireThatInputFieldsAreIncludedByUpdate() throws ParseException { DocumentUpdate docUpdate = new DocumentUpdate(docType, "id:scheme:my_input::"); docUpdate.addFieldUpdate(FieldUpdate.createAssign(docType.getField("my_str"), new StringFieldValue("69"))); - assertNotNull(docUpdate = Expression.execute(Expression.fromString("guard { input my_str | to_long | attribute my_lng }"), docUpdate)); + assertNotNull(docUpdate = Expression.execute(Expression.fromString("guard { input my_str | to_int | attribute my_lng }"), docUpdate)); assertEquals(0, docUpdate.fieldPathUpdates().size()); assertEquals(1, docUpdate.fieldUpdates().size()); 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 93a132e0afcd..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 @@ -10,10 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +29,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new HexDecodeExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows("Invalid expression 'hexdecode': Expected string input, but no input is provided", null, exp); + 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); } 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 39ef0963c949..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -31,7 +29,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new HexEncodeExpression(); assertVerify(DataType.LONG, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'hexencode': Expected long input, but no input is provided", null, exp); + 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); } 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 7c6074a10522..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 @@ -3,14 +3,7 @@ import com.yahoo.document.DataType; import com.yahoo.document.Field; -import com.yahoo.document.datatypes.ByteFieldValue; -import com.yahoo.document.datatypes.DoubleFieldValue; -import com.yahoo.document.datatypes.FieldValue; -import com.yahoo.document.datatypes.FloatFieldValue; -import com.yahoo.document.datatypes.IntegerFieldValue; -import com.yahoo.document.datatypes.LongFieldValue; -import com.yahoo.document.datatypes.NumericFieldValue; -import com.yahoo.document.datatypes.StringFieldValue; +import com.yahoo.document.datatypes.*; import com.yahoo.document.serialization.FieldReader; import com.yahoo.document.serialization.FieldWriter; import com.yahoo.document.serialization.XmlStream; @@ -22,11 +15,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static com.yahoo.vespa.indexinglanguage.expressions.IfThenExpression.Comparator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -47,10 +36,32 @@ public void requireThatAccessorsWork() { assertSame(ifFalse, exp.getIfFalseExpression()); } + @Test + public void requireThatRequiredInputTypeCompatibilityIsVerified() { + Expression exp = newRequiredInput(DataType.STRING, Comparator.EQ, DataType.STRING, + DataType.STRING, DataType.STRING); + assertVerify(DataType.STRING, exp, DataType.STRING); + 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("Invalid expression 'flatten': Expected string input, but no input is provided", null, new FlattenExpression() + 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() ); @@ -228,24 +239,17 @@ public void requireThatNullLeftOrRightHandSideEvaluatesToNull() { @Test public void testRequiredInputType() { - var ifExpression = new IfThenExpression(new InputExpression("int1"), + var ifExpression = new IfThenExpression(new InputExpression("field1"), Comparator.EQ, new ConstantExpression(new IntegerFieldValue(0)), wrapLikeTheParser(new ConstantExpression(new StringFieldValue("true"))), wrapLikeTheParser(new ConstantExpression(new StringFieldValue("false")))); - - SimpleTestAdapter adapter = new SimpleTestAdapter(); - adapter.createField(new Field("int1", DataType.INT)); - adapter.createField(new Field("string1", DataType.STRING)); - - ifExpression.verify(adapter); - assertNull(ifExpression.getInputType(new VerificationContext(adapter))); + assertNull(ifExpression.requiredInputType()); assertEquals(DataType.STRING, ifExpression.createdOutputType()); var expression = new ScriptExpression(new StatementExpression(ifExpression, - new AttributeExpression("string1"))); - expression.verify(adapter); - assertNull(expression.getInputType(new VerificationContext(adapter))); + new AttributeExpression(null))); + assertNull(expression.requiredInputType()); } private Expression wrapLikeTheParser(Expression expression) { diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java index c7ae3e55dcfd..298d414d6678 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/IndexExpressionTestCase.java @@ -3,6 +3,8 @@ import org.junit.Test; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertExecute; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertVerify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -27,4 +29,13 @@ public void requireThatHashCodeAndEqualsAreImplemented() { assertEquals(lhs.hashCode(), new IndexExpression("foo").hashCode()); } + @Test + public void requireThatExpressionCanBeVerified() { + assertVerify(new IndexExpression("foo")); + } + + @Test + public void requireThatExpressionCanBeExecuted() { + assertExecute(new IndexExpression("foo")); + } } 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 72e58f4e57bc..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 @@ -9,9 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -38,7 +36,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new JoinExpression(";"); assertVerify(DataType.getArray(DataType.INT), exp, DataType.STRING); assertVerify(DataType.getArray(DataType.STRING), exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'join \";\"': Expected Array input, got no value", null, exp); + 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); } 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 e2ea1425f472..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 @@ -9,9 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -30,7 +28,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new LowerCaseExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'lowercase': Expected string input, but no input is provided", null, exp); + 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); } 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 f246e498dcc8..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 @@ -2,12 +2,7 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.document.annotation.Annotation; -import com.yahoo.document.annotation.AnnotationTypes; -import com.yahoo.document.annotation.SpanList; -import com.yahoo.document.annotation.SpanNode; -import com.yahoo.document.annotation.SpanTree; -import com.yahoo.document.annotation.SpanTrees; +import com.yahoo.document.annotation.*; import com.yahoo.document.datatypes.IntegerFieldValue; import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.language.Linguistics; @@ -21,13 +16,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author bratseth @@ -57,7 +46,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new NGramExpression(new SimpleLinguistics(), 69); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'ngram 69': Expected string input, but no input is provided", null, exp); + 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); } 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 eb29387f16c2..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 @@ -9,15 +9,13 @@ import com.yahoo.language.process.Transformer; import com.yahoo.language.simple.SimpleLinguistics; import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; + import org.junit.Test; import org.mockito.Mockito; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -45,7 +43,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new NormalizeExpression(new SimpleLinguistics()); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'normalize': Expected string input, but no input is provided", null, exp); + 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); } 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 89e0de62fa54..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 @@ -7,17 +7,13 @@ import com.yahoo.document.datatypes.LongFieldValue; import com.yahoo.document.datatypes.PredicateFieldValue; import com.yahoo.document.predicate.Predicate; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; import org.mockito.Mockito; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyCtx; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -81,11 +77,11 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new OptimizePredicateExpression(); String prefix = "Invalid expression 'optimize_predicate': "; - assertVerifyThrows(prefix + "Expected predicate input, but no input is provided", null, exp); + 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(new SimpleTestAdapter()).setCurrentType(DataType.PREDICATE); + VerificationContext context = new VerificationContext().setCurrentType(DataType.PREDICATE); context.setVariable("arity", DataType.STRING); ExpressionAssert.assertVerifyThrows(prefix + "Variable 'arity' must have type int", exp, context); context.setVariable("arity", DataType.INT); 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 897240535ed7..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 @@ -7,9 +7,7 @@ import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -29,14 +27,12 @@ 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, "Invalid expression '" + exp + "': Expected input, but no input is specified"); + 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) { - var context = new VerificationContext(adapter).setCurrentType(value); - assertEquals(value, exp.setInputType(value, context)); - assertEquals(value, context.verify(exp).getCurrentType()); + assertEquals(value, new VerificationContext(adapter).setCurrentType(value).verify(exp).getCurrentType()); } public static void assertVerifyThrows(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 7c07a69d5abf..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 @@ -9,10 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -40,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("Invalid expression 'SimpleExpression': Expected int input, but no input is provided", null, exp); - assertVerifyThrows("Invalid expression 'SimpleExpression': Expected int input, got string", DataType.STRING, exp); + 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 22d4c77c6497..41f910a9ccb1 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 @@ -16,6 +16,8 @@ import java.util.List; +import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; +import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -59,6 +61,18 @@ public void requireThatHashCodeAndEqualsAreImplemented() { assertEquals(exp.hashCode(), newScript(foo, bar).hashCode()); } + @Test + public void requireThatExpressionCanBeVerified() { + Expression exp = newScript(newStatement(SimpleExpression.newConversion(DataType.INT, DataType.STRING))); + assertVerify(DataType.INT, exp, DataType.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("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 public void requireThatInputValueIsAvailableToAllStatements() { SimpleTestAdapter adapter = new SimpleTestAdapter(new Field("out-1", DataType.INT), @@ -247,6 +261,10 @@ private static StatementExpression newStatement(Expression... args) { private static class ThrowingExpression extends Expression { + public ThrowingExpression() { + super(null); + } + @Override protected void doExecute(ExecutionContext context) { throw new RuntimeException(); @@ -266,6 +284,7 @@ private static class PutCacheExpression extends Expression { private final String valueToSet; public PutCacheExpression(String keyToSet, String valueToSet) { + super(null); this.keyToSet = keyToSet; this.valueToSet = valueToSet; } @@ -289,6 +308,7 @@ private static class AssertCacheExpression extends Expression { private final String expectedValue; public AssertCacheExpression(String expectedKey, String expectedValue) { + super(null); this.expectedKey = expectedKey; this.expectedValue = expectedValue; } 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 505456393da1..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,7 +29,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SetLanguageExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'set_language': Expected string input, but no input is provided", null, exp); + 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); } 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 66b34e5109dc..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 @@ -9,10 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -39,10 +36,10 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new SetVarExpression("foo"); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'set_var foo': Expected input, but no input is provided", null, exp); + assertVerifyThrows("Invalid expression 'set_var foo': Expected any input, but no input is specified", null, exp); try { - new VerificationContext(new SimpleTestAdapter()).setVariable("foo", DataType.INT).setCurrentType(DataType.STRING).verify(exp); + new VerificationContext().setVariable("foo", DataType.INT).setCurrentType(DataType.STRING).verify(exp); fail(); } catch (VerificationException e) { assertEquals("Invalid expression 'set_var foo': Cannot set variable 'foo' to type string: It is already set to type int", e.getMessage()); diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java index 4f4f78b0d658..182823ee51de 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpression.java @@ -13,19 +13,20 @@ final class SimpleExpression extends Expression { private boolean hasVerifyValue = false; private FieldValue executeValue; private DataType verifyValue; - private final DataType requiredInput; private DataType createdOutput; public SimpleExpression() { - this(null); + super(null); } - public SimpleExpression(DataType requiredInput) { - this.requiredInput = requiredInput; + super(requiredInput); } - @Override - public boolean requiresInput() { return requiredInput != null; } + public SimpleExpression setExecuteValue(FieldValue executeValue) { + this.hasExecuteValue = true; + this.executeValue = executeValue; + return this; + } public SimpleExpression setVerifyValue(DataType verifyValue) { this.hasVerifyValue = true; @@ -33,27 +34,16 @@ public SimpleExpression setVerifyValue(DataType verifyValue) { return this; } - public SimpleExpression setExecuteValue(FieldValue executeValue) { - this.hasExecuteValue = true; - this.executeValue = executeValue; - return this; - } - public SimpleExpression setCreatedOutput(DataType createdOutput) { this.createdOutput = createdOutput; return this; } @Override - public DataType setInputType(DataType inputType, VerificationContext context) { - super.setInputType(inputType, requiredInput, context); - return createdOutput; - } - - @Override - public DataType setOutputType(DataType outputType, VerificationContext context) { - super.setOutputType(createdOutput, outputType, null, context); - return requiredInput; + protected void doExecute(ExecutionContext context) { + if (hasExecuteValue) { + context.setCurrentValue(executeValue); + } } @Override @@ -63,13 +53,6 @@ protected void doVerify(VerificationContext context) { } } - @Override - protected void doExecute(ExecutionContext context) { - if (hasExecuteValue) { - context.setCurrentValue(executeValue); - } - } - @Override public DataType createdOutputType() { return createdOutput; @@ -77,7 +60,7 @@ public DataType createdOutputType() { @Override public int hashCode() { - return hashCode(executeValue) + hashCode(verifyValue) + hashCode(createdOutput); + return hashCode(executeValue) + hashCode(verifyValue) + hashCode(requiredInputType()) + hashCode(createdOutput); } @Override @@ -87,6 +70,7 @@ public boolean equals(Object o) { if (!equals(executeValue, other.executeValue)) return false; if (hasVerifyValue != other.hasVerifyValue) return false; if (!equals(verifyValue, other.verifyValue)) return false; + if (!equals(requiredInputType(), other.requiredInputType())) return false; if (!equals(createdOutput, other.createdOutput)) return false; return true; } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java index f01400957e71..20f85cfa863d 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SimpleExpressionTestCase.java @@ -3,12 +3,9 @@ import com.yahoo.document.DataType; import com.yahoo.document.datatypes.IntegerFieldValue; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -18,12 +15,14 @@ public class SimpleExpressionTestCase { @Test public void requireThatAccessorsWork() { SimpleExpression exp = new SimpleExpression(); + assertNull(exp.requiredInputType()); assertNull(exp.createdOutputType()); assertNull(exp.execute()); - assertNull(exp.verify(new SimpleTestAdapter())); + assertNull(exp.verify()); + assertEquals(DataType.INT, new SimpleExpression(DataType.INT).requiredInputType()); assertEquals(DataType.INT, new SimpleExpression().setCreatedOutput(DataType.INT).createdOutputType()); - assertEquals(DataType.INT, new SimpleExpression().setVerifyValue(DataType.INT).verify(new SimpleTestAdapter())); + assertEquals(DataType.INT, new SimpleExpression().setVerifyValue(DataType.INT).verify()); assertEquals(new IntegerFieldValue(69), new SimpleExpression().setExecuteValue(new IntegerFieldValue(69)).execute()); } @@ -52,6 +51,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { assertEquals(exp, new SimpleExpression().setVerifyValue(DataType.INT)); exp = new SimpleExpression(DataType.INT); + assertFalse(exp.equals(new SimpleExpression(DataType.STRING))); assertEquals(exp, new SimpleExpression(DataType.INT)); exp = new SimpleExpression().setCreatedOutput(DataType.INT); 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 4c21c12d5aa3..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 @@ -10,10 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -40,7 +37,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SplitExpression(";"); assertVerify(DataType.STRING, exp, DataType.getArray(DataType.STRING)); - assertVerifyThrows("Invalid expression 'split \";\"': Expected string input, but no input is provided", null, exp); + 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); } 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 5aada78f6f72..8dea0126d566 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 @@ -4,18 +4,16 @@ import com.yahoo.document.DataType; import com.yahoo.document.datatypes.FieldValue; import com.yahoo.document.datatypes.IntegerFieldValue; +import com.yahoo.vespa.indexinglanguage.ScriptTester; import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; + import java.util.List; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -66,6 +64,21 @@ public void requireThatStatementIsFlattened() { assertEquals(newStatement(foo, bar), newStatement(newStatement(foo, bar))); } + @Test + public void requireThatRequiredInputIsDeterminedByFirstNonNullRequiredInput() { + assertEquals(DataType.INT, newStatement(SimpleExpression.newRequired(DataType.INT)).requiredInputType()); + assertEquals(DataType.INT, newStatement(new SimpleExpression(), + SimpleExpression.newRequired(DataType.INT)).requiredInputType()); + assertEquals(DataType.INT, newStatement(SimpleExpression.newRequired(DataType.INT), + SimpleExpression.newRequired(DataType.INT)).requiredInputType()); + } + + @Test + public void requireThatRequiredInputIsNullIfAnyOutputIsCreatedFirst() { + assertNull(newStatement(new SimpleExpression().setCreatedOutput(DataType.INT), + new SimpleExpression(DataType.INT)).requiredInputType()); + } + @Test public void requireThatCreatedOutputIsDeterminedByLastNonNullCreatedOutput() { assertEquals(DataType.STRING, newStatement(SimpleExpression.newOutput(DataType.STRING)).createdOutputType()); @@ -79,6 +92,7 @@ public void requireThatCreatedOutputIsDeterminedByLastNonNullCreatedOutput() { public void requireThatInternalVerificationIsPerformed() { Expression exp = newStatement(SimpleExpression.newOutput(DataType.STRING), SimpleExpression.newConversion(DataType.INT, DataType.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); 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 74b4f4dc5153..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 @@ -9,10 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -40,7 +37,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new SubstringExpression(6, 9); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'substring 6 9': Expected string input, but no input is provided", null, exp); + 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); } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java index d854a86a599c..063bfdb92446 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SummaryExpressionTestCase.java @@ -3,6 +3,8 @@ import org.junit.Test; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertExecute; +import static com.yahoo.vespa.indexinglanguage.expressions.OutputAssert.assertVerify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -27,4 +29,13 @@ public void requireThatHashCodeAndEqualsAreImplemented() { assertEquals(exp.hashCode(), new SummaryExpression("foo").hashCode()); } + @Test + public void requireThatExpressionCanBeVerified() { + assertVerify(new SummaryExpression("foo")); + } + + @Test + public void requireThatExpressionCanBeExecuted() { + assertExecute(new SummaryExpression("foo")); + } } 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 c82fc594d447..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 @@ -12,13 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -63,7 +57,7 @@ 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("Invalid expression 'switch { case \"foo\": SimpleExpression; }': Expected string input, but no input is provided", null, exp); + 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); } @@ -128,6 +122,7 @@ public void requireThatDefaultExpressionIsExecuted() { Expression exp = new SwitchExpression(cases, defaultExp); assertEvaluate(new StringFieldValue("foo"), exp, new StringFieldValue("bar")); assertEvaluate(new StringFieldValue("baz"), exp, new StringFieldValue("cox")); + assertEvaluate(null, exp, new StringFieldValue("cox")); } private static void assertEvaluate(FieldValue input, Expression exp, FieldValue expectedOutVar) { 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 a01d200e1ce1..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("Invalid expression 'this': Expected input, but no input is provided", null, exp); + 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 916d9ea7dfed..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("Invalid expression 'to_array': Expected input, but no input is provided", null, exp); + 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 3fe5dd8778d8..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("Invalid expression 'to_bool': Expected input, but no input is provided", null, exp); + 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 341f1e9c92ff..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToByteExpression(); assertVerify(DataType.INT, exp, DataType.BYTE); assertVerify(DataType.STRING, exp, DataType.BYTE); - assertVerifyThrows("Invalid expression 'to_byte': Expected input, but no input is provided", null, exp); + 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 07d5b0ddeb5f..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToDoubleExpression(); assertVerify(DataType.INT, exp, DataType.DOUBLE); assertVerify(DataType.STRING, exp, DataType.DOUBLE); - assertVerifyThrows("Invalid expression 'to_double': Expected input, but no input is provided", null, exp); + 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 edfd6b6d9a19..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 @@ -11,8 +11,8 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class ToEpochSecondExpressionTestCase { @Test @@ -28,7 +28,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToEpochSecondExpression(); assertVerify(DataType.STRING, exp, DataType.LONG); 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 provided", null, 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 cf9a124128e1..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToFloatExpression(); assertVerify(DataType.INT, exp, DataType.FLOAT); assertVerify(DataType.STRING, exp, DataType.FLOAT); - assertVerifyThrows("Invalid expression 'to_float': Expected input, but no input is provided", null, exp); + 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 a182f40e829a..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToIntegerExpression(); assertVerify(DataType.INT, exp, DataType.INT); assertVerify(DataType.STRING, exp, DataType.INT); - assertVerifyThrows("Invalid expression 'to_int': Expected input, but no input is provided", null, exp); + 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 b047edc0eb12..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToLongExpression(); assertVerify(DataType.INT, exp, DataType.LONG); assertVerify(DataType.STRING, exp, DataType.LONG); - assertVerifyThrows("Invalid expression 'to_long': Expected input, but no input is provided", null, exp); + 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 526727b70e40..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 @@ -12,9 +12,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -33,7 +31,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ToPositionExpression(); assertVerify(DataType.STRING, exp, PositionDataType.INSTANCE); - assertVerifyThrows("Invalid expression 'to_pos': Expected string input, but no input is provided", null, exp); + 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); } 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 e73e604c1cc4..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 @@ -10,9 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -32,7 +30,7 @@ public void requireThatExpressionCanBeVerified() { Expression exp = new ToStringExpression(); assertVerify(DataType.INT, exp, DataType.STRING); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'to_string': Expected input, but no input is provided", null, exp); + 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 ca5c207a7b86..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 @@ -61,7 +61,7 @@ private static void assertVerify(boolean createIfNonExistent, boolean removeIfZe ExpressionAssert.assertVerify(DataType.STRING, expression, DataType.getWeightedSet(DataType.STRING, createIfNonExistent, removeIfZero)); assertVerifyThrows("Invalid expression '" + expression + "': " + - "Expected input, but no input is provided", null, expression); + "Expected any input, but no input is specified", null, expression); } private static void assertConvert(boolean createIfNonExistent, boolean removeIfZero) { 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 8c4c328ed9c0..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 @@ -15,12 +15,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -53,7 +48,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new TokenizeExpression(new SimpleLinguistics(), new AnnotatorConfig()); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'tokenize': Expected string input, but no input is provided", null, exp); + 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); } 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 fef53ea92b38..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 @@ -9,9 +9,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Simon Thoresen Hult @@ -30,7 +28,7 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new TrimExpression(); assertVerify(DataType.STRING, exp, DataType.STRING); - assertVerifyThrows("Invalid expression 'trim': Expected string input, but no input is provided", null, exp); + 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); } diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java index 2f821907d943..a24ba1343bc0 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/VerificationContextTestCase.java @@ -2,7 +2,6 @@ package com.yahoo.vespa.indexinglanguage.expressions; import com.yahoo.document.DataType; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import org.junit.Test; import static org.junit.Assert.assertNull; @@ -15,7 +14,7 @@ public class VerificationContextTestCase { @Test public void requireThatValueCanBeSet() { - VerificationContext ctx = new VerificationContext(new SimpleTestAdapter()); + VerificationContext ctx = new VerificationContext(); DataType val = DataType.STRING; ctx.setCurrentType(val); assertSame(val, ctx.getCurrentType()); @@ -23,7 +22,7 @@ public void requireThatValueCanBeSet() { @Test public void requireThatVariablesCanBeSet() { - VerificationContext ctx = new VerificationContext(new SimpleTestAdapter()); + VerificationContext ctx = new VerificationContext(); DataType val = DataType.STRING; ctx.setVariable("foo", val); assertSame(val, ctx.getVariable("foo")); @@ -31,7 +30,7 @@ public void requireThatVariablesCanBeSet() { @Test public void requireThatClearRemovesValue() { - VerificationContext ctx = new VerificationContext(new SimpleTestAdapter()); + VerificationContext ctx = new VerificationContext(); ctx.setCurrentType(DataType.STRING); ctx.clear(); assertNull(ctx.getCurrentType()); @@ -39,7 +38,7 @@ public void requireThatClearRemovesValue() { @Test public void requireThatClearRemovesVariables() { - VerificationContext ctx = new VerificationContext(new SimpleTestAdapter()); + VerificationContext ctx = new VerificationContext(); ctx.setVariable("foo", DataType.STRING); ctx.clear(); assertNull(ctx.getVariable("foo")); 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 c14b74694a45..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 @@ -10,6 +10,7 @@ import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerify; import static com.yahoo.vespa.indexinglanguage.expressions.ExpressionAssert.assertVerifyThrows; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertFalse; /** @@ -29,8 +30,8 @@ public void requireThatHashCodeAndEqualsAreImplemented() { public void requireThatExpressionCanBeVerified() { Expression exp = new ZCurveExpression(); assertVerify(PositionDataType.INSTANCE, exp, DataType.LONG); - assertVerifyThrows("Invalid expression 'zcurve': Expected input, but no input is provided", null, exp); - assertVerifyThrows("Invalid expression 'zcurve': This requires a struct as input, but got int", DataType.INT, exp); + 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 diff --git a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java index 6857dd697241..7c371404c102 100644 --- a/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java +++ b/indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/parser/PrecedenceTestCase.java @@ -1,9 +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.parser; -import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter; import com.yahoo.vespa.indexinglanguage.expressions.Expression; -import com.yahoo.vespa.indexinglanguage.expressions.VerificationContext; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -19,11 +17,7 @@ public void requireThatMathPrecedesConcat() throws ParseException { assertEquals("33", evaluate("1 + 2 . 3")); } - private static String evaluate(String expressionString) throws ParseException { - var expression = Expression.fromString(expressionString); - // TODO: Move setInputType propagation from StatementExpression to Expression to force type resolution also when no statement? - expression.setInputType(null, new VerificationContext(new SimpleTestAdapter())); - return String.valueOf(expression.execute()); + private static String evaluate(String script) throws ParseException { + return String.valueOf(Expression.fromString(script).execute()); } - } diff --git a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/IndexingLanguageResolver.java b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/IndexingLanguageResolver.java index 2e38909ec467..4b60a9a91fe7 100644 --- a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/IndexingLanguageResolver.java +++ b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/resolvers/IndexingLanguageResolver.java @@ -58,11 +58,12 @@ public static void resolveIndexingLanguage(ParseContext context, SchemaNode inde if (indexingLanguageNode.isASTInstance(indexingElm.class)) { var expression = ((indexingElm)indexingLanguageNode.getOriginalSchemaNode()).expression; - if (expression != null && expression.requiresInput() && !context.fieldIndex().getIsInsideDoc(fieldDefinitionSymbol)) { + if (expression != null && expression.requiredInputType() != null && !context.fieldIndex().getIsInsideDoc(fieldDefinitionSymbol)) { diagnostics.add(new SchemaDiagnostic.Builder() .setRange(indexingLanguageNode.get(0).getRange()) .setMessage( - "Fields defined outside the document must start with an expression creating a value (e.g 'input ')") + "Expected " + expression.requiredInputType().getName() + " input, but no input is specified. " + + "Fields defined outside the document must start with indexing statements explicitly collecting input.") .setSeverity(DiagnosticSeverity.Error) .build()); } @@ -126,10 +127,10 @@ private void traverse(SchemaNode node, List diagnostics) { statement originalNode = (statement)node.getOriginalIndexingNode(); StatementExpression expression = originalNode.expression; - if (expression != null && expression.requiresInput() && !context.fieldIndex().getIsInsideDoc(containingFieldDefinition)) { + if (expression != null && expression.requiredInputType() != null && !context.fieldIndex().getIsInsideDoc(containingFieldDefinition)) { diagnostics.add(new SchemaDiagnostic.Builder() .setRange(node.getRange()) - .setMessage("Fields defined outside the document must start with an expression creating a value (e.g 'input ')") + .setMessage("Expected " + expression.requiredInputType().getName() + " input, but no input is specified. Fields defined outside the document must start with indexing statements explicitly collecting input.") .setSeverity(DiagnosticSeverity.Error) .build()); } diff --git a/model-integration/src/main/java/ai/vespa/embedding/huggingface/HuggingFaceEmbedder.java b/model-integration/src/main/java/ai/vespa/embedding/huggingface/HuggingFaceEmbedder.java index c8694bb82a91..d54a17609bd8 100644 --- a/model-integration/src/main/java/ai/vespa/embedding/huggingface/HuggingFaceEmbedder.java +++ b/model-integration/src/main/java/ai/vespa/embedding/huggingface/HuggingFaceEmbedder.java @@ -110,21 +110,22 @@ public void deconstruct() { tokenizer.close(); } + @SuppressWarnings("unchecked") @Override - public Tensor embed(String text, Context context, TensorType targetType) { - if (targetType.dimensions().size() != 1) { - throw new IllegalArgumentException("Error in embedding to type '" + targetType + "': should only have one dimension."); + public Tensor embed(String text, Context context, TensorType tensorType) { + if (tensorType.dimensions().size() != 1) { + throw new IllegalArgumentException("Error in embedding to type '" + tensorType + "': should only have one dimension."); } - if (!targetType.dimensions().get(0).isIndexed()) { - throw new IllegalArgumentException("Error in embedding to type '" + targetType + "': dimension should be indexed."); + if (!tensorType.dimensions().get(0).isIndexed()) { + throw new IllegalArgumentException("Error in embedding to type '" + tensorType + "': dimension should be indexed."); } var embeddingResult = lookupOrEvaluate(context, prependInstruction(text, context)); IndexedTensor tokenEmbeddings = embeddingResult.output; - if (targetType.valueType() == TensorType.Value.INT8) { - return binaryQuantization(embeddingResult, targetType); + if (tensorType.valueType() == TensorType.Value.INT8) { + return binaryQuantization(embeddingResult, tensorType); } else { - Tensor result = poolingStrategy.toSentenceEmbedding(targetType, tokenEmbeddings, embeddingResult.attentionMask); - return normalize ? normalize(result, targetType) : result; + Tensor result = poolingStrategy.toSentenceEmbedding(tensorType, tokenEmbeddings, embeddingResult.attentionMask); + return normalize ? normalize(result, tensorType) : result; } } @@ -191,21 +192,21 @@ private HuggingFaceEmbedder.HFEmbeddingResult evaluate(Context context, String t return new HFEmbeddingResult(tokenEmbeddings, attentionMask, context.getEmbedderId()); } - private Tensor binaryQuantization(HuggingFaceEmbedder.HFEmbeddingResult embeddingResult, TensorType targetType) { + private Tensor binaryQuantization(HuggingFaceEmbedder.HFEmbeddingResult embeddingResult, TensorType tensorType) { long outputDimensions = embeddingResult.output().shape()[2]; - long targetDimensions = targetType.dimensions().get(0).size().get(); - //🪆 flexibility - packing only the first 8*targetDimension float values from the model output - long targetUnpackagedDimensions = 8 * targetDimensions; - if (targetUnpackagedDimensions > outputDimensions) { - throw new IllegalArgumentException("Cannot pack " + outputDimensions + " into " + targetDimensions + " int8's"); + long targetDim = tensorType.dimensions().get(0).size().get(); + //🪆 flexibility - packing only the first 8*targetDim float values from the model output + long floatDimensions = 8 * targetDim; + if(floatDimensions > outputDimensions) { + throw new IllegalArgumentException("Cannot pack " + outputDimensions + " into " + targetDim + " int8s"); } //perform pooling and normalizing using float version before binary quantization TensorType poolingType = new TensorType.Builder(TensorType.Value.FLOAT). - indexed(targetType.indexedSubtype().dimensions().get(0).name(), targetUnpackagedDimensions) - .build(); + indexed(tensorType.indexedSubtype().dimensions().get(0).name(), + floatDimensions).build(); Tensor result = poolingStrategy.toSentenceEmbedding(poolingType, embeddingResult.output(), embeddingResult.attentionMask()); result = normalize? normalize(result, poolingType) : result; - result = binarize((IndexedTensor) result, targetType); + result = binarize((IndexedTensor) result, tensorType); return result; } @@ -247,6 +248,5 @@ private IndexedTensor createTensorRepresentation(List input, String dimens protected record HFEmbeddingResult(IndexedTensor output, Tensor attentionMask, String embedderId) {} protected record HFEmbedderCacheKey(String embedderId, Object embeddedValue) { } - }