diff --git a/pom.xml b/pom.xml
index 5f112295d..5975bb264 100644
--- a/pom.xml
+++ b/pom.xml
@@ -169,7 +169,6 @@
https://docs.spring.io/spring-framework/docs/current/javadoc-api/
https://jakarta.ee/specifications/platform/8/apidocs/
https://kafka.apache.org/30/javadoc/
- https://qpid.apache.org/releases/qpid-proton-j-0.33.7/api/
https://fasterxml.github.io/jackson-databind/javadoc/2.10/
8
diff --git a/sql/src/main/antlr4/imports/CESQLLexer.g4 b/sql/src/main/antlr4/imports/CESQLLexer.g4
index a889c058a..d83124abd 100644
--- a/sql/src/main/antlr4/imports/CESQLLexer.g4
+++ b/sql/src/main/antlr4/imports/CESQLLexer.g4
@@ -70,7 +70,7 @@ FALSE: 'FALSE';
DQUOTED_STRING_LITERAL: DQUOTA_STRING;
SQUOTED_STRING_LITERAL: SQUOTA_STRING;
-INTEGER_LITERAL: INT_DIGIT+;
+INTEGER_LITERAL: ('+' | '-')? INT_DIGIT+;
// Identifiers
diff --git a/sql/src/main/java/io/cloudevents/sql/EvaluationContext.java b/sql/src/main/java/io/cloudevents/sql/EvaluationContext.java
index e1853836f..144d183fb 100644
--- a/sql/src/main/java/io/cloudevents/sql/EvaluationContext.java
+++ b/sql/src/main/java/io/cloudevents/sql/EvaluationContext.java
@@ -18,19 +18,5 @@ public interface EvaluationContext {
*/
String expressionText();
- /**
- * Append a new exception to the evaluation context.
- * This exception will be propagated back in the evaluation result.
- *
- * @param exception exception to append
- */
- void appendException(EvaluationException exception);
-
- /**
- * Append a new exception to the evaluation context.
- * This exception will be propagated back in the evaluation result.
- *
- * @param exceptionFactory exception factory, which will automatically include expression interval and text
- */
- void appendException(EvaluationException.EvaluationExceptionFactory exceptionFactory);
+ ExceptionFactory exceptionFactory();
}
diff --git a/sql/src/main/java/io/cloudevents/sql/EvaluationException.java b/sql/src/main/java/io/cloudevents/sql/EvaluationException.java
index e5ea1f663..fff1d627d 100644
--- a/sql/src/main/java/io/cloudevents/sql/EvaluationException.java
+++ b/sql/src/main/java/io/cloudevents/sql/EvaluationException.java
@@ -20,7 +20,7 @@ public enum ErrorKind {
/**
* An implicit or an explicit casting failed.
*/
- INVALID_CAST,
+ CAST,
/**
* An event attribute was addressed, but missing.
*/
@@ -28,15 +28,19 @@ public enum ErrorKind {
/**
* Error happened while dispatching a function invocation. Reasons may be invalid function name or invalid arguments number.
*/
- FUNCTION_DISPATCH,
+ MISSING_FUNCTION,
/**
* Error happened while executing a function. This usually contains a non null cause.
*/
- FUNCTION_EXECUTION,
+ FUNCTION_EVALUATION,
/**
* Error happened while executing a math operation. Reason may be a division by zero.
*/
- MATH
+ MATH,
+ /**
+ * Any error that does not fall into the other error kinds
+ */
+ GENERIC,
}
private final ErrorKind errorKind;
diff --git a/sql/src/main/java/io/cloudevents/sql/EvaluationRuntime.java b/sql/src/main/java/io/cloudevents/sql/EvaluationRuntime.java
index 296b4f433..d4bdae48b 100644
--- a/sql/src/main/java/io/cloudevents/sql/EvaluationRuntime.java
+++ b/sql/src/main/java/io/cloudevents/sql/EvaluationRuntime.java
@@ -7,35 +7,6 @@
* The evaluation runtime takes care of the function resolution, casting and other core functionalities to execute an expression.
*/
public interface EvaluationRuntime {
-
- /**
- * Check if the cast can be executed from {@code value} to the {@code target} type.
- *
- * @param value the value to cast
- * @param target the type cast target
- * @return false if the cast trigger an error, true otherwise.
- */
- boolean canCast(Object value, Type target);
-
- /**
- * Return the {@code value} casted to the {@code target} type.
- *
- * @param ctx the evaluation context
- * @param value the value to cast
- * @param target the type cast target
- * @return the casted value, if the cast succeeds, otherwise the default value of the target type
- */
- Object cast(EvaluationContext ctx, Object value, Type target);
-
- /**
- * Return the {@code value} casted to the {@code target} type. If fails, this is going to throw an exception without the evaluation context.
- *
- * @param value the value to cast
- * @param target the type cast target
- * @return the casted value, if the cast succeeds, otherwise the default value of the target type
- */
- Object cast(Object value, Type target) throws EvaluationException;
-
/**
* Resolve a {@link Function} starting from its name and the concrete number of arguments.
*
diff --git a/sql/src/main/java/io/cloudevents/sql/ExceptionFactory.java b/sql/src/main/java/io/cloudevents/sql/ExceptionFactory.java
new file mode 100644
index 000000000..e194be836
--- /dev/null
+++ b/sql/src/main/java/io/cloudevents/sql/ExceptionFactory.java
@@ -0,0 +1,51 @@
+package io.cloudevents.sql;
+
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.misc.Interval;
+import org.antlr.v4.runtime.tree.ParseTree;
+
+public interface ExceptionFactory {
+ EvaluationException.EvaluationExceptionFactory invalidCastTarget(Class> from, Class> to);
+
+ EvaluationException.EvaluationExceptionFactory castError(Class> from, Class> to, Throwable cause);
+
+ EvaluationException missingAttribute(Interval interval, String expression, String key);
+
+ EvaluationException cannotDispatchFunction(Interval interval, String expression, String functionName, Throwable cause);
+
+ EvaluationException.EvaluationExceptionFactory functionExecutionError(String functionName, Throwable cause);
+
+ EvaluationException divisionByZero(Interval interval, String expression, Integer dividend);
+
+ EvaluationException mathError(Interval interval, String expression, String errorMessage);
+
+ static ParseException cannotParseValue(ParseTree node, Type target, Throwable cause) {
+ return new ParseException(
+ ParseException.ErrorKind.PARSE_VALUE,
+ node.getSourceInterval(),
+ node.getText(),
+ "Cannot parse to " + target.name() + ": " + cause.getMessage(),
+ cause
+ );
+ }
+
+ static ParseException recognitionError(RecognitionException e, String msg) {
+ return new ParseException(
+ ParseException.ErrorKind.RECOGNITION,
+ new Interval(e.getOffendingToken().getStartIndex(), e.getOffendingToken().getStopIndex()),
+ e.getOffendingToken().getText(),
+ "Cannot parse: " + msg,
+ e
+ );
+ }
+
+ static ParseException cannotEvaluateConstantExpression(EvaluationException exception) {
+ return new ParseException(
+ ParseException.ErrorKind.CONSTANT_EXPRESSION_EVALUATION,
+ exception.getExpressionInterval(),
+ exception.getExpressionText(),
+ "Cannot evaluate the constant expression: " + exception.getExpressionText(),
+ exception
+ );
+ }
+}
diff --git a/sql/src/main/java/io/cloudevents/sql/Function.java b/sql/src/main/java/io/cloudevents/sql/Function.java
index b0f6c6f12..314c5b89e 100644
--- a/sql/src/main/java/io/cloudevents/sql/Function.java
+++ b/sql/src/main/java/io/cloudevents/sql/Function.java
@@ -1,6 +1,7 @@
package io.cloudevents.sql;
import io.cloudevents.CloudEvent;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
@@ -15,9 +16,9 @@ public interface Function extends FunctionSignature {
* @param ctx the evaluation context
* @param evaluationRuntime the evaluation runtime
* @param event the expression input event
- * @param arguments the arguments passed to this function. Note: the arguments are already casted to the appropriate type declared in the signature
+ * @param arguments the arguments passed to this function. Note: the arguments are already cast to the appropriate type declared in the signature
* @return the return value of the function
*/
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments);
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments);
}
diff --git a/sql/src/main/java/io/cloudevents/sql/FunctionSignature.java b/sql/src/main/java/io/cloudevents/sql/FunctionSignature.java
index 894464260..b050b3a57 100644
--- a/sql/src/main/java/io/cloudevents/sql/FunctionSignature.java
+++ b/sql/src/main/java/io/cloudevents/sql/FunctionSignature.java
@@ -18,6 +18,11 @@ public interface FunctionSignature {
*/
Type typeOfParameter(int i) throws IllegalArgumentException;
+ /**
+ * @return function return type
+ */
+ Type returnType();
+
/**
* @return the arity, excluding the vararg parameter if {@code isVariadic() == true}
*/
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactory.java b/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactory.java
deleted file mode 100644
index ec03498e4..000000000
--- a/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactory.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package io.cloudevents.sql.impl;
-
-import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.ParseException;
-import io.cloudevents.sql.Type;
-import org.antlr.v4.runtime.RecognitionException;
-import org.antlr.v4.runtime.misc.Interval;
-import org.antlr.v4.runtime.tree.ParseTree;
-
-/**
- * This class includes a list of static methods to create {@link io.cloudevents.sql.ParseException} and {@link io.cloudevents.sql.EvaluationException}.
- */
-public class ExceptionFactory {
-
- private ExceptionFactory() {
- }
-
- public static EvaluationException.EvaluationExceptionFactory invalidCastTarget(Class> from, Class> to) {
- return (interval, expression) -> new EvaluationException(
- EvaluationException.ErrorKind.INVALID_CAST,
- interval,
- expression,
- "Cannot cast " + from + " to " + to + ": no cast defined.",
- null
- );
- }
-
- public static EvaluationException.EvaluationExceptionFactory castError(Class> from, Class> to, Throwable cause) {
- return (interval, expression) -> new EvaluationException(
- EvaluationException.ErrorKind.INVALID_CAST,
- interval,
- expression,
- "Cannot cast " + from + " to " + to + ": " + cause.getMessage(),
- cause
- );
- }
-
- public static EvaluationException missingAttribute(Interval interval, String expression, String key) {
- return new EvaluationException(
- EvaluationException.ErrorKind.MISSING_ATTRIBUTE,
- interval,
- expression,
- "Missing attribute " + key + " in the input event. Perhaps you should check with 'EXISTS " + key + "' if the input contains the provided key?",
- null
- );
- }
-
- public static EvaluationException cannotDispatchFunction(Interval interval, String expression, String functionName, Throwable cause) {
- return new EvaluationException(
- EvaluationException.ErrorKind.FUNCTION_DISPATCH,
- interval,
- expression,
- "Cannot dispatch function invocation to function " + functionName + ": " + cause.getMessage(),
- cause
- );
- }
-
- public static EvaluationException.EvaluationExceptionFactory functionExecutionError(String functionName, Throwable cause) {
- return (interval, expression) -> new EvaluationException(
- EvaluationException.ErrorKind.FUNCTION_EXECUTION,
- interval,
- expression,
- "Error while executing " + functionName + ": " + cause.getMessage(),
- cause
- );
- }
-
- public static EvaluationException divisionByZero(Interval interval, String expression, Integer dividend) {
- return new EvaluationException(
- EvaluationException.ErrorKind.MATH,
- interval,
- expression,
- "Division by zero: " + dividend + " / 0",
- null
- );
- }
-
- public static ParseException cannotParseValue(ParseTree node, Type target, Throwable cause) {
- return new ParseException(
- ParseException.ErrorKind.PARSE_VALUE,
- node.getSourceInterval(),
- node.getText(),
- "Cannot parse to " + target.name() + ": " + cause.getMessage(),
- cause
- );
- }
-
- public static ParseException recognitionError(RecognitionException e, String msg) {
- return new ParseException(
- ParseException.ErrorKind.RECOGNITION,
- new Interval(e.getOffendingToken().getStartIndex(), e.getOffendingToken().getStopIndex()),
- e.getOffendingToken().getText(),
- "Cannot parse: " + msg,
- e
- );
- }
-
- public static ParseException cannotEvaluateConstantExpression(EvaluationException exception) {
- return new ParseException(
- ParseException.ErrorKind.CONSTANT_EXPRESSION_EVALUATION,
- exception.getExpressionInterval(),
- exception.getExpressionText(),
- "Cannot evaluate the constant expression: " + exception.getExpressionText(),
- exception
- );
- }
-}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactoryImpl.java b/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactoryImpl.java
new file mode 100644
index 000000000..0f6b892a0
--- /dev/null
+++ b/sql/src/main/java/io/cloudevents/sql/impl/ExceptionFactoryImpl.java
@@ -0,0 +1,116 @@
+package io.cloudevents.sql.impl;
+
+import io.cloudevents.sql.EvaluationException;
+import org.antlr.v4.runtime.misc.Interval;
+
+/**
+ * This class includes a list of static methods to create {@link io.cloudevents.sql.ParseException} and {@link io.cloudevents.sql.EvaluationException}.
+ */
+public class ExceptionFactoryImpl implements io.cloudevents.sql.ExceptionFactory {
+ private final boolean shouldThrow;
+
+ public ExceptionFactoryImpl(boolean shouldThrow) {
+ this.shouldThrow = shouldThrow;
+ }
+
+ public EvaluationException.EvaluationExceptionFactory invalidCastTarget(Class> from, Class> to) {
+ return (interval, expression) -> {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.CAST,
+ interval,
+ expression,
+ "Cannot cast " + from + " to " + to + ": no cast defined.",
+ null
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ };
+ }
+
+ public EvaluationException.EvaluationExceptionFactory castError(Class> from, Class> to, Throwable cause) {
+ return (interval, expression) -> {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.CAST,
+ interval,
+ expression,
+ "Cannot cast " + from + " to " + to + ": " + cause.getMessage(),
+ cause
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ };
+ }
+
+ public EvaluationException missingAttribute(Interval interval, String expression, String key) {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.MISSING_ATTRIBUTE,
+ interval,
+ expression,
+ "Missing attribute " + key + " in the input event. Perhaps you should check with 'EXISTS " + key + "' if the input contains the provided key?",
+ null
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ }
+
+ public EvaluationException cannotDispatchFunction(Interval interval, String expression, String functionName, Throwable cause) {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.MISSING_FUNCTION,
+ interval,
+ expression,
+ "Cannot dispatch function invocation to function " + functionName + ": " + cause.getMessage(),
+ cause
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ }
+
+ public EvaluationException.EvaluationExceptionFactory functionExecutionError(String functionName, Throwable cause) {
+ return (interval, expression) -> {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.FUNCTION_EVALUATION,
+ interval,
+ expression,
+ "Error while executing " + functionName + ": " + cause.getMessage(),
+ cause
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ };
+ }
+
+ public EvaluationException divisionByZero(Interval interval, String expression, Integer dividend) {
+ return mathError(interval, expression, "Division by zero: " + dividend + " / 0");
+ }
+
+ @Override
+ public EvaluationException mathError(Interval interval, String expression, String errorMessage) {
+ final EvaluationException exception = new EvaluationException(
+ EvaluationException.ErrorKind.MATH,
+ interval,
+ expression,
+ errorMessage,
+ null
+ );
+
+ if (this.shouldThrow) {
+ throw exception;
+ }
+ return exception;
+ }
+}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/ExceptionThrower.java b/sql/src/main/java/io/cloudevents/sql/impl/ExceptionThrower.java
deleted file mode 100644
index a5bbc0d9d..000000000
--- a/sql/src/main/java/io/cloudevents/sql/impl/ExceptionThrower.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.cloudevents.sql.impl;
-
-import io.cloudevents.sql.EvaluationException;
-
-public interface ExceptionThrower {
-
- /**
- * This method might block the execution or not, depending on its implementation
- *
- * @param exception the exception to throw
- */
- void throwException(EvaluationException exception);
-}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/ExpressionInternal.java b/sql/src/main/java/io/cloudevents/sql/impl/ExpressionInternal.java
index 1a3c7da7a..0ae114dfb 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/ExpressionInternal.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/ExpressionInternal.java
@@ -2,6 +2,8 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public interface ExpressionInternal {
@@ -10,7 +12,7 @@ public interface ExpressionInternal {
String expressionText();
- Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower);
+ EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory);
T visit(ExpressionInternalVisitor visitor);
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/AccessAttributeExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/AccessAttributeExpression.java
index 63243a91a..7fd776bf4 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/AccessAttributeExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/AccessAttributeExpression.java
@@ -3,9 +3,9 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.SpecVersion;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
import java.util.Base64;
@@ -24,18 +24,15 @@ public AccessAttributeExpression(Interval expressionInterval, String expressionT
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
Object value = this.getter.apply(event);
if (value == null) {
- thrower.throwException(
- ExceptionFactory.missingAttribute(this.expressionInterval(), this.expressionText(), key)
- );
- return "";
+ return new EvaluationResult(false, exceptionFactory.missingAttribute(this.expressionInterval(), this.expressionText(), key));
}
// Because the CESQL type system is smaller than the CE type system,
// we need to coherce some values to string
- return coherceTypes(value);
+ return new EvaluationResult(coherceTypes(value));
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/AndExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/AndExpression.java
index 382fdf701..2e86281e6 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/AndExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/AndExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
+import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class AndExpression extends BaseBinaryExpression {
@@ -12,12 +14,15 @@ public AndExpression(Interval expressionInterval, String expressionText, Express
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
- boolean x = castToBoolean(runtime, exceptions, left);
- if (!x) {
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptionFactory);
+ EvaluationResult x = castToBoolean(exceptionFactory, left);
+ if (!(Boolean)x.value()) {
// Short circuit
- return false;
+ return x;
}
- return castToBoolean(runtime, exceptions, right);
+
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptionFactory);
+ return castToBoolean(exceptionFactory, right).wrapExceptions(left);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseBinaryExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseBinaryExpression.java
index 47b2ef199..1fb5311e4 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseBinaryExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseBinaryExpression.java
@@ -2,9 +2,11 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public abstract class BaseBinaryExpression extends BaseExpression {
@@ -18,14 +20,7 @@ protected BaseBinaryExpression(Interval expressionInterval, String expressionTex
this.rightOperand = rightOperand;
}
- public abstract Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions);
-
- @Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- Object left = leftOperand.evaluate(runtime, event, thrower);
- Object right = rightOperand.evaluate(runtime, event, thrower);
- return evaluate(runtime, left, right, thrower);
- }
+ public abstract EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory);
@Override
public T visit(ExpressionInternalVisitor visitor) {
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseExpression.java
index 1e62f1085..35d5923e3 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseExpression.java
@@ -1,10 +1,11 @@
package io.cloudevents.sql.impl.expressions;
-import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.Type;
-import io.cloudevents.sql.impl.ExceptionThrower;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.runtime.EvaluationContextImpl;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
import org.antlr.v4.runtime.misc.Interval;
public abstract class BaseExpression implements ExpressionInternal {
@@ -27,25 +28,25 @@ public String expressionText() {
return this.expressionText;
}
- public Boolean castToBoolean(EvaluationRuntime runtime, ExceptionThrower exceptions, Object value) {
- return (Boolean) runtime.cast(
- new EvaluationContextImpl(expressionInterval(), expressionText(), exceptions),
+ public EvaluationResult castToBoolean(ExceptionFactory exceptionFactory, EvaluationResult value) {
+ return TypeCastingProvider.cast(
+ new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory),
value,
Type.BOOLEAN
);
}
- public Integer castToInteger(EvaluationRuntime runtime, ExceptionThrower exceptions, Object value) {
- return (Integer) runtime.cast(
- new EvaluationContextImpl(expressionInterval(), expressionText(), exceptions),
+ public EvaluationResult castToInteger(ExceptionFactory exceptionFactory, EvaluationResult value) {
+ return TypeCastingProvider.cast(
+ new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory),
value,
Type.INTEGER
);
}
- public String castToString(EvaluationRuntime runtime, ExceptionThrower exceptions, Object value) {
- return (String) runtime.cast(
- new EvaluationContextImpl(expressionInterval(), expressionText(), exceptions),
+ public EvaluationResult castToString(ExceptionFactory exceptionFactory, EvaluationResult value) {
+ return TypeCastingProvider.cast(
+ new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory),
value,
Type.STRING
);
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseIntegerBinaryExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseIntegerBinaryExpression.java
index 4e5cceec0..6d53f8dc3 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseIntegerBinaryExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseIntegerBinaryExpression.java
@@ -1,8 +1,11 @@
package io.cloudevents.sql.impl.expressions;
+import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public abstract class BaseIntegerBinaryExpression extends BaseBinaryExpression {
@@ -11,16 +14,26 @@ public BaseIntegerBinaryExpression(Interval expressionInterval, String expressio
super(expressionInterval, expressionText, leftOperand, rightOperand);
}
- abstract Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions);
+ abstract EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptionFactory);
@Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptionFactory);
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptionFactory);
+
+ if (left.isMissingAttributeException() || right.isMissingAttributeException()) {
+ return left.wrapExceptions(right).copyWithDefaultValueForType(Type.INTEGER);
+ }
+
+ EvaluationResult x = castToInteger(exceptionFactory, left);
+ EvaluationResult y = castToInteger(exceptionFactory, right);
+
return this.evaluate(
runtime,
- castToInteger(runtime, exceptions, left).intValue(),
- castToInteger(runtime, exceptions, right).intValue(),
- exceptions
- );
+ (Integer)x.value(),
+ (Integer)y.value(),
+ exceptionFactory
+ ).wrapExceptions(x).wrapExceptions(y);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseUnaryExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseUnaryExpression.java
index 650b8e927..2cf3e74c7 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseUnaryExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseUnaryExpression.java
@@ -2,9 +2,12 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public abstract class BaseUnaryExpression extends BaseExpression {
@@ -16,11 +19,17 @@ public BaseUnaryExpression(Interval expressionInterval, String expressionText, E
this.internal = internal;
}
- public abstract Object evaluate(EvaluationRuntime runtime, Object value, ExceptionThrower exceptions);
+ public abstract EvaluationResult evaluate(EvaluationRuntime runtime, EvaluationResult result, ExceptionFactory exceptionFactory);
+
+ public abstract Type returnType();
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- return evaluate(runtime, internal.evaluate(runtime, event, thrower), thrower);
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult value = internal.evaluate(runtime, event, exceptionFactory);
+ if (value.isMissingAttributeException()) {
+ return value.copyWithDefaultValueForType(this.returnType());
+ }
+ return evaluate(runtime, value, exceptionFactory);
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ComparisonExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ComparisonExpression.java
new file mode 100644
index 000000000..a91f4d064
--- /dev/null
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ComparisonExpression.java
@@ -0,0 +1,56 @@
+package io.cloudevents.sql.impl.expressions;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationContextImpl;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
+import org.antlr.v4.runtime.misc.Interval;
+
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+public class ComparisonExpression extends BaseBinaryExpression {
+ public enum Comparison {
+ EQUALS(Objects::equals),
+ NOT_EQUALS((x, y) -> !Objects.equals(x, y));
+ private final BiFunction fn;
+ Comparison(BiFunction fn) {
+ this.fn = fn;
+ }
+ boolean evaluate(Object a, Object b) {
+ return this.fn.apply(a, b);
+ }
+ }
+
+ private final Comparison comparison;
+
+ public ComparisonExpression(Interval expressionInterval, String expressionText, ExpressionInternal leftOperand, ExpressionInternal rightOperand, Comparison comparison) {
+ super(expressionInterval, expressionText, leftOperand, rightOperand);
+ this.comparison = comparison;
+ }
+
+ // x = y: Boolean x Boolean -> Boolean
+ // x = y: Integer x Integer -> Boolean
+ // x = y: String x String -> Boolean
+ @Override
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptionFactory);
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptionFactory);
+
+ if (left.isMissingAttributeException() || right.isMissingAttributeException()) {
+ return left.wrapExceptions(right).copyWithDefaultValueForType(Type.BOOLEAN);
+ }
+
+ left = TypeCastingProvider.cast(
+ new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory),
+ left,
+ Type.fromValue(right.value())
+ );
+
+ return new EvaluationResult(this.comparison.evaluate(left.value(), right.value()), null, left, right);
+ }
+}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/DifferenceExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/DifferenceExpression.java
index 4f0670cb6..1f5d7a8a0 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/DifferenceExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/DifferenceExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class DifferenceExpression extends BaseIntegerBinaryExpression {
@@ -12,8 +14,8 @@ public DifferenceExpression(Interval expressionInterval, String expressionText,
}
@Override
- Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions) {
- return left - right;
+ EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptions) {
+ return new EvaluationResult(left - right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/DivisionExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/DivisionExpression.java
index 8e54f0ac9..c5af62a9b 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/DivisionExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/DivisionExpression.java
@@ -1,9 +1,9 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class DivisionExpression extends BaseIntegerBinaryExpression {
@@ -13,14 +13,11 @@ public DivisionExpression(Interval expressionInterval, String expressionText, Ex
}
@Override
- Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions) {
+ EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptionFactory) {
if (right == 0) {
- exceptions.throwException(
- ExceptionFactory.divisionByZero(expressionInterval(), expressionText(), left)
- );
- return 0;
+ return new EvaluationResult(0, exceptionFactory.divisionByZero(expressionInterval(), expressionText(), left));
}
- return left / right;
+ return new EvaluationResult(left / right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/EqualExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/EqualExpression.java
deleted file mode 100644
index fcfe6dbf5..000000000
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/EqualExpression.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package io.cloudevents.sql.impl.expressions;
-
-import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.Type;
-import io.cloudevents.sql.impl.ExceptionThrower;
-import io.cloudevents.sql.impl.ExpressionInternal;
-import io.cloudevents.sql.impl.runtime.EvaluationContextImpl;
-import org.antlr.v4.runtime.misc.Interval;
-
-import java.util.Objects;
-
-public class EqualExpression extends BaseBinaryExpression {
-
- public EqualExpression(Interval expressionInterval, String expressionText, ExpressionInternal leftOperand, ExpressionInternal rightOperand) {
- super(expressionInterval, expressionText, leftOperand, rightOperand);
- }
-
- // x = y: Boolean x Boolean -> Boolean
- // x = y: Integer x Integer -> Boolean
- // x = y: String x String -> Boolean
- @Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
- left = runtime.cast(
- new EvaluationContextImpl(expressionInterval(), expressionText(), exceptions),
- left,
- Type.fromValue(right)
- );
- return Objects.equals(left, right);
- }
-}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ExistsExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ExistsExpression.java
index d74cc5486..267156649 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ExistsExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ExistsExpression.java
@@ -2,8 +2,10 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class ExistsExpression extends BaseExpression {
@@ -16,8 +18,8 @@ public ExistsExpression(Interval expressionInterval, String expressionText, Stri
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- return hasContextAttribute(event, key);
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ return new EvaluationResult(hasContextAttribute(event, key));
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/FunctionInvocationExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/FunctionInvocationExpression.java
index 05183b851..830410997 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/FunctionInvocationExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/FunctionInvocationExpression.java
@@ -1,14 +1,12 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.CloudEvent;
-import io.cloudevents.sql.EvaluationContext;
-import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.Function;
-import io.cloudevents.sql.impl.ExceptionFactory;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.*;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
import io.cloudevents.sql.impl.runtime.EvaluationContextImpl;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
import org.antlr.v4.runtime.misc.Interval;
import java.util.ArrayList;
@@ -26,26 +24,27 @@ public FunctionInvocationExpression(Interval expressionInterval, String expressi
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- EvaluationContext context = new EvaluationContextImpl(expressionInterval(), expressionText(), thrower);
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationContext context = new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory);
Function function;
try {
function = runtime.resolveFunction(functionName, arguments.size());
} catch (Exception e) {
- thrower.throwException(
- ExceptionFactory.cannotDispatchFunction(expressionInterval(), expressionText(), functionName, e)
- );
- return "";
+ return new EvaluationResult(false, exceptionFactory.cannotDispatchFunction(expressionInterval(), expressionText(), functionName, e));
}
List computedArguments = new ArrayList<>(arguments.size());
+ List exceptions = new ArrayList<>(); // used to accumulate any exceptions encountered while evaluating the arguments to the function
for (int i = 0; i < arguments.size(); i++) {
ExpressionInternal expr = arguments.get(i);
- Object computed = expr.evaluate(runtime, event, thrower);
- Object casted = runtime
+ EvaluationResult computed = expr.evaluate(runtime, event, exceptionFactory);
+ EvaluationResult casted = TypeCastingProvider
.cast(context, computed, function.typeOfParameter(i));
- computedArguments.add(casted);
+ if (casted.causes() != null) {
+ exceptions.addAll(casted.causes());
+ }
+ computedArguments.add(casted.value());
}
return function.invoke(
@@ -53,7 +52,7 @@ public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThr
runtime,
event,
computedArguments
- );
+ ).wrapExceptions(exceptions);
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/InExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/InExpression.java
index d41c4f800..f80c13794 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/InExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/InExpression.java
@@ -2,11 +2,13 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.Type;
-import io.cloudevents.sql.impl.ExceptionThrower;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
import io.cloudevents.sql.impl.runtime.EvaluationContextImpl;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
import org.antlr.v4.runtime.misc.Interval;
import java.util.List;
@@ -26,18 +28,22 @@ public InExpression(Interval expressionInterval, String expressionText, Expressi
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- Object leftValue = leftExpression.evaluate(runtime, event, thrower);
- return setExpressions.stream()
- .anyMatch(expr -> {
- Object rightValue = runtime.cast(
- new EvaluationContextImpl(expressionInterval(), expressionText(), thrower),
- expr.evaluate(runtime, event, thrower),
- Type.fromValue(leftValue)
- );
-
- return Objects.equals(leftValue, rightValue);
- });
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult leftValue = leftExpression.evaluate(runtime, event, exceptionFactory);
+ for (ExpressionInternal setExpression : this.setExpressions) {
+ EvaluationResult rightValue = TypeCastingProvider.cast(
+ new EvaluationContextImpl(expressionInterval(), expressionText(), exceptionFactory),
+ setExpression.evaluate(runtime, event, exceptionFactory),
+ Type.fromValue(leftValue.value())
+ );
+
+ if (Objects.equals(leftValue.value(), rightValue.value())) {
+ return new EvaluationResult(true, null, leftValue, rightValue);
+ } else {
+ leftValue.wrapExceptions(rightValue);
+ }
+ }
+ return leftValue.copyWithValue(false);
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/IntegerComparisonBinaryExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/IntegerComparisonBinaryExpression.java
index bc1e2c818..81f36701b 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/IntegerComparisonBinaryExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/IntegerComparisonBinaryExpression.java
@@ -1,8 +1,11 @@
package io.cloudevents.sql.impl.expressions;
+import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
import java.util.function.BiFunction;
@@ -34,11 +37,21 @@ public IntegerComparisonBinaryExpression(Interval expressionInterval, String exp
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
- return this.operation.evaluate(
- castToInteger(runtime, exceptions, left),
- castToInteger(runtime, exceptions, right)
- );
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptionFactory);
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptionFactory);
+
+ if (left.isMissingAttributeException() || right.isMissingAttributeException()) {
+ return left.wrapExceptions(right).copyWithDefaultValueForType(Type.BOOLEAN);
+ }
+
+ EvaluationResult x = castToInteger(exceptionFactory, left);
+ EvaluationResult y = castToInteger(exceptionFactory, right);
+
+ return new EvaluationResult(this.operation.evaluate(
+ (Integer)x.value(),
+ (Integer)y.value()
+ )).wrapExceptions(x).wrapExceptions(y);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/LikeExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/LikeExpression.java
index dd6366cd3..13b7a2605 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/LikeExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/LikeExpression.java
@@ -2,9 +2,11 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
import java.util.regex.Pattern;
@@ -22,14 +24,13 @@ public LikeExpression(Interval expressionInterval, String expressionText, Expres
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- String value = castToString(
- runtime,
- thrower,
- internal.evaluate(runtime, event, thrower)
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptionFactory) {
+ EvaluationResult result = castToString(
+ exceptionFactory,
+ internal.evaluate(runtime, event, exceptionFactory)
);
- return pattern.matcher(value).matches();
+ return result.copyWithValue(pattern.matcher((String) result.value()).matches());
}
private Pattern convertLikePatternToRegex(String pattern) {
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ModuleExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ModuleExpression.java
index aa11a38c3..7ab422c06 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ModuleExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ModuleExpression.java
@@ -1,9 +1,9 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class ModuleExpression extends BaseIntegerBinaryExpression {
@@ -13,14 +13,11 @@ public ModuleExpression(Interval expressionInterval, String expressionText, Expr
}
@Override
- Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions) {
+ EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptionFactory) {
if (right == 0) {
- exceptions.throwException(
- ExceptionFactory.divisionByZero(expressionInterval(), expressionText(), left)
- );
- return 0;
+ return new EvaluationResult(0, exceptionFactory.divisionByZero(expressionInterval(), expressionText(), left));
}
- return left % right;
+ return new EvaluationResult(left % right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/MultiplicationExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/MultiplicationExpression.java
index 1a6da19e6..f240b5f20 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/MultiplicationExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/MultiplicationExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class MultiplicationExpression extends BaseIntegerBinaryExpression {
@@ -12,8 +14,8 @@ public MultiplicationExpression(Interval expressionInterval, String expressionTe
}
@Override
- Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions) {
- return left * right;
+ EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptions) {
+ return new EvaluationResult(left * right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/NegateExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/NegateExpression.java
index c3e359f60..ecccb1ad2 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/NegateExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/NegateExpression.java
@@ -1,8 +1,11 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class NegateExpression extends BaseUnaryExpression {
@@ -12,7 +15,13 @@ public NegateExpression(Interval expressionInterval, String expressionText, Expr
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object value, ExceptionThrower exceptions) {
- return -castToInteger(runtime, exceptions, value);
+ public Type returnType() {
+ return Type.INTEGER;
+ }
+
+ @Override
+ public EvaluationResult evaluate(EvaluationRuntime runtime, EvaluationResult result, ExceptionFactory exceptions) {
+ EvaluationResult x = castToInteger(exceptions, result);
+ return x.copyWithValue(-(Integer)x.value());
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/NotExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/NotExpression.java
index 7b0f5966d..5e1d1ec8a 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/NotExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/NotExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.Type;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class NotExpression extends BaseUnaryExpression {
@@ -12,7 +14,13 @@ public NotExpression(Interval expressionInterval, String expressionText, Express
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object value, ExceptionThrower exceptions) {
- return !castToBoolean(runtime, exceptions, value);
+ public Type returnType() {
+ return Type.BOOLEAN;
+ }
+
+ @Override
+ public EvaluationResult evaluate(EvaluationRuntime runtime, EvaluationResult value, ExceptionFactory exceptions) {
+ EvaluationResult x = castToBoolean(exceptions, value);
+ return x.copyWithValue(!(Boolean)x.value());
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/OrExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/OrExpression.java
index 58b72405f..618b22275 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/OrExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/OrExpression.java
@@ -1,8 +1,11 @@
package io.cloudevents.sql.impl.expressions;
+import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class OrExpression extends BaseBinaryExpression {
@@ -12,12 +15,15 @@ public OrExpression(Interval expressionInterval, String expressionText, Expressi
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
- boolean x = castToBoolean(runtime, exceptions, left);
- if (x) {
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptions) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptions);
+ EvaluationResult x = castToBoolean(exceptions, left);
+ if ((Boolean)x.value()) {
// Short circuit
- return true;
+ return x;
}
- return castToBoolean(runtime, exceptions, right);
+
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptions);
+ return castToBoolean(exceptions, right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/SumExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/SumExpression.java
index cafc6cb69..94c1a71a3 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/SumExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/SumExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class SumExpression extends BaseIntegerBinaryExpression {
@@ -12,8 +14,8 @@ public SumExpression(Interval expressionInterval, String expressionText, Express
}
@Override
- Object evaluate(EvaluationRuntime runtime, int left, int right, ExceptionThrower exceptions) {
- return left + right;
+ EvaluationResult evaluate(EvaluationRuntime runtime, int left, int right, ExceptionFactory exceptions) {
+ return new EvaluationResult(left + right);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ValueExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ValueExpression.java
index f5847a5fd..09c341ed7 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/ValueExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/ValueExpression.java
@@ -2,9 +2,11 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
import io.cloudevents.sql.impl.parser.LiteralUtils;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -18,8 +20,8 @@ public ValueExpression(Interval expressionInterval, String expressionText, Objec
}
@Override
- public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
- return value;
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory thrower) {
+ return new EvaluationResult(value);
}
@Override
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/expressions/XorExpression.java b/sql/src/main/java/io/cloudevents/sql/impl/expressions/XorExpression.java
index 67089819c..dc240f181 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/expressions/XorExpression.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/expressions/XorExpression.java
@@ -1,8 +1,10 @@
package io.cloudevents.sql.impl.expressions;
+import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import org.antlr.v4.runtime.misc.Interval;
public class XorExpression extends BaseBinaryExpression {
@@ -12,10 +14,15 @@ public XorExpression(Interval expressionInterval, String expressionText, Express
}
@Override
- public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
- return Boolean.logicalXor(
- castToBoolean(runtime, exceptions, left),
- castToBoolean(runtime, exceptions, right)
- );
+ public EvaluationResult evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionFactory exceptions) {
+ EvaluationResult left = this.getLeftOperand().evaluate(runtime, event, exceptions);
+ EvaluationResult right = this.getRightOperand().evaluate(runtime, event, exceptions);
+
+ EvaluationResult x = castToBoolean(exceptions, left);
+ EvaluationResult y = castToBoolean(exceptions, right);
+ return new EvaluationResult(Boolean.logicalXor(
+ (Boolean)x.value(),
+ (Boolean)y.value()
+ )).wrapExceptions(x).wrapExceptions(y);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/AbsFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/AbsFunction.java
new file mode 100644
index 000000000..52d404918
--- /dev/null
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/AbsFunction.java
@@ -0,0 +1,20 @@
+package io.cloudevents.sql.impl.functions;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.sql.EvaluationContext;
+import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+
+public class AbsFunction extends BaseOneArgumentFunction {
+ public AbsFunction() {
+ super("ABS", Integer.class, Integer.class);
+ }
+
+ @Override
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, Integer argument) {
+ if (argument == Integer.MIN_VALUE) {
+ return new EvaluationResult(Integer.MAX_VALUE, ctx.exceptionFactory().mathError(ctx.expressionInterval(), ctx.expressionText(), "integer overflow while computing absolute value of " + Integer.MIN_VALUE));
+ }
+ return new EvaluationResult(Math.abs(argument));
+ }
+}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseOneArgumentFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseOneArgumentFunction.java
index 0f18eb41e..5d4b1287a 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseOneArgumentFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseOneArgumentFunction.java
@@ -4,26 +4,35 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
-public abstract class BaseOneArgumentFunction extends BaseFunction {
+public abstract class BaseOneArgumentFunction extends BaseFunction {
private final Type argumentClass;
- public BaseOneArgumentFunction(String name, Class argumentClass) {
+ private final Type returnClass;
+
+ public BaseOneArgumentFunction(String name, Class argumentClass, Class returnClass) {
super(name);
this.argumentClass = Type.fromClass(argumentClass);
+ this.returnClass = Type.fromClass(returnClass);
}
- abstract Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, T argument);
+ abstract EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, T argument);
@SuppressWarnings("unchecked")
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
return this.invoke(ctx, evaluationRuntime, event, (T) arguments.get(0));
}
+ @Override
+ public Type returnType() {
+ return this.returnClass;
+ }
+
@Override
public Type typeOfParameter(int i) {
requireValidParameterIndex(i);
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseThreeArgumentFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseThreeArgumentFunction.java
index 05f211072..1b40f1de3 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseThreeArgumentFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseThreeArgumentFunction.java
@@ -4,28 +4,31 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
-public abstract class BaseThreeArgumentFunction extends BaseFunction {
+public abstract class BaseThreeArgumentFunction extends BaseFunction {
private final Type firstArg;
private final Type secondArg;
private final Type thirdArg;
+ private final Type returnType;
- public BaseThreeArgumentFunction(String name, Class firstArg, Class secondArg, Class thirdArg) {
+ public BaseThreeArgumentFunction(String name, Class firstArg, Class secondArg, Class thirdArg, Class returnClass) {
super(name);
this.firstArg = Type.fromClass(firstArg);
this.secondArg = Type.fromClass(secondArg);
this.thirdArg = Type.fromClass(thirdArg);
+ this.returnType = Type.fromClass(returnClass);
}
- abstract Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, X x, Y y, Z z);
+ abstract EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, W w, X x, Y y);
@SuppressWarnings("unchecked")
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
- return this.invoke(ctx, evaluationRuntime, event, (X) arguments.get(0), (Y) arguments.get(1), (Z) arguments.get(2));
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ return this.invoke(ctx, evaluationRuntime, event, (W) arguments.get(0), (X) arguments.get(1), (Y) arguments.get(2));
}
@Override
@@ -47,6 +50,11 @@ public int arity() {
return 3;
}
+ @Override
+ public Type returnType() {
+ return this.returnType;
+ }
+
@Override
public boolean isVariadic() {
return false;
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseTwoArgumentFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseTwoArgumentFunction.java
index 23030ea1b..944f20c3d 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseTwoArgumentFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/BaseTwoArgumentFunction.java
@@ -4,26 +4,28 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
-public abstract class BaseTwoArgumentFunction extends BaseFunction {
+public abstract class BaseTwoArgumentFunction extends BaseFunction {
private final Type firstArg;
private final Type secondArg;
+ private final Type returnType;
- public BaseTwoArgumentFunction(String name, Class firstArg, Class secondArg) {
+ public BaseTwoArgumentFunction(String name, Class firstArg, Class secondArg, Class returnClass) {
super(name);
this.firstArg = Type.fromClass(firstArg);
this.secondArg = Type.fromClass(secondArg);
-
+ this.returnType = Type.fromClass(returnClass);
}
- abstract Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, X x, Y y);
+ abstract EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, X x, Y y);
@SuppressWarnings("unchecked")
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
return this.invoke(ctx, evaluationRuntime, event, (X) arguments.get(0), (Y) arguments.get(1));
}
@@ -39,6 +41,11 @@ public Type typeOfParameter(int i) {
throw new IllegalArgumentException(); // This should be already checked by requireValidParameterIndex
}
+ @Override
+ public Type returnType() {
+ return this.returnType;
+ }
+
@Override
public int arity() {
return 2;
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/BoolFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/BoolFunction.java
index aee15c763..898f0bdb9 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/BoolFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/BoolFunction.java
@@ -4,15 +4,17 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
-public class BoolFunction extends BaseOneArgumentFunction {
+public class BoolFunction extends BaseOneArgumentFunction {
public BoolFunction() {
- super("BOOL", String.class);
+ super("BOOL", Object.class, Boolean.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
- return evaluationRuntime.cast(ctx, argument, Type.BOOLEAN);
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, Object argument) {
+ return TypeCastingProvider.cast(ctx, new EvaluationResult(argument), Type.BOOLEAN);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatFunction.java
index 0070c57db..da180f7f9 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatFunction.java
@@ -4,6 +4,7 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
import java.util.stream.Collectors;
@@ -15,10 +16,10 @@ public ConcatFunction() {
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
- return arguments.stream()
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ return new EvaluationResult(arguments.stream()
.map(o -> (String) o)
- .collect(Collectors.joining());
+ .collect(Collectors.joining()));
}
@Override
@@ -35,4 +36,9 @@ public int arity() {
public boolean isVariadic() {
return true;
}
+
+ @Override
+ public Type returnType() {
+ return Type.STRING;
+ }
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatWSFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatWSFunction.java
index e1cc14435..f8768c0bc 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatWSFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/ConcatWSFunction.java
@@ -4,6 +4,7 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.List;
import java.util.stream.Collectors;
@@ -15,11 +16,11 @@ public ConcatWSFunction() {
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
- return arguments.stream()
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ return new EvaluationResult(arguments.stream()
.skip(1)
.map(o -> (String) o)
- .collect(Collectors.joining((String) arguments.get(0)));
+ .collect(Collectors.joining((String) arguments.get(0))));
}
@Override
@@ -36,4 +37,9 @@ public int arity() {
public boolean isVariadic() {
return true;
}
+
+ @Override
+ public Type returnType() {
+ return Type.STRING;
+ }
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/InfallibleOneArgumentFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/InfallibleOneArgumentFunction.java
index b5b6367b4..566e27a02 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/InfallibleOneArgumentFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/InfallibleOneArgumentFunction.java
@@ -3,20 +3,21 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import java.util.function.Function;
-public class InfallibleOneArgumentFunction extends BaseOneArgumentFunction {
+public class InfallibleOneArgumentFunction extends BaseOneArgumentFunction {
- private final Function functionImplementation;
+ private final Function functionImplementation;
- public InfallibleOneArgumentFunction(String name, Class argumentClass, Function functionImplementation) {
- super(name, argumentClass);
+ public InfallibleOneArgumentFunction(String name, Class argumentClass, Class returnClass, Function functionImplementation) {
+ super(name, argumentClass, returnClass);
this.functionImplementation = functionImplementation;
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, T argument) {
- return this.functionImplementation.apply(argument);
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, T argument) {
+ return new EvaluationResult(this.functionImplementation.apply(argument));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/IntFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/IntFunction.java
index 82028e69d..7b5e3e289 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/IntFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/IntFunction.java
@@ -4,15 +4,17 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
-public class IntFunction extends BaseOneArgumentFunction {
+public class IntFunction extends BaseOneArgumentFunction {
public IntFunction() {
- super("INT", String.class);
+ super("INT", Object.class, Integer.class);
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
- return evaluationRuntime.cast(ctx, argument, Type.INTEGER);
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, Object argument) {
+ return TypeCastingProvider.cast(ctx, new EvaluationResult(argument), Type.INTEGER);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/IsBoolFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/IsBoolFunction.java
index c6a3f1375..7761143b1 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/IsBoolFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/IsBoolFunction.java
@@ -4,15 +4,17 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
-public class IsBoolFunction extends BaseOneArgumentFunction {
+public class IsBoolFunction extends BaseOneArgumentFunction {
public IsBoolFunction() {
- super("IS_BOOL", String.class);
+ super("IS_BOOL", String.class, Boolean.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
- return evaluationRuntime.canCast(argument, Type.BOOLEAN);
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
+ return new EvaluationResult(TypeCastingProvider.canCast(argument, Type.BOOLEAN));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/IsIntFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/IsIntFunction.java
index f9c78597a..95785b2d8 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/IsIntFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/IsIntFunction.java
@@ -4,15 +4,17 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
-public class IsIntFunction extends BaseOneArgumentFunction {
+public class IsIntFunction extends BaseOneArgumentFunction {
public IsIntFunction() {
- super("IS_INT", String.class);
+ super("IS_INT", String.class, Boolean.class);
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
- return evaluationRuntime.canCast(argument, Type.INTEGER);
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String argument) {
+ return new EvaluationResult(TypeCastingProvider.canCast(argument, Type.INTEGER));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/LeftFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/LeftFunction.java
index 563c6124b..d83dc8da2 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/LeftFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/LeftFunction.java
@@ -3,24 +3,22 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
-public class LeftFunction extends BaseTwoArgumentFunction {
+public class LeftFunction extends BaseTwoArgumentFunction {
public LeftFunction() {
- super("LEFT", String.class, Integer.class);
+ super("LEFT", String.class, Integer.class, String.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String s, Integer length) {
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String s, Integer length) {
if (length > s.length()) {
- return s;
+ return new EvaluationResult(s);
}
if (length < 0) {
- ctx.appendException(
- ExceptionFactory.functionExecutionError(name(), new IllegalArgumentException("The length of the LEFT substring is lower than 0: " + length))
- );
- return s;
+ return new EvaluationResult(s, ctx.exceptionFactory().functionExecutionError(name(), new IllegalArgumentException("The length of the LEFT substring is lower than 0: " + length)).create(ctx.expressionInterval(), ctx.expressionText()));
}
- return s.substring(0, length);
+
+ return new EvaluationResult(s.substring(0, length));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/RightFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/RightFunction.java
index b5817901c..47875ed70 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/RightFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/RightFunction.java
@@ -3,24 +3,21 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
-public class RightFunction extends BaseTwoArgumentFunction {
+public class RightFunction extends BaseTwoArgumentFunction {
public RightFunction() {
- super("RIGHT", String.class, Integer.class);
+ super("RIGHT", String.class, Integer.class, String.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String s, Integer length) {
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String s, Integer length) {
if (length > s.length()) {
- return s;
+ return new EvaluationResult(s);
}
if (length < 0) {
- ctx.appendException(
- ExceptionFactory.functionExecutionError(name(), new IllegalArgumentException("The length of the RIGHT substring is lower than 0: " + length))
- );
- return s;
+ return new EvaluationResult(s, ctx.exceptionFactory().functionExecutionError(name(), new IllegalArgumentException("The length of the RIGHT substring is lower than 0: " + length)).create(ctx.expressionInterval(), ctx.expressionText()));
}
- return s.substring(s.length() - length);
+ return new EvaluationResult(s.substring(s.length() - length));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/StringFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/StringFunction.java
index a1d53db82..0bdc93607 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/StringFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/StringFunction.java
@@ -4,15 +4,17 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Type;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
+import io.cloudevents.sql.impl.runtime.TypeCastingProvider;
-public class StringFunction extends BaseOneArgumentFunction {
+public class StringFunction extends BaseOneArgumentFunction {
public StringFunction() {
- super("STRING", Object.class);
+ super("STRING", Object.class, String.class);
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, Object argument) {
- return evaluationRuntime.cast(ctx, argument, Type.STRING);
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, Object argument) {
+ return TypeCastingProvider.cast(ctx, new EvaluationResult(argument), Type.STRING);
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringFunction.java
index 7123e1286..cb5db1568 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringFunction.java
@@ -3,23 +3,19 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
-public class SubstringFunction extends BaseTwoArgumentFunction {
+public class SubstringFunction extends BaseTwoArgumentFunction {
public SubstringFunction() {
- super("SUBSTRING", String.class, Integer.class);
+ super("SUBSTRING", String.class, Integer.class, String.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String x, Integer pos) {
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String x, Integer pos) {
try {
- return SubstringWithLengthFunction.substring(x, pos, null);
+ return new EvaluationResult(SubstringWithLengthFunction.substring(x, pos, null));
} catch (Exception e) {
- ctx.appendException(ExceptionFactory.functionExecutionError(
- name(),
- e
- ));
- return "";
+ return new EvaluationResult("", ctx.exceptionFactory().functionExecutionError(name(), e).create(ctx.expressionInterval(), ctx.expressionText()));
}
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringWithLengthFunction.java b/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringWithLengthFunction.java
index 50767507a..b81ba1f8c 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringWithLengthFunction.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/functions/SubstringWithLengthFunction.java
@@ -3,23 +3,19 @@
import io.cloudevents.CloudEvent;
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.impl.ExceptionFactory;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
-public class SubstringWithLengthFunction extends BaseThreeArgumentFunction {
+public class SubstringWithLengthFunction extends BaseThreeArgumentFunction {
public SubstringWithLengthFunction() {
- super("SUBSTRING", String.class, Integer.class, Integer.class);
+ super("SUBSTRING", String.class, Integer.class, Integer.class, String.class);
}
@Override
- Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String x, Integer pos, Integer len) {
+ EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, String x, Integer pos, Integer len) {
try {
- return substring(x, pos, len);
+ return new EvaluationResult(substring(x, pos, len));
} catch (Exception e) {
- ctx.appendException(ExceptionFactory.functionExecutionError(
- name(),
- e
- ));
- return "";
+ return new EvaluationResult("", ctx.exceptionFactory().functionExecutionError(name(), e).create(ctx.expressionInterval(), ctx.expressionText()));
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/parser/ConstantFoldingExpressionVisitor.java b/sql/src/main/java/io/cloudevents/sql/impl/parser/ConstantFoldingExpressionVisitor.java
index 8c4f76de3..6cc1ae39f 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/parser/ConstantFoldingExpressionVisitor.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/parser/ConstantFoldingExpressionVisitor.java
@@ -1,14 +1,12 @@
package io.cloudevents.sql.impl.parser;
import io.cloudevents.SpecVersion;
-import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.ExpressionInternalVisitor;
import io.cloudevents.sql.impl.expressions.BaseBinaryExpression;
import io.cloudevents.sql.impl.expressions.BaseUnaryExpression;
import io.cloudevents.sql.impl.expressions.ExistsExpression;
import io.cloudevents.sql.impl.expressions.ValueExpression;
-import io.cloudevents.sql.impl.runtime.FailFastExceptionThrower;
public class ConstantFoldingExpressionVisitor implements ExpressionInternalVisitor {
@@ -22,20 +20,6 @@ public ExpressionInternal visitBaseBinaryExpression(BaseBinaryExpression baseBin
ExpressionInternal left = baseBinaryExpression.getLeftOperand().visit(this);
ExpressionInternal right = baseBinaryExpression.getRightOperand().visit(this);
- if (left instanceof ValueExpression && right instanceof ValueExpression) {
- // I can do constant folding!
- return new ValueExpression(
- baseBinaryExpression.expressionInterval(),
- baseBinaryExpression.expressionText(),
- baseBinaryExpression.evaluate(
- EvaluationRuntime.getDefault(),
- ((ValueExpression) left).getValue(),
- ((ValueExpression) right).getValue(),
- FailFastExceptionThrower.getInstance()
- )
- );
- }
-
baseBinaryExpression.setLeftOperand(left);
baseBinaryExpression.setRightOperand(right);
return baseBinaryExpression;
@@ -54,14 +38,6 @@ public ExpressionInternal visitExistsExpression(ExistsExpression existsExpressio
public ExpressionInternal visitBaseUnaryExpression(BaseUnaryExpression baseUnaryExpression) {
ExpressionInternal inner = baseUnaryExpression.getOperand().visit(this);
- if (inner instanceof ValueExpression) {
- return new ValueExpression(
- baseUnaryExpression.expressionInterval(),
- baseUnaryExpression.expressionText(),
- baseUnaryExpression.evaluate(EvaluationRuntime.getDefault(), ((ValueExpression) inner).getValue(), FailFastExceptionThrower.getInstance())
- );
- }
-
baseUnaryExpression.setOperand(inner);
return baseUnaryExpression;
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/parser/ExpressionTranslatorVisitor.java b/sql/src/main/java/io/cloudevents/sql/impl/parser/ExpressionTranslatorVisitor.java
index 8ce88259f..86cb4f7c7 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/parser/ExpressionTranslatorVisitor.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/parser/ExpressionTranslatorVisitor.java
@@ -1,9 +1,9 @@
package io.cloudevents.sql.impl.parser;
+import io.cloudevents.sql.ExceptionFactory;
import io.cloudevents.sql.Type;
import io.cloudevents.sql.generated.CESQLParserBaseVisitor;
import io.cloudevents.sql.generated.CESQLParserParser;
-import io.cloudevents.sql.impl.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.expressions.*;
@@ -121,11 +121,11 @@ public ExpressionInternal visitBinaryComparisonExpression(CESQLParserParser.Bina
if (ctx.EQUAL() != null) {
// Equality operation is ambiguous, we have a specific implementation for it
- return new EqualExpression(ctx.getSourceInterval(), ctx.getText(), leftExpression, rightExpression);
+ return new ComparisonExpression(ctx.getSourceInterval(), ctx.getText(), leftExpression, rightExpression, ComparisonExpression.Comparison.EQUALS);
}
if (ctx.NOT_EQUAL() != null || ctx.LESS_GREATER() != null) {
// Equality operation is ambiguous, we have a specific implementation for it
- return new NotExpression(ctx.getSourceInterval(), ctx.getText(), new EqualExpression(ctx.getSourceInterval(), ctx.getText(), leftExpression, rightExpression));
+ return new ComparisonExpression(ctx.getSourceInterval(), ctx.getText(), leftExpression, rightExpression, ComparisonExpression.Comparison.NOT_EQUALS);
}
// From this onward, just operators defined on integers
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/parser/ParserImpl.java b/sql/src/main/java/io/cloudevents/sql/impl/parser/ParserImpl.java
index c0f042358..08d4a67be 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/parser/ParserImpl.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/parser/ParserImpl.java
@@ -1,12 +1,9 @@
package io.cloudevents.sql.impl.parser;
-import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.Expression;
-import io.cloudevents.sql.ParseException;
+import io.cloudevents.sql.*;
import io.cloudevents.sql.Parser;
import io.cloudevents.sql.generated.CESQLParserLexer;
import io.cloudevents.sql.generated.CESQLParserParser;
-import io.cloudevents.sql.impl.ExceptionFactory;
import io.cloudevents.sql.impl.ExpressionInternal;
import io.cloudevents.sql.impl.runtime.ExpressionImpl;
import org.antlr.v4.runtime.*;
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationContextImpl.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationContextImpl.java
index 1125079a5..fa60dce79 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationContextImpl.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationContextImpl.java
@@ -2,19 +2,20 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.impl.ExceptionThrower;
+import io.cloudevents.sql.ExceptionFactory;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import org.antlr.v4.runtime.misc.Interval;
public class EvaluationContextImpl implements EvaluationContext {
private final Interval expressionInterval;
private final String expressionText;
- private final ExceptionThrower exceptionThrower;
+ private final ExceptionFactory exceptionFactory;
- public EvaluationContextImpl(Interval expressionInterval, String expressionText, ExceptionThrower exceptionThrower) {
+ public EvaluationContextImpl(Interval expressionInterval, String expressionText, ExceptionFactory exceptionFactory) {
this.expressionInterval = expressionInterval;
this.expressionText = expressionText;
- this.exceptionThrower = exceptionThrower;
+ this.exceptionFactory = exceptionFactory;
}
@Override
@@ -28,12 +29,7 @@ public String expressionText() {
}
@Override
- public void appendException(EvaluationException exception) {
- this.exceptionThrower.throwException(exception);
- }
-
- @Override
- public void appendException(EvaluationException.EvaluationExceptionFactory exceptionFactory) {
- this.exceptionThrower.throwException(exceptionFactory.create(expressionInterval(), expressionText()));
+ public ExceptionFactory exceptionFactory() {
+ return this.exceptionFactory;
}
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationResult.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationResult.java
index d67ac1e18..d5392dc23 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationResult.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationResult.java
@@ -2,20 +2,79 @@
import io.cloudevents.sql.EvaluationException;
import io.cloudevents.sql.Result;
+import io.cloudevents.sql.Type;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class EvaluationResult implements Result {
private final Object value;
private final List exceptions;
+ private final EvaluationException latestException;
public EvaluationResult(Object value, List exceptions) {
this.value = value;
- this.exceptions = exceptions == null ? Collections.emptyList() : Collections.unmodifiableList(exceptions);
+ this.exceptions = exceptions == null ? new ArrayList<>() : exceptions;
+ this.latestException = null;
+ }
+
+ public EvaluationResult(Object value, EvaluationException exception, EvaluationResult left, EvaluationResult right) {
+ this.exceptions = Stream.concat(Stream.of(left, right).filter(Objects::nonNull).map(r -> r.exceptions).flatMap(Collection::stream), Stream.of(exception))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ this.latestException = exception;
+ this.value = value;
+ }
+
+ public EvaluationResult(Object value, EvaluationException exception) {
+ this(value, exception, null, null);
+ }
+
+ public EvaluationResult(Object value) {
+ this.value = value;
+ this.exceptions = new ArrayList<>();
+ this.latestException = null;
+ }
+
+ public EvaluationResult wrapExceptions(EvaluationResult other) {
+ if (other != null && other.exceptions != null) {
+ return this.wrapExceptions(other.exceptions);
+ }
+ return this;
+ }
+
+ public EvaluationResult wrapExceptions(List exceptions) {
+ if (!exceptions.isEmpty()) {
+ this.exceptions.addAll(exceptions);
+ }
+ return this;
+ }
+
+ public EvaluationResult copyWithValue(Object value) {
+ return new EvaluationResult(value, this.exceptions);
+ }
+
+ public EvaluationResult copyWithDefaultValueForType(Type type) {
+ Object value;
+ switch (type) {
+ case STRING:
+ value = "";
+ break;
+ case INTEGER:
+ value = 0;
+ break;
+ default:
+ value = false;
+ break;
+ }
+ return new EvaluationResult(value, this.exceptions);
+ }
+
+ // returns true is the most recent exception was a MISSING attribute exception
+ public boolean isMissingAttributeException() {
+ return (this.latestException != null && this.latestException.getKind() == EvaluationException.ErrorKind.MISSING_ATTRIBUTE);
}
/**
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeBuilder.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeBuilder.java
index e12ed6ce1..bdfd8e37c 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeBuilder.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeBuilder.java
@@ -18,7 +18,6 @@ public EvaluationRuntimeBuilder addFunction(Function function) throws IllegalArg
public EvaluationRuntime build() {
return new EvaluationRuntimeImpl(
- new TypeCastingProvider(),
functionTable
);
}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImpl.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImpl.java
index 67538f732..4f2d951a5 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImpl.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImpl.java
@@ -5,7 +5,7 @@
public class EvaluationRuntimeImpl implements EvaluationRuntime {
private static class SingletonContainer {
- private final static EvaluationRuntimeImpl INSTANCE = new EvaluationRuntimeImpl(new TypeCastingProvider(), FunctionTable.getDefaultInstance());
+ private final static EvaluationRuntimeImpl INSTANCE = new EvaluationRuntimeImpl(FunctionTable.getDefaultInstance());
}
/**
@@ -15,29 +15,12 @@ public static EvaluationRuntime getInstance() {
return EvaluationRuntimeImpl.SingletonContainer.INSTANCE;
}
- private final TypeCastingProvider typeCastingProvider;
private final FunctionTable functionTable;
- public EvaluationRuntimeImpl(TypeCastingProvider typeCastingProvider, FunctionTable functionTable) {
- this.typeCastingProvider = typeCastingProvider;
+ public EvaluationRuntimeImpl(FunctionTable functionTable) {
this.functionTable = functionTable;
}
- @Override
- public boolean canCast(Object value, Type target) {
- return this.typeCastingProvider.canCast(value, target);
- }
-
- @Override
- public Object cast(EvaluationContext ctx, Object value, Type target) {
- return this.typeCastingProvider.cast(ctx, value, target);
- }
-
- @Override
- public Object cast(Object value, Type target) throws EvaluationException {
- return this.typeCastingProvider.cast(FailFastExceptionThrower.getInstance(), value, target);
- }
-
@Override
public Function resolveFunction(String name, int args) throws IllegalStateException {
return functionTable.resolve(name, args);
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExceptionStore.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExceptionStore.java
deleted file mode 100644
index 9bb969531..000000000
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExceptionStore.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.cloudevents.sql.impl.runtime;
-
-import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.impl.ExceptionThrower;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class ExceptionStore implements ExceptionThrower {
-
- private List exceptions;
-
- ExceptionStore() {
- }
-
- @Override
- public void throwException(EvaluationException exception) {
- if (this.exceptions == null) {
- this.exceptions = new ArrayList<>();
- }
- this.exceptions.add(exception);
- }
-
- List getExceptions() {
- return exceptions;
- }
-}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExpressionImpl.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExpressionImpl.java
index 0206617da..67b8fb373 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExpressionImpl.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/ExpressionImpl.java
@@ -5,6 +5,7 @@
import io.cloudevents.sql.EvaluationRuntime;
import io.cloudevents.sql.Expression;
import io.cloudevents.sql.Result;
+import io.cloudevents.sql.impl.ExceptionFactoryImpl;
import io.cloudevents.sql.impl.ExpressionInternal;
public class ExpressionImpl implements Expression {
@@ -17,14 +18,15 @@ public ExpressionImpl(ExpressionInternal expressionInternal) {
@Override
public Result evaluate(EvaluationRuntime evaluationRuntime, CloudEvent event) {
- ExceptionStore exceptions = new ExceptionStore();
- Object value = this.expressionInternal.evaluate(evaluationRuntime, event, exceptions);
- return new EvaluationResult(value, exceptions.getExceptions());
+ ExceptionFactoryImpl exceptionFactory = new ExceptionFactoryImpl(false);
+ return this.expressionInternal.evaluate(evaluationRuntime, event, exceptionFactory);
+
}
@Override
public Object tryEvaluate(EvaluationRuntime evaluationRuntime, CloudEvent event) throws EvaluationException {
- return this.expressionInternal.evaluate(evaluationRuntime, event, FailFastExceptionThrower.getInstance());
+ ExceptionFactoryImpl exceptionFactory = new ExceptionFactoryImpl(true);
+ return this.expressionInternal.evaluate(evaluationRuntime, event, exceptionFactory).value();
}
public ExpressionInternal getExpressionInternal() {
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/FailFastExceptionThrower.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/FailFastExceptionThrower.java
deleted file mode 100644
index fdc2e1517..000000000
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/FailFastExceptionThrower.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package io.cloudevents.sql.impl.runtime;
-
-import io.cloudevents.sql.EvaluationContext;
-import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.impl.ExceptionThrower;
-import org.antlr.v4.runtime.misc.Interval;
-
-public class FailFastExceptionThrower implements ExceptionThrower, EvaluationContext {
-
- private static class SingletonContainer {
- private final static FailFastExceptionThrower INSTANCE = new FailFastExceptionThrower();
- }
-
- public static FailFastExceptionThrower getInstance() {
- return FailFastExceptionThrower.SingletonContainer.INSTANCE;
- }
-
- @Override
- public void throwException(EvaluationException exception) {
- throw exception;
- }
-
- @Override
- public Interval expressionInterval() {
- return Interval.INVALID;
- }
-
- @Override
- public String expressionText() {
- return "";
- }
-
- @Override
- public void appendException(EvaluationException exception) {
- throwException(exception);
- }
-
- @Override
- public void appendException(EvaluationException.EvaluationExceptionFactory exceptionFactory) {
- throwException(exceptionFactory.create(expressionInterval(), expressionText()));
- }
-}
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/FunctionTable.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/FunctionTable.java
index b74de42f7..b456f6c81 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/FunctionTable.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/FunctionTable.java
@@ -12,18 +12,18 @@ public class FunctionTable {
private static class SingletonContainer {
private final static FunctionTable INSTANCE = new FunctionTable(
Stream.of(
- new InfallibleOneArgumentFunction<>("ABS", Integer.class, Math::abs),
+ new AbsFunction(),
new IntFunction(),
new BoolFunction(),
new StringFunction(),
new IsBoolFunction(),
new IsIntFunction(),
- new InfallibleOneArgumentFunction<>("LENGTH", String.class, String::length),
+ new InfallibleOneArgumentFunction<>("LENGTH", String.class, Integer.class, String::length),
new ConcatFunction(),
new ConcatWSFunction(),
- new InfallibleOneArgumentFunction<>("LOWER", String.class, String::toLowerCase),
- new InfallibleOneArgumentFunction<>("UPPER", String.class, String::toUpperCase),
- new InfallibleOneArgumentFunction<>("TRIM", String.class, String::trim),
+ new InfallibleOneArgumentFunction<>("LOWER", String.class, String.class, String::toLowerCase),
+ new InfallibleOneArgumentFunction<>("UPPER", String.class, String.class, String::toUpperCase),
+ new InfallibleOneArgumentFunction<>("TRIM", String.class, String.class, String::trim),
new LeftFunction(),
new RightFunction(),
new SubstringFunction(),
diff --git a/sql/src/main/java/io/cloudevents/sql/impl/runtime/TypeCastingProvider.java b/sql/src/main/java/io/cloudevents/sql/impl/runtime/TypeCastingProvider.java
index ddfc6c468..418517cee 100644
--- a/sql/src/main/java/io/cloudevents/sql/impl/runtime/TypeCastingProvider.java
+++ b/sql/src/main/java/io/cloudevents/sql/impl/runtime/TypeCastingProvider.java
@@ -2,13 +2,12 @@
import io.cloudevents.sql.EvaluationContext;
import io.cloudevents.sql.Type;
-import io.cloudevents.sql.impl.ExceptionFactory;
import java.util.Objects;
public class TypeCastingProvider {
- boolean canCast(Object value, Type target) {
+ public static boolean canCast(Object value, Type target) {
if (target.valueClass().equals(value.getClass())) {
return true;
}
@@ -22,7 +21,7 @@ boolean canCast(Object value, Type target) {
return false;
}
}
- return false;
+ return value instanceof Boolean;
case BOOLEAN:
if (value instanceof String) {
try {
@@ -31,58 +30,55 @@ boolean canCast(Object value, Type target) {
} catch (IllegalArgumentException e) {
return false;
}
- }
- return false;
+ } else return value instanceof Integer;
}
return true;
}
- Object cast(EvaluationContext ctx, Object value, Type target) {
- Objects.requireNonNull(value);
- if (target.valueClass().equals(value.getClass())) {
- return value;
+ public static EvaluationResult cast(EvaluationContext ctx, EvaluationResult result, Type target) {
+ Objects.requireNonNull(result);
+ Objects.requireNonNull(result.value());
+ if (target.valueClass().equals(result.value().getClass())) {
+ return result;
}
switch (target) {
case ANY:
- return value;
+ return result;
case STRING:
- return Objects.toString(value);
+ return result.copyWithValue(Objects.toString(result.value()));
case INTEGER:
- if (value instanceof String) {
+ if (result.value() instanceof String) {
try {
- return Integer.parseInt((String) value);
+ return result.copyWithValue(Integer.parseInt((String) result.value()));
} catch (NumberFormatException e) {
- ctx.appendException(
- ExceptionFactory.castError(String.class, Integer.class, e)
- );
+ return new EvaluationResult(0, ctx.exceptionFactory().castError(String.class, Integer.class, e).create(ctx.expressionInterval(), ctx.expressionText()));
+ }
+ } else if (result.value() instanceof Boolean) {
+ if ((Boolean) result.value()) {
+ return result.copyWithValue(1);
}
+ return result.copyWithValue(0);
} else {
- ctx.appendException(
- ExceptionFactory.invalidCastTarget(value.getClass(), target.valueClass())
- );
+ return new EvaluationResult(0, ctx.exceptionFactory().invalidCastTarget(result.getClass(), target.valueClass()).create(ctx.expressionInterval(), ctx.expressionText()));
}
- return 0;
case BOOLEAN:
- if (value instanceof String) {
+ if (result.value() instanceof String) {
try {
- return parseBool((String) value);
+ return result.copyWithValue(parseBool((String) result.value()));
} catch (IllegalArgumentException e) {
- ctx.appendException(
- ExceptionFactory.castError(String.class, Boolean.class, e)
- );
+ return new EvaluationResult(false, ctx.exceptionFactory().castError(String.class, Boolean.class, e).create(ctx.expressionInterval(), ctx.expressionText()));
}
+ } else if (result.value() instanceof Integer) {
+ return result.copyWithValue(((Integer) result.value()) != 0);
} else {
- ctx.appendException(
- ExceptionFactory.invalidCastTarget(value.getClass(), target.valueClass())
- );
+ return new EvaluationResult(false, ctx.exceptionFactory().invalidCastTarget(result.getClass(), target.getClass()).create(ctx.expressionInterval(), ctx.expressionText()));
}
- return false;
}
// This should never happen
throw new IllegalArgumentException("target type doesn't correspond to a known type");
}
- private boolean parseBool(String val) {
+ private static boolean parseBool(String val) {
switch (val.toLowerCase()) {
case "true":
return true;
diff --git a/sql/src/test/java/io/cloudevents/sql/CustomFunctionsTest.java b/sql/src/test/java/io/cloudevents/sql/CustomFunctionsTest.java
index 9e91b4890..e77caad01 100644
--- a/sql/src/test/java/io/cloudevents/sql/CustomFunctionsTest.java
+++ b/sql/src/test/java/io/cloudevents/sql/CustomFunctionsTest.java
@@ -5,6 +5,7 @@
import io.cloudevents.core.test.Data;
import io.cloudevents.sql.impl.functions.BaseFunction;
import io.cloudevents.sql.impl.functions.InfallibleOneArgumentFunction;
+import io.cloudevents.sql.impl.runtime.EvaluationResult;
import io.cloudevents.sql.impl.runtime.EvaluationRuntimeBuilder;
import org.junit.jupiter.api.Test;
@@ -21,6 +22,7 @@ void addSimpleFunction() {
.addFunction(new InfallibleOneArgumentFunction<>(
"MY_STRING_PREDICATE",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
))
.build();
@@ -37,12 +39,12 @@ void addSimpleFunction() {
Parser.parseDefault("MY_STRING_PREDICATE('abc', 'xyz')")
.evaluate(runtime, Data.V1_MIN)
)
- .hasFailure(EvaluationException.ErrorKind.FUNCTION_DISPATCH);
+ .hasFailure(EvaluationException.ErrorKind.MISSING_FUNCTION);
assertThat(
Parser.parseDefault("MY_STRING_PR('abc', 'xyz')")
.evaluate(runtime, Data.V1_MIN)
)
- .hasFailure(EvaluationException.ErrorKind.FUNCTION_DISPATCH);
+ .hasFailure(EvaluationException.ErrorKind.MISSING_FUNCTION);
}
@Test
@@ -55,7 +57,7 @@ void addVariadicFunction() {
Parser.parseDefault("MY_STRING_FN('abc')")
.evaluate(runtime, Data.V1_MIN)
)
- .hasFailure(EvaluationException.ErrorKind.FUNCTION_DISPATCH);
+ .hasFailure(EvaluationException.ErrorKind.MISSING_FUNCTION);
assertThat(
Parser.parseDefault("MY_STRING_FN('abc', 'b')")
.evaluate(runtime, Data.V1_MIN)
@@ -85,6 +87,7 @@ void addSimpleFunctionAndVariadicFunction() {
.addFunction(new InfallibleOneArgumentFunction<>(
"MY_STRING_FN",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
))
.addFunction(new VariadicMockFunction("MY_STRING_FN", 2, Type.STRING))
@@ -126,6 +129,7 @@ void cannotAddVariadicWithFixedArgsLowerThanMaxArgsOverload() {
.addFunction(new InfallibleOneArgumentFunction<>(
"MY_STRING_FN",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
));
@@ -153,6 +157,7 @@ void addSimpleFunctionFails() {
.addFunction(new InfallibleOneArgumentFunction<>(
"MY_STRING_FN",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
));
@@ -160,6 +165,7 @@ void addSimpleFunctionFails() {
new InfallibleOneArgumentFunction<>(
"MY_STRING_FN",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
)
)).isInstanceOf(IllegalArgumentException.class);
@@ -167,6 +173,7 @@ void addSimpleFunctionFails() {
new InfallibleOneArgumentFunction<>(
"MY_STRING_FN",
Integer.class,
+ Boolean.class,
s -> s % 2 == 0
)
)).isInstanceOf(IllegalArgumentException.class);
@@ -178,6 +185,7 @@ void customFunctionSpecTest() {
.addFunction(new InfallibleOneArgumentFunction<>(
"MY_STRING_PREDICATE",
String.class,
+ Boolean.class,
s -> s.length() % 2 == 0
))
.build();
@@ -211,8 +219,8 @@ private VariadicMockFunction(String name, int fixedArgs, Type argsType) {
}
@Override
- public Object invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
- return arguments.size();
+ public EvaluationResult invoke(EvaluationContext ctx, EvaluationRuntime evaluationRuntime, CloudEvent event, List arguments) {
+ return new EvaluationResult(arguments.size());
}
@Override
@@ -229,6 +237,11 @@ public int arity() {
public boolean isVariadic() {
return true;
}
+
+ @Override
+ public Type returnType() {
+ return Type.INTEGER;
+ }
}
}
diff --git a/sql/src/test/java/io/cloudevents/sql/TCKTestSuite.java b/sql/src/test/java/io/cloudevents/sql/TCKTestSuite.java
index d48b7a3c4..3a5a16b89 100644
--- a/sql/src/test/java/io/cloudevents/sql/TCKTestSuite.java
+++ b/sql/src/test/java/io/cloudevents/sql/TCKTestSuite.java
@@ -78,15 +78,15 @@ public CloudEvent getTestInputEvent() {
public EvaluationException.ErrorKind getEvaluationExceptionErrorKind() {
switch (this.error) {
case CAST:
- return EvaluationException.ErrorKind.INVALID_CAST;
+ return EvaluationException.ErrorKind.CAST;
case MATH:
return EvaluationException.ErrorKind.MATH;
case MISSING_FUNCTION:
- return EvaluationException.ErrorKind.FUNCTION_DISPATCH;
+ return EvaluationException.ErrorKind.MISSING_FUNCTION;
case MISSING_ATTRIBUTE:
return EvaluationException.ErrorKind.MISSING_ATTRIBUTE;
case FUNCTION_EVALUATION:
- return EvaluationException.ErrorKind.FUNCTION_EXECUTION;
+ return EvaluationException.ErrorKind.FUNCTION_EVALUATION;
}
return null;
}
@@ -124,8 +124,7 @@ public Stream> tckTestCases() {
try {
return mapper.readValue(this.getClass().getResource(fileName), TestSuiteModel.class);
} catch (IOException e) {
- e.printStackTrace();
- return null;
+ throw new RuntimeException(fileName, e);
}
})
.filter(Objects::nonNull)
diff --git a/sql/src/test/java/io/cloudevents/sql/impl/parser/ConstantFoldingTest.java b/sql/src/test/java/io/cloudevents/sql/impl/parser/ConstantFoldingTest.java
index f219ce5b6..730f5189b 100644
--- a/sql/src/test/java/io/cloudevents/sql/impl/parser/ConstantFoldingTest.java
+++ b/sql/src/test/java/io/cloudevents/sql/impl/parser/ConstantFoldingTest.java
@@ -12,20 +12,6 @@
import static org.assertj.core.api.Assertions.assertThat;
public class ConstantFoldingTest {
-
- @Test
- void withBinaryExpression() {
- Expression expression = Parser.getDefault().parse("1 + 2");
- assertThat(expression)
- .isInstanceOf(ExpressionImpl.class);
-
- ExpressionInternal internal = ((ExpressionImpl) expression).getExpressionInternal();
- assertThat(internal)
- .isInstanceOf(ValueExpression.class)
- .extracting(v -> ((ValueExpression) v).getValue())
- .isEqualTo(3);
- }
-
@Test
void withUnaryExpression() {
Expression expression = Parser.getDefault().parse("-1");
diff --git a/sql/src/test/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImplTest.java b/sql/src/test/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImplTest.java
deleted file mode 100644
index b258c87a4..000000000
--- a/sql/src/test/java/io/cloudevents/sql/impl/runtime/EvaluationRuntimeImplTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package io.cloudevents.sql.impl.runtime;
-
-import io.cloudevents.sql.EvaluationException;
-import io.cloudevents.sql.EvaluationRuntime;
-import io.cloudevents.sql.Type;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-
-public class EvaluationRuntimeImplTest {
-
- @Test
- void castingFails() {
- assertThatCode(() -> EvaluationRuntime.getDefault().cast("123", Type.BOOLEAN))
- .isInstanceOf(EvaluationException.class);
- }
-
- @Test
- void castingSucceeds() {
- assertThat(EvaluationRuntime.getDefault().cast("TRUE", Type.BOOLEAN))
- .isEqualTo(true);
- }
-
-}
diff --git a/sql/src/test/resources/tck/README.md b/sql/src/test/resources/tck/README.md
index 5852abcfe..64b0cafa5 100644
--- a/sql/src/test/resources/tck/README.md
+++ b/sql/src/test/resources/tck/README.md
@@ -23,6 +23,7 @@ The `error` values could be any of the following:
* `parse`: Error while parsing the expression
* `math`: Math error while evaluating a math operator
* `cast`: Casting error
-* `missingAttribute`: Addressed a missing attribute
* `missingFunction`: Addressed a missing function
* `functionEvaluation`: Error while evaluating a function
+* `missingAttribute`: Error due to a missing attribute
+* `generic`: A generic error
diff --git a/sql/src/test/resources/tck/binary_comparison_operators.yaml b/sql/src/test/resources/tck/binary_comparison_operators.yaml
index 94a3071bb..f1f70f842 100644
--- a/sql/src/test/resources/tck/binary_comparison_operators.yaml
+++ b/sql/src/test/resources/tck/binary_comparison_operators.yaml
@@ -18,6 +18,10 @@ tests:
- name: abc is equal to abc
expression: "'abc' = 'abc'"
result: true
+ - name: Equals operator returns false when encountering a missing attribute
+ expression: missing = 2
+ result: false
+ error: missingAttribute
- name: True is not equal to false
expression: TRUE != FALSE
@@ -37,6 +41,10 @@ tests:
- name: abc is not equal to abc
expression: "'abc' != 'abc'"
result: false
+ - name: Not equal operator returns false when encountering a missing attribute
+ expression: missing != 2
+ result: false
+ error: missingAttribute
- name: True is not equal to false (diamond operator)
expression: TRUE <> FALSE
@@ -56,6 +64,10 @@ tests:
- name: abc is not equal to abc (diamond operator)
expression: "'abc' <> 'abc'"
result: false
+ - name: Diamond operator returns false when encountering a missing attribute
+ expression: missing <> 2
+ result: false
+ error: missingAttribute
- name: 1 is less or equal than 2
expression: 2 <= 2
@@ -81,6 +93,10 @@ tests:
- name: 2 is greater than 2
expression: 2 > 2
result: false
+ - name: Less than or equal operator returns false when encountering a missing attribute
+ expression: missing <= 2
+ result: false
+ error: missingAttribute
- name: implicit casting with string as right type
expression: "true = 'TRUE'"
diff --git a/sql/src/test/resources/tck/binary_logical_operators.yaml b/sql/src/test/resources/tck/binary_logical_operators.yaml
index 9b5d4ec96..bafc8e498 100644
--- a/sql/src/test/resources/tck/binary_logical_operators.yaml
+++ b/sql/src/test/resources/tck/binary_logical_operators.yaml
@@ -12,6 +12,13 @@ tests:
- name: True and true
expression: TRUE AND TRUE
result: true
+ - name: AND operator is short circuit evaluated
+ expression: "false and (1 != 1 / 0)"
+ result: false
+ - name: AND operator is NOT short circuit evaluated when the first operand evaluates to true
+ expression: "true and (1 = 1 / 0)"
+ error: math
+ result: false
- name: False or false
expression: FALSE OR FALSE
@@ -25,6 +32,13 @@ tests:
- name: True or true
expression: TRUE OR TRUE
result: true
+ - name: OR operator is short circuit evaluated
+ expression: "true or (1 != 1 / 0)"
+ result: true
+ - name: OR operator is NOT short circuit evaluated when the first operand evaluates to false
+ expression: "false or (1 = 1 / 0)"
+ error: math
+ result: false
- name: False xor false
expression: FALSE XOR FALSE
diff --git a/sql/src/test/resources/tck/binary_math_operators.yaml b/sql/src/test/resources/tck/binary_math_operators.yaml
index 57753288c..d1d0417ee 100644
--- a/sql/src/test/resources/tck/binary_math_operators.yaml
+++ b/sql/src/test/resources/tck/binary_math_operators.yaml
@@ -21,6 +21,14 @@ tests:
expression: 5 % 0
result: 0
error: math
+ - name: Missing attribute in division results in missing attribute error, not divide by 0 error
+ expression: missing / 0
+ result: 0
+ error: missingAttribute
+ - name: Missing attribute in modulo results in missing attribute error, not divide by 0 error
+ expression: missing % 0
+ result: 0
+ error: missingAttribute
- name: Positive plus positive number
expression: 4 + 1
@@ -50,11 +58,6 @@ tests:
- name: Implicit casting, with both values string
expression: "'5' + '3'"
result: 8
- - name: Implicit casting, with invalid boolean value
+ - name: Implicit casting, with boolean value
expression: "5 + TRUE"
- result: 5
- error: cast
- - name: Implicit casting, with invalid string value
- expression: "'5avc4' + 10"
- result: 10
- error: cast
+ result: 6
diff --git a/sql/src/test/resources/tck/casting_functions.yaml b/sql/src/test/resources/tck/casting_functions.yaml
index f5be8ac66..59349b89f 100644
--- a/sql/src/test/resources/tck/casting_functions.yaml
+++ b/sql/src/test/resources/tck/casting_functions.yaml
@@ -12,10 +12,12 @@ tests:
- name: Cast identity -1
expression: INT(-1)
result: -1
- - name: Invalid cast from boolean to int
+ - name: Cast from TRUE to int
expression: INT(TRUE)
+ result: 1
+ - name: Cast from FALSE to int
+ expression: INT(FALSE)
result: 0
- error: cast
- name: Invalid cast from string to int
expression: INT('ABC')
result: 0
@@ -37,10 +39,18 @@ tests:
expression: BOOL('ABC')
result: false
error: cast
- - name: Invalid cast from int to boolean
+ - name: Cast from 1 to boolean
expression: BOOL(1)
+ result: true
+ - name: Cast from 0 to boolean
+ expression: BOOL(0)
result: false
- error: cast
+ - name: Cast from 100 to boolean
+ expression: BOOL(100)
+ result: true
+ - name: Cast from -50 to boolean
+ expression: BOOL(-50)
+ result: true
- name: Cast TRUE to string
expression: STRING(TRUE)
@@ -57,33 +67,3 @@ tests:
- name: Cast identity "abc"
expression: STRING("abc")
result: "abc"
-
- - name: "'true' is a boolean"
- expression: IS_BOOL('true')
- result: true
- - name: "'FALSE' is a boolean"
- expression: IS_BOOL('FALSE')
- result: true
- - name: 1 is not a boolean
- expression: IS_BOOL(1)
- result: false
- - name: "'abc' is not a boolean"
- expression: IS_BOOL('abc')
- result: false
-
- - name: "'-1' is an int"
- expression: IS_INT('-1')
- result: true
- - name: "'1' is an int"
- expression: IS_INT('1')
- result: true
- - name: true is not an int
- expression: IS_INT(TRUE)
- result: false
- - name: "'abc' is not an int"
- expression: IS_INT('abc')
- result: false
-
- - name: IS_STRING does not exists
- expression: IS_STRING('ABC')
- error: missingFunction
diff --git a/sql/src/test/resources/tck/context_attributes_access.yaml b/sql/src/test/resources/tck/context_attributes_access.yaml
index 990a771e0..f3e46622d 100644
--- a/sql/src/test/resources/tck/context_attributes_access.yaml
+++ b/sql/src/test/resources/tck/context_attributes_access.yaml
@@ -17,7 +17,7 @@ tests:
id: myId
source: localhost.localdomain
type: myType
- result: ""
+ result: false
error: missingAttribute
- name: Access to optional boolean extension
expression: mybool
diff --git a/sql/src/test/resources/tck/integer_builtin_functions.yaml b/sql/src/test/resources/tck/integer_builtin_functions.yaml
index 988a201f6..3da97c562 100644
--- a/sql/src/test/resources/tck/integer_builtin_functions.yaml
+++ b/sql/src/test/resources/tck/integer_builtin_functions.yaml
@@ -9,3 +9,8 @@ tests:
- name: ABS (3)
expression: ABS(0)
result: 0
+ - name: ABS overflow
+ expression: ABS(-2147483648)
+ result: 2147483647
+ error: math
+
diff --git a/sql/src/test/resources/tck/like_expression.yaml b/sql/src/test/resources/tck/like_expression.yaml
index b6bc5a18b..d44d46705 100644
--- a/sql/src/test/resources/tck/like_expression.yaml
+++ b/sql/src/test/resources/tck/like_expression.yaml
@@ -116,3 +116,14 @@ tests:
- name: With type coercion from bool (4)
expression: "FALSE LIKE 'fal%'"
result: true
+
+ - name: Invalid string literal in comparison causes parse error
+ expression: "x LIKE 123"
+ result: false
+ error: parse
+ eventOverrides:
+ x: "123"
+ - name: Missing attribute returns empty string
+ expression: "missing LIKE 'missing'"
+ result: false
+ error: missingAttribute
diff --git a/sql/src/test/resources/tck/negate_operator.yaml b/sql/src/test/resources/tck/negate_operator.yaml
index c8721b1f4..c4ab30748 100644
--- a/sql/src/test/resources/tck/negate_operator.yaml
+++ b/sql/src/test/resources/tck/negate_operator.yaml
@@ -14,7 +14,11 @@ tests:
expression: --'10'
result: 10
- - name: Invalid boolean cast
+ - name: Minus with boolean cast
expression: -TRUE
+ result: -1
+
+ - name: Minus with missing attribute
+ expression: -missing
result: 0
- error: cast
+ error: missingAttribute
diff --git a/sql/src/test/resources/tck/not_operator.yaml b/sql/src/test/resources/tck/not_operator.yaml
index 519af4141..0c7e157c0 100644
--- a/sql/src/test/resources/tck/not_operator.yaml
+++ b/sql/src/test/resources/tck/not_operator.yaml
@@ -16,5 +16,9 @@ tests:
- name: Invalid int cast
expression: NOT 10
- result: true
- error: cast
+ result: false
+
+ - name: Not missing attribute
+ expression: NOT missing
+ result: false
+ error: missingAttribute
diff --git a/sql/src/test/resources/tck/spec_examples.yaml b/sql/src/test/resources/tck/spec_examples.yaml
index 92ce148be..9aada2259 100644
--- a/sql/src/test/resources/tck/spec_examples.yaml
+++ b/sql/src/test/resources/tck/spec_examples.yaml
@@ -62,3 +62,16 @@ tests:
eventOverrides:
subject: Francesco Guardiani
result: true
+
+ - name: Missing attribute (1)
+ expression: true AND (missing = "")
+ result: false
+ error: missingAttribute
+ - name: Missing attribute (2)
+ expression: missing * 5
+ result: 0
+ error: missingAttribute
+ - name: Missing attribute (3)
+ expression: 1 / missing
+ result: 0
+ error: missingAttribute
diff --git a/sql/src/test/resources/tck/string_builtin_functions.yaml b/sql/src/test/resources/tck/string_builtin_functions.yaml
index a7fa2a47c..c7200d95f 100644
--- a/sql/src/test/resources/tck/string_builtin_functions.yaml
+++ b/sql/src/test/resources/tck/string_builtin_functions.yaml
@@ -35,6 +35,7 @@ tests:
- name: CONCAT_WS without arguments doesn't exist
expression: CONCAT_WS()
error: missingFunction
+ result: false
- name: LOWER (1)
expression: "LOWER('ABC')"
diff --git a/sql/src/test/resources/tck/subscriptions_api_recreations.yaml b/sql/src/test/resources/tck/subscriptions_api_recreations.yaml
index 9bd8659d3..d513e67c1 100644
--- a/sql/src/test/resources/tck/subscriptions_api_recreations.yaml
+++ b/sql/src/test/resources/tck/subscriptions_api_recreations.yaml
@@ -17,6 +17,7 @@ tests:
myext: "customext"
- name: Prefix filter on missing string extension
expression: "myext LIKE 'custom%'"
+ result: false
error: missingAttribute
- name: Suffix filter (1)
@@ -36,6 +37,7 @@ tests:
myext: "customext"
- name: Suffix filter on missing string extension
expression: "myext LIKE '%ext'"
+ result: false
error: missingAttribute
- name: Exact filter (1)
@@ -55,6 +57,7 @@ tests:
myext: "customext"
- name: Exact filter on missing string extension
expression: "myext = 'customext'"
+ result: false
error: missingAttribute
- name: Prefix filter AND Suffix filter (1)
@@ -77,9 +80,10 @@ tests:
type: "com.github.error"
- name: Prefix AND Suffix filter (4)
expression: "type LIKE 'example.%' AND myext LIKE 'custom%'"
- error: missingAttribute
+ result: false
eventOverrides:
type: "example.event.type"
+ error: missingAttribute
- name: Prefix OR Suffix filter (1)
expression: "id LIKE 'my%' OR source LIKE '%.ca'"
@@ -107,20 +111,20 @@ tests:
source: "http://www.some-website.com"
- name: Disjunctive Normal Form (1)
- expresion: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
+ expression: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
result: true
eventOverrides:
id: "myId"
type: "example.event.success"
- name: Disjunctive Normal Form (2)
- expresion: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
+ expression: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
result: true
eventOverrides:
id: "notmyId"
type: "example.event.warning"
source: "http://localhost.localdomain"
- name: Disjunctive Normal Form (3)
- expresion: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
+ expression: "(id = 'myId' AND type LIKE '%.success') OR (id = 'notmyId' AND source LIKE 'http://%' AND type LIKE '%.warning')"
result: false
eventOverrides:
id: "notmyId"
@@ -150,18 +154,19 @@ tests:
source: "http://localhost.localdomain"
- name: Conjunctive Normal Form (4)
expression: "(id = 'myId' OR type LIKE '%.success') AND (id = 'notmyId' OR source LIKE 'https://%' OR type LIKE '%.warning')"
- result: true
+ result: false
eventOverrides:
id: "myId"
type: "example.event.success"
source: "http://localhost.localdomain"
- name: Conjunctive Normal Form (5)
expression: "(id = 'myId' OR type LIKE '%.success') AND (id = 'notmyId' OR source LIKE 'https://%' OR type LIKE '%.warning') AND (myext = 'customext')"
- error: missingAttribute
+ result: false
eventOverrides:
id: "myId"
- type: "example.event.success"
+ type: "example.event.warning"
source: "http://localhost.localdomain"
+ error: missingAttribute