diff --git a/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/TruthnessUtils.java b/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/TruthnessUtils.java index 131b889359..e41a8846d2 100644 --- a/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/TruthnessUtils.java +++ b/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/TruthnessUtils.java @@ -1,5 +1,7 @@ package org.evomaster.client.java.distance.heuristics; +import java.util.Arrays; + public class TruthnessUtils { /** @@ -13,7 +15,7 @@ public static double normalizeValue(double v) { throw new IllegalArgumentException("Negative value: " + v); } - if(Double.isInfinite(v) || v == Double.MAX_VALUE){ + if (Double.isInfinite(v) || v == Double.MAX_VALUE) { return 1d; } @@ -26,7 +28,6 @@ public static double normalizeValue(double v) { } - public static Truthness getEqualityTruthness(int a, int b) { double distance = DistanceHelper.getDistanceToEquality(a, b); double normalizedDistance = normalizeValue(distance); @@ -45,6 +46,15 @@ public static Truthness getEqualityTruthness(long a, long b) { ); } + + public static Truthness getLessThanTruthness(double a, double b) { + double distance = DistanceHelper.getDistanceToEquality(a, b); + return new Truthness( + a < b ? 1d : 1d / (1.1d + distance), + a >= b ? 1d : 1d / (1.1d + distance) + ); + } + public static Truthness getLessThanTruthness(long a, long b) { double distance = DistanceHelper.getDistanceToEquality(a, b); return new Truthness( @@ -79,4 +89,58 @@ public static Truthness getTruthnessToEmpty(int len) { } return t; } + + public static Truthness buildAndAggregationTruthness(Truthness... truthnesses) { + double averageOfTrue = averageOfTrue(truthnesses); + double falseOrAverageFalse = falseOrAverageFalse(truthnesses); + return new Truthness(averageOfTrue, falseOrAverageFalse); + } + + private static double averageOfTrue(Truthness... truthnesses) { + checkValidTruthnesses(truthnesses); + double[] getOfTrueValues = Arrays.stream(truthnesses).mapToDouble(Truthness::getOfTrue) + .toArray(); + return average(getOfTrueValues); + } + + private static void checkValidTruthnesses(Truthness[] truthnesses) { + if (truthnesses == null || truthnesses.length == 0 || Arrays.stream(truthnesses).anyMatch(e -> e == null)) { + throw new IllegalArgumentException("null or empty Truthness instance"); + } + } + + private static double average(double... values) { + if (values == null || values.length == 0) { + throw new IllegalArgumentException("null or empty values"); + } + double total = 0.0; + for (double v : values) { + total += v; + } + return total / values.length; + } + + private static double averageOfFalse(Truthness... truthnesses) { + checkValidTruthnesses(truthnesses); + double[] getOfFalseValues = Arrays.stream(truthnesses).mapToDouble(Truthness::getOfFalse) + .toArray(); + return average(getOfFalseValues); + } + + private static double falseOrAverageFalse(Truthness... truthnesses) { + checkValidTruthnesses(truthnesses); + if (Arrays.stream(truthnesses).anyMatch(t -> t.isFalse())) { + return 1.0d; + } else { + return averageOfFalse(truthnesses); + } + } + + public static Truthness buildScaledTruthness(double base, double ofTrueToScale) { + final double scaledOfTrue = DistanceHelper.scaleHeuristicWithBase(ofTrueToScale, base); + final double ofFalse = 1.0d; + return new Truthness(scaledOfTrue, ofFalse); + } + + } diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResult.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResult.java index ad11f36de8..36a7eccf49 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResult.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResult.java @@ -26,6 +26,8 @@ public QueryResult(List variableDescriptorList) { variableDescriptors.addAll(variableDescriptorList); } + + /** * WARNING: Constructor only needed for testing * @@ -173,4 +175,17 @@ public QueryResultDto toDto(){ return dto; } + + /** + * Retrieves the table name of this queryResult. + * + * @return the table name of the first {@code VariableDescriptor} in the {@code variableDescriptors} list. + * @throws IllegalStateException if the {@code variableDescriptors} list is empty. + */ + public String getTableName() { + if (variableDescriptors.isEmpty()) { + throw new IllegalStateException("No variable descriptors found"); + } + return variableDescriptors.get(0).getTableName(); + } } diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResultSet.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResultSet.java new file mode 100644 index 0000000000..0dd9f4e43d --- /dev/null +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/QueryResultSet.java @@ -0,0 +1,113 @@ +package org.evomaster.client.java.sql; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Represents a collection of query results mapped to table names, + * with support for both named and virtual tables. + * + * This class allows case-sensitive or case-insensitive handling of table names + * and provides mechanisms to add, retrieve, and manage query results. + * + */ +public class QueryResultSet { + + /** + * A map storing query results associated with table names. + * The keys are table names, and the values are {@link QueryResult} objects. + */ + private final Map queryResults; + + /** + * Indicates whether table name comparisons are case-sensitive. + */ + private final boolean isCaseSensitive; + + /** + * Stores the query result for a virtual table, if any. + */ + private QueryResult queryResultForVirtualTable; + + public QueryResultSet() { + this(true); + } + + /** + * Creates a new {@code QueryResultSet}. + * + * @param isCaseSensitive whether table name comparisons should be case-sensitive + */ + public QueryResultSet(boolean isCaseSensitive) { + queryResults = new TreeMap<>(isCaseSensitive ? null : String.CASE_INSENSITIVE_ORDER); + this.isCaseSensitive = isCaseSensitive; + } + + /** + * Returns whether table name comparisons are case-sensitive. + * + * @return {@code true} if comparisons are case-sensitive, {@code false} otherwise + */ + public boolean isCaseSensitive() { + return isCaseSensitive; + } + + /** + * Adds a query result to the result set. + *

+ * If the query result corresponds to a named table, it is stored in the map. + * If it corresponds to a virtual table, it is stored separately. + * Throws an exception if a duplicate table (named or virtual) is added. + *

+ * + * @param queryResult the query result to add + * @throws IllegalArgumentException if the table name already exists in the set + */ + public void addQueryResult(QueryResult queryResult) { + String tableName = queryResult.seeVariableDescriptors() + .stream() + .findFirst() + .map(VariableDescriptor::getTableName) + .orElse(null); + + if (tableName == null) { + handleVirtualTable(queryResult); + } else { + handleNamedTable(tableName, queryResult); + } + } + + private void handleNamedTable(String tableName, QueryResult queryResult) { + if (queryResults.containsKey(tableName)) { + throw new IllegalArgumentException("Duplicate table in QueryResultSet: " + tableName); + } + queryResults.put(tableName, queryResult); + } + + private void handleVirtualTable(QueryResult queryResult) { + if (queryResultForVirtualTable != null) { + throw new IllegalArgumentException("Duplicate values for virtual table"); + } + queryResultForVirtualTable = queryResult; + } + + /** + * Retrieves the query result associated with a named table. + * + * @param tableName the name of the table + * @return the query result for the table, or {@code null} if no result exists + */ + public QueryResult getQueryResultForNamedTable(String tableName) { + return queryResults.get(tableName); + } + + /** + * Retrieves the query result for a virtual table. + * + * @return the query result for the virtual table, or {@code null} if none exists + */ + public QueryResult getQueryResultForVirtualTable() { + return queryResultForVirtualTable; + } + +} diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlExpressionEvaluator.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlExpressionEvaluator.java new file mode 100644 index 0000000000..50744c842a --- /dev/null +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlExpressionEvaluator.java @@ -0,0 +1,580 @@ +package org.evomaster.client.java.sql.internal; + +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.arithmetic.*; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.AllColumns; +import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.Select; +import org.evomaster.client.java.distance.heuristics.Truthness; +import org.evomaster.client.java.distance.heuristics.TruthnessUtils; +import org.evomaster.client.java.sql.DataRow; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Stack; + +public class SqlExpressionEvaluator extends ExpressionVisitorAdapter { + + private final DataRow dataRow; + private final SqlNameContext sqlNameContext; + private final Stack computedTruthnesses = new Stack<>(); + private final Stack concreteValues = new Stack<>(); + + public SqlExpressionEvaluator(SqlNameContext sqlNameContext, DataRow dataRow) { + this.sqlNameContext = sqlNameContext; + this.dataRow = dataRow; + } + + + public Truthness getEvaluatedTruthness() { + if (computedTruthnesses.isEmpty()) { + throw new IllegalStateException("no Truthness was computed"); + } + return computedTruthnesses.peek(); + } + + private void visitComparisonOperator(ComparisonOperator comparisonOperator) { + final Object concreteRightValue = concreteValues.pop(); + final Object concreteLeftValue = concreteValues.pop(); + final Truthness truthness = computeTruthnessForComparisonOperator(concreteLeftValue, concreteRightValue, comparisonOperator); + computedTruthnesses.push(truthness); + } + + @Override + public void visit(EqualsTo equalsTo) { + super.visit(equalsTo); + visitComparisonOperator(equalsTo); + } + + private Truthness computeTruthnessForComparisonOperator(Object concreteLeftValue, Object concreteRightValue, ComparisonOperator comparisonOperator) { + final Truthness truthness; + if (concreteLeftValue == null && concreteRightValue == null) { + truthness = SqlHeuristicsCalculator.FALSE_TRUTHNESS; + } else if (concreteLeftValue == null || concreteRightValue == null) { + truthness = SqlHeuristicsCalculator.FALSE_TRUTHNESS_BETTER; + } else { + final Truthness truthnessOfExpression; + if (concreteLeftValue instanceof Number && concreteRightValue instanceof Number) { + double leftValueAsDouble = ((Number) concreteLeftValue).doubleValue(); + double rightValueAsDouble = ((Number) concreteRightValue).doubleValue(); + if (comparisonOperator instanceof EqualsTo) { + truthnessOfExpression = TruthnessUtils.getEqualityTruthness(leftValueAsDouble, rightValueAsDouble); + } else if (comparisonOperator instanceof NotEqualsTo) { + //a != b => !(a == b) + truthnessOfExpression = TruthnessUtils.getEqualityTruthness(leftValueAsDouble, rightValueAsDouble).invert(); + } else if (comparisonOperator instanceof GreaterThan) { + //a > b => b < a + truthnessOfExpression = TruthnessUtils.getLessThanTruthness(rightValueAsDouble, leftValueAsDouble); + } else if (comparisonOperator instanceof MinorThan) { + truthnessOfExpression = TruthnessUtils.getLessThanTruthness(leftValueAsDouble, rightValueAsDouble); + } else if (comparisonOperator instanceof MinorThanEquals) { + //a <= b => b >= a => !(b < a) + truthnessOfExpression = TruthnessUtils.getLessThanTruthness(rightValueAsDouble, leftValueAsDouble).invert(); + } else if (comparisonOperator instanceof GreaterThanEquals) { + //a >= b => ! (a < b) + truthnessOfExpression = TruthnessUtils.getLessThanTruthness(leftValueAsDouble, rightValueAsDouble).invert(); + } else { + throw new UnsupportedOperationException("Unsupported comparison operator: " + comparisonOperator); + } + } else if (concreteRightValue instanceof String && concreteLeftValue instanceof String) { + throw new UnsupportedOperationException("String comparison not yet supported"); + } else if (concreteLeftValue instanceof Timestamp || concreteRightValue instanceof Timestamp + || concreteLeftValue instanceof Instant || concreteRightValue instanceof Instant) { + throw new UnsupportedOperationException("Timestamp/Instant comparison not yet supported"); + } else if (concreteLeftValue instanceof Boolean && concreteRightValue instanceof Boolean) { + throw new UnsupportedOperationException("Boolean comparison not yet supported"); + } else { + throw new UnsupportedOperationException("type not supported"); + } + if (truthnessOfExpression.isTrue()) { + truthness = truthnessOfExpression; + } else { + truthness = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C_BETTER, truthnessOfExpression.getOfTrue()); + } + } + return truthness; + } + + @Override + public void visit(BitwiseRightShift bitwiseRightShift) { + throw new UnsupportedOperationException("Bitwise right shift not supported"); + } + + @Override + public void visit(BitwiseLeftShift bitwiseLeftShift) { + throw new UnsupportedOperationException("Bitwise left shift not supported"); + } + + @Override + public void visit(NullValue nullValue) { + throw new UnsupportedOperationException("Null value not supported"); + } + + @Override + public void visit(Function function) { + throw new UnsupportedOperationException("Function not supported"); + } + + @Override + public void visit(SignedExpression signedExpression) { + throw new UnsupportedOperationException("Signed expression not supported"); + } + + @Override + public void visit(JdbcParameter jdbcParameter) { + throw new UnsupportedOperationException("JdbcParameter not supported"); + } + + @Override + public void visit(JdbcNamedParameter jdbcNamedParameter) { + throw new UnsupportedOperationException("JdbcNamedParameter not supported"); + } + + @Override + public void visit(DoubleValue doubleValue) { + throw new UnsupportedOperationException("DoubleValue not supported"); + } + + @Override + public void visit(LongValue longValue) { + long concreteValue = longValue.getValue(); + concreteValues.push(concreteValue); + } + + @Override + public void visit(HexValue hexValue) { + throw new UnsupportedOperationException("HexValue not supported"); + } + + @Override + public void visit(DateValue dateValue) { + throw new UnsupportedOperationException("DateValue not supported"); + } + + @Override + public void visit(TimeValue timeValue) { + throw new UnsupportedOperationException("TimeValue not supported"); + } + + @Override + public void visit(TimestampValue timestampValue) { + throw new UnsupportedOperationException("TimestampValue not supported"); + } + + @Override + public void visit(Parenthesis parenthesis) { + throw new UnsupportedOperationException("Parenthesis not supported"); + } + + + @Override + public void visit(IntegerDivision integerDivision) { + throw new UnsupportedOperationException("visit(IntegerDivision) not supported"); + } + + @Override + public void visit(Multiplication multiplication) { + throw new UnsupportedOperationException("visit(Multiplication) not supported"); + } + + @Override + public void visit(Subtraction subtraction) { + throw new UnsupportedOperationException("visit(Subtraction) not supported"); + } + + @Override + public void visit(AndExpression andExpression) { + throw new UnsupportedOperationException("visit(AndExpression) not supported"); + } + + @Override + public void visit(OrExpression orExpression) { + throw new UnsupportedOperationException("visit(OrExpression) not supported"); + } + + @Override + public void visit(XorExpression xorExpression) { + throw new UnsupportedOperationException("visit(XorExpression) not supported"); + } + + @Override + public void visit(Between between) { + throw new UnsupportedOperationException("visit(Between) not supported"); + } + + @Override + public void visit(OverlapsCondition overlapsCondition) { + throw new UnsupportedOperationException("visit(OverlapsCondition) not supported"); + } + + @Override + public void visit(GreaterThan greaterThan) { + super.visit(greaterThan); + this.visitComparisonOperator(greaterThan); + } + + @Override + public void visit(GreaterThanEquals greaterThanEquals) { + super.visit(greaterThanEquals); + this.visitComparisonOperator(greaterThanEquals); + } + + @Override + public void visit(InExpression inExpression) { + throw new UnsupportedOperationException("visit(InExpression) not supported"); + } + + @Override + public void visit(FullTextSearch fullTextSearch) { + throw new UnsupportedOperationException("visit(FullTextSearch) not supported"); + } + + @Override + public void visit(IsNullExpression isNullExpression) { + throw new UnsupportedOperationException("visit(IsNullExpression) not supported"); + } + + @Override + public void visit(IsBooleanExpression isBooleanExpression) { + throw new UnsupportedOperationException("visit(IsBooleanExpression) not supported"); + } + + @Override + public void visit(LikeExpression likeExpression) { + throw new UnsupportedOperationException("visit(LikeExpression) not supported"); + } + + @Override + public void visit(MinorThan minorThan) { + super.visit(minorThan); + visitComparisonOperator(minorThan); + } + + @Override + public void visit(MinorThanEquals minorThanEquals) { + super.visit(minorThanEquals); + visitComparisonOperator(minorThanEquals); + } + + @Override + public void visit(NotEqualsTo notEqualsTo) { + super.visit(notEqualsTo); + visitComparisonOperator(notEqualsTo); + } + + @Override + public void visit(DoubleAnd doubleAnd) { + throw new UnsupportedOperationException("visit(DoubleAnd) not supported"); + } + + @Override + public void visit(Contains contains) { + throw new UnsupportedOperationException("visit(Contains) not supported"); + } + + @Override + public void visit(ContainedBy containedBy) { + throw new UnsupportedOperationException("visit(ContainedBy) not supported"); + } + + @Override + public void visit(ParenthesedSelect parenthesedSelect) { + throw new UnsupportedOperationException("visit(ParenthesedSelect) not supported"); + } + + @Override + public void visit(Column column) { + String name = column.getColumnName(); + String table = sqlNameContext.getTableName(column); + Object value = dataRow.getValueByName(name, table); + concreteValues.push(value); + } + + @Override + public void visit(CaseExpression caseExpression) { + throw new UnsupportedOperationException("visit(CaseExpression) not supported"); + } + + @Override + public void visit(WhenClause whenClause) { + throw new UnsupportedOperationException("visit(WhenClause) not supported"); + } + + @Override + public void visit(ExistsExpression existsExpression) { + throw new UnsupportedOperationException("visit(ExistsExpression) not supported"); + } + + @Override + public void visit(MemberOfExpression memberOfExpression) { + throw new UnsupportedOperationException("visit(MemberOfExpression) not supported"); + } + + @Override + public void visit(AnyComparisonExpression anyComparisonExpression) { + throw new UnsupportedOperationException("visit(AnyComparisonExpression) not supported"); + } + + @Override + public void visit(Concat concat) { + throw new UnsupportedOperationException("visit(Concat) not supported"); + } + + @Override + public void visit(Matches matches) { + throw new UnsupportedOperationException("visit(Matches) not supported"); + } + + @Override + public void visit(BitwiseAnd bitwiseAnd) { + throw new UnsupportedOperationException("visit(BitwiseAnd) not supported"); + } + + @Override + public void visit(BitwiseOr bitwiseOr) { + throw new UnsupportedOperationException("visit(BitwiseOr) not supported"); + } + + @Override + public void visit(BitwiseXor bitwiseXor) { + throw new UnsupportedOperationException("visit(BitwiseXor) not supported"); + } + + @Override + public void visit(CastExpression castExpression) { + throw new UnsupportedOperationException("visit(CastExpression) not supported"); + } + + @Override + public void visit(Modulo modulo) { + throw new UnsupportedOperationException("visit(Modulo) not supported"); + } + + @Override + public void visit(AnalyticExpression analyticExpression) { + throw new UnsupportedOperationException("visit(AnalyticExpression) not supported"); + } + + @Override + public void visit(ExtractExpression extractExpression) { + throw new UnsupportedOperationException("visit(ExtractExpression) not supported"); + } + + @Override + public void visit(IntervalExpression intervalExpression) { + throw new UnsupportedOperationException("visit(IntervalExpression) not supported"); + } + + @Override + public void visit(OracleHierarchicalExpression oracleHierarchicalExpression) { + throw new UnsupportedOperationException("visit(OracleHierarchicalExpression) not supported"); + } + + @Override + public void visit(RegExpMatchOperator regExpMatchOperator) { + throw new UnsupportedOperationException("visit(RegExpMatchOperator) not supported"); + } + + @Override + public void visit(JsonExpression jsonExpression) { + throw new UnsupportedOperationException("visit(JsonExpression) not supported"); + } + + @Override + public void visit(JsonOperator jsonOperator) { + throw new UnsupportedOperationException("visit(JsonOperator) not supported"); + } + + @Override + public void visit(UserVariable userVariable) { + throw new UnsupportedOperationException("visit(UserVariable) not supported"); + } + + @Override + public void visit(NumericBind numericBind) { + throw new UnsupportedOperationException("visit(NumericBind) not supported"); + } + + @Override + public void visit(KeepExpression keepExpression) { + throw new UnsupportedOperationException("visit(KeepExpression) not supported"); + } + + @Override + public void visit(MySQLGroupConcat mySQLGroupConcat) { + throw new UnsupportedOperationException("visit(MySQLGroupConcat) not supported"); + } + + @Override + public void visit(ExpressionList expressionList) { + throw new UnsupportedOperationException("visit(ExpressionList) not supported"); + } + + @Override + public void visit(RowConstructor rowConstructor) { + throw new UnsupportedOperationException("visit(RowConstructor) not supported"); + } + + @Override + public void visit(RowGetExpression rowGetExpression) { + throw new UnsupportedOperationException("visit(RowGetExpression) not supported"); + } + + @Override + public void visit(OracleHint oracleHint) { + throw new UnsupportedOperationException("visit(OracleHint) not supported"); + } + + @Override + public void visit(TimeKeyExpression timeKeyExpression) { + throw new UnsupportedOperationException("visit(TimeKeyExpression) not supported"); + } + + @Override + public void visit(DateTimeLiteralExpression dateTimeLiteralExpression) { + throw new UnsupportedOperationException("visit(DateTimeLiteralExpression) not supported"); + } + + @Override + public void visit(NotExpression notExpression) { + throw new UnsupportedOperationException("visit(NotExpression) not supported"); + } + + @Override + public void visit(NextValExpression nextValExpression) { + throw new UnsupportedOperationException("visit(NextValExpression) not supported"); + } + + @Override + public void visit(CollateExpression collateExpression) { + throw new UnsupportedOperationException("visit(CollateExpression) not supported"); + } + + @Override + public void visit(SimilarToExpression similarToExpression) { + throw new UnsupportedOperationException("visit(SimilarToExpression) not supported"); + } + + @Override + public void visit(ArrayExpression arrayExpression) { + throw new UnsupportedOperationException("visit(ArrayExpression) not supported"); + } + + @Override + public void visit(ArrayConstructor arrayConstructor) { + throw new UnsupportedOperationException("visit(ArrayConstructor) not supported"); + } + + @Override + public void visit(VariableAssignment variableAssignment) { + throw new UnsupportedOperationException("visit(VariableAssignment) not supported"); + } + + @Override + public void visit(XMLSerializeExpr xmlSerializeExpr) { + throw new UnsupportedOperationException("visit(XMLSerializeExpr) not supported"); + } + + @Override + public void visit(TimezoneExpression timezoneExpression) { + throw new UnsupportedOperationException("visit(TimezoneExpression) not supported"); + } + + @Override + public void visit(JsonAggregateFunction jsonAggregateFunction) { + throw new UnsupportedOperationException("visit(JsonAggregateFunction) not supported"); + } + + @Override + public void visit(JsonFunction jsonFunction) { + throw new UnsupportedOperationException("visit(JsonFunction) not supported"); + } + + @Override + public void visit(ConnectByRootOperator connectByRootOperator) { + throw new UnsupportedOperationException("visit(ConnectByRootOperator) not supported"); + } + + @Override + public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { + throw new UnsupportedOperationException("visit(OracleNamedFunctionParameter) not supported"); + } + + @Override + public void visit(AllColumns allColumns) { + throw new UnsupportedOperationException("visit(AllColumns) not supported"); + } + + @Override + public void visit(AllTableColumns allTableColumns) { + throw new UnsupportedOperationException("visit(AllTableColumns) not supported"); + } + + @Override + public void visit(AllValue allValue) { + throw new UnsupportedOperationException("visit(AllValue) not supported"); + } + + @Override + public void visit(IsDistinctExpression isDistinctExpression) { + throw new UnsupportedOperationException("visit(IsDistinctExpression) not supported"); + } + + @Override + public void visit(GeometryDistance geometryDistance) { + throw new UnsupportedOperationException("visit(GeometryDistance) not supported"); + } + + @Override + public void visit(Select select) { + throw new UnsupportedOperationException("visit(Select) not supported"); + } + + @Override + public void visit(TranscodingFunction transcodingFunction) { + throw new UnsupportedOperationException("visit(TranscodingFunction) not supported"); + } + + @Override + public void visit(TrimFunction trimFunction) { + throw new UnsupportedOperationException("visit(TrimFunction) not supported"); + } + + @Override + public void visit(RangeExpression rangeExpression) { + throw new UnsupportedOperationException("visit(RangeExpression) not supported"); + } + + @Override + public void visit(TSQLLeftJoin tsqlLeftJoin) { + throw new UnsupportedOperationException("visit(TSQLLeftJoin) not supported"); + } + + @Override + public void visit(TSQLRightJoin tsqlRightJoin) { + throw new UnsupportedOperationException("visit(TSQLRightJoin) not supported"); + } + + @Override + public void visit(StringValue stringValue) { + throw new UnsupportedOperationException("visit(StringValue) not supported"); + } + + @Override + public void visit(Addition addition) { + throw new UnsupportedOperationException("visit(Addition) not supported"); + } + + @Override + public void visit(Division division) { + throw new UnsupportedOperationException("visit(Division) not supported"); + } + + +} diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculator.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculator.java index 46d825ee5e..f8d27ce6bd 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculator.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculator.java @@ -3,28 +3,140 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.Join; import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto; +import org.evomaster.client.java.distance.heuristics.Truthness; +import org.evomaster.client.java.distance.heuristics.TruthnessUtils; +import org.evomaster.client.java.sql.DataRow; import org.evomaster.client.java.sql.QueryResult; +import org.evomaster.client.java.sql.QueryResultSet; -import static org.evomaster.client.java.sql.internal.SqlParserUtils.getFrom; -import static org.evomaster.client.java.sql.internal.SqlParserUtils.getWhere; +import java.util.Collection; +import java.util.List; + +import static org.evomaster.client.java.sql.internal.SqlParserUtils.*; public class SqlHeuristicsCalculator { + public static double C = 0.1; + public static double C_BETTER = C + (C / 2); + public static Truthness TRUE_TRUTHNESS = new Truthness(1, C); + public static Truthness FALSE_TRUTHNESS = TRUE_TRUTHNESS.invert(); + public static Truthness FALSE_TRUTHNESS_BETTER = new Truthness(C_BETTER, 1); + + private final QueryResultSet queryResultSet; + private final SqlNameContext sqlNameContext; + private SqlHeuristicsCalculator(SqlNameContext sqlNameContext, QueryResult[] data) { + final boolean isCaseSensitive = false; + this.sqlNameContext = sqlNameContext; + this.queryResultSet = new QueryResultSet(isCaseSensitive); + for (QueryResult queryResult : data) { + queryResultSet.addQueryResult(queryResult); + } + } + public static SqlDistanceWithMetrics computeDistance(String sqlCommand, DbInfoDto schema, TaintHandler taintHandler, QueryResult... data) { Statement parsedSqlCommand = SqlParserUtils.parseSqlCommand(sqlCommand); - Expression whereClause= getWhere(parsedSqlCommand); - FromItem fromItem = getFrom(parsedSqlCommand); - - if (fromItem == null && whereClause == null) { - return new SqlDistanceWithMetrics(0.0,0,false); + SqlNameContext sqlNameContext = new SqlNameContext(parsedSqlCommand); + if (schema != null) { + sqlNameContext.setSchema(schema); } + SqlHeuristicsCalculator calculator = new SqlHeuristicsCalculator(sqlNameContext, data); + Truthness t = calculator.computeCommand(parsedSqlCommand); + double distanceToTrue = 1 - t.getOfTrue(); + return new SqlDistanceWithMetrics(distanceToTrue, 0, false); + } + + private Truthness computeCommand(Statement parsedSqlCommand) { + final Expression whereClause = getWhere(parsedSqlCommand); + final FromItem fromItem = getFrom(parsedSqlCommand); + final List joins = getJoins(parsedSqlCommand); + if (fromItem == null && joins == null) { + /** + * Result will depend on the contents of the virtual table + */ + return getTruthnessForTable(null); + } else if (fromItem != null && joins == null && whereClause == null) { + return getTruthnessForTable(fromItem); + } else if (fromItem != null && joins == null && whereClause != null) { + return getTruthnessForCondition(whereClause, fromItem); + } else if (fromItem != null && joins != null && whereClause == null) { + final Join join = joins.get(0); + final FromItem leftFromItem = fromItem; + final FromItem rightFromItem = join.getRightItem(); + final Collection onExpressions = join.getOnExpressions(); + if (join.isLeft()) { + return getTruthnessForTable(leftFromItem); + } else if (join.isRight()) { + return getTruthnessForTable(rightFromItem); + } else if (join.isCross()) { + Truthness truthnessLeftTable = getTruthnessForTable(leftFromItem); + Truthness truthnessRightTable = getTruthnessForTable(rightFromItem); + return TruthnessUtils.buildAndAggregationTruthness(truthnessLeftTable, truthnessRightTable); + } else { + // inner join? + } + } return null; } + + private Truthness getTruthnessForCondition(Expression whereClause, FromItem fromItem) { + + double maxOfTrue = 0.0d; + int rowCount = 0; + QueryResult queryResult = getQueryResultForFromItem(fromItem); + if (queryResult.isEmpty()) { + return FALSE_TRUTHNESS; + } else { + for (DataRow row : queryResult.seeRows()) { + Truthness t = getTruthnessForExpression(whereClause, row); + if (t.isTrue()) { + return TRUE_TRUTHNESS; + } else { + if (t.getOfTrue() > maxOfTrue) { + maxOfTrue = t.getOfTrue(); + } + } + rowCount++; + } + return TruthnessUtils.buildScaledTruthness(C, maxOfTrue); + } + + + } + + private Truthness getTruthnessForExpression(Expression whereClause, DataRow row) { + SqlExpressionEvaluator expressionEvaluator = new SqlExpressionEvaluator(sqlNameContext, row); + whereClause.accept(expressionEvaluator); + return expressionEvaluator.getEvaluatedTruthness(); + } + + private Truthness getTruthnessForTable(FromItem fromItem) { + final QueryResult tableData = getQueryResultForFromItem(fromItem); + final int len = tableData.size(); + final Truthness t = TruthnessUtils.getTruthnessToEmpty(len).invert(); + return t; + } + + private QueryResult getQueryResultForFromItem(FromItem fromItem) { + final QueryResult tableData; + if (fromItem == null) { + tableData = queryResultSet.getQueryResultForVirtualTable(); + } else { + if (!SqlParserUtils.isTable(fromItem)) { + throw new IllegalArgumentException("Cannot compute Truthness for form item that it is not a table " + fromItem); + } + String tableName = SqlParserUtils.getTableName(fromItem); + tableData = queryResultSet.getQueryResultForNamedTable(tableName); + } + return tableData; + } + + } diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlNameContext.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlNameContext.java index ec231749c6..6da3b46736 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlNameContext.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlNameContext.java @@ -124,51 +124,61 @@ public String getTableName(Column column) { private List getTableNamesInFrom() { - FromItem fromItem = getFromItem(); - List names = new ArrayList<>(); + if (hasFromItem()) { + FromItem fromItem = getFromItem(); - FromItemVisitorAdapter visitor = new FromItemVisitorAdapter(){ - @Override - public void visit(Table table) { - names.add(table.getName().toLowerCase()); - } - }; - - fromItem.accept(visitor); + FromItemVisitorAdapter visitor = new FromItemVisitorAdapter() { + @Override + public void visit(Table table) { + names.add(table.getName().toLowerCase()); + } + }; + fromItem.accept(visitor); + } return names; } - private FromItem getFromItem() { - - FromItem fromItem = null; + private boolean hasFromItem() { if(statement instanceof Select) { Select select = (Select)statement; PlainSelect plainSelect = select.getPlainSelect(); - fromItem = plainSelect.getFromItem(); + FromItem fromItem = plainSelect.getFromItem(); + return fromItem != null; + } else { + return false; } + } - if(fromItem == null) { - throw new IllegalArgumentException("Cannot handle FromItem for: " + statement); - } else { - return fromItem; + private FromItem getFromItem() { + if (!hasFromItem()) { + throw new IllegalStateException("Cannot get FromItem from statement without a FROM clause"); + } + if (!(statement instanceof Select)) { + throw new IllegalStateException("Cannot get FromItem from statement without a SELECT clause"); } + Select select = (Select)statement; + PlainSelect plainSelect = select.getPlainSelect(); + FromItem fromItem = plainSelect.getFromItem(); + return fromItem; } private void computeAliases() { if (statement instanceof Select) { - FromItem fromItem = getFromItem(); - fromItem.accept(new AliasVisitor(tableAliases)); + if (hasFromItem()) { + FromItem fromItem = getFromItem(); + fromItem.accept(new AliasVisitor(tableAliases)); - Select select = (Select)statement; - PlainSelect plainSelect = select.getPlainSelect(); + Select select = (Select)statement; + PlainSelect plainSelect = select.getPlainSelect(); - List joins = plainSelect.getJoins(); - if (joins != null) { - joins.forEach(j -> j.getRightItem().accept(new AliasVisitor(tableAliases))); + List joins = plainSelect.getJoins(); + if (joins != null) { + joins.forEach(j -> j.getRightItem().accept(new AliasVisitor(tableAliases))); + } } } else if(statement instanceof Delete){ //no alias required? diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlParserUtils.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlParserUtils.java index 00003de09f..a587a3850d 100644 --- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlParserUtils.java +++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlParserUtils.java @@ -3,13 +3,17 @@ import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.Join; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.update.Update; +import java.util.List; + public class SqlParserUtils { /** @@ -95,6 +99,16 @@ public static FromItem getFrom(Statement parsedStatement) { } } + public static List getJoins(Statement parsedStatement) { + if (parsedStatement instanceof Select) { + Select select = (Select) parsedStatement; + PlainSelect plainSelect = select.getPlainSelect(); + return plainSelect.getJoins(); + } else { + throw new IllegalArgumentException("Cannot get Joins From: " + parsedStatement.toString()); + } + } + /** * This method assumes that the SQL command can be successfully parsed. * @@ -118,4 +132,36 @@ public static boolean canParseSqlStatement(String sqlCommand){ return false; } } + + /** + * Checks if the given FromItem is a Table. + * + * @param fromItem the FromItem to check + * @return true if the FromItem is a Table, false otherwise + */ + public static boolean isTable(FromItem fromItem) { + return fromItem instanceof Table; + } + + /** + * Retrieves the fully qualified name of a table from the provided {@link FromItem}. + *

+ * This method checks if the given {@code fromItem} is an instance of {@link Table}. + * If it is, the method extracts and returns the fully qualified name of the table. + * Otherwise, it throws an {@link IllegalArgumentException}. + *

+ * + * @param fromItem the {@link FromItem} instance to extract the table name from. + * @return the fully qualified name of the table as a {@link String}. + * @throws IllegalArgumentException if the provided {@code fromItem} is not an instance of {@link Table}. + * @see net.sf.jsqlparser.schema.Table#getFullyQualifiedName() + */ + public static String getTableName(FromItem fromItem) { + if (fromItem instanceof Table) { + Table table = (Table) fromItem; + return table.getFullyQualifiedName(); + } else { + throw new IllegalArgumentException("From item " + fromItem + " is not a table"); + } + } } diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculatorTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculatorTest.java index 962b675083..ad13e014a4 100644 --- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculatorTest.java +++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHeuristicsCalculatorTest.java @@ -1,5 +1,6 @@ package org.evomaster.client.java.sql.internal; +import org.evomaster.client.java.distance.heuristics.TruthnessUtils; import org.evomaster.client.java.sql.DataRow; import org.evomaster.client.java.sql.QueryResult; import org.junit.jupiter.api.Test; @@ -10,12 +11,218 @@ public class SqlHeuristicsCalculatorTest { + @Test + public void testNoWhereNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM Person"; + QueryResult personContents = new QueryResult(Arrays.asList("name"), "Person"); + personContents.addRow(new DataRow("name","John", "Person")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personContents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + @Test + public void testWhereNoFromClause() { + String sqlCommand = "SELECT 1 AS example_column WHERE 1 = 0"; + QueryResult virtualTableContents = new QueryResult(Arrays.asList("example_column"), null); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, virtualTableContents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test public void testNoWhereNoFromClause() { String sqlCommand = "SELECT 1 AS example_column"; - QueryResult data = new QueryResult(Arrays.asList("example_column"), null); - data.addRow(new DataRow("example_column",1, null)); - SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, data); + QueryResult virtualTableContents = new QueryResult(Arrays.asList("example_column"), null); + virtualTableContents.addRow(new DataRow("example_column",1, null)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, virtualTableContents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + + @Test + public void testNoWhereNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM Person"; + QueryResult personContents = new QueryResult(Arrays.asList("name"), "Person"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personContents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testLeftJoinNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM TableA LEFT JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + tableAcontents.addRow(new DataRow("name","John", "TableA")); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + + @Test + public void testRightJoinNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM TableA RIGHT JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + tableBcontents.addRow(new DataRow("name","John", "TableB")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + @Test + public void testRightJoinNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM TableA RIGHT JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + tableAcontents.addRow(new DataRow("name","John", "TableA")); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testLeftJoinNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM TableA LEFT JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + tableBcontents.addRow(new DataRow("name","John", "TableB")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testLeftOuterJoinNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM TableA LEFT OUTER JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + tableBcontents.addRow(new DataRow("name","John", "TableB")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testRightOuterJoinNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM TableA RIGHT OUTER JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + tableAcontents.addRow(new DataRow("name","John", "TableA")); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testLeftOuterJoinNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM TableA LEFT OUTER JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + tableAcontents.addRow(new DataRow("name","John", "TableA")); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); assertEquals(0, distanceWithMetrics.sqlDistance); } + + + @Test + public void testRightOuterJoinNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM TableA RIGHT OUTER JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + tableBcontents.addRow(new DataRow("name","John", "TableB")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + @Test + public void testCrossJoinNoFromTableNoRows() { + String sqlCommand = "SELECT name FROM TableA CROSS JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + double expectedDistance = 1 - SqlHeuristicsCalculator.C; + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testCrossJoinNoFromTableWithRows() { + String sqlCommand = "SELECT name FROM TableA CROSS JOIN TableB"; + QueryResult tableAcontents = new QueryResult(Arrays.asList("name"), "TableA"); + QueryResult tableBcontents = new QueryResult(Arrays.asList("name"), "TableB"); + tableAcontents.addRow(new DataRow("name","John", "TableA")); + tableBcontents.addRow(new DataRow("name","John", "TableB")); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, tableAcontents, tableBcontents); + assertEquals(0, distanceWithMetrics.sqlDistance); + } + + @Test + public void testSelectNotEquals() { + String sqlCommand = "SELECT name, age FROM Persons WHERE age=18"; + QueryResult personsContents = new QueryResult(Arrays.asList("name","age"), "Persons"); + personsContents.addRow(Arrays.asList("name","age"),"Persons",Arrays.asList("John", 18)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personsContents); + assertEquals(0.0, distanceWithMetrics.sqlDistance); + } + + @Test + public void testSelectMinorThan() { + String sqlCommand = "SELECT name, age FROM Persons WHERE age<18"; + QueryResult personsContents = new QueryResult(Arrays.asList("name","age"), "Persons"); + personsContents.addRow(Arrays.asList("name","age"),"Persons",Arrays.asList("John", 33)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personsContents); + + double equalityTruthness = TruthnessUtils.getLessThanTruthness(33.0d, 18.0d).getOfTrue(); + double scaledTruthnessBetter = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C_BETTER, equalityTruthness).getOfTrue(); + double scaledTruthness = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C, scaledTruthnessBetter).getOfTrue(); + double expectedDistance = 1 - scaledTruthness; + + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testSelectMinorThanEquals() { + String sqlCommand = "SELECT name, age FROM Persons WHERE age<=18"; + QueryResult personsContents = new QueryResult(Arrays.asList("name","age"), "Persons"); + personsContents.addRow(Arrays.asList("name","age"),"Persons",Arrays.asList("John", 33)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personsContents); + + double equalityTruthness = TruthnessUtils.getLessThanTruthness(18.0d, 33.0d).invert().getOfTrue(); + double scaledTruthnessBetter = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C_BETTER, equalityTruthness).getOfTrue(); + double scaledTruthness = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C, scaledTruthnessBetter).getOfTrue(); + double expectedDistance = 1 - scaledTruthness; + + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testSelectGreaterThan() { + String sqlCommand = "SELECT name, age FROM Persons WHERE 18>age"; + QueryResult personsContents = new QueryResult(Arrays.asList("name","age"), "Persons"); + personsContents.addRow(Arrays.asList("name","age"),"Persons",Arrays.asList("John", 33)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personsContents); + + double equalityTruthness = TruthnessUtils.getLessThanTruthness( 33,18.0d).getOfTrue(); + double scaledTruthnessBetter = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C_BETTER, equalityTruthness).getOfTrue(); + double scaledTruthness = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C, scaledTruthnessBetter).getOfTrue(); + double expectedDistance = 1 - scaledTruthness; + + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + + @Test + public void testSelectGreaterThanEquals() { + String sqlCommand = "SELECT name, age FROM Persons WHERE 18>=age"; + QueryResult personsContents = new QueryResult(Arrays.asList("name","age"), "Persons"); + personsContents.addRow(Arrays.asList("name","age"),"Persons",Arrays.asList("John", 33)); + SqlDistanceWithMetrics distanceWithMetrics = SqlHeuristicsCalculator.computeDistance(sqlCommand, null, null, personsContents); + + double equalityTruthness = TruthnessUtils.getLessThanTruthness( 18,33.0d).invert().getOfTrue(); + double scaledTruthnessBetter = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C_BETTER, equalityTruthness).getOfTrue(); + double scaledTruthness = TruthnessUtils.buildScaledTruthness(SqlHeuristicsCalculator.C, scaledTruthnessBetter).getOfTrue(); + double expectedDistance = 1 - scaledTruthness; + + assertEquals(expectedDistance, distanceWithMetrics.sqlDistance); + } + }