diff --git a/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/DistanceHelper.java b/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/DistanceHelper.java
index 08fe510043..98a568053b 100644
--- a/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/DistanceHelper.java
+++ b/client-java/distance-heuristics/src/main/java/org/evomaster/client/java/distance/heuristics/DistanceHelper.java
@@ -7,14 +7,15 @@
import java.time.chrono.ChronoLocalDateTime;
import java.util.Date;
import java.util.Objects;
+import java.util.UUID;
/**
* All generic distance functions on JVM types should be defined here.
* Recall the distinction between "distance" and "heuristic" terms in EvoMaster:
- *
+ *
* - distance: a value between 0 and MAX. If 0, constraint is solved.
* - heuristic: a value between 0 and 1. If 0, constraint is NOT solved. If solved, value is 1.
- *
+ *
* The "distance"s are what usually used in literature.
* However, in EvoMaster we need [0,1] "heuristic"s (due to the handling of Many Objective Optimization).
*/
@@ -44,23 +45,23 @@ public class DistanceHelper {
* @param delta
* @return
*/
- public static double increasedDistance(double distance, double delta){
+ public static double increasedDistance(double distance, double delta) {
- if(distance < 0){
+ if (distance < 0) {
throw new IllegalArgumentException("Negative distance: " + distance);
}
- if(delta < 0){
+ if (delta < 0) {
throw new IllegalArgumentException("Invalid negative delta: " + delta);
}
- if(delta == 0){
+ if (delta == 0) {
throw new IllegalArgumentException("Meaningless 0 delta");
}
- if(Double.isInfinite(distance) || distance == Double.MAX_VALUE){
+ if (Double.isInfinite(distance) || distance == Double.MAX_VALUE) {
return distance;
}
- if(distance > (Double.MAX_VALUE - delta)){
+ if (distance > (Double.MAX_VALUE - delta)) {
return Double.MAX_VALUE;
}
@@ -75,10 +76,10 @@ public static double increasedDistance(double distance, double delta){
* @return
*/
public static double addDistances(double a, double b) {
- if(a < 0){
+ if (a < 0) {
throw new IllegalArgumentException("Negative distance: " + a);
}
- if(b < 0){
+ if (b < 0) {
throw new IllegalArgumentException("Negative distance: " + b);
}
double sum = a + b;
@@ -92,36 +93,37 @@ public static double addDistances(double a, double b) {
/**
* Return a h=[0,1] heuristics from a scaled distance, taking into account a starting base
+ *
* @param base
* @param distance
* @return
*/
- public static double heuristicFromScaledDistanceWithBase(double base, double distance){
+ public static double heuristicFromScaledDistanceWithBase(double base, double distance) {
- if(base < 0 || base >= 1){
+ if (base < 0 || base >= 1) {
throw new IllegalArgumentException("Invalid base: " + base);
}
- if(distance < 0){
+ if (distance < 0) {
throw new IllegalArgumentException("Negative distance: " + distance);
}
- if(Double.isInfinite(distance) || distance == Double.MAX_VALUE){
+ if (Double.isInfinite(distance) || distance == Double.MAX_VALUE) {
return base;
}
- return base + ((1 - base) / (distance + 1));
+ return base + ((1 - base) / (distance + 1));
}
- public static double scaleHeuristicWithBase(double heuristic, double base){
+ public static double scaleHeuristicWithBase(double heuristic, double base) {
- if(heuristic < 0 || heuristic >= 1){
+ if (heuristic < 0 || heuristic >= 1) {
throw new IllegalArgumentException("Invalid heuristic: " + base);
}
- if(base < 0 || base >= 1){
+ if (base < 0 || base >= 1) {
throw new IllegalArgumentException("Invalid base: " + base);
}
- return base + ((1-base)*heuristic);
+ return base + ((1 - base) * heuristic);
}
public static int distanceToDigit(char c) {
@@ -144,7 +146,7 @@ public static int distanceToRange(int c, int minInclusive, int maxInclusive) {
//1 of 2 will be necessarily a 0
long dist = Math.max(diffAfter, 0) + Math.max(diffBefore, 0);
- if(dist > Integer.MAX_VALUE){
+ if (dist > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
assert (dist >= 0);
@@ -166,7 +168,7 @@ public static long getLeftAlignmentDistance(String a, String b) {
dist += Math.abs(a.charAt(i) - b.charAt(i));
}
- if(dist < 0){
+ if (dist < 0) {
dist = Long.MAX_VALUE; // overflow
}
@@ -242,10 +244,10 @@ public static double getDistanceToEquality(LocalTime a, LocalTime b) {
public static double getDistance(Object left, Object right) {
- if(left == null && right == null){
+ if (left == null && right == null) {
return 0;
}
- if(left == null || right == null){
+ if (left == null || right == null) {
return Double.MAX_VALUE;
}
@@ -330,4 +332,19 @@ public static double getDistance(Object left, Object right) {
return distance;
}
+
+ /**
+ * Computes the Hamming distance between two UUIDs. The Hamming distance is determined by
+ * counting the number of differing bits between the most and least significant bits
+ * of the two UUIDs.
+ *
+ * @param left the first UUID
+ * @param right the second UUID
+ * @return the Hamming distance between the two UUIDs
+ */
+ public static int getDistance(UUID left, UUID right) {
+ long diff1 = left.getMostSignificantBits() ^ right.getMostSignificantBits();
+ long diff2 = left.getLeastSignificantBits() ^ right.getLeastSignificantBits();
+ return Long.bitCount(diff1) + Long.bitCount(diff2);
+ }
}
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 dc7d54d689..ef172d9590 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
@@ -2,6 +2,7 @@
import java.util.Arrays;
import java.util.Objects;
+import java.util.UUID;
public class TruthnessUtils {
@@ -132,6 +133,7 @@ public static Truthness getEqualityTruthness(double a, double b) {
/**
* Returns a truthness value for comparing how close a length was to 0.
+ *
* @param len a positive value for a length
* @return a Truthness instance
*/
@@ -190,13 +192,13 @@ public static Truthness buildOrAggregationTruthness(Truthness... truthnesses) {
*
* This method returns XOR(a,b) as (a AND NOT b) OR (NOT a AND b).
*
- * @param left the first Truthness instance
+ * @param left the first Truthness instance
* @param right the second Truthness instance
* @return a new Truthness instance representing the XOR aggregation of the input Truthness instances
*/
public static Truthness buildXorAggregationTruthness(Truthness left, Truthness right) {
- Truthness leftAndNotRight = buildAndAggregationTruthness(left,right.invert());
- Truthness notLeftAndRight = buildAndAggregationTruthness(left.invert(),right);
+ Truthness leftAndNotRight = buildAndAggregationTruthness(left, right.invert());
+ Truthness notLeftAndRight = buildAndAggregationTruthness(left.invert(), right);
Truthness orAggregation = buildOrAggregationTruthness(leftAndNotRight, notLeftAndRight);
return orAggregation;
}
@@ -294,7 +296,7 @@ private static double trueOrAverageTrue(Truthness... truthnesses) {
* and creates a Truthness instance where the `ofTrue` field is the scaled value and
* the `ofFalse` field is set to 1.0.
*
- * @param base the base value used for scaling
+ * @param base the base value used for scaling
* @param ofTrueToScale the value to be scaled
* @return a new Truthness instance with the scaled `ofTrue` value and `ofFalse` set to 1.0
*/
@@ -305,5 +307,16 @@ public static Truthness buildScaledTruthness(double base, double ofTrueToScale)
}
+ public static Truthness getEqualityTruthness(UUID left, UUID right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
+
+ double distance = DistanceHelper.getDistance(left, right);
+ double normalizedDistance = normalizeValue(distance);
+ return new Truthness(
+ 1d - normalizedDistance,
+ !left.equals(right) ? 1d : 0d
+ );
+ }
}
diff --git a/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/DistanceHelperTest.java b/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/DistanceHelperTest.java
index 8700cdf562..be785edd23 100644
--- a/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/DistanceHelperTest.java
+++ b/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/DistanceHelperTest.java
@@ -2,6 +2,8 @@
import org.junit.jupiter.api.Test;
+import java.util.UUID;
+
import static org.evomaster.client.java.distance.heuristics.DistanceHelper.*;
import static org.junit.jupiter.api.Assertions.*;
@@ -68,9 +70,33 @@ public void testDoubleOverflowsDistance() {
@Test
public void testDoubleMaxDistance() {
- double upperBound = Double.MAX_VALUE /2;
+ double upperBound = Double.MAX_VALUE / 2;
double lowerBound = -upperBound;
double distance = getDistanceToEquality(lowerBound, upperBound);
assertEquals(Double.MAX_VALUE, distance);
}
-}
\ No newline at end of file
+
+ @Test
+ public void testDistanceUUIDEquals() {
+ UUID left = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ UUID right = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ double distance = getDistance(left, right);
+ assertEquals(0, distance);
+ }
+
+ @Test
+ public void testDistanceUUIDNotEquals() {
+ UUID left = UUID.fromString("123e4567-e89b-12d3-a456-426614174001");
+ UUID right = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ double distance = getDistance(left, right);
+ assertEquals(1, distance);
+ }
+
+ @Test
+ public void testDistanceUUIDNotEqualsThree() {
+ UUID left = UUID.fromString("123e4567-e89b-12d3-a456-426614174003");
+ UUID right = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ double distance = getDistance(left, right);
+ assertEquals(2, distance);
+ }
+}
diff --git a/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/TruthnessUtilsTest.java b/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/TruthnessUtilsTest.java
new file mode 100644
index 0000000000..5af69e921c
--- /dev/null
+++ b/client-java/distance-heuristics/src/test/java/org/evomaster/client/java/distance/heuristics/TruthnessUtilsTest.java
@@ -0,0 +1,32 @@
+package org.evomaster.client.java.distance.heuristics;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.evomaster.client.java.distance.heuristics.TruthnessUtils.normalizeValue;
+import static org.junit.jupiter.api.Assertions.*;
+
+class TruthnessUtilsTest {
+
+ @Test
+ public void testGetEqualityTruthnessEqualsUUID() {
+ UUID left = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ UUID right = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ Truthness t = TruthnessUtils.getEqualityTruthness(left, right);
+ assertTrue(t.isTrue());
+ assertEquals(1.0, t.getOfTrue());
+ assertEquals(0.0, t.getOfFalse());
+ }
+
+ @Test
+ public void testGetEqualityTruthnessNotEqualsUUID() {
+ UUID left = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ UUID right = UUID.fromString("123e4567-e89b-12d3-a456-426614174001");
+ Truthness t = TruthnessUtils.getEqualityTruthness(left, right);
+ assertFalse(t.isTrue());
+ assertEquals(1.0 - normalizeValue(1), t.getOfTrue());
+ assertEquals(1.0, t.getOfFalse());
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DataRow.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DataRow.java
index 608f6b3d2b..838486fab4 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/DataRow.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/DataRow.java
@@ -9,6 +9,7 @@
import java.util.*;
import java.util.stream.Collectors;
+import static org.evomaster.client.java.sql.heuristic.SqlStringUtils.nullSafeEndsWithIgnoreCase;
import static org.evomaster.client.java.sql.heuristic.SqlStringUtils.nullSafeEqualsIgnoreCase;
/**
@@ -102,10 +103,10 @@ public Object getValueByName(String name, String table) {
* since 'y','n','on','off', 'yes' and 'no'
* are also considered boolean literals.
*/
- if (n!=null && n.equalsIgnoreCase("true")) {
+ if (n != null && n.equalsIgnoreCase("true")) {
return true;
}
- if (n!= null && n.equalsIgnoreCase("false")) {
+ if (n != null && n.equalsIgnoreCase("false")) {
return false;
}
@@ -128,11 +129,11 @@ public Object getValueByName(String name, String table) {
boolean matchColumnName = nullSafeEqualsIgnoreCase(n, desc.getColumnName())
|| nullSafeEqualsIgnoreCase(n, desc.getAliasColumnName());
- if (!matchColumnName){
+ if (!matchColumnName) {
continue;
}
//no defined table, or exact match
- if(t == null || t.isEmpty() || nullSafeEqualsIgnoreCase(t, desc.getTableName()) ){
+ if (t == null || t.isEmpty() || nullSafeEqualsIgnoreCase(t, desc.getTableName())) {
return getValue(i);
}
/*
@@ -141,20 +142,20 @@ there can be many unnamed tables (eg results of sub-selects)
with same column names. At this moment, we would not
be able to distinguish them
*/
- if(nullSafeEqualsIgnoreCase(t, SqlNameContext.UNNAMED_TABLE)){
+ if (nullSafeEqualsIgnoreCase(t, SqlNameContext.UNNAMED_TABLE)) {
candidates.add(i);
}
- if(!t.contains(".") && desc.getTableName().toLowerCase().endsWith("."+t.toLowerCase())){
+ if (!t.contains(".") && nullSafeEndsWithIgnoreCase(desc.getTableName(), "." + t)) {
candidates.add(i);
}
}
- if(candidates.size() > 1){
+ if (candidates.size() > 1) {
SimpleLogger.uniqueWarn("More than one table candidate for: " + t);
}
- if(candidates.size() >= 1){
+ if (candidates.size() >= 1) {
return getValue(candidates.get(0));
}
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 4144c1492a..a523fdb094 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
@@ -1,5 +1,6 @@
package org.evomaster.client.java.sql;
+import net.sf.jsqlparser.statement.select.OrderByElement;
import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto;
import java.lang.reflect.Method;
@@ -195,4 +196,26 @@ public String getTableName() {
}
return variableDescriptors.get(0).getTableName();
}
+
+ /**
+ * Creates a new QueryResult containing only the first 'limit' rows
+ * of the current QueryResult.
+ *
+ * @param limit the maximum number of rows to include in the new QueryResult
+ * @return a new QueryResult with at most 'limit' rows
+ */
+ public QueryResult limit(long limit) {
+ if (limit < 0) {
+ throw new IllegalArgumentException("Limit must be non-negative");
+ }
+ if (limit >= rows.size()) {
+ return this;
+ }
+ QueryResult limitedResult = new QueryResult(this.variableDescriptors);
+ for (int i = 0; i < limit; i++) {
+ limitedResult.addRow(this.rows.get(i));
+ }
+ return limitedResult;
+ }
+
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/SqlDataType.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/SqlDataType.java
index 0ef250f2c1..897122d1af 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/SqlDataType.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/SqlDataType.java
@@ -52,6 +52,9 @@ public enum SqlDataType {
VARCHAR_IGNORECASE,
MEDIUMTEXT,
+ // UUID Type
+ UUID,
+
// Binary Types
LONGBLOB,
MEDIUMBLOB,
@@ -167,4 +170,8 @@ public static boolean isBooleanType(SqlDataType dataType) {
}
}
+ public static boolean isUUIDType(SqlDataType dataType) {
+ return dataType == SqlDataType.UUID;
+ }
+
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/ConversionHelper.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/ConversionHelper.java
index 0e9906ad6d..e81df576e6 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/ConversionHelper.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/ConversionHelper.java
@@ -6,6 +6,7 @@
import java.sql.Time;
import java.time.*;
import java.util.Date;
+import java.util.UUID;
import static java.util.Objects.nonNull;
import static org.evomaster.client.java.sql.heuristic.SqlHeuristicsCalculator.FALSE_TRUTHNESS;
@@ -113,4 +114,21 @@ public static Boolean convertToBoolean(Object object) {
}
}
+ public static UUID convertToUUID(Object object) {
+ if (nonNull(object)) {
+ return convertToNonNullUUID(object);
+ } else {
+ return null;
+ }
+ }
+
+ private static UUID convertToNonNullUUID(Object object) {
+ if (object instanceof UUID) {
+ return (UUID) object;
+ } else if (object instanceof String){
+ return UUID.fromString((String) object);
+ } else {
+ throw new IllegalArgumentException("Type must be UUID or string");
+ }
+ }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/QueryResultUtils.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/QueryResultUtils.java
index 309c6a0556..48ecf5c6fb 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/QueryResultUtils.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/QueryResultUtils.java
@@ -4,10 +4,7 @@
import org.evomaster.client.java.sql.QueryResult;
import org.evomaster.client.java.sql.VariableDescriptor;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
import java.util.stream.Collectors;
public class QueryResultUtils {
@@ -79,4 +76,23 @@ public static QueryResult addAliasToQueryResult(QueryResult queryResult, String
return newQueryResult;
}
+
+ /**
+ * Creates a new QueryResult containing only distinct rows from the given QueryResult.
+ *
+ * @param source
+ * @return
+ */
+ public static QueryResult createDistinctQueryResult(QueryResult source) {
+ QueryResult result = new QueryResult(source.seeVariableDescriptors());
+ for (DataRow row : source.seeRows()) {
+ List values = row.seeValues();
+ boolean alreadyExists = result.seeRows().stream()
+ .anyMatch(r -> r.seeValues().equals(values));
+ if (!alreadyExists) {
+ result.addRow(row);
+ }
+ }
+ return result;
+ }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlBaseTableReference.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlBaseTableReference.java
index 6929bc1b59..62046e65d7 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlBaseTableReference.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlBaseTableReference.java
@@ -12,13 +12,17 @@ public class SqlBaseTableReference extends SqlTableReference {
private final SqlTableId tableId;
- public SqlBaseTableReference(String name) {
- Objects.requireNonNull(name);
- this.tableId = new SqlTableId(name);
+ public SqlBaseTableReference(String catalog, String schema, String baseTableName) {
+ Objects.requireNonNull(baseTableName);
+ this.tableId = new SqlTableId(catalog, schema, baseTableName);
+ }
+
+ public SqlBaseTableReference(String baseTableName) {
+ this(null, null, baseTableName);
}
public String getName() {
- return tableId.getTableId();
+ return tableId.getTableName();
}
public SqlTableId getTableId() {
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlCastHelper.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlCastHelper.java
index e368fd9247..0ce259aa56 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlCastHelper.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlCastHelper.java
@@ -3,6 +3,7 @@
import org.evomaster.client.java.sql.SqlDataType;
import java.util.Objects;
+import java.util.UUID;
import static org.evomaster.client.java.sql.SqlDataType.*;
@@ -179,6 +180,21 @@ public static Object castTo(SqlDataType dataType, Object value) {
return castToDateTime(value);
}
+ if (SqlDataType.isUUIDType(dataType)) {
+ return castoToUUID(value);
+ }
+
throw new IllegalArgumentException("Must implement casting to " + dataType + ": " + value);
}
+
+ private static Object castoToUUID(Object value) {
+ if (value instanceof String) {
+ String s = (String) value;
+ return java.util.UUID.fromString(s);
+ } else if (value instanceof UUID){
+ return value;
+ } else {
+ throw new IllegalArgumentException("Cannot cast to UUID: " + value);
+ }
+ }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTableReference.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTable.java
similarity index 79%
rename from client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTableReference.java
rename to client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTable.java
index 72dc60ab8b..86eaf4db2f 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTableReference.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlDerivedTable.java
@@ -7,11 +7,11 @@
* Derived tables are temporary tables created within a query.
* A derived table is defined by a subquery in the FROM clause, or an the WHERE clause.
*/
-public class SqlDerivedTableReference extends SqlTableReference{
+public class SqlDerivedTable extends SqlTableReference {
private final Select select;
- public SqlDerivedTableReference(Select select) {
+ public SqlDerivedTable(Select select) {
this.select = select;
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluator.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluator.java
index 9be451f955..af342f8517 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluator.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluator.java
@@ -305,6 +305,8 @@ private Truthness evaluateTruthnessForComparisonOperator(Object concreteLeftValu
truthnessOfExpression = calculateTruthnessForInstantComparison(convertToInstant(concreteLeftValue), convertToInstant(concreteRightValue), comparisonOperatorType);
} else if (concreteLeftValue instanceof OffsetTime || concreteRightValue instanceof OffsetTime) {
truthnessOfExpression = calculateTruthnessForInstantComparison(convertToInstant(concreteLeftValue), convertToInstant(concreteLeftValue), comparisonOperatorType);
+ } else if (concreteLeftValue instanceof UUID || concreteRightValue instanceof UUID) {
+ truthnessOfExpression = calculateTruthnessForUUIDComparison(convertToUUID(concreteLeftValue), convertToUUID(concreteRightValue), comparisonOperatorType);
} else if (concreteLeftValue instanceof Object[] && concreteRightValue instanceof Object[]) {
truthnessOfExpression = calculateTruthnessForArrayComparison((Object[]) concreteLeftValue, (Object[]) concreteRightValue, comparisonOperatorType);
} else {
@@ -319,6 +321,20 @@ private Truthness evaluateTruthnessForComparisonOperator(Object concreteLeftValu
return truthness;
}
+ private static Truthness calculateTruthnessForUUIDComparison(UUID left, UUID right, ComparisonOperatorType comparisonOperatorType) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
+
+ switch (comparisonOperatorType) {
+ case EQUALS_TO:
+ return TruthnessUtils.getEqualityTruthness(left, right);
+ case NOT_EQUALS_TO:
+ return TruthnessUtils.getEqualityTruthness(left, right).invert();
+ default:
+ throw new IllegalArgumentException("Unsupported UUID binary operator: " + comparisonOperatorType);
+ }
+ }
+
private static Truthness calculateTruthnessForInstantComparison(Instant leftInstant, Instant rightInstant, ComparisonOperatorType comparisonOperatorType) {
Objects.requireNonNull(leftInstant);
Objects.requireNonNull(rightInstant);
@@ -381,6 +397,9 @@ private static double toDouble(Boolean booleanValue) {
}
public static Truthness getEqualityTruthness(String a, String b) {
+ Objects.requireNonNull(a);
+ Objects.requireNonNull(b);
+
if (a.equals(b)) {
return TRUE_TRUTHNESS;
} else {
@@ -484,6 +503,37 @@ public void visit(NullValue nullValue) {
evaluationStack.push(null);
}
+ private Object visitAggregationFunction(SqlAggregateFunction sqlAggregateFunction, Expression parameterExpression) {
+ final Object functionResult;
+ List values = new ArrayList<>();
+ if (parameterExpression instanceof Column) {
+ for (DataRow dataRow : this.getCurrentQueryResult().seeRows()) {
+ SqlExpressionEvaluator expressionEvaluator = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder()
+ .withTaintHandler(this.taintHandler)
+ .withTableColumnResolver(this.tableColumnResolver)
+ .withQueryResultSet(this.queryResultSet)
+ .withCurrentQueryResult(this.getCurrentQueryResult())
+ .withDataRowStack(this.dataRowStack)
+ .withCurrentDataRow(dataRow)
+ .withParentStatementEvaluator(this.parentStatementEvaluator)
+ .build();
+ parameterExpression.accept(expressionEvaluator);
+ final Object value = expressionEvaluator.popAsSingleValue();
+ values.add(value);
+ }
+ } else if (parameterExpression instanceof AllColumns) {
+ for (DataRow dataRow : getCurrentQueryResult().seeRows()) {
+ values.add(dataRow);
+ }
+ } else {
+ parameterExpression.accept(this);
+ Object value = this.popAsSingleValue();
+ values.add(value);
+ }
+ functionResult = sqlAggregateFunction.evaluate(values);
+ return functionResult;
+ }
+
@Override
public void visit(Function function) {
String functionName = function.getName();
@@ -492,42 +542,33 @@ public void visit(Function function) {
throw new UnsupportedOperationException("Function " + functionName + " needs to be implemented");
}
final Object functionResult;
- List values = new ArrayList<>();
if (sqlFunction instanceof SqlAggregateFunction) {
- Expression parameterExpression = function.getParameters().get(0);
-
- if (parameterExpression instanceof Column) {
- for (DataRow dataRow : this.getCurrentQueryResult().seeRows()) {
- SqlExpressionEvaluator expressionEvaluator = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder()
- .withTaintHandler(this.taintHandler)
- .withTableColumnResolver(this.tableColumnResolver)
- .withQueryResultSet(this.queryResultSet)
- .withCurrentQueryResult(this.getCurrentQueryResult())
- .withDataRowStack(this.dataRowStack)
- .withCurrentDataRow(dataRow)
- .withParentStatementEvaluator(this.parentStatementEvaluator)
- .build();
- parameterExpression.accept(expressionEvaluator);
- final Object value = expressionEvaluator.popAsSingleValue();
- values.add(value);
- }
- } else if (parameterExpression instanceof AllColumns) {
- for (DataRow dataRow : getCurrentQueryResult().seeRows()) {
- values.add(dataRow);
- }
- } else {
- parameterExpression.accept(this);
- Object value = this.popAsSingleValue();
- values.add(value);
+ if (function.getParameters().size() != 1) {
+ throw new UnsupportedOperationException(
+ String.format("Unsupported aggregate function %s with %s parameters",
+ functionName,
+ function.getParameters().size()));
}
- functionResult = sqlFunction.evaluate(values);
+ Expression parameterExpression = function.getParameters().get(0);
+ functionResult = visitAggregationFunction(
+ (SqlAggregateFunction) sqlFunction,
+ parameterExpression);
} else {
super.visit(function);
- for (int i = 0; i < function.getParameters().size(); i++) {
- Object concreteParameter = popAsSingleValue();
- values.add(concreteParameter);
+ final List values;
+ if (function.getParameters() != null && !function.getParameters().isEmpty()) {
+ List concreteParameters = popAsListOfValues();
+ if (function.getParameters().size() != concreteParameters.size()) {
+ throw new IllegalStateException(
+ String.format("Mismatch in number of parameters for function %s: %s expected but %s found",
+ functionName,
+ function.getParameters().size(),
+ concreteParameters.size()));
+ }
+ values = concreteParameters;
+ } else {
+ values = new ArrayList<>();
}
- Collections.reverse(values);
functionResult = sqlFunction.evaluate(values.toArray(new Object[]{}));
}
this.evaluationStack.push(functionResult);
@@ -1248,9 +1289,29 @@ public void visit(TimeKeyExpression timeKeyExpression) {
@Override
public void visit(DateTimeLiteralExpression dateTimeLiteralExpression) {
- String dateTimeAsString = dateTimeLiteralExpression.getValue();
- String dateTimeWithoutEnclosingQuotes = SqlStringUtils.removeEnclosingQuotes(dateTimeAsString);
- evaluationStack.push(dateTimeWithoutEnclosingQuotes);
+ final String dateTimeAsString = SqlStringUtils.removeEnclosingQuotes(dateTimeLiteralExpression.getValue());
+ final DateTimeLiteralExpression.DateTime dateTimeType = dateTimeLiteralExpression.getType();
+ Object dateTimeValue;
+ switch (dateTimeType) {
+ case DATE:
+ dateTimeValue = java.sql.Date.valueOf(dateTimeAsString);
+ break;
+ case TIME:
+ dateTimeValue = java.sql.Time.valueOf(dateTimeAsString);
+ break;
+ case TIMESTAMP:
+ dateTimeValue = java.sql.Timestamp.valueOf(dateTimeAsString);
+ break;
+ case TIMESTAMPTZ:
+ // Example literal: 2025-01-22 15:30:45+02:00
+ // Convert spaces to 'T' to comply with ISO-8601
+ String isoString = dateTimeAsString.replace(' ', 'T');
+ dateTimeValue = java.time.OffsetDateTime.parse(isoString);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported DateTimeLiteralExpression type: " + dateTimeType);
+ }
+ evaluationStack.push(dateTimeValue);
}
@Override
@@ -1388,7 +1449,7 @@ public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) {
public void visit(AllColumns allColumns) {
List values = new ArrayList<>(getCurrentDataRow().seeValues());
evaluationStack.push(values);
- }
+ }
@Override
public void visit(AllTableColumns allTableColumns) {
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculator.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculator.java
index 07c51feda3..2d9a83cd00 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculator.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculator.java
@@ -2,6 +2,7 @@
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
+import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
@@ -10,15 +11,13 @@
import net.sf.jsqlparser.statement.update.Update;
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 org.evomaster.client.java.sql.VariableDescriptor;
+import org.evomaster.client.java.sql.*;
import org.evomaster.client.java.sql.heuristic.function.FunctionFinder;
import org.evomaster.client.java.sql.heuristic.function.SqlAggregateFunction;
import org.evomaster.client.java.sql.heuristic.function.SqlFunction;
import org.evomaster.client.java.sql.internal.SqlDistanceWithMetrics;
import org.evomaster.client.java.sql.internal.SqlParserUtils;
+import org.evomaster.client.java.sql.internal.SqlTableId;
import org.evomaster.client.java.sql.internal.TaintHandler;
import org.evomaster.client.java.utils.SimpleLogger;
@@ -406,36 +405,67 @@ private QueryResult createQueryResultLeftJoin(QueryResult leftQueryResult, Query
SqlHeuristicResult computeHeuristic(Select select) {
tableColumnResolver.enterStatementeContext(select);
final SqlHeuristicResult heuristicResult;
- if (select instanceof SetOperationList) {
- SetOperationList unionQuery = (SetOperationList) select;
- List subqueries = unionQuery.getSelects();
- heuristicResult = computeHeuristicUnion(subqueries);
- } else if (select instanceof ParenthesedSelect) {
- ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
- Select subquery = parenthesedSelect.getSelect();
- heuristicResult = computeHeuristic(subquery);
- } else if (select instanceof PlainSelect) {
- PlainSelect plainSelect = (PlainSelect) select;
- final FromItem fromItem = getFrom(plainSelect);
- final List joins = getJoins(plainSelect);
- final Expression whereClause = getWhere(plainSelect);
- if (plainSelect.getGroupBy() != null) {
- heuristicResult = computeHeuristicSelectGroupByHaving(
- plainSelect.getSelectItems(),
- fromItem,
- joins,
- whereClause,
- plainSelect.getGroupBy().getGroupByExpressionList(),
- plainSelect.getHaving());
+ if (select.getLimit() != null && getLimitValue(select.getLimit()) == 0) {
+ // Handle case of LIMIT 0
+ heuristicResult = new SqlHeuristicResult(FALSE_TRUTHNESS, new QueryResult(Collections.emptyList()));
+ } else {
+ if (select instanceof SetOperationList) {
+ // Handle case of UNION/INTERSECT/EXCEPT
+ SetOperationList unionQuery = (SetOperationList) select;
+ List subqueries = unionQuery.getSelects();
+ heuristicResult = computeHeuristicUnion(subqueries, unionQuery.getLimit());
+ } else if (select instanceof ParenthesedSelect) {
+ // Handle case of parenthesed SELECT
+ ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
+ Select subquery = parenthesedSelect.getSelect();
+ final SqlHeuristicResult subqueryHeuristicResult = computeHeuristic(subquery);
+ if (parenthesedSelect.getLimit() != null) {
+ long limitValue = getLimitValue(parenthesedSelect.getLimit());
+ QueryResult queryResult = subqueryHeuristicResult.getQueryResult().limit(limitValue);
+ heuristicResult = new SqlHeuristicResult(subqueryHeuristicResult.getTruthness(), queryResult);
+ } else {
+ heuristicResult = subqueryHeuristicResult;
+ }
+ } else if (select instanceof PlainSelect) {
+ // Handle case of plain SELECT
+ PlainSelect plainSelect = (PlainSelect) select;
+ final FromItem fromItem = getFrom(plainSelect);
+ final List joins = getJoins(plainSelect);
+ final Expression whereClause = getWhere(plainSelect);
+ if (plainSelect.getGroupBy() != null) {
+ heuristicResult = computeHeuristicSelectGroupByHaving(
+ plainSelect.getSelectItems(),
+ fromItem,
+ joins,
+ whereClause,
+ plainSelect.getGroupBy().getGroupByExpressionList(),
+ plainSelect.getHaving(),
+ plainSelect.getOrderByElements(),
+ plainSelect.getLimit());
+ } else {
+ SqlHeuristicResult heuristicResultBeforeDistinct = computeHeuristicSelect(
+ plainSelect.getSelectItems(),
+ fromItem,
+ joins,
+ whereClause,
+ plainSelect.getOrderByElements(),
+ plainSelect.getLimit());
+
+ if (plainSelect.getDistinct() != null) {
+ QueryResult distinctQueryResult;
+ if (plainSelect.getDistinct().getOnSelectItems() != null && !plainSelect.getDistinct().getOnSelectItems().isEmpty()) {
+ distinctQueryResult = createQueryResultDistinctOn(heuristicResultBeforeDistinct.getQueryResult(), plainSelect.getDistinct().getOnSelectItems());
+ } else {
+ distinctQueryResult = QueryResultUtils.createDistinctQueryResult(heuristicResultBeforeDistinct.getQueryResult());
+ }
+ heuristicResult = new SqlHeuristicResult(heuristicResultBeforeDistinct.getTruthness(), distinctQueryResult);
+ } else {
+ heuristicResult = heuristicResultBeforeDistinct;
+ }
+ }
} else {
- heuristicResult = computeHeuristicSelect(
- plainSelect.getSelectItems(),
- fromItem,
- joins,
- whereClause);
+ throw new IllegalArgumentException("Cannot calculate heuristics for SQL command of type " + select.getClass().getName());
}
- } else {
- throw new IllegalArgumentException("Cannot calculate heuristics for SQL command of type " + select.getClass().getName());
}
tableColumnResolver.exitCurrentStatementContext();
return heuristicResult;
@@ -448,13 +478,54 @@ private SqlHeuristicResult computeHeuristicSelect(List> selectItem
return heuristicResult;
}
+ private SqlHeuristicResult computeHeuristicSelect(List> selectItems,
+ FromItem fromItem,
+ List joins,
+ Expression whereClause,
+ Limit limit) {
+ final SqlHeuristicResult intermediateHeuristicResult = computeHeuristic(fromItem, joins, whereClause);
+ QueryResult queryResult = createQueryResult(intermediateHeuristicResult.getQueryResult(), selectItems);
+
+ if (limit != null) {
+ long limitValue = getLimitValue(limit);
+ queryResult = queryResult.limit(limitValue);
+ }
+
+ final SqlHeuristicResult heuristicResult = new SqlHeuristicResult(intermediateHeuristicResult.getTruthness(), queryResult);
+ return heuristicResult;
+ }
+
+ private SqlHeuristicResult computeHeuristicSelect(List> selectItems,
+ FromItem fromItem,
+ List joins,
+ Expression whereClause,
+ List orderByElements,
+ Limit limit) {
+ final SqlHeuristicResult intermediateHeuristicResult = computeHeuristic(fromItem, joins, whereClause);
+ QueryResult queryResult = createQueryResult(intermediateHeuristicResult.getQueryResult(), selectItems);
+
+ if (orderByElements != null && !orderByElements.isEmpty()) {
+ queryResult = createQueryResultOrderBy(queryResult, orderByElements);
+ }
+
+ if (limit != null) {
+ long limitValue = getLimitValue(limit);
+ queryResult = queryResult.limit(limitValue);
+ }
+
+ final SqlHeuristicResult heuristicResult = new SqlHeuristicResult(intermediateHeuristicResult.getTruthness(), queryResult);
+ return heuristicResult;
+ }
+
private SqlHeuristicResult computeHeuristicSelectGroupByHaving(
List> selectItems,
FromItem fromItem,
List joins,
Expression whereClause,
List groupByExpressions,
- Expression having) {
+ Expression having,
+ List orderByElements,
+ Limit limit) {
final SqlHeuristicResult intermediateHeuristicResult = computeHeuristic(fromItem, joins, whereClause);
@@ -475,9 +546,11 @@ private SqlHeuristicResult computeHeuristicSelectGroupByHaving(
final List truthnesses = new ArrayList<>();
for (QueryResult groupByQueryResult : groupByQueryResults.values()) {
QueryResult aggregatedQueryResult = createQueryResult(groupByQueryResult, selectItems);
- if (aggregatedQueryResult.size() != 1) {
- throw new IllegalStateException("An aggregated query result cannot have " + aggregatedQueryResult.size() + "rows");
+ if (aggregatedQueryResult.isEmpty()) {
+ throw new IllegalStateException("An aggregated query result cannot be empty");
}
+ // If more than one row is present, we always pick the first one.
+ // This is the semantics of the SELECT DISTINCT ON (columns) in PostgreSQL
DataRow dataRow = aggregatedQueryResult.seeRows().get(0);
if (having != null) {
final Truthness truthness = evaluateAll(Collections.singletonList(having), groupByQueryResult);
@@ -490,7 +563,7 @@ private SqlHeuristicResult computeHeuristicSelectGroupByHaving(
}
}
final List variableDescriptors = createSelectVariableDescriptors(selectItems, sourceQueryResult.seeVariableDescriptors());
- final QueryResult queryResult = new QueryResult(variableDescriptors);
+ QueryResult queryResult = new QueryResult(variableDescriptors);
for (DataRow groupByDataRow : groupByDataRows) {
queryResult.addRow(groupByDataRow);
}
@@ -505,6 +578,15 @@ private SqlHeuristicResult computeHeuristicSelectGroupByHaving(
intermediateHeuristicResult.getTruthness(),
havingTruthness);
+ if (orderByElements != null && !orderByElements.isEmpty()) {
+ queryResult = createQueryResultOrderBy(queryResult, orderByElements);
+ }
+
+ if (limit != null) {
+ long limitValue = getLimitValue(limit);
+ queryResult = queryResult.limit(limitValue);
+ }
+
return new SqlHeuristicResult(groupByHavingTruthness, queryResult);
}
@@ -669,6 +751,9 @@ private static boolean hasAnyTableColumn(Expression expression) {
}
}
}
+ } else if (expression instanceof Parenthesis) {
+ Parenthesis parenthesisExpression = (Parenthesis) expression;
+ return hasAnyTableColumn(parenthesisExpression.getExpression());
}
return false;
}
@@ -728,7 +813,7 @@ && isAggregateFunction(((Function) selectItem.getExpression()).getName())) {
return selectVariableDescriptors;
}
- private SqlHeuristicResult computeHeuristicUnion(List subqueries) {
+ private SqlHeuristicResult computeHeuristicUnion(List subqueries, Limit limit) {
List subqueryResults = new ArrayList<>();
for (Select subquery : subqueries) {
SqlHeuristicResult subqueryResult = computeHeuristic(subquery);
@@ -742,7 +827,12 @@ private SqlHeuristicResult computeHeuristicUnion(List subqueries) {
List queryResults = subqueryResults.stream()
.map(SqlHeuristicResult::getQueryResult)
.collect(Collectors.toList());
- final QueryResult unionRowSet = QueryResultUtils.createUnionRowSet(queryResults);
+ QueryResult unionRowSet = QueryResultUtils.createUnionRowSet(queryResults);
+
+ if (limit != null) {
+ long limitValue = getLimitValue(limit);
+ unionRowSet = unionRowSet.limit(limitValue);
+ }
return new SqlHeuristicResult(t, unionRowSet);
}
@@ -826,23 +916,127 @@ private Truthness evaluateAll(Collection conditions, DataRow row) {
return evaluateAll(conditions, row, null);
}
+ private QueryResult createQueryResultDistinctOn(QueryResult queryResult, List> distinctOnSelectItems) {
+ if (distinctOnSelectItems == null || distinctOnSelectItems.isEmpty()) {
+ throw new IllegalArgumentException("Cannot evaluate empty DISTINCT ON select items");
+ }
+
+ QueryResult result = new QueryResult(queryResult.seeVariableDescriptors());
+
+ // LinkedHashSet keeps insertion order (important: DISTINCT ON keeps FIRST matching row)
+ Set> seenKeys = new LinkedHashSet<>();
+
+ for (DataRow row : queryResult.seeRows()) {
+
+ List key = new ArrayList<>(distinctOnSelectItems.size());
+ for (SelectItem selectItem : distinctOnSelectItems) {
+ Expression expression = selectItem.getExpression();
+ Object value = evaluate(expression, row);
+ key.add(value);
+ }
+
+ // If this combination of values hasn't been seen, collect the row
+ if (!seenKeys.contains(key)) {
+ seenKeys.add(key);
+ result.addRow(row);
+ }
+ }
+ return result;
+ }
+
+
+ private QueryResult createQueryResultOrderBy(QueryResult queryResult, List orderByElements) {
+ if (orderByElements == null || orderByElements.isEmpty()) {
+ throw new IllegalArgumentException("Cannot evaluate empty orderBy elements");
+ }
+
+ QueryResult sortedResult = new QueryResult(queryResult.seeVariableDescriptors());
+
+ List sortedRows = new ArrayList<>(queryResult.seeRows());
+ sortedRows.sort(new OrderByComparator(orderByElements));
+
+ for (DataRow row : sortedRows) {
+ sortedResult.addRow(row);
+ }
+
+ return sortedResult;
+ }
+
private QueryResult createQueryResult(FromItem fromItem) {
- final QueryResult tableData;
if (fromItem == null) {
- tableData = new QueryResult(Collections.emptyList());
+ return new QueryResult(Collections.emptyList());
} 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);
- if (fromItem.getAlias() != null) {
- tableData = QueryResultUtils.addAliasToQueryResult(sourceQueryResultSet.getQueryResultForNamedTable(tableName), fromItem.getAlias().getName());
+ final QueryResult tableData;
+ Table table = (Table) fromItem;
+ if (this.tableColumnResolver.resolve(table) != null) {
+ SqlTableReference sqlTableReference = this.tableColumnResolver.resolve(table);
+ if (sqlTableReference instanceof SqlBaseTableReference) {
+ SqlBaseTableReference sqlBaseTableReference = (SqlBaseTableReference) sqlTableReference;
+ SqlTableId sqlTableId = sqlBaseTableReference.getTableId();
+ tableData = sourceQueryResultSet.getQueryResultForNamedTable(sqlTableId.getTableName());
+ } else if (sqlTableReference instanceof SqlDerivedTable) {
+ SqlDerivedTable sqlDerivedTable = (SqlDerivedTable) sqlTableReference;
+ Select select = sqlDerivedTable.getSelect();
+ SqlHeuristicResult sqlHeuristicResult = this.computeHeuristic(select);
+ tableData = sqlHeuristicResult.getQueryResult();
+ } else {
+ throw new IllegalArgumentException("Cannot compute Truthness for form item that it is not a table " + table);
+ }
} else {
+ // if no table reference is found for the table, then we default to base table
tableData = sourceQueryResultSet.getQueryResultForNamedTable(tableName);
}
+ if (fromItem.getAlias() != null) {
+ // add alias to table data
+ return QueryResultUtils.addAliasToQueryResult(tableData, fromItem.getAlias().getName());
+ } else {
+ return tableData;
+ }
+ }
+ }
+
+ private class OrderByComparator implements Comparator {
+
+ private final List orderByElements;
+
+ public OrderByComparator(List orderByElements) {
+ this.orderByElements = orderByElements;
+ }
+
+ @Override
+ public int compare(DataRow r1, DataRow r2) {
+ for (OrderByElement orderByElement : orderByElements) {
+ Expression orderByElementExpression = orderByElement.getExpression();
+ Object val1 = evaluate(orderByElementExpression, r1);
+ Object val2 = evaluate(orderByElementExpression, r2);
+
+ int comparison;
+ if (val1 == null && val2 == null) {
+ comparison = 0;
+ } else if (val1 == null) {
+ comparison = -1;
+ } else if (val2 == null) {
+ comparison = 1;
+ } else {
+ if (val1 instanceof Comparable && val2 instanceof Comparable) {
+ comparison = ((Comparable) val1).compareTo(val2);
+ } else {
+ // Cannot compare, treat as equal
+ comparison = 0;
+ }
+ }
+
+ if (comparison != 0) {
+ return orderByElement.isAsc() ? comparison : -comparison;
+ }
+ }
+ return 0;
}
- return tableData;
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlStringUtils.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlStringUtils.java
index c0293deaf6..4d3ab0afde 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlStringUtils.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlStringUtils.java
@@ -40,13 +40,31 @@ private static boolean startsAndEndsWith(String input, String str) {
* @param a the first string to compare, may be null
* @param b the second string to compare, may be null
* @return {@code true} if both strings are equal ignoring case, or both are null;
- * {@code false} otherwise
+ * {@code false} otherwise
*/
- public static boolean nullSafeEqualsIgnoreCase(String a, String b){
- if (a==null) {
- return b==null;
+ public static boolean nullSafeEqualsIgnoreCase(String a, String b) {
+ if (a == null) {
+ return b == null;
} else {
return a.equalsIgnoreCase(b);
}
}
+
+ /**
+ * Checks if a string ends with another string, ignoring case considerations.
+ * It safely handles null values.
+ *
+ * @param a
+ * @param suffix
+ * @return
+ */
+ public static boolean nullSafeEndsWithIgnoreCase(String a, String suffix) {
+ if (a == null) {
+ return false;
+ } else if (suffix == null) {
+ return false;
+ } else {
+ return a.toLowerCase().endsWith(suffix.toLowerCase());
+ }
+ }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlTableName.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlTableName.java
new file mode 100644
index 0000000000..99002d26f7
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/SqlTableName.java
@@ -0,0 +1,42 @@
+package org.evomaster.client.java.sql.heuristic;
+
+import net.sf.jsqlparser.schema.Table;
+
+import java.util.Objects;
+
+/**
+ * Represents a reference to a base table in SQL.
+ * Base tables are physical tables in the database.
+ */
+public class SqlTableName extends SqlTableReference {
+
+ private final Table table;
+
+ public SqlTableName(Table table) {
+ Objects.requireNonNull(table);
+ this.table = table;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public String toString() {
+ return table.toString();
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof SqlTableName) {
+ SqlTableName other = (SqlTableName) obj;
+ return table.equals(other.table);
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return table.hashCode();
+ }
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasContext.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasContext.java
new file mode 100644
index 0000000000..9b4496d903
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasContext.java
@@ -0,0 +1,90 @@
+package org.evomaster.client.java.sql.heuristic;
+
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.select.Select;
+
+import java.util.Objects;
+import java.util.TreeMap;
+
+/**
+ * The TableAliasContext class manages table aliases within a specific SQL context.
+ * It allows adding aliases for both physical table names and derived tables (subqueries),
+ * and provides methods to check for the existence of aliases and retrieve their corresponding
+ */
+public class TableAliasContext {
+ private final TreeMap tableNameAliases = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private final TreeMap derivedTableAliases = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private boolean containsAlias(Alias alias) {
+ Objects.requireNonNull(alias, "alias cannot be null");
+ return containsAlias(alias.getName());
+ }
+
+ /**
+ * Check if the given alias is declared in the current context.
+ *
+ * @param aliasName
+ * @return
+ */
+ public boolean containsAlias(String aliasName) {
+ Objects.requireNonNull(aliasName, "aliasName cannot be null");
+ String lowerCaseAliasName = aliasName.toLowerCase();
+ return tableNameAliases.containsKey(lowerCaseAliasName)
+ || derivedTableAliases.containsKey(lowerCaseAliasName);
+ }
+
+ /**
+ * Get the table reference corresponding to the given alias.
+ *
+ * @param aliasName
+ * @return
+ */
+ public SqlTableReference getTableReference(String aliasName) {
+ Objects.requireNonNull(aliasName, "aliasName cannot be null");
+ String lowerCaseAliasName = aliasName.toLowerCase();
+ if (tableNameAliases.containsKey(lowerCaseAliasName)) {
+ return tableNameAliases.get(lowerCaseAliasName);
+ } else if (derivedTableAliases.containsKey(lowerCaseAliasName)) {
+ return derivedTableAliases.get(lowerCaseAliasName);
+ } else {
+ throw new IllegalArgumentException("Alias not found in the current context: " + aliasName);
+ }
+ }
+
+ private String getAliasName(Alias alias) {
+ Objects.requireNonNull(alias, "alias cannot be null");
+
+ return alias.getName().toLowerCase();
+ }
+
+ /**
+ * Add an alias for a table name.
+ *
+ * @param alias
+ * @param target
+ */
+ public void addAliasToTableName(Alias alias, Table target) {
+ Objects.requireNonNull(alias, "alias cannot be null");
+ Objects.requireNonNull(target, "target cannot be null");
+ if (containsAlias(alias)) {
+ throw new IllegalArgumentException("Alias already declared in the current context: " + alias.getName());
+ }
+ String aliasName = getAliasName(alias);
+ tableNameAliases.put(aliasName, new SqlTableName(target));
+ }
+
+ /**
+ * Add an alias for a derived table (subquery).
+ *
+ * @param alias
+ * @param subquery
+ */
+ public void addAliasToDerivedTable(Alias alias, Select subquery) {
+ if (containsAlias(alias)) {
+ throw new IllegalArgumentException("Alias already declared in the current context: " + alias.getName());
+ }
+ String aliasName = getAliasName(alias);
+ derivedTableAliases.put(aliasName, new SqlDerivedTable(subquery));
+ }
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasResolver.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasResolver.java
index db6d9f6cd0..cf3192b0a4 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasResolver.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableAliasResolver.java
@@ -9,21 +9,20 @@
import java.util.*;
/**
- * This class is responsible for resolving table aliases in SQL statements.
- * Every time a new SQL alias context (e.g. subselect) is entered, the
- * method enterAliasContext should be called. Every time the context is exited,
- * the method exitAliasContext should be called.
+ * The TableAliasResolver class is designed to manage and resolve
+ * table aliases in SQL statements. It maintains a stack-based alias
+ * resolution context and supports various SQL operations including
+ * SELECT, UPDATE, and DELETE statements.
*
- * Alias resolution is case-insensitive.
+ * The class ensures correct mapping of aliases to corresponding tables
+ * or derived tables within SQL query structures.
+ *
+ * Since it has no access to the database schema, it does not resolve
+ * if a given table name is a physical table or a view.
*/
class TableAliasResolver {
-
-
- /**
- * A stack of maps to store table aliases in different contexts.
- */
- private final Deque> stackOfTableAliases = new ArrayDeque<>();
+ private final Deque stackOfTableAliases = new ArrayDeque<>();
/**
* This method is called when entering a new alias context.
@@ -100,8 +99,7 @@ private void processSelect(Select select) {
} else if (select instanceof ParenthesedSelect) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
if (parenthesedSelect.getAlias() != null) {
- final String lowerCaseAliasName = parenthesedSelect.getAlias().getName();
- stackOfTableAliases.peek().put(lowerCaseAliasName, new SqlDerivedTableReference(parenthesedSelect));
+ stackOfTableAliases.peek().addAliasToDerivedTable(parenthesedSelect.getAlias(), parenthesedSelect);
}
Select innerSelect = parenthesedSelect.getSelect();
processSelect(innerSelect);
@@ -110,23 +108,18 @@ private void processSelect(Select select) {
}
private void createNewAliasContext() {
- stackOfTableAliases.push(new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+ stackOfTableAliases.push(new TableAliasContext());
}
private void processWithItemsList(List withItemsList) {
for (WithItem withItem : withItemsList) {
if (withItem.getAlias() != null) {
-
- final String aliasName = withItem.getAlias().getName();
- final String lowerCaseAliasName = aliasName.toLowerCase();
final Select subquery = withItem.getSelect();
- final SqlTableReference derivedSqlTableReference = new SqlDerivedTableReference(subquery);
- stackOfTableAliases.peek().put(lowerCaseAliasName, derivedSqlTableReference);
+ stackOfTableAliases.peek().addAliasToDerivedTable(withItem.getAlias(), subquery);
}
}
}
-
private void processPlainSelect(PlainSelect select) {
Objects.requireNonNull(select, "select cannot be null");
@@ -148,13 +141,31 @@ private void processFromItem(FromItem fromItem) {
Table table = (Table) fromItem;
if (table.getAlias() != null) {
final String lowerCaseAliasName = table.getAlias().getName().toLowerCase();
- stackOfTableAliases.peek().put(lowerCaseAliasName, new SqlBaseTableReference(table.getFullyQualifiedName()));
+ final String schemaName = table.getSchemaName();
+ final String tableName = table.getName();
+ if (schemaName == null && isAliasDeclaredInCurrentContext(tableName)) {
+ // if there is an alias, then we need to resolve to the actual table reference
+ // (e.g. could be an alias to a common table expression)
+ final SqlTableReference tableReference = this.resolveAlias(tableName);
+ if (tableReference instanceof SqlDerivedTable) {
+ SqlDerivedTable derivedTable = (SqlDerivedTable) tableReference;
+ stackOfTableAliases.peek().addAliasToDerivedTable(table.getAlias(), derivedTable.getSelect());
+ } else if (tableReference instanceof SqlTableName) {
+ SqlTableName tableNameRef = (SqlTableName) tableReference;
+ stackOfTableAliases.peek().addAliasToTableName(table.getAlias(), tableNameRef.getTable());
+ } else {
+ throw new IllegalArgumentException("Unexpected table reference type: " + tableReference.getClass().getName());
+ }
+ } else {
+ // if no alias is declared in the current context, we can safely assume that it is a table from the schema
+ stackOfTableAliases.peek().addAliasToTableName(table.getAlias(), table);
+ }
}
} else if (fromItem instanceof ParenthesedSelect) {
ParenthesedSelect subSelect = (ParenthesedSelect) fromItem;
if (subSelect.getAlias() != null) {
final String lowerCaseAliasName = subSelect.getAlias().getName().toLowerCase();
- stackOfTableAliases.peek().put(lowerCaseAliasName, new SqlDerivedTableReference(subSelect));
+ stackOfTableAliases.peek().addAliasToDerivedTable(subSelect.getAlias(), subSelect);
}
}
}
@@ -166,7 +177,7 @@ private void processFromItem(FromItem fromItem) {
* @param alias the alias to resolve
* @return a TableReference object with the table or the derived table (e.g. view)
*/
- public SqlTableReference resolveTableReference(String alias) {
+ public SqlTableReference resolveAlias(String alias) {
if (!isAliasDeclaredInAnyContext(alias)) {
throw new IllegalArgumentException("Alias not found in any context: " + alias);
}
@@ -175,9 +186,9 @@ public SqlTableReference resolveTableReference(String alias) {
* The Deque<> iterator traverses the stack of context maps
* in a LIFO (Last-In-First-Out) order.
*/
- for (Map context : stackOfTableAliases) {
- if (context.containsKey(alias)) {
- return context.get(alias);
+ for (TableAliasContext context : stackOfTableAliases) {
+ if (context.containsAlias(alias)) {
+ return context.getTableReference(alias);
}
}
throw new IllegalArgumentException("Alias not found in any context: " + alias);
@@ -202,30 +213,28 @@ public int getContextDepth() {
/**
* Checks if the alias is declared in the current context.
*
- * @param alias The alias to check.
+ * @param aliasName The alias to check.
* @return true if the alias is declared in the current context, false otherwise.
*/
- public boolean isAliasDeclaredInCurrentContext(String alias) {
- Objects.requireNonNull(alias, "alias cannot be null");
+ public boolean isAliasDeclaredInCurrentContext(String aliasName) {
+ Objects.requireNonNull(aliasName, "alias cannot be null");
if (stackOfTableAliases.isEmpty()) {
- throw new IllegalArgumentException("Alias stack is empty. Cannot resolve alias: " + alias);
+ throw new IllegalArgumentException("Alias stack is empty. Cannot resolve alias: " + aliasName);
}
- final String lowerCaseAliasName = alias.toLowerCase();
- return stackOfTableAliases.peek().containsKey(lowerCaseAliasName);
+ return stackOfTableAliases.peek().containsAlias(aliasName);
}
/**
* Checks if the alias is declared in any context.
*
- * @param alias
+ * @param aliasName
* @return
*/
- public boolean isAliasDeclaredInAnyContext(String alias) {
- Objects.requireNonNull(alias, "alias cannot be null");
- final String lowerCaseAliasName = alias.toLowerCase();
- for (Map context : stackOfTableAliases) {
- if (context.containsKey(lowerCaseAliasName)) {
+ public boolean isAliasDeclaredInAnyContext(String aliasName) {
+ Objects.requireNonNull(aliasName, "alias cannot be null");
+ for (TableAliasContext context : stackOfTableAliases) {
+ if (context.containsAlias(aliasName)) {
return true;
}
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableColumnResolver.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableColumnResolver.java
index e48a0c9ca1..ac01a2b6bd 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableColumnResolver.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableColumnResolver.java
@@ -1,7 +1,6 @@
package org.evomaster.client.java.sql.heuristic;
import net.sf.jsqlparser.expression.Alias;
-import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
@@ -9,12 +8,12 @@
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto;
+import org.evomaster.client.java.controller.api.dto.database.schema.TableIdDto;
import org.evomaster.client.java.sql.internal.SqlColumnId;
import org.evomaster.client.java.sql.internal.SqlParserUtils;
import org.evomaster.client.java.sql.internal.SqlTableId;
import java.util.*;
-import java.util.stream.Collectors;
/**
* Resolves table names and columns in a SQL query.
@@ -75,27 +74,36 @@ public void exitCurrentStatementContext() {
* @param r a (potentially null) name
* @return
*/
- private boolean equalNames(String l, String r) {
+ private static boolean equalNames(String l, String r) {
Objects.requireNonNull(l);
return l.equalsIgnoreCase(r);
}
- private boolean isBaseTable(String tableName) {
+
+ private boolean isBaseTable(String schemaName, String tableName) {
+ Objects.requireNonNull(tableName);
+
+ return this.schema.tables.stream()
+ .anyMatch(t -> (new TableNameMatcher(t.id).matches(schemaName, tableName)));
+ }
+
+ private TableIdDto findFirstTableIdDto(String schemaName, String tableName) {
Objects.requireNonNull(tableName);
return this.schema.tables.stream()
- .filter(t -> equalNames(t.id.name, tableName))
- .count() > 0;
+ .filter(t -> (new TableNameMatcher(t.id).matches(schemaName, tableName)))
+ .map(t -> t.id)
+ .findFirst()
+ .orElse(null);
}
private boolean hasColumn(SqlTableId sqlTableId, SqlColumnId sqlColumnId) {
Objects.requireNonNull(sqlTableId);
return this.schema.tables.stream()
- .filter(t -> new SqlTableId(t.id.name).equals(sqlTableId))
+ .filter(t -> (new TableNameMatcher(t.id).matches(sqlTableId.getSchemaName(), sqlTableId.getTableName())))
.flatMap(t -> t.columns.stream())
- .filter(c -> new SqlColumnId(c.name).equals(sqlColumnId))
- .count() > 0;
+ .anyMatch(c -> new SqlColumnId(c.name).equals(sqlColumnId));
}
/**
@@ -114,7 +122,7 @@ public SqlColumnReference resolve(Column column) {
*/
for (Statement contextStatement : contextStatementStack) {
SqlColumnReference sqlColumnReference = resolveInContextStatement(column, contextStatement);
- if (sqlColumnReference!=null) {
+ if (sqlColumnReference != null) {
return sqlColumnReference;
}
}
@@ -138,10 +146,10 @@ private SqlColumnReference resolveInContextStatement(Column column, Statement co
if (hasColumn(sqlBaseTableReference.getTableId(), columnId)) {
return new SqlColumnReference(sqlBaseTableReference, column.getColumnName());
}
- } else if (sqlTableReference instanceof SqlDerivedTableReference) {
- final SqlDerivedTableReference sqlDerivedTableReference = (SqlDerivedTableReference) sqlTableReference;
- if (findBaseTableColumnReference(sqlDerivedTableReference.getSelect(), column.getColumnName()) != null) {
- return new SqlColumnReference(sqlDerivedTableReference, column.getColumnName());
+ } else if (sqlTableReference instanceof SqlDerivedTable) {
+ final SqlDerivedTable sqlDerivedTable = (SqlDerivedTable) sqlTableReference;
+ if (findBaseTableColumnReference(sqlDerivedTable.getSelect(), column.getColumnName()) != null) {
+ return new SqlColumnReference(sqlDerivedTable, column.getColumnName());
}
} else {
throw new IllegalArgumentException("Unknown table reference type: " + sqlTableReference.getClass().getName());
@@ -202,7 +210,7 @@ private SqlColumnReference resolveInContextStatement(String columnName, Statemen
} else if (contextStatement instanceof Select) {
Select select = (Select) contextStatement;
if (findBaseTableColumnReference(select, columnName) != null) {
- return new SqlColumnReference(new SqlDerivedTableReference(select), columnName);
+ return new SqlColumnReference(new SqlDerivedTable(select), columnName);
}
}
// column was not found in this context statement
@@ -248,7 +256,7 @@ private SqlColumnReference createColumnReference(FromItem fromItem, String colum
ParenthesedFromItem parenthesedFromItem = (ParenthesedFromItem) fromItem;
return createColumnReference(parenthesedFromItem.getFromItem(), columnName);
} else {
- return new SqlColumnReference(new SqlDerivedTableReference((Select) fromItem), columnName);
+ return new SqlColumnReference(new SqlDerivedTable((Select) fromItem), columnName);
}
}
@@ -261,17 +269,38 @@ private SqlColumnReference createColumnReference(FromItem fromItem, String colum
public SqlTableReference resolve(Table table) {
Objects.requireNonNull(table);
Objects.requireNonNull(table.getName());
+ final String schemaName = table.getSchemaName();
final String tableName = table.getName();
+
+ // 1. Explicitly qualified table → always a base reference
+ if (schemaName != null) {
+ // if schema is used, assume table is a base table
+ TableIdDto tableIdDto = findFirstTableIdDto(schemaName, tableName);
+ return new SqlBaseTableReference(tableIdDto.catalog, tableIdDto.schema, tableIdDto.name);
+ }
+
+ // 2. No schema → maybe it's an alias?
if (tableAliasResolver.isAliasDeclaredInAnyContext(tableName)) {
- return tableAliasResolver.resolveTableReference(tableName);
- } else if (isBaseTable(tableName)) {
- return new SqlBaseTableReference(tableName);
- } else {
- // table was not found in any context
- return null;
+ SqlTableReference tableReference = tableAliasResolver.resolveAlias(tableName);
+ if (tableReference instanceof SqlTableName) {
+ SqlTableName sqlTableName = (SqlTableName) tableReference;
+ return resolve(sqlTableName.getTable());
+ } else if (tableReference instanceof SqlDerivedTable) {
+ return (SqlDerivedTable) tableReference;
+ } else {
+ throw new IllegalArgumentException("Unexpected table reference type: " + tableReference.getClass().getName());
+ }
}
- }
+ // 3. Otherwise, try resolving as a base table in the default schema
+ if (isBaseTable(null, tableName)) {
+ TableIdDto tableIdDto = findFirstTableIdDto(null, tableName);
+ return new SqlBaseTableReference(tableIdDto.catalog, tableIdDto.schema, tableIdDto.name);
+ }
+
+ // 4. Unknown table
+ return null;
+ }
private SqlColumnReference findBaseTableColumnReference(FromItem fromItem, String columnName) {
Objects.requireNonNull(fromItem);
@@ -286,15 +315,8 @@ private SqlColumnReference findBaseTableColumnReference(FromItem fromItem, Strin
}
} else if (fromItem instanceof Table) {
Table table = (Table) fromItem;
- String tableName = table.getFullyQualifiedName();
- SqlTableReference sqlTableReference;
- if (tableAliasResolver.isAliasDeclaredInAnyContext(tableName)) {
- sqlTableReference = tableAliasResolver.resolveTableReference(tableName);
- } else if (isBaseTable(tableName)) {
- sqlTableReference = new SqlBaseTableReference(table.getFullyQualifiedName());
- } else {
- // table was expected to be in the schema, but it is missing.
- // Therefore, we cannot resolve the column
+ SqlTableReference sqlTableReference = resolve(table);
+ if (sqlTableReference == null) {
return null;
}
if (sqlTableReference instanceof SqlBaseTableReference) {
@@ -303,9 +325,9 @@ private SqlColumnReference findBaseTableColumnReference(FromItem fromItem, Strin
if (hasColumn(sqlBaseTableReference.getTableId(), columnId)) {
return new SqlColumnReference(sqlTableReference, columnName);
}
- } else if (sqlTableReference instanceof SqlDerivedTableReference) {
- SqlDerivedTableReference sqlDerivedTableReference = (SqlDerivedTableReference) sqlTableReference;
- if (findBaseTableColumnReference(sqlDerivedTableReference.getSelect(), columnName) != null) {
+ } else if (sqlTableReference instanceof SqlDerivedTable) {
+ SqlDerivedTable sqlDerivedTable = (SqlDerivedTable) sqlTableReference;
+ if (findBaseTableColumnReference(sqlDerivedTable.getSelect(), columnName) != null) {
return new SqlColumnReference(sqlTableReference, columnName);
}
} else {
@@ -395,12 +417,12 @@ private SqlColumnReference findBaseTableColumnReference(PlainSelect plainSelect,
}
} else {
Alias alias = selectItem.getAlias();
- if (alias!=null && equalNames(alias.getName(), columnName)) {
+ if (alias != null && equalNames(alias.getName(), columnName)) {
/* If an alias exists in the current SELECT that
defines the column name, then the column is
defined within the current SELECT (i.e., a derived table)
*/
- return new SqlColumnReference(new SqlDerivedTableReference(plainSelect), columnName);
+ return new SqlColumnReference(new SqlDerivedTable(plainSelect), columnName);
}
}
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableNameMatcher.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableNameMatcher.java
new file mode 100644
index 0000000000..b4a6a09150
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/TableNameMatcher.java
@@ -0,0 +1,36 @@
+package org.evomaster.client.java.sql.heuristic;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.TableIdDto;
+
+import java.util.Objects;
+
+public class TableNameMatcher {
+
+ private final TableIdDto tableIdDto;
+
+ public TableNameMatcher(TableIdDto tableIdDto) {
+ this.tableIdDto = tableIdDto;
+ }
+
+ public boolean matches(String schemaName, String tableName) {
+ // TODO This method should also support catalog name.
+ // TODO Consider merging functionality with SqlDtoUtils.matchByName()
+ return (schemaName == null || matchSchemaName(schemaName, tableIdDto.schema))
+ && matchTableName(tableName, tableIdDto.name);
+ }
+
+ private static boolean matchSchemaName(String schemaName1, String schemaName2) {
+ Objects.requireNonNull(schemaName1);
+ return equalNames(schemaName1, schemaName2);
+ }
+
+ private static boolean equalNames(String l, String r) {
+ Objects.requireNonNull(l);
+ return l.equalsIgnoreCase(r);
+ }
+
+ private static boolean matchTableName(String tableName1, String tableName2) {
+ return equalNames(tableName1, tableName2);
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunction.java
new file mode 100644
index 0000000000..76e51731f4
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunction.java
@@ -0,0 +1,38 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+/**
+ * Represents the SQL COALESCE() function, which returns the first non-null argument.
+ */
+public class CoalesceFunction extends SqlFunction {
+
+ public CoalesceFunction() {
+ super("COALESCE");
+ }
+
+ @Override
+ public Object evaluate(Object... arguments) {
+ if (arguments.length ==0) {
+ throw new IllegalArgumentException("COALESCE() function requires at least one argument");
+ }
+
+ final Object arg = returnFirstNonNullArgument(arguments);
+ return arg;
+ }
+
+ /**
+ * Returns the first non-null argument from arguments.
+ * If none of them is non-null, returns null.
+ *
+ * @param arguments
+ * @return
+ */
+ private static Object returnFirstNonNullArgument(Object[] arguments) {
+ for (Object arg : arguments) {
+ if (arg != null) {
+ return arg;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateFunction.java
new file mode 100644
index 0000000000..309a842f82
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateFunction.java
@@ -0,0 +1,79 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.evomaster.client.java.sql.internal.SqlDateTimeParser;
+
+import java.sql.Date;
+import java.sql.Timestamp;
+import java.time.*;
+
+/**
+ * Represents the SQL DATE() function, which converts a given value into a SQL Date object.
+ *
+ * The DATE() function accepts a single argument and attempts to convert it into a SQL Date.
+ *
+ * It supports the following types of inputs:
+ * - {@link Date}: Returns the argument as it is since it is already a valid SQL Date.
+ * - {@link Timestamp}: Converts the timestamp to a date by extracting the date part.
+ * - {@link java.util.Date}: Converts the date into a SQL Date.
+ * - {@link String}: Attempts to parse the string using known SQL date/time formats. Returns a SQL Date if parsing succeeds.
+ *
+ * When the input value is null, the function returns null, maintaining SQL semantics.
+ *
+ * If the input type is unsupported or the string cannot be parsed into a valid date, an {@link IllegalArgumentException} is thrown.
+ */
+public class DateFunction extends SqlFunction {
+
+
+ public DateFunction() {
+ super("DATE");
+ }
+
+ @Override
+ public Object evaluate(Object... arguments) {
+ if (arguments.length != 1) {
+ throw new IllegalArgumentException("DATE() function takes exactly one argument but got: " + arguments.length);
+ }
+
+ Object arg = arguments[0];
+ if (arg == null) {
+ return null; // SQL DATE(NULL) → NULL
+ }
+
+ // --- java.sql.Date ---
+ if (arg instanceof Date) {
+ return arg; // Already a pure DATE
+ }
+
+ // --- java.sql.Timestamp ---
+ if (arg instanceof Timestamp) {
+ Timestamp ts = (Timestamp) arg;
+ LocalDate d = ts.toLocalDateTime().toLocalDate();
+ return Date.valueOf(d);
+ }
+
+ // --- java.util.Date (includes java.sql.Time) ---
+ if (arg instanceof java.util.Date) {
+ java.util.Date d = (java.util.Date) arg;
+ Instant i = Instant.ofEpochMilli(d.getTime());
+ LocalDate ld = i.atZone(ZoneId.systemDefault()).toLocalDate();
+ return Date.valueOf(ld);
+ }
+
+ // --- String: parse with multiple fallback formats ---
+ if (arg instanceof String) {
+ String s = (String) arg;
+ s = s.trim();
+ if (s.isEmpty()) {
+ return null;
+ }
+
+ SqlDateTimeParser parser = new SqlDateTimeParser();
+ return parser.parseDate(s);
+ }
+
+ throw new IllegalArgumentException(
+ "Unsupported type for DATE(): " + arg.getClass().getName()
+ );
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunction.java
new file mode 100644
index 0000000000..b1e1c71389
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunction.java
@@ -0,0 +1,151 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.evomaster.client.java.sql.internal.ColumnTypeParser;
+
+import java.time.*;
+import java.time.temporal.TemporalAccessor;
+import java.util.Objects;
+
+public class DateTruncFunction extends SqlFunction {
+
+ public static final String MONTH = "month";
+ public static final String HH = "hh";
+ public static final String HOUR = "hour";
+ public static final String DD = "dd";
+ public static final String DAY = "day";
+ public static final String SECOND = "second";
+ public static final String MINUTE = "minute";
+ public static final String YEAR = "year";
+
+ private static final String DATE_TRUNC = "DATE_TRUNC";
+
+ public DateTruncFunction() {
+ super(DATE_TRUNC);
+ }
+
+ @Override
+ public Object evaluate(Object... arguments) {
+ if (arguments.length != 2) {
+ throw new IllegalArgumentException("DATE_TRUNC requires two arguments but got: " + arguments.length);
+ }
+ final String unit = String.valueOf(arguments[0]).toLowerCase();
+ final Object tsObj = arguments[1];
+ if (tsObj == null) {
+ return null;
+ }
+
+ if (tsObj instanceof String) {
+ Instant instant = ColumnTypeParser.getAsInstant((String) tsObj);
+ return truncateInstant(instant, unit);
+ } else if (tsObj instanceof java.util.Date) {
+ // legacy datetime API from Java <8
+ Instant instant = ((java.util.Date) tsObj).toInstant();
+ Instant truncatedInstant = truncateInstant(instant, unit);
+ return java.util.Date.from(truncatedInstant);
+ } else if (tsObj instanceof TemporalAccessor) {
+ // java.time API from Java 8+
+ return truncateTemporalAccessor((TemporalAccessor) tsObj, unit);
+ } else {
+ throw new IllegalArgumentException("Unsupported timestamp object type: " + tsObj.getClass());
+ }
+ }
+
+ private static TemporalAccessor truncateTemporalAccessor(TemporalAccessor t, String unit) {
+ unit = unit.toLowerCase();
+
+ // Determine what actual type we are working with
+ if (t instanceof LocalDateTime) {
+ return truncateLocalDateTime((LocalDateTime) t, unit);
+ }
+ if (t instanceof LocalDate) {
+ return truncateLocalDate((LocalDate) t, unit);
+ }
+ if (t instanceof LocalTime) {
+ return truncateLocalTime((LocalTime) t, unit);
+ }
+ if (t instanceof OffsetDateTime) {
+ return truncateOffsetDateTime((OffsetDateTime) t, unit);
+ }
+ if (t instanceof ZonedDateTime) {
+ return truncateZonedDateTime((ZonedDateTime) t, unit);
+ }
+ if (t instanceof OffsetTime) {
+ return truncateOffsetTime((OffsetTime) t, unit);
+ }
+ if (t instanceof Instant) {
+ return truncateInstant((Instant) t, unit);
+ }
+
+ throw new UnsupportedOperationException(
+ "Unsupported TemporalAccessor type: " + t.getClass());
+ }
+
+ private static Instant truncateInstant(Instant t, String unit) {
+ // convert to UTC LocalDateTime, truncate, and convert back
+ LocalDateTime ldt = LocalDateTime.ofInstant(t, ZoneOffset.UTC);
+ LocalDateTime truncated = truncateLocalDateTime(ldt, unit);
+ return truncated.toInstant(ZoneOffset.UTC);
+ }
+
+ // ---- Concrete truncators -------------------------------------------------
+
+ private static LocalDateTime truncateLocalDateTime(LocalDateTime dt, String unit) {
+ switch (unit) {
+ case SECOND:
+ return dt.withNano(0);
+ case MINUTE:
+ return dt.withSecond(0).withNano(0);
+ case HOUR:
+ return dt.withMinute(0).withSecond(0).withNano(0);
+ case DAY:
+ return dt.toLocalDate().atStartOfDay();
+ case MONTH:
+ return LocalDate.of(dt.getYear(), dt.getMonth(), 1).atStartOfDay();
+ case YEAR:
+ return LocalDate.of(dt.getYear(), 1, 1).atStartOfDay();
+ default:
+ throw new IllegalArgumentException("Unsupported unit: " + unit);
+ }
+ }
+
+ private static LocalDate truncateLocalDate(LocalDate d, String unit) {
+ switch (unit) {
+ case DAY:
+ return d;
+ case MONTH:
+ return d.withDayOfMonth(1);
+ case YEAR:
+ return d.withDayOfYear(1);
+ default:
+ throw new IllegalArgumentException("Unsupported unit for LocalDate: " + unit);
+ }
+ }
+
+ private static LocalTime truncateLocalTime(LocalTime t, String unit) {
+ switch (unit) {
+ case SECOND:
+ return t.withNano(0);
+ case MINUTE:
+ return t.withSecond(0).withNano(0);
+ case HOUR:
+ return t.withMinute(0).withSecond(0).withNano(0);
+ default:
+ throw new IllegalArgumentException("Unsupported unit for LocalTime: " + unit);
+ }
+ }
+
+ private static OffsetDateTime truncateOffsetDateTime(OffsetDateTime dt, String unit) {
+ LocalDateTime truncated = truncateLocalDateTime(dt.toLocalDateTime(), unit);
+ return truncated.atOffset(dt.getOffset());
+ }
+
+ private static OffsetTime truncateOffsetTime(OffsetTime t, String unit) {
+ LocalTime truncated = truncateLocalTime(t.toLocalTime(), unit);
+ return truncated.atOffset(t.getOffset());
+ }
+
+ private static ZonedDateTime truncateZonedDateTime(ZonedDateTime dt, String unit) {
+ LocalDateTime truncated = truncateLocalDateTime(dt.toLocalDateTime(), unit);
+ return truncated.atZone(dt.getZone());
+ }
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/FunctionFinder.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/FunctionFinder.java
index 9723a355e7..9b8c48b416 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/FunctionFinder.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/FunctionFinder.java
@@ -27,7 +27,13 @@ private FunctionFinder() {
super();
this.addFunction(new TimeFunction());
this.addFunction(new UpperFunction());
+ this.addFunction(new LowerFunction());
this.addFunction(new StringDecodeFunction());
+ this.addFunction(new CoalesceFunction());
+ this.addFunction(new DateTruncFunction());
+ this.addFunction(new DateFunction());
+ this.addFunction(new NowFunction());
+
// aggregation functions
this.addFunction(new SqlCountFunction());
this.addFunction(new SqlMaxFunction());
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/LowerFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/LowerFunction.java
new file mode 100644
index 0000000000..c20c47ec56
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/LowerFunction.java
@@ -0,0 +1,25 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+public class LowerFunction extends SqlFunction {
+
+ public LowerFunction() {
+ super("LOWER");
+ }
+
+ @Override
+ public Object evaluate(Object... arguments) {
+ if (arguments.length !=1) {
+ throw new IllegalArgumentException("LOWER() function takes exactly one argument but got:" + arguments.length);
+ }
+
+ Object concreteValue = arguments[0];
+ if (concreteValue==null) {
+ return null;
+ } else if (concreteValue instanceof String) {
+ return ((String) concreteValue).toLowerCase();
+ } else {
+ throw new IllegalArgumentException("LOWER() function takes a string argument, but got a " + concreteValue.getClass().getName());
+ }
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/NowFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/NowFunction.java
new file mode 100644
index 0000000000..079751bc33
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/NowFunction.java
@@ -0,0 +1,23 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+
+public class NowFunction extends SqlFunction {
+
+ public static final Timestamp CANONICAL_NOW_VALUE = java.sql.Timestamp.from(Instant.EPOCH);
+
+ public NowFunction() {
+ super("NOW");
+ }
+
+ @Override
+ public Object evaluate(Object... arguments) {
+ if (arguments.length != 0) {
+ throw new IllegalArgumentException("NOW() function takes no argument but got:" + arguments.length + " arguments.");
+ }
+ Timestamp nowValue = CANONICAL_NOW_VALUE;
+ return nowValue;
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/SqlFunctionName.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/SqlFunctionName.java
index 96930fe9be..257046504a 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/SqlFunctionName.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/SqlFunctionName.java
@@ -5,13 +5,28 @@
public class SqlFunctionName implements Comparable {
+ /**
+ * The name of the SQL function.
+ */
private final String functionName;
+ /**
+ * Constructs a new SqlFunctionName instance with the specified function name.
+ * The function name is converted to lowercase to ensure consistency.
+ *
+ * @param functionName the name of the SQL function. Must not be null.
+ * Throws {@code NullPointerException} if the input is null.
+ */
public SqlFunctionName(String functionName) {
Objects.requireNonNull(functionName);
this.functionName = functionName.toLowerCase();
}
+ /**
+ * Retrieves the name of the SQL function.
+ *
+ * @return the name of the SQL function as a string. This value is stored in lowercase.
+ */
public String getFunctionName() {
return functionName;
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/StringDecodeFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/StringDecodeFunction.java
index b653454a5b..30f9494454 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/StringDecodeFunction.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/StringDecodeFunction.java
@@ -9,7 +9,7 @@ public StringDecodeFunction() {
@Override
public Object evaluate(Object... arguments) {
if (arguments.length !=1) {
- throw new IllegalArgumentException("STRINGDECODE() function takes exactly one argument");
+ throw new IllegalArgumentException("STRINGDECODE() function takes exactly one argument but got: " + arguments.length + "");
}
Object concreteValue = arguments[0];
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/TimeFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/TimeFunction.java
index a39a7d8b87..aaf8137edb 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/TimeFunction.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/TimeFunction.java
@@ -13,12 +13,20 @@ public TimeFunction() {
@Override
public Object evaluate(Object... arguments) {
if (arguments.length != 1) {
- throw new IllegalArgumentException("TIME() function takes exactly one argument");
+ throw new IllegalArgumentException("TIME() function takes exactly one argument but got:" + arguments.length);
}
Object argument = arguments[0];
if (argument instanceof Date) {
Date date = (Date) argument;
return timeOf(date);
+ } else if (argument instanceof String) {
+ String s = (String) argument;
+ try {
+ Date date = Timestamp.valueOf(s);
+ return timeOf(date);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid string format for TIME() function: " + s + " due to " + e.getMessage());
+ }
}
throw new UnsupportedOperationException("Implement this");
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/UpperFunction.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/UpperFunction.java
index 3ecfa05a78..3bc5287841 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/UpperFunction.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/heuristic/function/UpperFunction.java
@@ -9,7 +9,7 @@ public UpperFunction() {
@Override
public Object evaluate(Object... arguments) {
if (arguments.length !=1) {
- throw new IllegalArgumentException("UPPER() function takes exactly one argument");
+ throw new IllegalArgumentException("UPPER() function takes exactly one argument but got: " + arguments.length);
}
Object concreteValue = arguments[0];
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzer.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzer.java
index 5f878c4444..3fca408062 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzer.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzer.java
@@ -29,7 +29,9 @@ public static SqlTableId getDeletedTable(String delete) {
Table table = stmt.getTable();
if (table != null) {
- SqlTableId sqlTableId = new SqlTableId(table.getFullyQualifiedName());
+ final String schemaName = table.getSchemaName();
+ final String tableName = table.getName();
+ SqlTableId sqlTableId = new SqlTableId(null, schemaName, tableName);
return sqlTableId;
} else {
//TODO need to handle special cases of multi-tables with JOINs
@@ -47,8 +49,10 @@ public static Map.Entry> getInsertedDataFields(Stri
Insert stmt = (Insert) SqlParserUtils.parseSqlCommand(insert);
Table table = stmt.getTable();
if (table != null) {
+ final String schemaName = table.getSchemaName();
+ final String tableName = table.getName();
Map.Entry> insertedDataFields = new AbstractMap.SimpleEntry<>(
- new SqlTableId(table.getFullyQualifiedName()),
+ new SqlTableId(null, schemaName, tableName),
Collections.singleton(new SqlColumnId("*")));
return insertedDataFields;
} else {
@@ -68,8 +72,10 @@ public static Map.Entry> getUpdatedDataFields(Strin
Table table = stmt.getTable();
if (table != null) {
+ final String schemaName = table.getSchemaName();
+ final String tableName = table.getName();
Map.Entry> updatedDataFields = new AbstractMap.SimpleEntry<>(
- new SqlTableId(table.getFullyQualifiedName()),
+ new SqlTableId(null, schemaName, tableName),
Collections.singleton(new SqlColumnId("*")));
return updatedDataFields;
} else {
@@ -125,7 +131,10 @@ public static Map> getSelectReadDataFields(String s
}
private static void handleTable(Map> map, Table table) {
- Set columns = map.computeIfAbsent(new SqlTableId(table.getFullyQualifiedName()), k -> new HashSet<>());
+ final String schemaName = table.getSchemaName();
+ final String tableName = table.getName();
+ final SqlTableId tableId = new SqlTableId(null, schemaName, tableName);
+ Set columns = map.computeIfAbsent(tableId, k -> new HashSet<>());
//TODO: should check actual fields... would likely need to pass SelectBody as input as well
if (!columns.contains("*")) {
columns.add(new SqlColumnId("*"));
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/QueryResultTransformer.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/QueryResultTransformer.java
index f5fe2838ae..11a043872f 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/QueryResultTransformer.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/QueryResultTransformer.java
@@ -35,7 +35,7 @@ public static QueryResult[] convertInsertionDtosToQueryResults(List> maps = new HashMap<>();
for (SqlTableId tableId : columns.keySet()) {
List kresults = new ArrayList<>();
- insertionDtos.stream().filter(d -> d.targetTable.equalsIgnoreCase(tableId.getTableId())).forEach(insertionDto -> {
+ insertionDtos.stream().filter(d -> d.targetTable.equalsIgnoreCase(tableId.getTableName())).forEach(insertionDto -> {
QueryResult qr = convertInsertionDtoToQueryResult(insertionDto, tableId, columns.get(tableId), schemaDto, kresults);
if (qr != null && (!qr.isEmpty()))
kresults.add(qr);
@@ -46,7 +46,13 @@ public static QueryResult[] convertInsertionDtosToQueryResults(List> qrPerTable = cartesianProduct(maps.keySet().stream().sorted().map(maps::get).collect(Collectors.toList()));
+ List> qrPerTable = cartesianProduct(maps
+ .keySet()
+ .stream()
+ .sorted(Comparator.comparing(Object::toString))
+ .map(maps::get)
+ .collect(Collectors.toList()));
+
if (qrPerTable == null)
return null;
@@ -145,18 +151,18 @@ private static QueryResult convertInsertionDtoToQueryResult(InsertionDto inserti
final QueryResult existingQueryResult;
if (!existingQueryResults.isEmpty())
- existingQueryResult = existingQueryResults.stream().filter(qr -> qr.sameVariableNames(relatedColumnNames, tableId.getTableId())).findAny().orElse(null);
+ existingQueryResult = existingQueryResults.stream().filter(qr -> qr.sameVariableNames(relatedColumnNames, tableId.getTableName())).findAny().orElse(null);
else
existingQueryResult = null;
final QueryResult queryResult;
if (existingQueryResult == null)
- queryResult = new QueryResult(relatedColumnNames, tableId.getTableId());
+ queryResult = new QueryResult(relatedColumnNames, tableId.getTableName());
else
queryResult = existingQueryResult;
Optional foundTableSchema = dto.tables.stream()
- .filter(t -> SqlDtoUtils.matchByName(t, tableId.getTableId()))
+ .filter(t -> SqlDtoUtils.matchByName(t, tableId.getTableName()))
.findFirst();
if (foundTableSchema.isPresent()) {
@@ -173,7 +179,7 @@ private static QueryResult convertInsertionDtoToQueryResult(InsertionDto inserti
throw new IllegalArgumentException("Cannot find column schema of " + relatedColumnNames.get(i) + " in Table " + tableId);
values.add(getColumnValueBasedOnPrintableValue(printableValue.get(i), columnDto));
}
- queryResult.addRow(relatedColumnNames, tableId.getTableId(), values);
+ queryResult.addRow(relatedColumnNames, tableId.getTableName(), values);
} else {
throw new IllegalArgumentException("Cannot find table schema of " + tableId);
@@ -243,7 +249,7 @@ public static QueryResultSet translateInsertionDtos(
QueryResultSet queryResultSet = new QueryResultSet();
for (SqlTableId tableId : columns.keySet()) {
TableDto tableDto = schema.tables.stream()
- .filter(t -> t.id.name.equalsIgnoreCase(tableId.getTableId()))
+ .filter(t -> t.id.name.equalsIgnoreCase(tableId.getTableName()))
.findFirst().orElseThrow(() -> new IllegalArgumentException("Cannot find table schema of " + tableId));
List variableDescriptors = tableDto.columns.stream()
@@ -252,7 +258,7 @@ public static QueryResultSet translateInsertionDtos(
QueryResult queryResult = new QueryResult(variableDescriptors);
for (InsertionDto dto : insertionDtos) {
- if (tableId.getTableId().equalsIgnoreCase(dto.targetTable)) {
+ if (tableId.getTableName().equalsIgnoreCase(dto.targetTable)) {
List concreteValues = new ArrayList<>();
for (VariableDescriptor variableDescriptor : variableDescriptors) {
Object concreteValueOrNull = findConcreteValueOrNull(variableDescriptor.getColumnName(), tableDto, dto.data);
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlColumnId.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlColumnId.java
index 0a198e0f27..f16a29a504 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlColumnId.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlColumnId.java
@@ -10,7 +10,7 @@
* No case sensitive is considered when comparing column ids within
* this class.
*/
-public class SqlColumnId implements Comparable {
+public class SqlColumnId {
private final String columnId;
@@ -41,9 +41,4 @@ public boolean equals(Object obj) {
public int hashCode() {
return columnId.hashCode();
}
- @Override
- public int compareTo(SqlColumnId o) {
- Objects.requireNonNull(o);
- return this.getColumnId().compareTo(o.getColumnId());
- }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDateTimeParser.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDateTimeParser.java
new file mode 100644
index 0000000000..ab2093a26a
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlDateTimeParser.java
@@ -0,0 +1,59 @@
+package org.evomaster.client.java.sql.internal;
+
+import java.sql.Date;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Objects;
+
+public class SqlDateTimeParser {
+
+ private static final DateTimeFormatter[] DATE_FORMATS = new DateTimeFormatter[]{
+ DateTimeFormatter.ISO_LOCAL_DATE, // yyyy-MM-dd
+ DateTimeFormatter.ISO_LOCAL_DATE_TIME, // yyyy-MM-ddTHH:mm:ss
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), // MySQL classic format
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") // Fallback
+ };
+
+ public Date parseDate(String s) {
+ Objects.requireNonNull(s);
+
+ // Try each known SQL format
+ for (DateTimeFormatter f : DATE_FORMATS) {
+ final Date ld = parseDate(s, f);
+ if (ld != null) {
+ return ld;
+ }
+
+ final Date ldt = parseDateTime(s, f);
+ if (ldt != null) {
+ return ldt;
+ }
+ }
+ throw new IllegalArgumentException("Cannot parse DATE(): unsupported date format: " + s);
+ }
+
+ private static Date parseDateTime(String s, DateTimeFormatter f) {
+ try {
+ // Try as LocalDateTime
+ LocalDateTime ldt = LocalDateTime.parse(s, f);
+ return Date.valueOf(ldt.toLocalDate());
+ } catch (DateTimeParseException ignored) {
+ return null;
+ }
+ }
+
+ private static Date parseDate(String s, DateTimeFormatter f) {
+ try {
+ // Try as LocalDate
+ LocalDate ld = LocalDate.parse(s, f);
+ return Date.valueOf(ld);
+ } catch (DateTimeParseException ignored) {
+ return null;
+ }
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java
index 0062873679..c258409b81 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlHandler.java
@@ -192,7 +192,7 @@ public SqlExecutionsDto getExecutionDto() {
sqlExecutionsDto.failedWhere.putAll(getTableToColumnsMap(failedWhere));
sqlExecutionsDto.insertedData.putAll(getTableToColumnsMap(insertedData));
sqlExecutionsDto.updatedData.putAll(getTableToColumnsMap(updatedData));
- sqlExecutionsDto.deletedData.addAll(deletedData.stream().map(SqlTableId::getTableId).collect(Collectors.toSet()));
+ sqlExecutionsDto.deletedData.addAll(deletedData.stream().map(SqlTableId::getTableName).collect(Collectors.toSet()));
sqlExecutionsDto.numberOfSqlCommands = this.numberOfSqlCommands;
sqlExecutionsDto.sqlParseFailureCount = this.sqlParseFailureCount;
sqlExecutionsDto.sqlExecutionLogDtoList.addAll(executedSqlCommands);
@@ -202,7 +202,7 @@ public SqlExecutionsDto getExecutionDto() {
private static Map> getTableToColumnsMap(Map> originalMap) {
Map> result = new HashMap<>();
for (Map.Entry> originalEntry : originalMap.entrySet()) {
- result.put(originalEntry.getKey().getTableId(), originalEntry.getValue().stream().map(SqlColumnId::getColumnId).collect(Collectors.toSet()));
+ result.put(originalEntry.getKey().getTableName(), originalEntry.getValue().stream().map(SqlColumnId::getColumnId).collect(Collectors.toSet()));
}
return result;
}
@@ -345,15 +345,20 @@ private Map> extractColumnsInvolvedInStatement(Stat
private List getQueryResultsForComputingSqlDistance(final Map> columnsInWhere) throws SQLException {
List queryResults = new ArrayList<>();
// we sort the table and column identifiers to improve testeability (i.e. allow mocking)
- for (SqlTableId tableId : columnsInWhere.keySet().stream().sorted().collect(Collectors.toList())) {
- List columnIds = columnsInWhere.get(tableId).stream().sorted().collect(Collectors.toList());
+ for (SqlTableId tableId : columnsInWhere.keySet()
+ .stream()
+ .sorted(Comparator.comparing(Object::toString))
+ .collect(Collectors.toList())) {
+ List columnIds = columnsInWhere.get(tableId).stream()
+ .sorted(Comparator.comparing(Object::toString))
+ .collect(Collectors.toList());
final String select;
if (columnIds.isEmpty()) {
// the table is required but no specific column was required.
// Therefore, we need to fetch all columns for DELETE and UPDATE.
- select = createSelectForSingleTable(tableId, Collections.singletonList(new SqlColumnId("*")));
+ select = SqlSelectBuilder.buildSelect(schema.databaseType, tableId, Collections.singletonList(new SqlColumnId("*")));
} else {
- select = createSelectForSingleTable(tableId, columnIds);
+ select = SqlSelectBuilder.buildSelect(schema.databaseType, tableId, columnIds);
}
QueryResult queryResult = SqlScriptRunner.execCommand(connection, select);
queryResults.add(queryResult);
@@ -431,7 +436,7 @@ But SELECT could be complex with many JOINs... whereas DIP would be simple(r)?
tableName = tableToColumns.getKey();
columnNames = tableToColumns.getValue().stream().sorted().collect(Collectors.toList());
}
- select = createSelectForSingleTable(tableName, columnNames);
+ select = SqlSelectBuilder.buildSelect(schema.databaseType, tableName, columnNames);
}
QueryResult data;
@@ -445,20 +450,6 @@ But SELECT could be complex with many JOINs... whereas DIP would be simple(r)?
return HeuristicsCalculator.computeDistance(sqlCommand, schema, taintHandler, data);
}
- private static String createSelectForSingleTable(SqlTableId tableId, List columnIds) {
-
- StringBuilder buffer = new StringBuilder();
- buffer.append("SELECT ");
-
- String variables = columnIds.stream().map(SqlColumnId::getColumnId).collect(Collectors.joining(", "));
-
- buffer.append(variables);
- buffer.append(" FROM ");
- buffer.append(tableId.getTableId());
-
- return buffer.toString();
- }
-
/**
* Check the fields involved in the WHERE clause (if any).
* Return a map from table name to column names of the involved fields.
@@ -488,17 +479,17 @@ Map> extractColumnsInvolvedInWhere(Statement statem
@Override
public void visit(Column column) {
- String tableName = context.getFullyQualifiedTableName(column);
+ String fullyQualifiedTableName = context.getFullyQualifiedTableName(column);
// String tableName = context.getTableName(column);
- if (tableName.equalsIgnoreCase(SqlNameContext.UNNAMED_TABLE)) {
+ if (fullyQualifiedTableName.equalsIgnoreCase(SqlNameContext.UNNAMED_TABLE)) {
// TODO handle it properly when ll have support for sub-selects
return;
}
String columnName = column.getColumnName().toLowerCase();
- if (!context.hasColumn(tableName, columnName)) {
+ if (!context.hasColumn(fullyQualifiedTableName, columnName)) {
/*
This is an issue with the JsqlParser library. Until we upgrade it, or fix it if not fixed yet,
@@ -518,13 +509,13 @@ public void visit(Column column) {
//case in which a boolean constant is wrongly treated as a column name.
//TODO not sure what we can really do here without modifying the parser
} else {
- SimpleLogger.warn("Cannot find column '" + columnName + "' in table '" + tableName + "'");
+ SimpleLogger.warn("Cannot find column '" + columnName + "' in table '" + fullyQualifiedTableName + "'");
}
return;
}
-
- result.putIfAbsent(new SqlTableId(tableName), new HashSet<>());
- Set columnIds = result.get(new SqlTableId(tableName));
+ final SqlTableId tableId = SqlTableIdParser.parseFullyQualifiedTableName(fullyQualifiedTableName, schema.databaseType);
+ result.putIfAbsent(tableId, new HashSet<>());
+ Set columnIds = result.get(tableId);
columnIds.add(new SqlColumnId(columnName));
}
};
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 1781383cdf..bae8175f43 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
@@ -118,6 +118,8 @@ public static List getJoins(Statement parsedStatement) {
}
}
+
+
/**
* This method assumes that the SQL command can be successfully parsed.
*
@@ -219,4 +221,15 @@ public static List getFromAndJoinItems(Select select) {
}
return fromAndJoinItems;
}
+
+ /**
+ * Extracts the limit value from a JSQLParser Limit object.
+ *
+ * @param limit the Limit object from which to extract the limit value
+ * @return the limit value as a long
+ * @throws IllegalArgumentException if the row count is not a LongValue
+ */
+ public static long getLimitValue(Limit limit) {
+ return ((net.sf.jsqlparser.expression.LongValue) limit.getRowCount()).getValue();
+ }
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlSelectBuilder.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlSelectBuilder.java
new file mode 100644
index 0000000000..1e9bb38488
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlSelectBuilder.java
@@ -0,0 +1,28 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class SqlSelectBuilder {
+
+ public static String buildSelect(
+ DatabaseType databaseType,
+ SqlTableId tableId,
+ List columnIds
+ ) {
+ if (columnIds == null || columnIds.isEmpty()) {
+ throw new IllegalArgumentException("Column list cannot be empty");
+ }
+
+ String columnList = columnIds.stream()
+ .map(SqlColumnId::getColumnId)
+ .collect(Collectors.joining(", "));
+
+ final String fullyQualifiedTableName = tableId.buildQualifiedTableName(databaseType);
+ return String.format("SELECT %s FROM %s", columnList, fullyQualifiedTableName);
+ }
+
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableId.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableId.java
index fefa3980b2..cd7140ae06 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableId.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableId.java
@@ -1,49 +1,108 @@
package org.evomaster.client.java.sql.internal;
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+
import java.util.Objects;
/**
- * A class representing a SQL table identifier
- * from a physical database table.
- * This class simply is a wrapper class for the string for the table id.
- *
- * No case sensitivity is considered when comparing this class.
+ * Represents an identifier for a SQL table, including an optional catalog and schema.
+ * This class is immutable and supports case-insensitive comparisons for schema and table IDs.
*/
-public class SqlTableId implements Comparable {
+public class SqlTableId {
+
+ private final String catalogName;
+ private final String schemaName;
+ private final String tableName;
- private final String tableId;
+ /**
+ * Creates a new table identifier.
+ * Catalog and schema names may be null.
+ * Catalog, schema and table names must not contain dots.
+ * Catalog and schema names are case-insensitive.
+ *
+ * @param catalogName
+ * @param schemaName
+ * @param tableName
+ */
+ public SqlTableId(String catalogName, String schemaName, String tableName) {
+ Objects.requireNonNull(tableName);
- public SqlTableId(String tableId) {
- Objects.requireNonNull(tableId);
- this.tableId = tableId.toLowerCase();
+ if (catalogName!=null && catalogName.contains(".")) {
+ throw new IllegalArgumentException("Catalog name cannot contain dots: " + catalogName);
+ }
+ if (schemaName!=null && schemaName.contains(".")) {
+ throw new IllegalArgumentException("Schema name cannot contain dots: " + schemaName);
+ }
+ if (tableName.contains(".")) {
+ throw new IllegalArgumentException("Table name cannot contain dots: " + tableName);
+ }
+ this.catalogName = catalogName ==null ? null : catalogName.toLowerCase();
+ this.schemaName = schemaName == null ? null : schemaName.toLowerCase();
+ this.tableName = tableName.toLowerCase();
}
- public String getTableId() {
- return tableId;
+ public String getTableName() {
+ return tableName;
}
- public String toString() {
- return tableId;
+ public String getSchemaName() {
+ return schemaName;
}
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj instanceof SqlTableId) {
- SqlTableId other = (SqlTableId) obj;
- return tableId.equalsIgnoreCase(other.tableId);
- }
- return false;
+ public String getCatalogName() {
+ return catalogName;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SqlTableId)) return false;
+ SqlTableId that = (SqlTableId) o;
+ return Objects.equals(getCatalogName(), that.getCatalogName())
+ && Objects.equals(getSchemaName(), that.getSchemaName())
+ && Objects.equals(getTableName(), that.getTableName());
+ }
+
+ @Override
public int hashCode() {
- return tableId.hashCode();
+ return Objects.hash(getCatalogName(), getSchemaName(), getTableName());
}
@Override
- public int compareTo(SqlTableId o) {
- Objects.requireNonNull(o);
- return this.getTableId().compareTo(o.getTableId());
+ public String toString() {
+ return String.valueOf(catalogName) + '.' +
+ String.valueOf(schemaName) + '.' + tableName;
+ }
+
+ /**
+ * Returns the fully qualified table name, including catalog and schema if present.
+ *
+ * @param databaseType
+ * @return
+ */
+ public String buildQualifiedTableName(DatabaseType databaseType) {
+ StringBuilder sb = new StringBuilder();
+
+ if (databaseType == DatabaseType.MYSQL) {
+ // MySQL: schema is ignored unless catalog is null
+ if (getCatalogName() != null) {
+ sb.append(getCatalogName()).append(".");
+ } else if (getSchemaName() != null) {
+ sb.append(getSchemaName()).append(".");
+ }
+ } else {
+ // Standard behavior: include both if present
+ if (getCatalogName() != null) {
+ sb.append(getCatalogName()).append(".");
+ }
+ if (getSchemaName() != null) {
+ sb.append(getSchemaName()).append(".");
+ }
+ }
+
+ sb.append(getTableName());
+ return sb.toString();
}
+
+
}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableIdParser.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableIdParser.java
new file mode 100644
index 0000000000..900319d39a
--- /dev/null
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/SqlTableIdParser.java
@@ -0,0 +1,43 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+
+import java.util.Objects;
+
+public class SqlTableIdParser {
+
+ public static SqlTableId parseFullyQualifiedTableName(String fullyQualifiedTableName, DatabaseType databaseType) {
+ Objects.requireNonNull(databaseType);
+ Objects.requireNonNull(fullyQualifiedTableName);
+
+ if (fullyQualifiedTableName.isEmpty()) {
+ throw new IllegalArgumentException("Name cannot be empty");
+ }
+
+ String[] parts = fullyQualifiedTableName.split("\\.");
+
+ switch (parts.length) {
+ case 1:
+ // table only
+ return new SqlTableId(null, null, parts[0]);
+
+ case 2:
+ if (databaseType == DatabaseType.MYSQL || databaseType == DatabaseType.MARIADB) {
+ // catalog.table
+ return new SqlTableId(parts[0], null, parts[1]);
+ } else {
+ // schema.table
+ return new SqlTableId(null, parts[0], parts[1]);
+ }
+ case 3:
+ // catalog.schema.table
+ return new SqlTableId(parts[0], parts[1], parts[2]);
+
+ default:
+ throw new IllegalArgumentException(
+ "Invalid fully qualified name for a table: expected 1, 2, or 3 parts, got " + parts.length
+ );
+ }
+ }
+
+}
diff --git a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinder.java b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinder.java
index 3fa90de9fc..e88303c265 100644
--- a/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinder.java
+++ b/client-java/sql/src/main/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinder.java
@@ -1,5 +1,6 @@
package org.evomaster.client.java.sql.internal;
+import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
@@ -8,6 +9,7 @@
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import net.sf.jsqlparser.util.TablesNamesFinder;
+import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto;
import org.evomaster.client.java.sql.heuristic.*;
@@ -107,7 +109,7 @@ public void visit(AllColumns allColumns) {
if (columnReference.getTableReference() instanceof SqlBaseTableReference) {
SqlBaseTableReference baseTableReference = (SqlBaseTableReference) columnReference.getTableReference();
addColumnReference(baseTableReference, columnReference);
- } else if (columnReference.getTableReference() instanceof SqlDerivedTableReference) {
+ } else if (columnReference.getTableReference() instanceof SqlDerivedTable) {
/*
* If the table is a derived table, the columns
* that are used are collected when processing the
@@ -126,7 +128,7 @@ public void visit(AllColumns allColumns) {
public void visit(AllTableColumns allTableColumns) {
super.visit(allTableColumns);
SqlTableReference tableReference = tableColumnResolver.resolve(allTableColumns.getTable());
- if (tableReference instanceof SqlDerivedTableReference) {
+ if (tableReference instanceof SqlDerivedTable) {
/*
* If the table is a derived table, the columns
* that are used are collected when processing the
@@ -136,9 +138,10 @@ public void visit(AllTableColumns allTableColumns) {
*/
} else if (tableReference instanceof SqlBaseTableReference) {
SqlBaseTableReference baseTableReference = (SqlBaseTableReference) tableReference;
-
+ String schemaName = baseTableReference.getTableId().getSchemaName();
+ String tableName = baseTableReference.getTableId().getTableName();
this.schema.tables.stream()
- .filter(t -> new SqlTableId(t.id.name).equals(baseTableReference.getTableId()))
+ .filter(t -> new TableNameMatcher(t.id).matches(schemaName, tableName))
.flatMap(t -> t.columns.stream())
.map(c -> new SqlColumnReference(baseTableReference, c.name))
.forEach(c -> addColumnReference(baseTableReference, c));
@@ -174,12 +177,16 @@ public boolean containsColumnReferences(SqlBaseTableReference baseTableReference
private Set findColumnReferences(SqlTableId baseTableId) {
Objects.requireNonNull(baseTableId);
-
+ String schemaName = baseTableId.getSchemaName();
+ String tableName = baseTableId.getTableName();
return this.schema.tables.stream()
- .filter(t -> new SqlTableId(t.id.name).equals(baseTableId))
- .flatMap(t -> t.columns.stream())
- .map(c -> new SqlColumnReference(new SqlBaseTableReference(c.table), c.name))
- .collect(Collectors.toSet());
+ .filter(t -> new TableNameMatcher(t.id).matches(schemaName, tableName))
+ .findFirst()
+ .map(t -> t.columns.stream()
+ .map(c -> new SqlColumnReference(
+ new SqlBaseTableReference(t.id.catalog, t.id.schema, t.id.name), c.name))
+ .collect(Collectors.toSet()))
+ .orElse(Collections.emptySet());
}
private Set findColumnReferences(FromItem fromItem) {
@@ -194,9 +201,9 @@ private Set findColumnReferences(FromItem fromItem) {
if (tableReference instanceof SqlBaseTableReference) {
SqlBaseTableReference sqlBaseTableReference = (SqlBaseTableReference) tableReference;
return findColumnReferences(sqlBaseTableReference.getTableId());
- } else if (tableReference instanceof SqlDerivedTableReference) {
- SqlDerivedTableReference sqlDerivedTableReference = (SqlDerivedTableReference) tableReference;
- return findColumnReferences(sqlDerivedTableReference.getSelect());
+ } else if (tableReference instanceof SqlDerivedTable) {
+ SqlDerivedTable sqlDerivedTable = (SqlDerivedTable) tableReference;
+ return findColumnReferences(sqlDerivedTable.getSelect());
} else {
throw new IllegalArgumentException("Cannot handle reference of class " + tableReference.getClass().getName());
}
@@ -264,5 +271,26 @@ private Set findColumnReferences(Select select) {
}
}
+ /**
+ * This should be implemented in the superclass. It looks
+ * as a bug in TablesNamesFinder.
+ *
+ * @param isNullExpression
+ */
+ @Override
+ public void visit(IsNullExpression isNullExpression) {
+ isNullExpression.getLeftExpression().accept(this);
+ }
+
+ /**
+ * This should be implemented in the superclass. It looks
+ * as a bug in TablesNamesFinder.
+ *
+ * @param isBooleanExpression
+ */
+ @Override
+ public void visit(IsBooleanExpression isBooleanExpression) {
+ isBooleanExpression.getLeftExpression().accept(this);
+ }
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/DataRowTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/DataRowTest.java
new file mode 100644
index 0000000000..7b6fe85a86
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/DataRowTest.java
@@ -0,0 +1,28 @@
+package org.evomaster.client.java.sql;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DataRowTest {
+
+ @Test
+ public void getValueByNameThrowsWhenDescriptorIsNull() {
+ List descriptors = new ArrayList<>();
+ VariableDescriptor baseTableVarDescriptor = new VariableDescriptor("id", "id", "public.product", "product0_");
+ VariableDescriptor derivedTableVarDescriptor = new VariableDescriptor("id", "id", null);
+ descriptors.add(baseTableVarDescriptor);
+ descriptors.add(baseTableVarDescriptor);
+ DataRow row = new DataRow(Arrays.asList(baseTableVarDescriptor, derivedTableVarDescriptor),
+ Arrays.asList(1L, 2L));
+
+ Object value = row.getValueByName("id", "product");
+ assertEquals(1l, value);
+ }
+
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/QueryResultTestBase.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/QueryResultTestBase.java
index 47c1a74b55..1e98aad83f 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/QueryResultTestBase.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/QueryResultTestBase.java
@@ -11,6 +11,7 @@
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
+import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -312,4 +313,31 @@ public void testSubqueryReference() throws Exception {
}
+ @Test
+ public void testUUIDColumn() throws Exception {
+ /**
+ * Column type UUID is not supported in MySQL
+ */
+ Assumptions.assumeFalse(getDbType() == DatabaseType.MYSQL);
+
+
+ // set up the database
+ SqlScriptRunner.execCommand(getConnection(), "CREATE TABLE example_table (\n" +
+ " uuid_column UUID NOT NULL\n" +
+ ");");
+ SqlScriptRunner.execCommand(getConnection(), "INSERT INTO example_table (uuid_column) \n" +
+ "VALUES ('00000000-0000-015f-0000-00000000014e');\n");
+
+ // create the queryResult
+ QueryResult queryResult = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM example_table");
+
+ // check the results
+ assertEquals(1, queryResult.seeRows().size());
+ DataRow row = queryResult.seeRows().get(0);
+ Object actual = row.getValueByName("uuid_column", "example_table");
+ assertTrue(actual instanceof UUID);
+ UUID expected = UUID.fromString("00000000-0000-015f-0000-00000000014e");
+ assertEquals(expected, actual);
+ }
+
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/QueryResultUtilsTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/QueryResultUtilsTest.java
index cb78dcaa83..2b7c68f84c 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/QueryResultUtilsTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/QueryResultUtilsTest.java
@@ -74,4 +74,17 @@ public void testCreateDataRowOfNullValuesWithMultipleColumns() {
assertNull(nullDataRow.seeValues().get(1));
}
+ @Test
+ public void testCreateDistinctQueryResult() {
+ QueryResult queryResult = new QueryResult(Arrays.asList(new VariableDescriptor("column1", null, null)));
+ queryResult.addRow(Arrays.asList("column1"), null, Arrays.asList("value1"));
+ queryResult.addRow(Arrays.asList("column1"), null, Arrays.asList("value1"));
+ queryResult.addRow(Arrays.asList("column1"), null, Arrays.asList("value1"));
+ queryResult.addRow(Arrays.asList("column1"), null, Arrays.asList("value1"));
+ queryResult.addRow(Arrays.asList("column1"), null, Arrays.asList("value2"));
+
+ QueryResult distinctResult = QueryResultUtils.createDistinctQueryResult(queryResult);
+
+ assertEquals(2, distinctResult.seeRows().size());
+ }
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluatorTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluatorTest.java
index e08026c596..95a617122b 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluatorTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlExpressionEvaluatorTest.java
@@ -14,6 +14,7 @@
import org.evomaster.client.java.sql.DataRow;
import org.evomaster.client.java.sql.QueryResult;
+import org.evomaster.client.java.sql.heuristic.function.NowFunction;
import org.evomaster.client.java.sql.internal.SqlParserUtils;
import org.evomaster.client.java.sql.internal.TaintHandler;
import org.junit.jupiter.api.BeforeEach;
@@ -23,14 +24,12 @@
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.ZoneOffset;
+import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
+import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
@@ -1445,4 +1444,318 @@ public void testMin() {
assertTrue(evaluator.getEvaluatedValue() instanceof Integer);
assertEquals(10_000, evaluator.getEvaluatedValue());
}
+
+ @Test
+ public void testCoalesce() {
+ String sql = "COALESCE(null, 'hello', 'world')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("hello", value);
+ }
+
+ @Test
+ public void testCoalesceWithAllNull() {
+ String sql = "COALESCE(null, null, null)";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertNull(value);
+ }
+
+ @Test
+ public void testCoalesceWithFirstValueNotNull() {
+ String sql = "COALESCE('hello', 'world', null)";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actualString = evaluator.getEvaluatedValue();
+ assertEquals("hello", actualString);
+ }
+
+ @Test
+ public void testUpper() {
+ String sql = "UPPER('hello')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("HELLO", value);
+ }
+
+ @Test
+ public void testTimeFunction() {
+ String sql = "TIME('2025-01-14 12:30:45')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actualTime = evaluator.getEvaluatedValue();
+ final Time exptectedTime = Time.valueOf("12:30:45");
+ assertEquals(exptectedTime.toString(), actualTime.toString());
+ }
+
+ @Test
+ public void testTimestampExpression() {
+ String sql = "TIMESTAMP '2025-01-14 12:30:45'";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actualTimestamp = evaluator.getEvaluatedValue();
+ assertTrue(actualTimestamp instanceof Timestamp);
+ final Timestamp exptectedTimestamp = java.sql.Timestamp.valueOf("2025-01-14 12:30:45");
+ assertEquals(exptectedTimestamp, actualTimestamp);
+ }
+
+ @Test
+ public void testDateExpression() {
+ String sql = "DATE '2025-01-14'";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actualDate = evaluator.getEvaluatedValue();
+
+ assertTrue(actualDate instanceof java.sql.Date);
+
+ final java.sql.Date expectedDate = java.sql.Date.valueOf("2025-01-14");
+ assertEquals(expectedDate, actualDate);
+ }
+
+ @Test
+ public void testTimeExpression() {
+ String sql = "TIME '12:30:45'";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actualTime = evaluator.getEvaluatedValue();
+
+ assertTrue(actualTime instanceof java.sql.Time);
+
+ final java.sql.Time expectedTime = java.sql.Time.valueOf("12:30:45");
+ assertEquals(expectedTime, actualTime);
+ }
+
+ @Test
+ public void testCoalesceOneExpression() {
+ String sql = "COALESCE('hello')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("hello", value);
+ }
+
+ @Test
+ public void dateTruncDayTruncatesTimestamp() {
+ String sql = "DATE_TRUNC('day', TIMESTAMP '2025-01-14 12:30:45')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actual = evaluator.getEvaluatedValue();
+ assertTrue(actual instanceof java.util.Date);
+ Instant actualInstant = ((java.util.Date) actual).toInstant();
+ Instant expectedInstant = Instant.parse("2025-01-14T00:00:00Z");
+ assertEquals(expectedInstant, actualInstant);
+ }
+
+ @Test
+ public void dateTruncHourTruncatesTimestamp() {
+ String sql = "DATE_TRUNC('hour', TIMESTAMP '2025-01-14 12:30:45')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actual = evaluator.getEvaluatedValue();
+ assertTrue(actual instanceof java.util.Date);
+ Instant actualInstant = ((java.util.Date) actual).toInstant();
+ final Timestamp expected = Timestamp.valueOf("2025-01-14 12:00:00");
+ Instant expectedInstant = expected.toInstant();
+ assertEquals(expectedInstant, actualInstant);
+ }
+
+ @Test
+ public void dateTruncUnknownUnitThrowsIllegalArgumentException() {
+ String sql = "DATE_TRUNC('millennium', TIMESTAMP '2025-01-14 12:30:45')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ expression.accept(evaluator);
+ // if evaluation returns something, force get to trigger potential errors
+ evaluator.getEvaluatedValue();
+ });
+ }
+
+ @Test
+ public void testTimestampTzExpression() {
+ String sql = "TIMESTAMPTZ '2025-01-22 15:30:45+02:00'";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actual = evaluator.getEvaluatedValue();
+
+ assertTrue(actual instanceof java.time.OffsetDateTime);
+
+ java.time.OffsetDateTime expected =
+ java.time.OffsetDateTime.parse("2025-01-22T15:30:45+02:00");
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testNowStableValue() {
+ String sql = "NOW()";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actual = evaluator.getEvaluatedValue();
+ assertTrue(actual instanceof Timestamp);
+ assertEquals(NowFunction.CANONICAL_NOW_VALUE, actual);
+ }
+
+ @Test
+ public void testLower() {
+ String sql = "LOWER('HELLO')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("hello", value);
+ }
+
+ @Test
+ public void testLowerCaseInsensitive() {
+ String sql = "lower('HELLO')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("hello", value);
+ }
+
+ @Test
+ public void testUpperCaseInsenstive() {
+ String sql = "upper('hello')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals("HELLO", value);
+ }
+
+ @Test
+ public void testDate() {
+ String sql = "date('2025-01-14 12:00:00')";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object value = evaluator.getEvaluatedValue();
+ assertEquals(java.sql.Date.valueOf("2025-01-14"), value);
+ }
+
+ @Test
+ public void testUUID() {
+ String sql = "'00000000-0000-015f-0000-00000000014e'::uuid";
+ Expression expression = assertDoesNotThrow(() -> CCJSqlParserUtil.parseExpression(sql));
+
+ SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder builder = new SqlExpressionEvaluator.SqlExpressionEvaluatorBuilder();
+ SqlExpressionEvaluator evaluator = builder
+ .withTableColumnResolver(new TableColumnResolver(new DbInfoDto()))
+ .build();
+
+ expression.accept(evaluator);
+ Object actual = evaluator.getEvaluatedValue();
+ UUID expected = UUID.fromString("00000000-0000-015f-0000-00000000014e");
+ assertEquals(expected, actual);
+ }
+
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHandlerGetDistanceTestBase.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHandlerGetDistanceTestBase.java
index 9a4622925d..7c534af582 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHandlerGetDistanceTestBase.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHandlerGetDistanceTestBase.java
@@ -1,5 +1,8 @@
package org.evomaster.client.java.sql.heuristic;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.Statement;
import org.evomaster.client.java.controller.api.dto.database.execution.SqlExecutionLogDto;
import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto;
@@ -8,6 +11,7 @@
import org.evomaster.client.java.sql.SqlScriptRunner;
import org.evomaster.client.java.sql.internal.SqlCommandWithDistance;
import org.evomaster.client.java.sql.internal.SqlHandler;
+import org.evomaster.client.java.sql.internal.TablesAndColumnsFinder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -15,8 +19,10 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
+import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public abstract class SqlHandlerGetDistanceTestBase {
@@ -721,4 +727,37 @@ public void testBestEffort() throws SQLException {
}
+ @Test
+ public void testTableFinder() throws JSQLParserException {
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ String sql = "Select * From customers";
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ SqlTableReference sqlTableReference = finder.getBaseTableReferences().iterator().next();
+ assertTrue(sqlTableReference instanceof SqlBaseTableReference);
+ SqlBaseTableReference baseTableReference = (SqlBaseTableReference) sqlTableReference;
+ assertEquals("customers", baseTableReference.getTableId().getTableName());
+ assertEquals("public", baseTableReference.getTableId().getSchemaName().toLowerCase());
+
+ Set columnReferences = finder.getColumnReferences(baseTableReference);
+ assertEquals(3, columnReferences.size());
+
+ assertTrue(columnReferences.contains(new SqlColumnReference(baseTableReference, "name")));
+ assertTrue(columnReferences.contains(new SqlColumnReference(baseTableReference, "id")));
+ assertTrue(columnReferences.contains(new SqlColumnReference(baseTableReference, "age")));
+ }
+
+ @Test
+ public void testTableFinderWithAlias() throws JSQLParserException {
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ String sql = "SELECT * FROM customers c WHERE name = 'joh' AND EXISTS (SELECT customer_id FROM orders WHERE customer_id = c.id)";
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ assertEquals(2, finder.getBaseTableReferences().size());
+
+ }
+
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculatorTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculatorTest.java
index 074f2831bd..38f740a10b 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculatorTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlHeuristicsCalculatorTest.java
@@ -19,6 +19,7 @@
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
+import java.sql.Timestamp;
import java.util.*;
import static org.evomaster.client.java.sql.heuristic.SqlHeuristicsCalculator.TRUE_TRUTHNESS;
@@ -567,6 +568,7 @@ public void testCrossJoin() {
TableDto projectsTable = createTableDto("Projects");
projectsTable.columns.add(createColumnDto("project_id"));
projectsTable.columns.add(createColumnDto("project_name"));
+ projectsTable.columns.add(createColumnDto("project_start_time"));
TableDto tableA = createTableDto("TableA");
@@ -773,10 +775,10 @@ public void testSelectFromSubqueryWithAlias() {
tableColumnResolver.enterStatementeContext(select);
SqlColumnReference nameSqlColumnReference = tableColumnResolver.resolve(nameColumn);
- assertTrue(nameSqlColumnReference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(nameSqlColumnReference.getTableReference() instanceof SqlDerivedTable);
SqlColumnReference incomeSqlColumnReference = tableColumnResolver.resolve(incomeColumn);
- assertTrue(incomeSqlColumnReference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(incomeSqlColumnReference.getTableReference() instanceof SqlDerivedTable);
assertEquals("John", heuristicResult.getQueryResult().seeRows().get(0).getValueByName(nameSqlColumnReference.getColumnName()));
assertEquals(10000, heuristicResult.getQueryResult().seeRows().get(0).getValueByName(incomeSqlColumnReference.getColumnName()));
@@ -848,7 +850,6 @@ public void testInnerJoinWithSubqueries() {
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
assertTrue(heuristicResult.getTruthness().isTrue());
-
assertEquals(2, heuristicResult.getQueryResult().seeVariableDescriptors().size());
assertEquals(new VariableDescriptor("name", "name", null), heuristicResult.getQueryResult().seeVariableDescriptors().get(0));
assertEquals(new VariableDescriptor("department_name", "department_name", null), heuristicResult.getQueryResult().seeVariableDescriptors().get(1));
@@ -1003,102 +1004,51 @@ public void testUnionSelectNoFromNoWhere() {
}
@Test
- public void testSelectFromTableWithRowsNoWhere() {
+ public void testSelectWithLimit() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT name FROM Person";
+ String sqlCommand = "SELECT name FROM Person LIMIT 2";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
-
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("name", "name", "person"), heuristicResult.getQueryResult().seeVariableDescriptors().get(0));
-
-
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- DataRow row = heuristicResult.getQueryResult().seeRows().get(0);
- assertEquals("John", row.getValueByName("name"));
- }
-
-
- @Test
- public void testSelectFromTableWithRowsNoWhereUsingAlias() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT name AS person_name FROM Person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
-
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
+ assertTrue(heuristicResult.getTruthness().isTrue());
QueryResult queryResult = heuristicResult.getQueryResult();
assertEquals(1, queryResult.seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("name", "person_name", "person"),
- queryResult.seeVariableDescriptors().iterator().next());
+ assertEquals(new VariableDescriptor("name", "name", "person"), queryResult.seeVariableDescriptors().get(0));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- DataRow row = heuristicResult.getQueryResult().seeRows().get(0);
- assertEquals("John", row.getValueByName("person_name"));
+ assertEquals(2, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(1).getValueByName("name"));
}
@Test
- public void testSelectAllFromTable() {
+ public void testUnionWithLimit() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT * FROM Person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50_000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ String sqlCommand = "(SELECT name FROM Employees) UNION (SELECT department_name AS name FROM Departments) LIMIT 2";
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
-
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
-
- QueryResult queryResult = heuristicResult.getQueryResult();
- assertEquals(3, queryResult.seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("name", "name", "person"),
- queryResult.seeVariableDescriptors().get(0));
- assertEquals(new VariableDescriptor("age", "age", "person"),
- queryResult.seeVariableDescriptors().get(1));
- assertEquals(new VariableDescriptor("salary", "salary", "person"),
- queryResult.seeVariableDescriptors().get(2));
+ QueryResult employees = new QueryResult(Collections.singletonList("name"), "Employees");
+ employees.addRow(Collections.singletonList("name"), "Employees", Collections.singletonList("John"));
+ employees.addRow(Collections.singletonList("name"), "Employees", Collections.singletonList("Jane"));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- DataRow row = heuristicResult.getQueryResult().seeRows().get(0);
- assertEquals("John", row.getValueByName("name"));
- assertEquals(30, row.getValueByName("age"));
- assertEquals(50_000, row.getValueByName("salary"));
- }
- @Test
- public void testSelectAllFromSubquery() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT * FROM (SELECT salary, age FROM (SELECT * FROM person))";
+ QueryResult departments = new QueryResult(Collections.singletonList("department_name"), "Departments");
+ departments.addRow(Collections.singletonList("department_name"), "Departments", Collections.singletonList("Sales"));
+ departments.addRow(Collections.singletonList("department_name"), "Departments", Collections.singletonList("Marketing"));
- QueryResult personQueryResult = new QueryResult(Arrays.asList("name", "age", "salary"), "person");
- personQueryResult.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(personQueryResult);
+ QueryResultSet queryResultSet = QueryResultSet.build(employees, departments);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
@@ -1107,87 +1057,59 @@ public void testSelectAllFromSubquery() {
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
+ assertTrue(heuristicResult.getTruthness().isTrue());
+
QueryResult queryResult = heuristicResult.getQueryResult();
- assertEquals(2, queryResult.seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("salary", "salary", null),
- queryResult.seeVariableDescriptors().get(0));
- assertEquals(new VariableDescriptor("age", "age", null),
- queryResult.seeVariableDescriptors().get(1));
+ assertEquals(1, queryResult.seeVariableDescriptors().size());
+ assertEquals(new VariableDescriptor("name", "name", null), queryResult.seeVariableDescriptors().get(0));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- DataRow row = heuristicResult.getQueryResult().seeRows().get(0);
- assertEquals(30, row.getValueByName("age"));
- assertEquals(50_000, row.getValueByName("salary"));
+ assertEquals(2, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(1).getValueByName("name"));
}
-
@Test
- public void testSelfJoin() {
+ public void testParenthesizedSelectWithLimit() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT\n" +
- " child.name AS category,\n" +
- " parent.name AS parent_category\n" +
- "FROM categories child\n" +
- "LEFT JOIN categories parent ON child.parent_id = parent.id;\n";
+ String sqlCommand = "(SELECT name FROM Person) LIMIT 2";
- QueryResult categoriesResultSet = new QueryResult(Arrays.asList("id", "name", "parent_id"), "categories");
- categoriesResultSet.addRow(new DataRow("categories", Arrays.asList("id", "name", "parent_id"), Arrays.asList(1, "Electronics", null)));
- categoriesResultSet.addRow(new DataRow("categories", Arrays.asList("id", "name", "parent_id"), Arrays.asList(2, "Computers", 1)));
- categoriesResultSet.addRow(new DataRow("categories", Arrays.asList("id", "name", "parent_id"), Arrays.asList(3, "Laptops", 2)));
- categoriesResultSet.addRow(new DataRow("categories", Arrays.asList("id", "name", "parent_id"), Arrays.asList(4, "Phones", 1)));
- categoriesResultSet.addRow(new DataRow("categories", Arrays.asList("id", "name", "parent_id"), Arrays.asList(5, "Accessories", 2)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(categoriesResultSet);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- QueryResult queryResult = heuristicResult.getQueryResult();
- assertEquals(2, queryResult.seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("name", "category", "categories"),
- queryResult.seeVariableDescriptors().get(0));
- assertEquals(new VariableDescriptor("name", "parent_category", "categories"),
- queryResult.seeVariableDescriptors().get(1));
-
- final List dataRows = heuristicResult.getQueryResult().seeRows();
- assertEquals(5, dataRows.size());
- assertEquals("Electronics", dataRows.get(0).getValueByName("category"));
- assertEquals(null, dataRows.get(0).getValueByName("parent_category"));
-
- assertEquals("Computers", dataRows.get(1).getValueByName("category"));
- assertEquals("Electronics", dataRows.get(1).getValueByName("parent_category"));
-
- assertEquals("Laptops", dataRows.get(2).getValueByName("category"));
- assertEquals("Computers", dataRows.get(2).getValueByName("parent_category"));
-
- assertEquals("Phones", dataRows.get(3).getValueByName("category"));
- assertEquals("Electronics", dataRows.get(3).getValueByName("parent_category"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
- assertEquals("Accessories", dataRows.get(4).getValueByName("category"));
- assertEquals("Computers", dataRows.get(4).getValueByName("parent_category"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.seeVariableDescriptors().size());
+ assertEquals(new VariableDescriptor("name", "name", "person"), queryResult.seeVariableDescriptors().get(0));
+ assertEquals(2, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(1).getValueByName("name"));
}
@Test
- public void testLeftJoinWithTable() {
+ public void testGroupByWithLimit() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT\n" +
- " e.*\n" +
- "FROM employees e\n" +
- "LEFT JOIN projects p ON e.project_id = p.project_id;\n";
-
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- employees.addRow(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees", Arrays.asList("John Doe", "John", null, 1, 50_000));
+ String sqlCommand = "SELECT department_id, COUNT(*) FROM Employees GROUP BY department_id LIMIT 1";
- QueryResult projects = new QueryResult(Arrays.asList("project_id", "project_name"), "projects");
- projects.addRow(Arrays.asList("project_id", "project_name"), "projects", Arrays.asList(1, "ProjectX"));
+ QueryResult employees = new QueryResult(Arrays.asList("name", "department_id"), "Employees");
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("John", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Jane", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Joe", 2));
- QueryResultSet queryResultSet = QueryResultSet.build(employees, projects);
+ QueryResultSet queryResultSet = QueryResultSet.build(employees);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
@@ -1196,587 +1118,357 @@ public void testLeftJoinWithTable() {
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- QueryResult queryResult = heuristicResult.getQueryResult();
- assertEquals(5, queryResult.seeVariableDescriptors().size());
- assertEquals(new VariableDescriptor("name", "name", "employees", "e"),
- queryResult.seeVariableDescriptors().get(0));
- assertEquals(new VariableDescriptor("first_name", "first_name", "employees", "e"),
- queryResult.seeVariableDescriptors().get(1));
- assertEquals(new VariableDescriptor("department_id", "department_id", "employees", "e"),
- queryResult.seeVariableDescriptors().get(2));
- assertEquals(new VariableDescriptor("project_id", "project_id", "employees", "e"),
- queryResult.seeVariableDescriptors().get(3));
- assertEquals(new VariableDescriptor("salary", "salary", "employees", "e"),
- queryResult.seeVariableDescriptors().get(4));
-
- final List dataRows = heuristicResult.getQueryResult().seeRows();
- assertEquals(1, dataRows.size());
- assertEquals("John Doe", dataRows.get(0).getValueByName("name"));
- assertEquals("John", dataRows.get(0).getValueByName("first_name"));
- assertEquals(null, dataRows.get(0).getValueByName("department_id"));
- assertEquals(1, dataRows.get(0).getValueByName("project_id"));
- assertEquals(50_000, dataRows.get(0).getValueByName("salary"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.size());
}
-
@Test
- public void testNull() {
+ public void testSelectWithLimitZero() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT NULL AS null_value";
+ String sqlCommand = "SELECT * FROM Person LIMIT 0";
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ QueryResult contents = new QueryResult(Arrays.asList("name", "age"), "Person");
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age"), Arrays.asList("John", 30)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age"), Arrays.asList("Jane", 25)));
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- }
-
- @Test
- public void testNullInSubquery() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT * FROM (SELECT NULL UNION ALL SELECT name FROM employees)";
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- QueryResultSet queryResultSet = QueryResultSet.build(employees);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
+ SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
- .withSourceQueryResultSet(queryResultSet)
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
-
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(0).getValue(0));
- }
-
- @Test
- public void testCaseWhen() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT \n" +
- " CASE \n" +
- " WHEN age < 18 THEN 'Minor'\n" +
- " ELSE 'Adult'\n" +
- " END AS age_group\n" +
- " FROM person;\n";
-
- QueryResult personQueryResult = new QueryResult(Arrays.asList("age"), "person");
- personQueryResult.addRow(new DataRow("Person", Arrays.asList("age"), Arrays.asList(17)));
- QueryResultSet queryResultSet = QueryResultSet.build(personQueryResult);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
- .withTableColumnResolver(new TableColumnResolver(schema))
- .withSourceQueryResultSet(queryResultSet)
- .build();
+ assertTrue(heuristicResult.getTruthness().isFalse());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
-
- assertEquals("Minor", heuristicResult.getQueryResult().seeRows().get(0).getValue(0));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(0, queryResult.size());
}
@Test
- public void testCaseWhenElse() {
+ public void testSelectWithOrderByDesc() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT \n" +
- " CASE \n" +
- " WHEN age < 18 THEN 'Minor'\n" +
- " ELSE 'Adult'\n" +
- " END AS age_group\n" +
- " FROM person;\n";
-
- QueryResult personQueryResult = new QueryResult(Arrays.asList("age"), "person");
- personQueryResult.addRow(new DataRow("Person", Arrays.asList("age"), Arrays.asList(21)));
- QueryResultSet queryResultSet = QueryResultSet.build(personQueryResult);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
- .withTableColumnResolver(new TableColumnResolver(schema))
- .withSourceQueryResultSet(queryResultSet)
- .build();
+ String sqlCommand = "SELECT name FROM Person ORDER BY name DESC";
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- assertEquals("Adult", heuristicResult.getQueryResult().seeRows().get(0).getValue(0));
- }
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
- @Test
- public void testCaseSwitch() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT \n" +
- " CASE age \n" +
- " WHEN 1 THEN 'one year'\n" +
- " WHEN 2 THEN 'two years'\n" +
- " WHEN 3 THEN 'three years'\n" +
- " ELSE 'more than 3 years'\n" +
- " END AS age_group\n" +
- " FROM person;\n";
-
- QueryResult personQueryResult = new QueryResult(Arrays.asList("age"), "person");
- personQueryResult.addRow(new DataRow("Person", Arrays.asList("age"), Arrays.asList(2)));
- QueryResultSet queryResultSet = QueryResultSet.build(personQueryResult);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
+ SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
- .withSourceQueryResultSet(queryResultSet)
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
-
- assertEquals("two years", heuristicResult.getQueryResult().seeRows().get(0).getValue(0));
- }
- @Test
- public void testCaseSwitchElse() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT \n" +
- " CASE age \n" +
- " WHEN 1 THEN 'one year'\n" +
- " WHEN 2 THEN 'two years'\n" +
- " WHEN 3 THEN 'three years'\n" +
- " ELSE 'more than 3 years'\n" +
- " END AS age_group\n" +
- " FROM person;\n";
-
- QueryResult personQueryResult = new QueryResult(Arrays.asList("age"), "person");
- personQueryResult.addRow(new DataRow("Person", Arrays.asList("age"), Arrays.asList(21)));
- QueryResultSet queryResultSet = QueryResultSet.build(personQueryResult);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder
- .withTableColumnResolver(new TableColumnResolver(schema))
- .withSourceQueryResultSet(queryResultSet)
- .build();
+ assertTrue(heuristicResult.getTruthness().isTrue());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeVariableDescriptors().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.seeVariableDescriptors().size());
+ assertEquals(new VariableDescriptor("name", "name", "person"), queryResult.seeVariableDescriptors().get(0));
- assertEquals("more than 3 years", heuristicResult.getQueryResult().seeRows().get(0).getValue(0));
+ assertEquals(3, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Joe", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(2).getValueByName("name"));
}
@Test
- public void testCountAllColumnsWhenNonEmpty() {
+ public void testSelectWithOrderByAndLimit() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT COUNT(*) AS number_of_persons FROM person";
+ String sqlCommand = "SELECT name FROM Person ORDER BY name DESC LIMIT 1";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(2L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("number_of_persons"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
+
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
}
@Test
- public void testCountAllColumnsWhenEmpty() {
+ public void testSelectWithOrderByAscending() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT COUNT(*) AS number_of_persons FROM person";
+ String sqlCommand = "SELECT name FROM Person ORDER BY name ASC";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(0L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("number_of_persons"));
-
- }
-
- @Test
- public void testCountAllColumnsWhenNullAndNonNulls() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT COUNT(*) AS number_of_persons FROM person";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList(null, null, null)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ assertTrue(heuristicResult.getTruthness().isTrue());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(3L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("number_of_persons"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(3, queryResult.size());
+ assertEquals("Jane", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Joe", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals("John", queryResult.seeRows().get(2).getValueByName("name"));
}
@Test
- public void testMaxNonNull() {
+ public void testSelectWithOrderByAlias() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT MAX(age) AS max_age FROM person";
+ String sqlCommand = "SELECT name FROM Person p ORDER BY p.name ASC";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "Joe", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(30, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_age"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
+
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(3, queryResult.size());
+ assertEquals("Jane", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Joe", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals("John", queryResult.seeRows().get(2).getValueByName("name"));
}
@Test
- public void testMaxOnlyNullValues() {
+ public void testSelectWithOrderByFunction() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT MAX(age) AS max_age FROM person";
+ String sqlCommand = "SELECT name FROM Person ORDER BY UPPER(name) ASC";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", null, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", null, 20000)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "john", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "adam", "Person"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_age"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
+
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(3, queryResult.size());
+ assertEquals("adam", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals("john", queryResult.seeRows().get(2).getValueByName("name"));
}
@Test
- public void testMaxNonNullAndNullValues() {
+ public void testWithClauseAndDerivedTable() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT MAX(age) AS max_age FROM person";
+ String sqlCommand = "WITH PersonCTE AS (SELECT * FROM Person) SELECT name FROM PersonCTE";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", null, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
+ QueryResult contents = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 25, 60000)));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(21, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_age"));
- }
-
- @Test
- public void testMaxEmptyReturnsNull() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT MAX(age) AS max_age FROM person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ assertTrue(heuristicResult.getTruthness().isTrue());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_age"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.seeVariableDescriptors().size());
+ assertEquals(new VariableDescriptor("name", null, null), queryResult.seeVariableDescriptors().get(0));
+ assertEquals(2, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Jane", queryResult.seeRows().get(1).getValueByName("name"));
}
@Test
- public void testMinNonNull() {
+ public void testWithClauseAndDerivedTableWithAlias() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT MIN(age) AS min_age FROM person";
+ String sqlCommand = "WITH PersonCTE AS (SELECT * FROM Person) SELECT name FROM PersonCTE pCTE WHERE pCTE.name='John'";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
+ QueryResult contents = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 25, 60000)));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(21, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("min_age"));
-
- }
-
- @Test
- public void testSumOfRealNumbers() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT SUM(salary) AS sum_salary FROM person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 1000.50d)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 500.50d)));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ assertTrue(heuristicResult.getTruthness().isTrue());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertNotNull(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary"));
- assertTrue(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary") instanceof Double);
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.seeVariableDescriptors().size());
+ assertEquals(new VariableDescriptor("name", null, null), queryResult.seeVariableDescriptors().get(0));
- double actual = (Double) heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary");
- assertEquals(Double.valueOf(1000.50d + 500.50d), actual);
+ assertEquals(1, queryResult.seeRows().size());
+ assertEquals("John", queryResult.seeRows().get(0).getValueByName("name"));
}
@Test
- public void testSumOfIntegerNumbers() {
+ public void testSelectWithParenthesisSelectItem() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT SUM(salary) AS sum_salary FROM person";
+ String sqlCommand = "SELECT (p.name) FROM Person p";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertNotNull(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary"));
- assertTrue(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary") instanceof Long);
-
- long actual = (Long) heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary");
- assertEquals(Long.valueOf(70000), actual);
- }
-
- @Test
- public void testCountColumnWhenNonEmpty() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT COUNT(age) AS number_of_persons FROM person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ assertTrue(heuristicResult.getTruthness().isFalse());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(2L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("number_of_persons"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(0, queryResult.size());
}
- @Test
- public void testCountColumnNullAndNonNull() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT COUNT(age) AS number_of_persons FROM person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", null, 20000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
-
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(2L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("number_of_persons"));
-
- }
+ /**
+ * SELECT DISTINCT ON (name)
+ * *
+ * FROM person
+ * ORDER BY name, updated_at DESC;
+ */
@Test
- public void testSumOfNullAndNonNullValues() {
+ public void testSelectDistinctOn() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT SUM(salary) AS sum_salary FROM person";
+ String sqlCommand = "SELECT DISTINCT ON (p.name) p.* FROM Person p ORDER BY p.name";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, 50000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, 20000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 21, null)));
+ QueryResult contents = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Alice", 30, 50_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Alice", 35, 55_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Bob", 28, 45_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Bob", 40, 45_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Charly", 22, 30_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertNotNull(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary"));
- assertTrue(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary") instanceof Long);
- long actual = (Long) heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary");
- assertEquals(Long.valueOf(70000), actual);
- }
+ assertTrue(heuristicResult.getTruthness().isTrue());
- @Test
- public void testSumOfAllNullValues() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT SUM(salary) AS sum_salary FROM person";
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(3, queryResult.size());
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, null)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, null)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 21, null)));
+ assertEquals("Alice", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals(30, queryResult.seeRows().get(0).getValueByName("age"));
+ assertEquals(50_000, queryResult.seeRows().get(0).getValueByName("salary"));
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ assertEquals("Bob", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals(28, queryResult.seeRows().get(1).getValueByName("age"));
+ assertEquals(45_000, queryResult.seeRows().get(1).getValueByName("salary"));
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ assertEquals("Charly", queryResult.seeRows().get(2).getValueByName("name"));
+ assertEquals(22, queryResult.seeRows().get(2).getValueByName("age"));
+ assertEquals(30_000, queryResult.seeRows().get(2).getValueByName("salary"));
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertNull(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary"));
}
@Test
- public void testSumOfEmptyTable() {
+ public void testSelectDistinctWithOrderBy() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT SUM(salary) AS sum_salary FROM person";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ String sqlCommand = "SELECT DISTINCT name FROM Person p ORDER BY p.name ASC";
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
-
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertNull(heuristicResult.getQueryResult().seeRows().get(0).getValueByName("sum_salary"));
- }
-
- @Test
- public void testAvgOfAllNullValues() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT AVG(age) AS avg_age FROM person";
+ // Input data for the query: notice duplicate names to test DISTINCT
+ QueryResult contents = new QueryResult(Collections.singletonList("name"), "Person");
+ contents.addRow(new DataRow("name", "John", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person"));
+ contents.addRow(new DataRow("name", "John", "Person")); // duplicate
+ contents.addRow(new DataRow("name", "Joe", "Person"));
+ contents.addRow(new DataRow("name", "Jane", "Person")); // duplicate
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 30, null)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 21, null)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 21, null)));
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder =
+ new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
+ SqlHeuristicsCalculator calculator = builder
+ .withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals((30L + 21L + 21L) / 3, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("avg_age"));
- }
-
- @Test
- public void testAvgEmptyTable() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT AVG(age) AS avg_age FROM person";
+ SqlHeuristicResult heuristicResult =
+ calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ assertTrue(heuristicResult.getTruthness().isTrue());
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResult queryResult = heuristicResult.getQueryResult();
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ // We expect 3 DISTINCT values: Jane, Joe, John
+ assertEquals(3, queryResult.size());
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("avg_age"));
+ // Sorted alphabetically because of ORDER BY
+ assertEquals("Jane", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals("Joe", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals("John", queryResult.seeRows().get(2).getValueByName("name"));
}
@Test
- public void testMaxInWhere() {
+ public void testGroupByOrderBy() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT name, salary FROM person WHERE salary=(SELECT MAX(salary) FROM person)";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 21, 50_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 23, 20_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 31, 50_000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
-
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ String sqlCommand = "SELECT department_id, COUNT(*) AS department_count FROM Employees GROUP BY department_id ORDER BY department_id";
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(2, heuristicResult.getQueryResult().seeRows().size());
- assertEquals("John", heuristicResult.getQueryResult().seeRows().get(0).getValueByName("name"));
- assertEquals(50_000, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("salary"));
+ QueryResult employees = new QueryResult(Arrays.asList("name", "department_id"), "Employees");
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("John", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Jane", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Joe", 2));
- assertEquals("Jane", heuristicResult.getQueryResult().seeRows().get(1).getValueByName("name"));
- assertEquals(50_000, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("salary"));
- }
-
- @Test
- public void testMaxInSelectItem() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT (SELECT MAX(salary) FROM person) AS max_salary, name FROM Person;";
-
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 21, 50_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 23, 20_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 31, 40_000)));
-
- QueryResultSet queryResultSet = QueryResultSet.build(person);
+ QueryResultSet queryResultSet = QueryResultSet.build(employees);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
@@ -1784,32 +1476,31 @@ public void testMaxInSelectItem() {
.build();
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(3, heuristicResult.getQueryResult().seeRows().size());
- assertEquals("John", heuristicResult.getQueryResult().seeRows().get(0).getValueByName("name"));
- assertEquals(50_000, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_salary"));
-
- assertEquals("Jack", heuristicResult.getQueryResult().seeRows().get(1).getValueByName("name"));
- assertEquals(50_000, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("max_salary"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
- assertEquals("Jane", heuristicResult.getQueryResult().seeRows().get(2).getValueByName("name"));
- assertEquals(50_000, heuristicResult.getQueryResult().seeRows().get(2).getValueByName("max_salary"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(2, queryResult.size());
+ assertEquals(1, ((Number) queryResult.seeRows().get(0).getValueByName("department_id")).intValue());
+ assertEquals(2, ((Number) queryResult.seeRows().get(0).getValueByName("department_count")).intValue());
+ assertEquals(2, ((Number) queryResult.seeRows().get(1).getValueByName("department_id")).intValue());
+ assertEquals(1, ((Number) queryResult.seeRows().get(1).getValueByName("department_count")).intValue());
}
+
@Test
- public void testMaxInSelectItemWithEmptyTable() {
+ public void tesDistinctGroupByOrderBy() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT (SELECT MAX(salary) FROM employees) AS max_salary, name FROM Person;";
+ String sqlCommand = "SELECT DISTINCT department_id, COUNT(*) AS department_count FROM Employees GROUP BY department_id ORDER BY department_id";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 21, 50_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 23, 20_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 31, 40_000)));
+ QueryResult employees = new QueryResult(Arrays.asList("name", "department_id"), "Employees");
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("John", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Jane", 1));
+ employees.addRow(Arrays.asList("name", "department_id"), "Employees", Arrays.asList("Joe", 2));
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- QueryResultSet queryResultSet = QueryResultSet.build(person, employees);
+ QueryResultSet queryResultSet = QueryResultSet.build(employees);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
@@ -1817,206 +1508,106 @@ public void testMaxInSelectItemWithEmptyTable() {
.build();
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(3, heuristicResult.getQueryResult().seeRows().size());
- assertEquals("John", heuristicResult.getQueryResult().seeRows().get(0).getValueByName("name"));
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("max_salary"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
- assertEquals("Jack", heuristicResult.getQueryResult().seeRows().get(1).getValueByName("name"));
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("max_salary"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(2, queryResult.size());
+ assertEquals(1, ((Number) queryResult.seeRows().get(0).getValueByName("department_id")).intValue());
+ assertEquals(2, ((Number) queryResult.seeRows().get(0).getValueByName("department_count")).intValue());
- assertEquals("Jane", heuristicResult.getQueryResult().seeRows().get(2).getValueByName("name"));
- assertEquals(null, heuristicResult.getQueryResult().seeRows().get(2).getValueByName("max_salary"));
+ assertEquals(2, ((Number) queryResult.seeRows().get(1).getValueByName("department_id")).intValue());
+ assertEquals(1, ((Number) queryResult.seeRows().get(1).getValueByName("department_count")).intValue());
}
@Test
- public void testCountInSelectItemWithEmptyTable() {
+ public void testSelectGroupBy() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT (SELECT COUNT(*) FROM employees) AS count_employees, name FROM Person;";
+ String sqlCommand = "SELECT name, age FROM Person p GROUP BY p.name ORDER BY p.name ASC";
- QueryResult person = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("John", 21, 50_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jack", 23, 20_000)));
- person.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Jane", 31, 40_000)));
+ QueryResult contents = new QueryResult(Arrays.asList("name", "age", "salary"), "Person");
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Alice", 30, 50_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Alice", 35, 55_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Bob", 28, 45_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Bob", 40, 45_000)));
+ contents.addRow(new DataRow("Person", Arrays.asList("name", "age", "salary"), Arrays.asList("Charly", 22, 30_000)));
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
-
- QueryResultSet queryResultSet = QueryResultSet.build(person, employees);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(3, heuristicResult.getQueryResult().seeRows().size());
- assertEquals("John", heuristicResult.getQueryResult().seeRows().get(0).getValueByName("name"));
- assertEquals(0L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("count_employees"));
+ assertTrue(heuristicResult.getTruthness().isTrue());
- assertEquals("Jack", heuristicResult.getQueryResult().seeRows().get(1).getValueByName("name"));
- assertEquals(0L, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("count_employees"));
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(3, queryResult.size());
- assertEquals("Jane", heuristicResult.getQueryResult().seeRows().get(2).getValueByName("name"));
- assertEquals(0L, heuristicResult.getQueryResult().seeRows().get(2).getValueByName("count_employees"));
+ assertEquals("Alice", queryResult.seeRows().get(0).getValueByName("name"));
+ assertEquals(30, queryResult.seeRows().get(0).getValueByName("age"));
+
+ assertEquals("Bob", queryResult.seeRows().get(1).getValueByName("name"));
+ assertEquals(28, queryResult.seeRows().get(1).getValueByName("age"));
+
+ assertEquals("Charly", queryResult.seeRows().get(2).getValueByName("name"));
+ assertEquals(22, queryResult.seeRows().get(2).getValueByName("age"));
}
@Test
- public void testGroupBy() {
+ public void testDate() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT department_id, COUNT(*) As num_employees FROM employees GROUP BY department_id;";
-
-
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("John Doe", "John", 1, 2, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jack Doe", "Jack", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jane Doe", "Jane", 1, 1, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Janet Doe", "Janet", 2, 1, 10_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(employees);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
+ String sqlCommand = "SELECT project_name FROM projects WHERE DATE(project_start_time)>'2023-08-22 00:00:00.000000'";
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(2, heuristicResult.getQueryResult().seeRows().size());
- assertEquals(1, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("department_id"));
- assertEquals(3L, heuristicResult.getQueryResult().seeRows().get(0).getValueByName("num_employees"));
-
- assertEquals(2, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("department_id"));
- assertEquals(1L, heuristicResult.getQueryResult().seeRows().get(1).getValueByName("num_employees"));
-
- }
+ QueryResult contents = new QueryResult(Arrays.asList("project_id", "project_name", "project_start_time"), "projects");
+ contents.addRow(Arrays.asList("project_id", "project_name", "project_start_time"), "projects", Arrays.asList(1, "ProjectX", Timestamp.valueOf("2023-09-15 12:00:00")));
- @Test
- public void testGroupByMoreThanOneField() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT department_id, project_id, COUNT(*) As employees_per_project " +
- "FROM employees " +
- "GROUP BY department_id, project_id;";
-
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("John Doe", "John", 1, 2, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jack Doe", "Jack", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jane Doe", "Jane", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Janet Doe", "Janet", 2, 1, 10_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(employees);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(3, heuristicResult.getQueryResult().seeRows().size());
-
- assertEquals(1,
- heuristicResult.getQueryResult().seeRows().stream()
- .filter(row -> row.getValueByName("department_id").equals(1))
- .filter(row -> row.getValueByName("project_id").equals(2))
- .filter(row -> row.getValueByName("employees_per_project").equals(1L))
- .count());
-
- assertEquals(1,
- heuristicResult.getQueryResult().seeRows().stream()
- .filter(row -> row.getValueByName("department_id").equals(1))
- .filter(row -> row.getValueByName("project_id").equals(3))
- .filter(row -> row.getValueByName("employees_per_project").equals(2L))
- .count());
-
- assertEquals(1,
- heuristicResult.getQueryResult().seeRows().stream()
- .filter(row -> row.getValueByName("department_id").equals(2))
- .filter(row -> row.getValueByName("project_id").equals(1))
- .filter(row -> row.getValueByName("employees_per_project").equals(1L))
- .count());
+ assertTrue(heuristicResult.getTruthness().isTrue());
+
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.size());
}
@Test
- public void testGroupByWithHaving() {
+ public void testUUID() {
DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT department_id, project_id, COUNT(*) As employees_per_project " +
- "FROM employees " +
- "GROUP BY department_id, project_id " +
- "HAVING COUNT(*) > 1;";
-
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("John Doe", "John", 1, 2, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jack Doe", "Jack", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jane Doe", "Jane", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Janet Doe", "Janet", 2, 1, 10_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(employees);
- SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
- SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
- .withTableColumnResolver(new TableColumnResolver(schema))
- .build();
-
- SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(1, heuristicResult.getQueryResult().seeRows().size());
-
-
- assertEquals(1,
- heuristicResult.getQueryResult().seeRows().stream()
- .filter(row -> row.getValueByName("department_id").equals(1))
- .filter(row -> row.getValueByName("project_id").equals(3))
- .filter(row -> row.getValueByName("employees_per_project").equals(2L))
- .count());
- }
+ String sqlCommand = "SELECT project_id, project_name FROM projects WHERE project_id = '00000000-0000-015f-0000-00000000014e'::uuid";
+ QueryResult contents = new QueryResult(Arrays.asList("project_id", "project_name", "project_start_time"), "projects");
+ contents.addRow(Arrays.asList("project_id", "project_name", "project_start_time"), "projects", Arrays.asList(UUID.fromString("00000000-0000-015f-0000-00000000014e"), "ProjectX", Timestamp.valueOf("2023-09-15 12:00:00")));
- @Test
- public void testGroupByWithHavingNotSatisfied() {
- DbInfoDto schema = buildSchema();
- String sqlCommand = "SELECT department_id, COUNT(*) As employees_per_department " +
- "FROM employees " +
- "GROUP BY department_id " +
- "HAVING COUNT(*) = 2;";
-
- QueryResult employees = new QueryResult(Arrays.asList("name", "first_name", "department_id", "project_id", "salary"), "employees");
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("John Doe", "John", 1, 2, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jack Doe", "Jack", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Jane Doe", "Jane", 1, 3, 10_000)));
- employees.addRow(new DataRow("employees", Arrays.asList("name", "first_name", "department_id", "project_id", "salary"),
- Arrays.asList("Janet Doe", "Janet", 2, 1, 10_000)));
- QueryResultSet queryResultSet = QueryResultSet.build(employees);
+ QueryResultSet queryResultSet = new QueryResultSet();
+ queryResultSet.addQueryResult(contents);
SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder builder = new SqlHeuristicsCalculator.SqlHeuristicsCalculatorBuilder();
SqlHeuristicsCalculator calculator = builder.withSourceQueryResultSet(queryResultSet)
.withTableColumnResolver(new TableColumnResolver(schema))
.build();
-
SqlHeuristicResult heuristicResult = calculator.computeHeuristic((Select) SqlParserUtils.parseSqlCommand(sqlCommand));
- assertEquals(0, heuristicResult.getQueryResult().seeRows().size());
- Truthness expectedTruthness =
- TruthnessUtils.buildAndAggregationTruthness(new Truthness(1.0, 0.2), TruthnessUtils.buildOrAggregationTruthness(
- new Truthness(0.575, 1.0),
- new Truthness(0.575, 1.0)
- ));
- assertEquals(expectedTruthness.getOfTrue(), heuristicResult.getTruthness().getOfTrue());
- assertEquals(expectedTruthness.getOfFalse(), heuristicResult.getTruthness().getOfFalse());
- }
+ assertTrue(heuristicResult.getTruthness().isTrue());
-}
+ QueryResult queryResult = heuristicResult.getQueryResult();
+ assertEquals(1, queryResult.size());
+ UUID expectedUUID = UUID.fromString("00000000-0000-015f-0000-00000000014e");
+ assertEquals(expectedUUID, queryResult.seeRows().get(0).getValueByName("project_id"));
+ assertEquals("ProjectX", queryResult.seeRows().get(0).getValueByName("project_name"));
+ }
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlStringUtilsTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlStringUtilsTest.java
index 0786d633d8..8b682804ca 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlStringUtilsTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/SqlStringUtilsTest.java
@@ -136,5 +136,37 @@ void testEmptyStrings() {
void testOneEmptyOneNonEmpty() {
assertFalse(SqlStringUtils.nullSafeEqualsIgnoreCase("", "nonempty"));
}
+
+
+ @Test
+ void testNullAReturnsFalse() {
+ assertFalse(SqlStringUtils.nullSafeEndsWithIgnoreCase(null, "suffix"));
+ }
+
+ @Test
+ void testEndsWithIgnoreCaseReturnsTrue() {
+ assertTrue(SqlStringUtils.nullSafeEndsWithIgnoreCase("HelloWORLD", "world"));
+ }
+
+ @Test
+ void testDoesNotEndWithReturnsFalse() {
+ assertFalse(SqlStringUtils.nullSafeEndsWithIgnoreCase("hello", "WORLDs"));
+ }
+
+ @Test
+ void testEmptySuffixReturnsTrue() {
+ assertTrue(SqlStringUtils.nullSafeEndsWithIgnoreCase("anything", ""));
+ }
+
+ @Test
+ void testSuffixLongerThanInputReturnsFalse() {
+ assertFalse(SqlStringUtils.nullSafeEndsWithIgnoreCase("ab", "abc"));
+ }
+
+ @Test
+ void testBNullThrowsNullPointerException() {
+ assertFalse(SqlStringUtils.nullSafeEndsWithIgnoreCase("abc", null));
+ }
+
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasContextTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasContextTest.java
new file mode 100644
index 0000000000..a636f8192e
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasContextTest.java
@@ -0,0 +1,127 @@
+package org.evomaster.client.java.sql.heuristic;
+
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.select.Select;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class TableAliasContextTest {
+
+
+ @Test
+ public void testContainsAliasWithNull() {
+ TableAliasContext context = new TableAliasContext();
+ assertThrows(NullPointerException.class, () -> context.containsAlias((String) null));
+ }
+
+ @Test
+ public void testAddAliasToTableNameWithNullAlias() {
+ Table table = new Table("Employees");
+ TableAliasContext context = new TableAliasContext();
+ assertThrows(NullPointerException.class, () -> context.addAliasToTableName(null, table));
+ }
+
+ @Test
+ public void testAddAliasToTableNameWithNullTable() {
+ Alias alias = new Alias("e");
+ TableAliasContext context = new TableAliasContext();
+ assertThrows(NullPointerException.class, () -> context.addAliasToTableName(alias, null));
+ }
+
+ @Test
+ public void testAddAliasToTableName() {
+ Alias alias = new Alias("e");
+ Table table = new Table("Employees");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToTableName(alias, table);
+
+ assertTrue(context.containsAlias("e"));
+ assertTrue(context.containsAlias("E"));
+
+ SqlTableReference ref = context.getTableReference("e");
+ assertTrue(ref instanceof SqlTableName);
+ assertEquals("Employees", ((SqlTableName) ref).getTable().getName());
+ }
+
+ @Test
+ public void testAddDuplicateAliasToTableName() {
+ Alias alias1 = new Alias("e");
+ Table table1 = new Table("Employees");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToTableName(alias1, table1);
+
+ Alias alias2 = new Alias("E"); // Case insensitive check
+ Table table2 = new Table("Engineers");
+
+ assertThrows(IllegalArgumentException.class, () -> context.addAliasToTableName(alias2, table2));
+ }
+
+ @Test
+ public void testAddAliasToDerivedTable() throws Exception {
+ Alias alias = new Alias("sub");
+ Select subquery = (Select) CCJSqlParserUtil.parse("SELECT * FROM Employees");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToDerivedTable(alias, subquery);
+
+ assertTrue(context.containsAlias("sub"));
+ assertTrue(context.containsAlias("SUB"));
+
+ SqlTableReference ref = context.getTableReference("sub");
+ assertTrue(ref instanceof SqlDerivedTable);
+ assertEquals(subquery, ((SqlDerivedTable) ref).getSelect());
+ }
+
+ @Test
+ public void testAddDuplicateAliasToDerivedTable() throws Exception {
+ Alias alias1 = new Alias("sub");
+ Select subquery1 = (Select) CCJSqlParserUtil.parse("SELECT * FROM Employees");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToDerivedTable(alias1, subquery1);
+
+ Alias alias2 = new Alias("sub");
+ Select subquery2 = (Select) CCJSqlParserUtil.parse("SELECT * FROM Departments");
+
+ assertThrows(IllegalArgumentException.class, () -> context.addAliasToDerivedTable(alias2, subquery2));
+ }
+
+ @Test
+ public void testAddMixedDuplicateAliases() throws Exception {
+ Alias alias1 = new Alias("a");
+ Table table = new Table("T");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToTableName(alias1, table);
+
+ Alias alias2 = new Alias("a");
+ Select subquery = (Select) CCJSqlParserUtil.parse("SELECT * FROM T2");
+
+ assertThrows(IllegalArgumentException.class, () -> context.addAliasToDerivedTable(alias2, subquery));
+ }
+
+ @Test
+ public void testGetTableReferenceNonExisting() {
+ TableAliasContext context = new TableAliasContext();
+ assertThrows(IllegalArgumentException.class, () -> context.getTableReference("nonexistent"));
+ }
+
+ @Test
+ public void testGetTableReferenceCaseInsensitivity() {
+ Alias alias = new Alias("MyAlias");
+ Table table = new Table("MyTable");
+
+ TableAliasContext context = new TableAliasContext();
+ context.addAliasToTableName(alias, table);
+
+ assertNotNull(context.getTableReference("myalias"));
+ assertNotNull(context.getTableReference("MYALIAS"));
+ assertNotNull(context.getTableReference("MyAlias"));
+ }
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasResolverTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasResolverTest.java
index 75fa169544..d50345ffb3 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasResolverTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableAliasResolverTest.java
@@ -1,5 +1,6 @@
package org.evomaster.client.java.sql.heuristic;
+import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.delete.Delete;
@@ -21,7 +22,7 @@ public void resolvesSimpleTableAlias() throws Exception {
TableAliasResolver resolver = new TableAliasResolver();
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -34,10 +35,10 @@ public void resolvesAliasInSubquery() throws Exception {
TableAliasResolver resolver = new TableAliasResolver();
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery"));
- SqlTableReference sqlTableReference = resolver.resolveTableReference("subquery");
- assertTrue(sqlTableReference instanceof SqlDerivedTableReference);
- assertEquals("SELECT * FROM Employees", ((SqlDerivedTableReference) sqlTableReference).getSelect().getPlainSelect().toString());
- ParenthesedSelect parenthesedSelect = (ParenthesedSelect) ((SqlDerivedTableReference) sqlTableReference).getSelect();
+ SqlTableReference sqlTableReference = resolver.resolveAlias("subquery");
+ assertTrue(sqlTableReference instanceof SqlDerivedTable);
+ assertEquals("SELECT * FROM Employees", ((SqlDerivedTable) sqlTableReference).getSelect().getPlainSelect().toString());
+ ParenthesedSelect parenthesedSelect = (ParenthesedSelect) ((SqlDerivedTable) sqlTableReference).getSelect();
assertEquals("subquery", parenthesedSelect.getAlias().getName());
assertEquals(1, resolver.getContextDepth());
@@ -58,8 +59,8 @@ public void resolvesAliasInJoin() throws Exception {
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
}
@@ -72,15 +73,15 @@ public void resolvesAliasInNestedSubquery() throws Exception {
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery2"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("subquery1"));
- assertEquals("SELECT * FROM (SELECT * FROM Employees) AS subquery1", ((SqlDerivedTableReference) resolver.resolveTableReference("subquery2")).getSelect().getPlainSelect().toString());
+ assertEquals("SELECT * FROM (SELECT * FROM Employees) AS subquery1", ((SqlDerivedTable) resolver.resolveAlias("subquery2")).getSelect().getPlainSelect().toString());
- Select subquery2 = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery2")).getSelect().getPlainSelect();
+ Select subquery2 = ((SqlDerivedTable) resolver.resolveAlias("subquery2")).getSelect().getPlainSelect();
resolver.enterTableAliasContext(subquery2);
assertFalse(resolver.isAliasDeclaredInCurrentContext("subquery2"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery1"));
- assertEquals("SELECT * FROM Employees", ((SqlDerivedTableReference) resolver.resolveTableReference("subquery1")).getSelect().getPlainSelect().toString());
+ assertEquals("SELECT * FROM Employees", ((SqlDerivedTable) resolver.resolveAlias("subquery1")).getSelect().getPlainSelect().toString());
- Select selectFromEmployees = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery1")).getSelect().getPlainSelect();
+ Select selectFromEmployees = ((SqlDerivedTable) resolver.resolveAlias("subquery1")).getSelect().getPlainSelect();
resolver.enterTableAliasContext(selectFromEmployees);
assertFalse(resolver.isAliasDeclaredInCurrentContext("subquery2"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("subquery1"));
@@ -105,13 +106,13 @@ public void resolvesAliasInUnion() throws Exception {
resolver.enterTableAliasContext(select.getSetOperationList().getSelects().get(0));
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
resolver.enterTableAliasContext(select.getSetOperationList().getSelects().get(1));
assertFalse(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
resolver.exitTableAliasContext();
@@ -126,7 +127,7 @@ public void resolvesAliasInWithClause() throws Exception {
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery"));
- Select subquery = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery")).getSelect();
+ Select subquery = ((SqlDerivedTable) resolver.resolveAlias("subquery")).getSelect();
assertEquals("(SELECT * FROM Employees)", subquery.toString());
resolver.enterTableAliasContext(subquery);
@@ -155,10 +156,10 @@ public void resolvesAliasInComplexJoin() throws Exception {
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals("SELECT * FROM Departments", ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect().toString());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("SELECT * FROM Departments", ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect().toString());
- Select subSelect = ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect();
+ Select subSelect = ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect();
resolver.enterTableAliasContext(subSelect);
assertFalse(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
@@ -178,10 +179,10 @@ public void resolvesAliasInMultipleWithClauses() throws Exception {
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery1"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery2"));
- Select subquery1 = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery1")).getSelect();
+ Select subquery1 = ((SqlDerivedTable) resolver.resolveAlias("subquery1")).getSelect();
assertEquals("(SELECT * FROM Employees)", subquery1.toString());
- Select subquery2 = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery2")).getSelect();
+ Select subquery2 = ((SqlDerivedTable) resolver.resolveAlias("subquery2")).getSelect();
assertEquals("(SELECT * FROM Departments)", subquery2.toString());
resolver.enterTableAliasContext(subquery1);
@@ -210,12 +211,12 @@ public void resolvesAliasInNestedWithClauses() throws Exception {
TableAliasResolver resolver = new TableAliasResolver();
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery1"));
- final Select subquery1 = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery1")).getSelect();
+ final Select subquery1 = ((SqlDerivedTable) resolver.resolveAlias("subquery1")).getSelect();
assertEquals("(WITH subquery2 AS (SELECT * FROM Departments) SELECT * FROM subquery2)", subquery1.toString());
resolver.enterTableAliasContext(subquery1.getPlainSelect());
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery2"));
- final Select subquery2 = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery2")).getSelect();
+ final Select subquery2 = ((SqlDerivedTable) resolver.resolveAlias("subquery2")).getSelect();
assertEquals("(SELECT * FROM Departments)", subquery2.toString());
resolver.exitTableAliasContext();
@@ -236,15 +237,15 @@ public void resolvesAliasInComplexUnion() throws Exception {
resolver.enterTableAliasContext(select.getSetOperationList().getSelects().get(0));
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
resolver.enterTableAliasContext(select.getSetOperationList().getSelects().get(1));
assertFalse(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals("SELECT * FROM Departments", ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect().toString());
+ assertEquals("SELECT * FROM Departments", ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect().toString());
- Select subSelect = ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect();
+ Select subSelect = ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect();
resolver.enterTableAliasContext(subSelect);
assertFalse(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
@@ -265,7 +266,7 @@ public void resolvesAliasInSubqueryWithJoin() throws Exception {
resolver.enterTableAliasContext(select);
assertTrue(resolver.isAliasDeclaredInCurrentContext("subquery"));
- Select subquery = ((SqlDerivedTableReference) resolver.resolveTableReference("subquery")).getSelect().getPlainSelect();
+ Select subquery = ((SqlDerivedTable) resolver.resolveAlias("subquery")).getSelect().getPlainSelect();
assertEquals("SELECT e.name, d.name FROM Employees e JOIN Departments d ON e.department_id = d.department_id", subquery.toString());
resolver.enterTableAliasContext(subquery);
@@ -273,8 +274,8 @@ public void resolvesAliasInSubqueryWithJoin() throws Exception {
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
resolver.exitTableAliasContext();
@@ -302,28 +303,28 @@ public void resolvesDuplicateAlias() throws Exception {
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertTrue(resolver.resolveTableReference("e") instanceof SqlDerivedTableReference);
- assertTrue(resolver.resolveTableReference("d") instanceof SqlDerivedTableReference);
+ assertTrue(resolver.resolveAlias("e") instanceof SqlDerivedTable);
+ assertTrue(resolver.resolveAlias("d") instanceof SqlDerivedTable);
assertEquals("SELECT id, first_name, department_id FROM employees e WHERE e.status = 'active'",
- ((SqlDerivedTableReference) resolver.resolveTableReference("e")).getSelect().getPlainSelect().toString());
+ ((SqlDerivedTable) resolver.resolveAlias("e")).getSelect().getPlainSelect().toString());
assertEquals("SELECT id, department_name FROM departments e WHERE e.is_active = 1",
- ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect().toString());
+ ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect().toString());
- Select e = ((SqlDerivedTableReference) resolver.resolveTableReference("e")).getSelect().getPlainSelect();
- Select d = ((SqlDerivedTableReference) resolver.resolveTableReference("d")).getSelect().getPlainSelect();
+ Select e = ((SqlDerivedTable) resolver.resolveAlias("e")).getSelect().getPlainSelect();
+ Select d = ((SqlDerivedTable) resolver.resolveAlias("d")).getSelect().getPlainSelect();
resolver.enterTableAliasContext(e);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
- assertTrue(resolver.resolveTableReference("e") instanceof SqlBaseTableReference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertTrue(resolver.resolveAlias("e") instanceof SqlTableName);
+ assertEquals("employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
resolver.enterTableAliasContext(d);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertFalse(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("departments", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
resolver.exitTableAliasContext();
@@ -338,7 +339,7 @@ public void resolvesAliasInDeleteStatement() throws Exception {
resolver.enterTableAliasContext(delete);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -352,7 +353,7 @@ public void resolvesAliasInUpdateStatement() throws Exception {
resolver.enterTableAliasContext(update);
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -367,8 +368,8 @@ public void resolvesAliasInDeleteWithJoin() throws Exception {
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -383,8 +384,8 @@ public void resolvesAliasInUpdateWithJoin() throws Exception {
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -400,14 +401,14 @@ public void resolvesAliasInDeleteWithWithClause() throws Exception {
// Verify the alias in WITH clause
assertTrue(resolver.isAliasDeclaredInCurrentContext("dept_to_delete"));
- SqlTableReference withTableReference = resolver.resolveTableReference("dept_to_delete");
- assertTrue(withTableReference instanceof SqlDerivedTableReference);
+ SqlTableReference withTableReference = resolver.resolveAlias("dept_to_delete");
+ assertTrue(withTableReference instanceof SqlDerivedTable);
assertEquals("SELECT id FROM Departments WHERE name = 'HR'",
- ((SqlDerivedTableReference) withTableReference).getSelect().getPlainSelect().toString());
+ ((SqlDerivedTable) withTableReference).getSelect().getPlainSelect().toString());
// Verify the alias in the DELETE statement
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -423,8 +424,8 @@ public void resolvesAliasInMultiTableDelete() throws Exception {
// Verify the aliases for both tables
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
@@ -439,7 +440,7 @@ public void resolvesAliasInSelectWithInSubquery() throws Exception {
// Verify alias in the main query
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
// Access the subquery in IN expression
InExpression inExpression = (InExpression) ((PlainSelect) select).getWhere();
@@ -448,7 +449,7 @@ public void resolvesAliasInSelectWithInSubquery() throws Exception {
// Verify alias in the subquery
assertTrue(resolver.isAliasDeclaredInCurrentContext("d"));
- assertEquals(new SqlTableId("Departments"), ((SqlBaseTableReference) resolver.resolveTableReference("d")).getTableId());
+ assertEquals("Departments", ((SqlTableName) resolver.resolveAlias("d")).getTable().getName());
resolver.exitTableAliasContext();
resolver.exitTableAliasContext();
@@ -465,11 +466,41 @@ public void resolvesAliasCaseInsensitivity() throws Exception {
// Check case-sensitive alias resolution
assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
assertTrue(resolver.isAliasDeclaredInCurrentContext("E")); // Case-sensitive check
- assertEquals(new SqlTableId("Employees"), ((SqlBaseTableReference) resolver.resolveTableReference("e")).getTableId());
+ assertEquals("Employees", ((SqlTableName) resolver.resolveAlias("e")).getTable().getName());
resolver.exitTableAliasContext();
assertEquals(0, resolver.getContextDepth());
}
+
+ @Test
+ void testCommonTableExpressionWithAliasForEmployees() throws JSQLParserException {
+ String sql = "WITH EmployeeCTE AS (SELECT first_name, salary FROM employees WHERE salary > 50000) " +
+ "SELECT e.first_name FROM EmployeeCTE e WHERE e.first_name='John' ";
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ TableAliasResolver resolver = new TableAliasResolver();
+ resolver.enterTableAliasContext(select);
+
+ assertTrue(resolver.isAliasDeclaredInCurrentContext("e"));
+ SqlTableReference withTableReference = resolver.resolveAlias("e");
+ assertTrue(withTableReference instanceof SqlDerivedTable);
+ assertEquals("SELECT first_name, salary FROM employees WHERE salary > 50000",
+ ((SqlDerivedTable) withTableReference).getSelect().getPlainSelect().toString());
+
+ }
+
+ @Test
+ void testAliasToSchemaBaseTable() throws JSQLParserException {
+ String sql = "SELECT u.user_id FROM public.users u";
+ TableAliasResolver resolver = new TableAliasResolver();
+ resolver.enterTableAliasContext(CCJSqlParserUtil.parse(sql));
+ SqlTableReference tableReference = resolver.resolveAlias("u");
+ assertNotNull(tableReference);
+ assertTrue(tableReference instanceof SqlTableName);
+ SqlTableName tableNameReference = (SqlTableName) tableReference;
+ assertEquals("public", tableNameReference.getTable().getSchemaName());
+ assertEquals("users", tableNameReference.getTable().getName());
+
+ }
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableColumnResolverTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableColumnResolverTest.java
index 060324173e..88f7c3c61b 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableColumnResolverTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/TableColumnResolverTest.java
@@ -1,5 +1,6 @@
package org.evomaster.client.java.sql.heuristic;
+import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
@@ -28,30 +29,35 @@ class TableColumnResolverTest {
void setUp() {
schema = new DbInfoDto();
- TableDto employeesTable = createTableDto("employees");
+ TableDto employeesTable = createTableDto(null, "employees");
employeesTable.columns.add(createColumnDto("employees", "id"));
employeesTable.columns.add(createColumnDto("employees", "first_name"));
employeesTable.columns.add(createColumnDto("employees", "department_id"));
employeesTable.columns.add(createColumnDto("employees", "salary"));
- TableDto departmentsTable = createTableDto("departments");
+ TableDto departmentsTable = createTableDto(null, "departments");
departmentsTable.columns.add(createColumnDto("departments", "id"));
departmentsTable.columns.add(createColumnDto("departments", "department_name"));
- TableDto ordersTable = createTableDto("orders");
+ TableDto ordersTable = createTableDto(null, "orders");
ordersTable.columns.add(createColumnDto("orders", "order_id"));
ordersTable.columns.add(createColumnDto("orders", "order_date"));
ordersTable.columns.add(createColumnDto("orders", "customer_id"));
- TableDto customersTable = createTableDto("customers");
+ TableDto customersTable = createTableDto(null, "customers");
customersTable.columns.add(createColumnDto("customers", "customer_id"));
customersTable.columns.add(createColumnDto("customers", "customer_name"));
+ TableDto usersTable = createTableDto("public", "users");
+ usersTable.columns.add(createColumnDto("users", "user_id"));
+ usersTable.columns.add(createColumnDto("users", "user_name"));
+
schema.tables.add(employeesTable);
schema.tables.add(departmentsTable);
schema.tables.add(ordersTable);
schema.tables.add(customersTable);
+ schema.tables.add(usersTable);
resolver = new TableColumnResolver(schema);
}
@@ -63,9 +69,10 @@ private static ColumnDto createColumnDto(String tableName, String columnName) {
return column;
}
- private static TableDto createTableDto(String tableName) {
+ private static TableDto createTableDto(String schemaName, String tableName) {
TableDto table = new TableDto();
table.id = new TableIdDto();
+ table.id.schema = schemaName;
table.id.name = tableName;
return table;
}
@@ -82,7 +89,7 @@ void testResolveColumnWithExplicitTable() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -99,7 +106,7 @@ void testResolveColumnWithoutExplicitTable() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -117,7 +124,7 @@ void testResolveColumnInJoin() throws Exception {
SqlColumnReference referenceE = resolver.resolve(columnE);
assertNotNull(referenceE);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) referenceE.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) referenceE.getTableReference()).getTableId());
assertEquals("first_name", referenceE.getColumnName());
Column columnD = new Column();
@@ -126,7 +133,7 @@ void testResolveColumnInJoin() throws Exception {
SqlColumnReference referenceD = resolver.resolve(columnD);
assertNotNull(referenceD);
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) referenceD.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"departments"), ((SqlBaseTableReference) referenceD.getTableReference()).getTableId());
assertEquals("department_name", referenceD.getColumnName());
resolver.exitCurrentStatementContext();
@@ -144,8 +151,8 @@ void testResolveColumnInSubquery() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
- assertEquals("(SELECT * FROM employees) e", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
+ assertEquals("(SELECT * FROM employees) e", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -164,7 +171,7 @@ void testResolveColumnInNestedSubquery() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -182,7 +189,7 @@ void testResolveColumnInWithClause() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -199,7 +206,7 @@ void testResolveAmbiguousColumnInJoin() throws Exception {
SqlColumnReference sqlColumnReference = resolver.resolve(column);
assertEquals("id", sqlColumnReference.getColumnName());
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) sqlColumnReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) sqlColumnReference.getTableReference()).getTableId());
resolver.exitCurrentStatementContext();
}
@@ -215,7 +222,7 @@ void testResolveColumnInUnion() throws Exception {
SqlColumnReference sqlColumnReference = resolver.resolve(column);
assertEquals("id", sqlColumnReference.getColumnName());
- assertEquals("SELECT id FROM employees UNION SELECT id FROM departments", ((SqlDerivedTableReference) sqlColumnReference.getTableReference()).getSelect().toString());
+ assertEquals("SELECT id FROM employees UNION SELECT id FROM departments", ((SqlDerivedTable) sqlColumnReference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -232,7 +239,7 @@ void testResolveColumnInComplexJoinWithSubquery() throws Exception {
SqlColumnReference referenceE = resolver.resolve(columnE);
assertNotNull(referenceE);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) referenceE.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) referenceE.getTableReference()).getTableId());
assertEquals("first_name", referenceE.getColumnName());
Column columnSubquery = new Column();
@@ -241,7 +248,7 @@ void testResolveColumnInComplexJoinWithSubquery() throws Exception {
SqlColumnReference referenceSubquery = resolver.resolve(columnSubquery);
assertNotNull(referenceSubquery);
- assertTrue(referenceSubquery.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(referenceSubquery.getTableReference() instanceof SqlDerivedTable);
assertEquals("department_name", referenceSubquery.getColumnName());
resolver.exitCurrentStatementContext();
@@ -258,7 +265,7 @@ void testResolveAmbiguousColumnInRightJoin() throws Exception {
SqlColumnReference sqlColumnReference = resolver.resolve(column);
assertEquals("department_name", sqlColumnReference.getColumnName());
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) sqlColumnReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"departments"), ((SqlBaseTableReference) sqlColumnReference.getTableReference()).getTableId());
resolver.exitCurrentStatementContext();
}
@@ -288,9 +295,9 @@ void testResolveColumnFromSubquery() throws Exception {
SqlColumnReference sqlColumnReference = resolver.resolve(column);
assertNotNull(sqlColumnReference);
- assertTrue(sqlColumnReference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(sqlColumnReference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", sqlColumnReference.getColumnName());
- assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTableReference) sqlColumnReference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTable) sqlColumnReference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -313,10 +320,10 @@ void testResolveColumnInJoinWithSubquery() throws Exception {
SqlColumnReference referenceEmployeeId = resolver.resolve(columnEmployeeId);
assertNotNull(referenceEmployeeId);
- assertTrue(referenceEmployeeId.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(referenceEmployeeId.getTableReference() instanceof SqlDerivedTable);
assertEquals("employee_id", referenceEmployeeId.getColumnName());
assertEquals("(SELECT id AS employee_id, department_id FROM employees WHERE salary > 50000) e",
- ((SqlDerivedTableReference) referenceEmployeeId.getTableReference()).getSelect().toString());
+ ((SqlDerivedTable) referenceEmployeeId.getTableReference()).getSelect().toString());
// Resolve department_name from table d
Column columnDepartmentName = new Column();
@@ -324,7 +331,7 @@ void testResolveColumnInJoinWithSubquery() throws Exception {
SqlColumnReference referenceDepartmentName = resolver.resolve(columnDepartmentName);
assertNotNull(referenceDepartmentName);
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) referenceDepartmentName.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"departments"), ((SqlBaseTableReference) referenceDepartmentName.getTableReference()).getTableId());
assertEquals("department_name", referenceDepartmentName.getColumnName());
resolver.exitCurrentStatementContext();
@@ -341,9 +348,9 @@ void testResolveColumnInSubqueryWithNoAlias() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
- assertEquals("(SELECT * FROM employees)", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT * FROM employees)", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -361,9 +368,9 @@ void resolveColumnAliasInSubqueryWithWhereClause() throws Exception {
SqlColumnReference referenceName = resolver.resolve(columnName);
assertNotNull(referenceName);
- assertTrue(referenceName.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(referenceName.getTableReference() instanceof SqlDerivedTable);
assertEquals("name", referenceName.getColumnName());
- assertEquals("(SELECT first_name AS name, salary AS income FROM Employees) AS subquery", ((SqlDerivedTableReference) referenceName.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name AS name, salary AS income FROM Employees) AS subquery", ((SqlDerivedTable) referenceName.getTableReference()).getSelect().toString());
// Resolve alias 'income' from subquery
Column columnIncome = new Column();
@@ -372,9 +379,9 @@ void resolveColumnAliasInSubqueryWithWhereClause() throws Exception {
SqlColumnReference referenceIncome = resolver.resolve(columnIncome);
assertNotNull(referenceIncome);
- assertTrue(referenceIncome.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(referenceIncome.getTableReference() instanceof SqlDerivedTable);
assertEquals("income", referenceIncome.getColumnName());
- assertEquals("(SELECT first_name AS name, salary AS income FROM Employees) AS subquery", ((SqlDerivedTableReference) referenceIncome.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name AS name, salary AS income FROM Employees) AS subquery", ((SqlDerivedTable) referenceIncome.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -391,7 +398,7 @@ void testResolveAllTableColumns() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals("(SELECT employees.* FROM employees) AS e", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT employees.* FROM employees) AS e", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -408,7 +415,7 @@ void testResolveColumnWithAlias() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -426,9 +433,9 @@ void testResolveColumnAliasInSubquery() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("fname", reference.getColumnName());
- assertEquals("(SELECT first_name AS fname FROM employees) subquery", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name AS fname FROM employees) subquery", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -446,7 +453,7 @@ void testResolveColumnAliasInJoin() throws Exception {
SqlColumnReference referenceE = resolver.resolve(columnE);
assertNotNull(referenceE);
- assertTrue(referenceE.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(referenceE.getTableReference() instanceof SqlDerivedTable);
assertEquals("fname", referenceE.getColumnName());
// Resolve department_name from table d
@@ -456,7 +463,7 @@ void testResolveColumnAliasInJoin() throws Exception {
SqlColumnReference referenceD = resolver.resolve(columnD);
assertNotNull(referenceD);
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) referenceD.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"departments"), ((SqlBaseTableReference) referenceD.getTableReference()).getTableId());
assertEquals("department_name", referenceD.getColumnName());
resolver.exitCurrentStatementContext();
@@ -473,9 +480,9 @@ void testColumnInParenthesizedSelect() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
- assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -491,9 +498,9 @@ void testColumnInNestedParenthesizedSelect() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
- assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("(SELECT first_name FROM employees)", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -509,9 +516,9 @@ void testColumnInSetOperationListWithAlias() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("emp_id", reference.getColumnName());
- assertEquals("SELECT id AS emp_id FROM employees UNION SELECT id AS dept_id FROM departments", ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ assertEquals("SELECT id AS emp_id FROM employees UNION SELECT id AS dept_id FROM departments", ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -527,7 +534,7 @@ void testResolveColumnInDeleteStatement() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
assertEquals("department_id", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -545,7 +552,7 @@ void testResolveColumnInUpdateStatement() throws Exception {
SqlColumnReference setReference = resolver.resolve(setColumn);
assertNotNull(setReference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) setReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) setReference.getTableReference()).getTableId());
assertEquals("salary", setReference.getColumnName());
// Resolve column in WHERE clause
@@ -554,7 +561,7 @@ void testResolveColumnInUpdateStatement() throws Exception {
SqlColumnReference whereReference = resolver.resolve(whereColumn);
assertNotNull(whereReference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) whereReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) whereReference.getTableReference()).getTableId());
assertEquals("department_id", whereReference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -572,7 +579,7 @@ void testResolveColumnInDeleteWithSubquery() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) reference.getTableReference()).getTableId());
assertEquals("department_id", reference.getColumnName());
// Resolve alias 'dept_id' in the subquery
@@ -598,7 +605,7 @@ void testResolveColumnInDeleteWithSubquery() throws Exception {
SqlColumnReference subqueryReference = resolver.resolve(d_id_column);
assertNotNull(subqueryReference);
- assertEquals(new SqlTableId("departments"), ((SqlBaseTableReference) subqueryReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"departments"), ((SqlBaseTableReference) subqueryReference.getTableReference()).getTableId());
assertEquals("id", subqueryReference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -620,7 +627,7 @@ void testResolveColumnsInInnerJoin() throws Exception {
SqlColumnReference orderIdReference = resolver.resolve(orderIdColumn);
assertNotNull(orderIdReference);
- assertEquals(new SqlTableId("orders"), ((SqlBaseTableReference) orderIdReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"orders"), ((SqlBaseTableReference) orderIdReference.getTableReference()).getTableId());
assertEquals("order_id", orderIdReference.getColumnName());
// Resolve Customers.CustomerName
@@ -630,7 +637,7 @@ void testResolveColumnsInInnerJoin() throws Exception {
SqlColumnReference customerNameReference = resolver.resolve(customerNameColumn);
assertNotNull(customerNameReference);
- assertEquals(new SqlTableId("customers"), ((SqlBaseTableReference) customerNameReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"customers"), ((SqlBaseTableReference) customerNameReference.getTableReference()).getTableId());
assertEquals("customer_name", customerNameReference.getColumnName());
// Resolve Orders.OrderDate
@@ -640,7 +647,7 @@ void testResolveColumnsInInnerJoin() throws Exception {
SqlColumnReference orderDateReference = resolver.resolve(orderDateColumn);
assertNotNull(orderDateReference);
- assertEquals(new SqlTableId("orders"), ((SqlBaseTableReference) orderDateReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"orders"), ((SqlBaseTableReference) orderDateReference.getTableReference()).getTableId());
assertEquals("order_date", orderDateReference.getColumnName());
// Resolve Customer.CustomerId
@@ -651,7 +658,7 @@ void testResolveColumnsInInnerJoin() throws Exception {
SqlColumnReference customerIdReference = resolver.resolve(customerIdColumn);
assertNotNull(customerIdReference);
assertEquals("customer_id", customerIdReference.getColumnName());
- assertEquals(new SqlTableId("customers"), ((SqlBaseTableReference) customerIdReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"customers"), ((SqlBaseTableReference) customerIdReference.getTableReference()).getTableId());
resolver.exitCurrentStatementContext();
}
@@ -674,10 +681,10 @@ void testResolveColumnInSubqueryWithAlias() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("dept_id", reference.getColumnName());
assertEquals("(SELECT d.id AS dept_id FROM departments d WHERE d.department_name = 'HR') sub",
- ((SqlDerivedTableReference) reference.getTableReference()).getSelect().toString());
+ ((SqlDerivedTable) reference.getTableReference()).getSelect().toString());
resolver.exitCurrentStatementContext();
}
@@ -693,7 +700,7 @@ void testResolveColumnInNestedSubqueryNoAlliases() throws Exception {
SqlColumnReference reference = resolver.resolve(column);
assertNotNull(reference);
- assertTrue(reference.getTableReference() instanceof SqlDerivedTableReference);
+ assertTrue(reference.getTableReference() instanceof SqlDerivedTable);
assertEquals("first_name", reference.getColumnName());
resolver.exitCurrentStatementContext();
@@ -712,7 +719,7 @@ void testResolveOuterColumn() throws Exception {
assertNotNull(outerReference);
assertTrue(outerReference.getTableReference() instanceof SqlBaseTableReference);
assertEquals("first_name", outerReference.getColumnName());
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) outerReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) outerReference.getTableReference()).getTableId());
String innerSql = "SELECT 1 FROM departments WHERE first_name = department_name";
Select innerSelect = (Select) SqlParserUtils.parseSqlCommand(innerSql);
@@ -722,7 +729,7 @@ void testResolveOuterColumn() throws Exception {
assertNotNull(innerReference);
assertTrue(innerReference.getTableReference() instanceof SqlBaseTableReference);
assertEquals("first_name", innerReference.getColumnName());
- assertEquals(new SqlTableId("employees"), ((SqlBaseTableReference) innerReference.getTableReference()).getTableId());
+ assertEquals(new SqlTableId(null,null,"employees"), ((SqlBaseTableReference) innerReference.getTableReference()).getTableId());
resolver.exitCurrentStatementContext();
resolver.exitCurrentStatementContext();
@@ -742,8 +749,8 @@ void testResolveNullColumn() throws Exception {
SqlColumnReference columnReference = resolver.resolve(column);
assertNotNull(columnReference);
assertEquals("null_value", columnReference.getColumnName());
- assertTrue(columnReference.getTableReference() instanceof SqlDerivedTableReference);
- assertEquals("(SELECT NULL AS null_value) e", ((SqlDerivedTableReference) columnReference.getTableReference()).getSelect().toString());
+ assertTrue(columnReference.getTableReference() instanceof SqlDerivedTable);
+ assertEquals("(SELECT NULL AS null_value) e", ((SqlDerivedTable) columnReference.getTableReference()).getSelect().toString());
}
@Test
@@ -765,7 +772,7 @@ void testResolveColumnTableNotInSchema() throws Exception {
Assumptions.assumeTrue(this.schema.tables.stream()
.filter(t -> t.id.name.equals("Foo"))
- .count()==0);
+ .count() == 0);
String sql = "SELECT * FROM Foo";
@@ -785,7 +792,7 @@ void testResolveTableNotInSchema() throws Exception {
Assumptions.assumeTrue(this.schema.tables.stream()
.filter(t -> t.id.name.equals("Foo"))
- .count()==0);
+ .count() == 0);
String sql = "SELECT * FROM Foo";
@@ -798,4 +805,103 @@ void testResolveTableNotInSchema() throws Exception {
SqlColumnReference columnReference = resolver.resolve(column);
assertNull(columnReference);
}
+
+ @Test
+ void testCommonTableExpressionWithAliasForEmployees() throws JSQLParserException {
+ String sql = "WITH EmployeeCTE AS (SELECT first_name, salary FROM employees WHERE salary > 50000) " +
+ "SELECT e.first_name FROM EmployeeCTE e WHERE e.first_name='John' ";
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ resolver.enterStatementeContext(select);
+
+ Column column = new Column();
+ column.setColumnName("first_name");
+ column.setTable(new Table("e"));
+
+ SqlColumnReference columnReference = resolver.resolve(column);
+ assertNotNull(columnReference);
+ assertEquals("first_name", columnReference.getColumnName());
+ assertTrue(columnReference.getTableReference() instanceof SqlDerivedTable);
+ SqlDerivedTable derivedTableReference = (SqlDerivedTable) columnReference.getTableReference();
+ assertEquals("(SELECT first_name, salary FROM employees WHERE salary > 50000)", derivedTableReference.getSelect().toString());
+ }
+
+
+ @Test
+ void testResolveTableWithExplicitSchema() throws Exception {
+
+ String sql = "SELECT user_id FROM public.users";
+
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ resolver.enterStatementeContext(select);
+
+
+ Table publicUsers = new Table("public", "users");
+ SqlTableReference tableReference = resolver.resolve(publicUsers);
+ assertNotNull(tableReference);
+ assertTrue(tableReference instanceof SqlBaseTableReference);
+ SqlBaseTableReference baseTableReference = (SqlBaseTableReference) tableReference;
+
+ assertEquals("users", baseTableReference.getTableId().getTableName());
+ assertEquals("public", baseTableReference.getTableId().getSchemaName());
+ }
+
+
+ @Test
+ void testResolveColumnFromExplicitSchema() throws Exception {
+
+ String sql = "SELECT user_id FROM public.users";
+
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ resolver.enterStatementeContext(select);
+
+
+ Column userIdColumn = new Column(null, "user_id");
+ SqlColumnReference columnReference = resolver.resolve(userIdColumn);
+ assertNotNull(columnReference);
+ assertNotNull(columnReference.getTableReference());
+ assertTrue(columnReference.getTableReference() instanceof SqlBaseTableReference);
+ SqlBaseTableReference baseTableReference = (SqlBaseTableReference) columnReference.getTableReference();
+
+ assertEquals("user_id", columnReference.getColumnName());
+ assertEquals("users", baseTableReference.getTableId().getTableName());
+ assertEquals("public", baseTableReference.getTableId().getSchemaName());
+ }
+
+ @Test
+ void testResolveColumnFromImplicitSchema() throws Exception {
+
+ String sql = "SELECT user_id FROM users";
+
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ resolver.enterStatementeContext(select);
+
+ Column userIdColumn = new Column(null, "user_id");
+ SqlColumnReference columnReference = resolver.resolve(userIdColumn);
+ assertNotNull(columnReference);
+ assertNotNull(columnReference.getTableReference());
+ assertTrue(columnReference.getTableReference() instanceof SqlBaseTableReference);
+ SqlBaseTableReference baseTableReference = (SqlBaseTableReference) columnReference.getTableReference();
+
+ assertEquals("user_id", columnReference.getColumnName());
+ assertEquals("users", baseTableReference.getTableId().getTableName());
+ assertEquals("public", baseTableReference.getTableId().getSchemaName());
+ }
+
+ @Test
+ void testResolveTableAlias() throws Exception {
+
+ String sql = "SELECT u.user_id FROM public.users u";
+
+ Select select = (Select) CCJSqlParserUtil.parse(sql);
+ resolver.enterStatementeContext(select);
+
+ Table tableAlias = new Table(null, "u");
+ SqlTableReference tableReference = resolver.resolve(tableAlias);
+ assertNotNull(tableReference);
+ assertTrue(tableReference instanceof SqlBaseTableReference);
+ SqlBaseTableReference baseTableReference = (SqlBaseTableReference) tableReference;
+
+ assertEquals("users", baseTableReference.getTableId().getTableName());
+ assertEquals("public", baseTableReference.getTableId().getSchemaName());
+ }
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunctionTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunctionTest.java
new file mode 100644
index 0000000000..6e22c92b29
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/CoalesceFunctionTest.java
@@ -0,0 +1,102 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CoalesceFunctionTest {
+
+ @Test
+ void testReturnsFirstNonNullArgument() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate("first", "second", "third");
+ assertEquals("first", result);
+ }
+
+ @Test
+ void testReturnsSecondArgumentWhenFirstIsNull() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, "second", "third");
+ assertEquals("second", result);
+ }
+
+ @Test
+ void testReturnsLastArgumentWhenAllPreviousAreNull() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, null, null, "last");
+ assertEquals("last", result);
+ }
+
+ @Test
+ void testReturnsNullWhenAllArgumentsAreNull() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, null, null);
+ assertNull(result);
+ }
+
+ @Test
+ void testWorksWithSingleNonNullArgument() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate("only");
+ assertEquals("only", result);
+ }
+
+ @Test
+ void testReturnsNullWithSingleNullArgument() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate((Object) null);
+ assertNull(result);
+ }
+
+ @Test
+ void testThrowsExceptionWhenNoArguments() {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new CoalesceFunction().evaluate()
+ );
+ assertEquals("COALESCE() function requires at least one argument", exception.getMessage());
+ }
+
+ @Test
+ void testWorksWithIntegerArguments() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, 42, 100);
+ assertEquals(42, result);
+ }
+
+ @Test
+ void testWorksWithMixedTypes() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, null, 123, "string");
+ assertEquals(123, result);
+ }
+
+ @Test
+ void testWorksWithBooleanArguments() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, true, false);
+ assertEquals(true, result);
+ }
+
+ @Test
+ void testReturnsFirstNonNullEvenIfZero() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, 0, 42);
+ assertEquals(0, result);
+ }
+
+ @Test
+ void testReturnsFirstNonNullEvenIfEmptyString() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, "", "non-empty");
+ assertEquals("", result);
+ }
+
+ @Test
+ void testReturnsFirstNonNullEvenIfFalse() {
+ final CoalesceFunction coalesceFunction = new CoalesceFunction();
+ Object result = coalesceFunction.evaluate(null, false, true);
+ assertEquals(false, result);
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateFunctionTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateFunctionTest.java
new file mode 100644
index 0000000000..fb2c533278
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateFunctionTest.java
@@ -0,0 +1,327 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DateFunctionTest {
+
+ @Test
+ void testEvaluateWithNullArgument() {
+ Object result = new DateFunction().evaluate((Object) null);
+ assertNull(result);
+ }
+
+ @Test
+ void testEvaluateWithNoArguments() {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new DateFunction().evaluate()
+ );
+ assertTrue(exception.getMessage().contains("exactly one argument"));
+ }
+
+ @Test
+ void testEvaluateWithMultipleArguments() {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new DateFunction().evaluate("2023-12-25", "2023-12-26")
+ );
+ assertTrue(exception.getMessage().contains("exactly one argument"));
+ }
+
+ @Test
+ void testEvaluateWithSqlDate() {
+ Date inputDate = Date.valueOf("2023-12-25");
+ Object result = new DateFunction().evaluate(inputDate);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(inputDate, result);
+ }
+
+ @Test
+ void testEvaluateWithSqlTimestamp() {
+ Timestamp timestamp = Timestamp.valueOf("2023-12-25 14:30:45.123");
+ Object result = new DateFunction().evaluate(timestamp);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithSqlTimestampAtMidnight() {
+ Timestamp timestamp = Timestamp.valueOf("2023-12-25 00:00:00.000");
+ Object result = new DateFunction().evaluate(timestamp);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithSqlTimestampEndOfDay() {
+ Timestamp timestamp = Timestamp.valueOf("2023-12-25 23:59:59.999");
+ Object result = new DateFunction().evaluate(timestamp);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithJavaUtilDate() {
+ LocalDateTime localDateTime = LocalDateTime.of(2023, 12, 25, 14, 30, 45);
+ java.util.Date utilDate = java.sql.Timestamp.valueOf(localDateTime);
+
+ Object result = new DateFunction().evaluate(utilDate);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithSqlTime() {
+ // java.sql.Time extends java.util.Date
+ Time time = Time.valueOf("14:30:45");
+
+ Object result = new DateFunction().evaluate(time);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ // The date will be derived from the epoch date or system default
+ }
+
+ @Test
+ void testEvaluateWithStringIsoFormat() {
+ String dateString = "2023-12-25";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithStringDateTimeFormat() {
+ String dateString = "2023-12-25 14:30:45";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithStringIsoDateTimeFormat() {
+ String dateString = "2023-12-25T14:30:45";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithStringWithMilliseconds() {
+ String dateString = "2023-12-25 14:30:45.123";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithStringWithMicroseconds() {
+ String dateString = "2023-12-25 14:30:45.123456";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithStringWithWhitespace() {
+ String dateString = " 2023-12-25 ";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateWithEmptyString() {
+ String dateString = "";
+ Object result = new DateFunction().evaluate(dateString);
+ assertNull(result);
+ }
+
+ @Test
+ void testEvaluateWithWhitespaceOnlyString() {
+ String dateString = " ";
+ Object result = new DateFunction().evaluate(dateString);
+ assertNull(result);
+ }
+
+ @Test
+ void testEvaluateWithInvalidStringFormat() {
+ String dateString = "25-12-2023";
+ assertThrows(IllegalArgumentException.class, () -> new DateFunction().evaluate(dateString));
+ }
+
+ @Test
+ void testEvaluateWithInvalidDate() {
+ String dateString = "2023-02-30";
+ assertThrows(IllegalArgumentException.class, () -> new DateFunction().evaluate(dateString));
+ }
+
+ @Test
+ void testEvaluateWithUnsupportedType() {
+ Integer unsupportedArg = 12345;
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new DateFunction().evaluate(unsupportedArg)
+ );
+ assertTrue(exception.getMessage().contains("Unsupported type for DATE()"));
+ }
+
+ @Test
+ void testEvaluateWithLeapYearDate() {
+ String dateString = "2024-02-29";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2024-02-29"), result);
+ }
+
+ @Test
+ void testEvaluateWithDateStartOfYear() {
+ String dateString = "2023-01-01";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-01-01"), result);
+ }
+
+ @Test
+ void testEvaluateWithDateEndOfYear() {
+ String dateString = "2023-12-31";
+ Object result = new DateFunction().evaluate(dateString);
+
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-31"), result);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-01-01",
+ "2023-06-15",
+ "2023-12-31",
+ "2024-02-29",
+ "2000-01-01",
+ "1999-12-31"
+ })
+ void testEvaluateWithVariousValidDates(String dateString) {
+ Object result = new DateFunction().evaluate(dateString);
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-12-25 00:00:00",
+ "2023-12-25 12:00:00",
+ "2023-12-25 23:59:59",
+ "2023-12-25T14:30:45",
+ "2023-12-25 14:30:45.123",
+ "2023-12-25 14:30:45.123456"
+ })
+ void testEvaluateWithVariousDateTimeFormats(String dateString) {
+ Object result = new DateFunction().evaluate(dateString);
+ assertNotNull(result);
+ assertTrue(result instanceof Date);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testEvaluateExtractsOnlyDateFromTimestamp() {
+ Timestamp timestamp = Timestamp.valueOf("2023-12-25 14:30:45.123456789");
+ Object result = new DateFunction().evaluate(timestamp);
+
+ assertNotNull(result);
+ Date date = (Date) result;
+ LocalDate localDate = date.toLocalDate();
+
+ assertEquals(2023, localDate.getYear());
+ assertEquals(12, localDate.getMonthValue());
+ assertEquals(25, localDate.getDayOfMonth());
+ }
+
+ @Test
+ void testEvaluateConsistencyAcrossTypes() {
+ Date sqlDate = Date.valueOf("2023-12-25");
+ Timestamp timestamp = Timestamp.valueOf("2023-12-25 14:30:45");
+ String isoString = "2023-12-25";
+ String dateTimeString = "2023-12-25 14:30:45";
+
+ Object result1 = new DateFunction().evaluate(sqlDate);
+ Object result2 = new DateFunction().evaluate(timestamp);
+ Object result3 = new DateFunction().evaluate(isoString);
+ Object result4 = new DateFunction().evaluate(dateTimeString);
+
+ assertEquals(result1, result2);
+ assertEquals(result2, result3);
+ assertEquals(result3, result4);
+ }
+
+ @Test
+ void testEvaluateWithBoundaryDates() {
+ // Test minimum date
+ Date minDate = Date.valueOf("0001-01-01");
+ Object result1 = new DateFunction().evaluate(minDate);
+ assertEquals(minDate, result1);
+
+ // Test far future date
+ Date maxDate = Date.valueOf("9999-12-31");
+ Object result2 = new DateFunction().evaluate(maxDate);
+ assertEquals(maxDate, result2);
+ }
+
+ @Test
+ void testEvaluateWithDoubleType() {
+ Double doubleArg = 123.45;
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new DateFunction().evaluate(doubleArg)
+ );
+ assertTrue(exception.getMessage().contains("Unsupported type"));
+ }
+
+ @Test
+ void testEvaluateWithBooleanType() {
+ Boolean booleanArg = true;
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new DateFunction().evaluate(booleanArg)
+ );
+ assertTrue(exception.getMessage().contains("Unsupported type"));
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunctionTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunctionTest.java
new file mode 100644
index 0000000000..df1df586e8
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/DateTruncFunctionTest.java
@@ -0,0 +1,193 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.evomaster.client.java.sql.heuristic.ConversionHelper;
+import org.evomaster.client.java.sql.internal.ColumnTypeParser;
+import org.junit.jupiter.api.Test;
+
+import java.sql.Timestamp;
+import java.time.*;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DateTruncFunctionTest {
+
+ @Test
+ void localDateTimeTruncateToMinute() {
+ LocalDateTime dt = LocalDateTime.of(2020, 5, 10, 12, 34, 56, 789_000_000);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("minute", dt);
+ assertTrue(res instanceof LocalDateTime);
+ assertEquals(LocalDateTime.of(2020, 5, 10, 12, 34, 0, 0), res);
+ }
+
+ @Test
+ void localTimeTruncateToHour() {
+ LocalTime t = LocalTime.of(3, 45, 12, 123_000_000);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("hour", t);
+ assertTrue(res instanceof LocalTime);
+ assertEquals(LocalTime.of(3, 0, 0), res);
+ }
+
+ @Test
+ void instantTruncateToDay() {
+ Instant i = Instant.parse("2020-05-10T12:34:56.789Z");
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("day", i);
+ assertTrue(res instanceof Instant);
+ assertEquals(Instant.parse("2020-05-10T00:00:00Z"), res);
+ }
+
+ @Test
+ void offsetDateTimeTruncateToMonthKeepsOffset() {
+ OffsetDateTime odt = OffsetDateTime.of(2021, 3, 15, 10, 11, 12, 345_000_000, ZoneOffset.ofHours(-5));
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("month", odt);
+ assertTrue(res instanceof OffsetDateTime);
+ assertEquals(OffsetDateTime.of(2021, 3, 1, 0, 0, 0, 0, ZoneOffset.ofHours(-5)), res);
+ }
+
+ @Test
+ void zonedDateTimeTruncateToYearPreservesZone() {
+ ZonedDateTime zdt = ZonedDateTime.of(2019, 7, 20, 15, 30, 45, 999_000_000, ZoneId.of("Europe/Rome"));
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("year", zdt);
+ assertTrue(res instanceof ZonedDateTime);
+ ZonedDateTime expected = ZonedDateTime.of(LocalDateTime.of(2019, 1, 1, 0, 0), ZoneId.of("Europe/Rome"));
+ assertEquals(expected, res);
+ }
+
+ @Test
+ void localDateTruncateToMonth() {
+ LocalDate d = LocalDate.of(2019, 12, 25);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("month", d);
+ assertTrue(res instanceof LocalDate);
+ assertEquals(LocalDate.of(2019, 12, 1), res);
+ }
+
+ @Test
+ void offsetTimeTruncateToSecond() {
+ OffsetTime t = OffsetTime.of(12, 30, 45, 987_000_000, ZoneOffset.ofHours(2));
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("second", t);
+ assertTrue(res instanceof OffsetTime);
+ assertEquals(OffsetTime.of(12, 30, 45, 0, ZoneOffset.ofHours(2)), res);
+ }
+
+ @Test
+ void unsupportedUnitForLocalTimeThrows() {
+ LocalTime t = LocalTime.of(1, 2, 3);
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(IllegalArgumentException.class, () -> f.evaluate("month", t));
+ }
+
+ @Test
+ void nullTimestampReturnsNull() {
+ DateTruncFunction f = new DateTruncFunction();
+ assertNull(f.evaluate("day", (Object) null));
+ }
+
+ @Test
+ void unsupportedTemporalAccessorTypeThrows() {
+ // Year implements TemporalAccessor but is not handled by DateTrunc
+ Year y = Year.of(2020);
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(UnsupportedOperationException.class, () -> f.evaluate("day", y));
+ }
+
+ @Test
+ void localDateTruncateToDayReturnsSameDate() {
+ LocalDate d = LocalDate.of(2021, 8, 17);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("day", d);
+ assertTrue(res instanceof LocalDate);
+ assertEquals(LocalDate.of(2021, 8, 17), res);
+ }
+
+ @Test
+ void localDateTruncateToYear() {
+ LocalDate d = LocalDate.of(2018, 6, 30);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("year", d);
+ assertTrue(res instanceof LocalDate);
+ assertEquals(LocalDate.of(2018, 1, 1), res);
+ }
+
+ @Test
+ void localDateUnsupportedUnitThrows() {
+ LocalDate d = LocalDate.of(2020, 1, 1);
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(IllegalArgumentException.class, () -> f.evaluate("second", d));
+ }
+
+ @Test
+ void localDateTimeTruncateToSecond() {
+ LocalDateTime dt = LocalDateTime.of(2022, 1, 2, 3, 4, 5, 123_456_789);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("second", dt);
+ assertTrue(res instanceof LocalDateTime);
+ assertEquals(LocalDateTime.of(2022, 1, 2, 3, 4, 5, 0), res);
+ }
+
+ @Test
+ void localDateTimeTruncateToMonth() {
+ LocalDateTime dt = LocalDateTime.of(2022, 7, 15, 10, 11, 12, 345_000_000);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("month", dt);
+ assertTrue(res instanceof LocalDateTime);
+ assertEquals(LocalDateTime.of(2022, 7, 1, 0, 0), res);
+ }
+
+ @Test
+ void localDateTimeTruncateToYear() {
+ LocalDateTime dt = LocalDateTime.of(1999, 12, 31, 23, 59, 59, 999_000_000);
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("year", dt);
+ assertTrue(res instanceof LocalDateTime);
+ assertEquals(LocalDateTime.of(1999, 1, 1, 0, 0), res);
+ }
+
+ @Test
+ void localDateTimeUnsupportedUnitThrows() {
+ LocalDateTime dt = LocalDateTime.of(2020, 2, 29, 12, 0);
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(IllegalArgumentException.class, () -> f.evaluate("unsupported_unit", dt));
+ }
+
+
+ @Test
+ void testTruncateHourTimestamp() {
+ final Timestamp timestamp = Timestamp.valueOf("2025-01-14 12:30:45");
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("hour", timestamp);
+ assertTrue(res instanceof java.util.Date);
+ Instant expectedInstant = Timestamp.valueOf("2025-01-14 12:00:00").toInstant();
+ assertEquals(expectedInstant, ((Date) res).toInstant());
+ }
+
+ @Test
+ void testTruncatDayTimestamp() {
+ final Timestamp timestamp = Timestamp.valueOf("2025-01-14 12:30:45");
+ DateTruncFunction f = new DateTruncFunction();
+ Object res = f.evaluate("day", timestamp);
+ assertTrue(res instanceof java.util.Date);
+ Instant expectedInstant = Instant.parse("2025-01-14T00:00:00Z");
+ assertEquals(expectedInstant, ((Date) res).toInstant());
+ }
+
+ @Test
+ void testTruncateEmptyArguments() {
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(IllegalArgumentException.class, () -> f.evaluate());
+ }
+
+ @Test
+ void testTruncateMoreArgumentsThanExpected() {
+ LocalTime t = LocalTime.of(3, 45, 12, 123_000_000);
+ DateTruncFunction f = new DateTruncFunction();
+ assertThrows(IllegalArgumentException.class, () -> f.evaluate("hour", t, "extraArgument"));
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/LowerFunctionTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/LowerFunctionTest.java
new file mode 100644
index 0000000000..63d0b62b39
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/heuristic/function/LowerFunctionTest.java
@@ -0,0 +1,42 @@
+package org.evomaster.client.java.sql.heuristic.function;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class LowerFunctionTest {
+
+ @Test
+ void testEvaluateWithValidString() {
+ LowerFunction lowerFunction = new LowerFunction();
+ assertEquals("test", lowerFunction.evaluate("TEST"));
+ assertEquals("test", lowerFunction.evaluate("test"));
+ assertEquals("123", lowerFunction.evaluate("123"));
+ assertEquals("", lowerFunction.evaluate(""));
+ }
+
+ @Test
+ void testEvaluateWithNull() {
+ LowerFunction lowerFunction = new LowerFunction();
+ assertNull(lowerFunction.evaluate((Object) null));
+ }
+
+ @Test
+ void testEvaluateWithNoArguments() {
+ LowerFunction lowerFunction = new LowerFunction();
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> lowerFunction.evaluate());
+ assertTrue(exception.getMessage().contains("exactly one argument"));
+ }
+
+ @Test
+ void testEvaluateWithTooManyArguments() {
+ LowerFunction lowerFunction = new LowerFunction();
+ assertThrows(IllegalArgumentException.class, () -> lowerFunction.evaluate("a", "b"));
+ }
+
+ @Test
+ void testEvaluateWithNonStringArgument() {
+ LowerFunction lowerFunction = new LowerFunction();
+ assertThrows(IllegalArgumentException.class, () -> lowerFunction.evaluate(123));
+ }
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzerTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzerTest.java
index 78b009c578..e0e3708cc0 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzerTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/ColumnTableAnalyzerTest.java
@@ -26,9 +26,20 @@ public void testInsertWithQualifier() {
Map.Entry> data = ColumnTableAnalyzer.getInsertedDataFields(sql);
- assertEquals(new SqlTableId("Bar.Foo"), data.getKey());
+ assertEquals("bar", data.getKey().getSchemaName());
+ assertEquals("foo", data.getKey().getTableName());
}
+ @Test
+ public void testSimpleUpdateWithQualifier() {
+
+ String sql = "UPDATE Bar.Foo SET x=42";
+
+ Map.Entry> data = ColumnTableAnalyzer.getUpdatedDataFields(sql);
+
+ assertEquals("bar", data.getKey().getSchemaName());
+ assertEquals("foo", data.getKey().getTableName());
+ }
@Test
public void testInsertInSimpleTable() {
@@ -37,7 +48,7 @@ public void testInsertInSimpleTable() {
Map.Entry> data = ColumnTableAnalyzer.getInsertedDataFields(sql);
- assertEquals(new SqlTableId("Foo"), data.getKey());
+ assertEquals(new SqlTableId(null,null,"Foo"), data.getKey());
}
@@ -48,7 +59,7 @@ public void testUpdateInSimpleTable() {
Map.Entry> data = ColumnTableAnalyzer.getUpdatedDataFields(sql);
- assertEquals(new SqlTableId("Foo"), data.getKey());
+ assertEquals(new SqlTableId(null,null,"Foo"), data.getKey());
//TODO check on actual fields when implemented
}
@@ -66,7 +77,7 @@ public void testDeleteSimpleTable() {
SqlTableId deletedTableId = ColumnTableAnalyzer.getDeletedTable(sql);
assertNotNull(deletedTableId);
- assertEquals(new SqlTableId("Foo"), deletedTableId);
+ assertEquals(new SqlTableId(null,null,"Foo"), deletedTableId);
}
@Test
@@ -77,13 +88,15 @@ public void testDeleteWithQualifier() {
DbInfoDto schema = new DbInfoDto();
TableDto tableDto = new TableDto();
tableDto.id = new TableIdDto();
- tableDto.id.name = "v1.Foo";
+ tableDto.id.schema = "v1";
+ tableDto.id.name = "Foo";
schema.tables.add(tableDto);
SqlTableId deletedTableId = ColumnTableAnalyzer.getDeletedTable(sql);
assertNotNull(deletedTableId);
- assertEquals(new SqlTableId("v1.Foo"), deletedTableId);
+ assertEquals("v1", deletedTableId.getSchemaName());
+ assertEquals("foo", deletedTableId.getTableName());
}
@@ -94,7 +107,7 @@ public void testSelectReadAllFromSingleTable() {
Map> data = ColumnTableAnalyzer.getSelectReadDataFields(select);
assertEquals(1, data.size());
- Set columns = data.get(new SqlTableId("Foo"));
+ Set columns = data.get(new SqlTableId(null,null,"Foo"));
assertEquals(1, columns.size());
assertTrue(columns.contains(new SqlColumnId("*")));
@@ -112,13 +125,13 @@ public void testSelectReadFromJoinedTables() {
assertEquals(2, data.size());
- final Set ordersColumns = data.get(new SqlTableId("Orders"));
+ final Set ordersColumns = data.get(new SqlTableId(null,null,"Orders"));
//FIXME: once supporting actual fields instead of *
assertEquals(1, ordersColumns.size());
assertTrue(ordersColumns.contains(new SqlColumnId("*")));
- final Set customersColumns = data.get(new SqlTableId("Customers"));
+ final Set customersColumns = data.get(new SqlTableId(null,null,"Customers"));
//FIXME: once supporting actual fields instead of *
assertEquals(1, customersColumns.size());
@@ -126,5 +139,4 @@ public void testSelectReadFromJoinedTables() {
}
-
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/HeuristicsCalculatorWithInsertionDtoTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/HeuristicsCalculatorWithInsertionDtoTest.java
index de9d54cb23..76106fbebb 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/HeuristicsCalculatorWithInsertionDtoTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/HeuristicsCalculatorWithInsertionDtoTest.java
@@ -31,7 +31,7 @@ private DbInfoDto createSchemaDtoWithFooTableAndXColumn(String xDataType){
return schemaDto;
}
- private final Map> selectWhereXofFoo = new HashMap>(){{put(new SqlTableId("Foo"), Collections.singleton(new SqlColumnId("x")));}};
+ private final Map> selectWhereXofFoo = new HashMap>(){{put(new SqlTableId(null,null,"Foo"), Collections.singleton(new SqlColumnId("x")));}};
@Test
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/QueryResultTransformerTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/QueryResultTransformerTest.java
index 7fcb700155..b959a8e848 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/QueryResultTransformerTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/QueryResultTransformerTest.java
@@ -67,8 +67,8 @@ public void testConvertInsertionDtosToQueryResults(){
.dtos();
Map> columns = new HashMap>() {{
- put(new SqlTableId("FooTable"), new HashSet<>(Arrays.asList(new SqlColumnId("fooA"), new SqlColumnId("fooC"))));
- put(new SqlTableId("BarTable"), new HashSet<>(Arrays.asList(new SqlColumnId("barB"), new SqlColumnId("barC"), new SqlColumnId("barD"), new SqlColumnId("barE"))));
+ put(new SqlTableId(null,null,"FooTable"), new HashSet<>(Arrays.asList(new SqlColumnId("fooA"), new SqlColumnId("fooC"))));
+ put(new SqlTableId(null,null,"BarTable"), new HashSet<>(Arrays.asList(new SqlColumnId("barB"), new SqlColumnId("barC"), new SqlColumnId("barD"), new SqlColumnId("barE"))));
}};
@@ -255,13 +255,13 @@ public void testTranslateInsertionDtos() {
DbInfoDto schema = createSchema();
Map> columns = new HashMap<>();
- columns.put(new SqlTableId("employees"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"employees"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("income"),
new SqlColumnId("department_id"))));
- columns.put(new SqlTableId("departments"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"departments"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("location_id"))));
@@ -295,13 +295,13 @@ public void testTranslateInsertionDtosNoInsertions() {
DbInfoDto schema = createSchema();
Map> columns = new HashMap<>();
- columns.put(new SqlTableId("employees"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"employees"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("income"),
new SqlColumnId("department_id"))));
- columns.put(new SqlTableId("departments"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"departments"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("location_id"))));
@@ -337,13 +337,13 @@ public void testFillWithNull() {
DbInfoDto schema = createSchema();
Map> columns = new HashMap<>();
- columns.put(new SqlTableId("employees"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"employees"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("income"),
new SqlColumnId("department_id"))));
- columns.put(new SqlTableId("departments"), new HashSet<>(Arrays.asList(
+ columns.put(new SqlTableId(null,null,"departments"), new HashSet<>(Arrays.asList(
new SqlColumnId("id"),
new SqlColumnId("name"),
new SqlColumnId("location_id"))));
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlDateTimeParserTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlDateTimeParserTest.java
new file mode 100644
index 0000000000..afcfa3c7fe
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlDateTimeParserTest.java
@@ -0,0 +1,298 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.sql.Date;
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SqlDateTimeParserTest {
+
+ @Test
+ void testParseIsoLocalDate() {
+ String dateString = "2023-12-25";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateWithLeadingZeros() {
+ String dateString = "2023-01-05";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-01-05"), result);
+ }
+
+ @Test
+ void testParseDateLeapYear() {
+ String dateString = "2024-02-29";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2024-02-29"), result);
+ }
+
+ @Test
+ void testParseDateEndOfYear() {
+ String dateString = "2023-12-31";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-31"), result);
+ }
+
+ @Test
+ void testParseDateStartOfYear() {
+ String dateString = "2023-01-01";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-01-01"), result);
+ }
+
+ @Test
+ void testParseIsoLocalDateTime() {
+ // Format: yyyy-MM-ddTHH:mm:ss
+ String dateString = "2023-12-25T14:30:45";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseMySqlClassicFormat() {
+ // Format: yyyy-MM-dd HH:mm:ss
+ String dateString = "2023-12-25 14:30:45";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateTimeWithMilliseconds() {
+ // Format: yyyy-MM-dd HH:mm:ss.SSS
+ String dateString = "2023-12-25 14:30:45.123";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateTimeWithMicroseconds() {
+ // Format: yyyy-MM-dd HH:mm:ss.SSSSSS
+ String dateString = "2023-12-25 14:30:45.123456";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateTimeWithMinutes() {
+ // Format: yyyy-MM-dd HH:mm
+ String dateString = "2023-12-25 14:30";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateTimeAtMidnight() {
+ String dateString = "2023-12-25 00:00:00";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseDateTimeEndOfDay() {
+ String dateString = "2023-12-25 23:59:59";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @Test
+ void testParseNullDate() {
+ assertThrows(NullPointerException.class, () -> new SqlDateTimeParser().parseDate(null));
+ }
+
+ @Test
+ void testParseEmptyDate() {
+ String dateString = "";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidDateFormat() {
+ String dateString = "25-12-2023";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidDate() {
+ String dateString = "2023-02-30";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidMonth() {
+ String dateString = "2023-13-01";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidDay() {
+ String dateString = "2023-12-32";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidLeapYear() {
+ String dateString = "2023-02-29";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidTime() {
+ String dateString = "2023-12-25 25:00:00";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidMinutes() {
+ String dateString = "2023-12-25 14:65:00";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseInvalidSeconds() {
+ String dateString = "2023-12-25 14:30:70";
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseUnsupportedFormat() {
+ String dateString = "12/25/2023";
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> new SqlDateTimeParser().parseDate(dateString)
+ );
+ assertTrue(exception.getMessage().contains("unsupported date format"));
+ }
+
+ @Test
+ void testParseDateBoundaryMinimum() {
+ String dateString = "0001-01-01";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("0001-01-01"), result);
+ }
+
+ @Test
+ void testParseDateBoundaryMaximum() {
+ String dateString = "9999-12-31";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ assertEquals(Date.valueOf("9999-12-31"), result);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-01-01",
+ "2023-06-15",
+ "2023-12-31",
+ "2024-02-29",
+ "2000-01-01",
+ "1999-12-31"
+ })
+ void testParseVariousValidDates(String dateString) {
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+ assertNotNull(result);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-12-25 00:00:00",
+ "2023-12-25 12:00:00",
+ "2023-12-25 23:59:59",
+ "2023-12-25T14:30:45",
+ "2023-12-25 14:30:45.123",
+ "2023-12-25 14:30:45.123456",
+ "2023-12-25 14:30"
+ })
+ void testParseVariousValidDateTimeFormats(String dateString) {
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+ assertNotNull(result);
+ assertEquals(Date.valueOf("2023-12-25"), result);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "invalid",
+ "2023/12/25",
+ "25-12-2023",
+ "2023-13-01",
+ "2023-12-32",
+ "2023-02-30",
+ "2023-12-25 25:00:00",
+ "not a date"
+ })
+ void testParseVariousInvalidFormats(String dateString) {
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseDateWithWhitespace() {
+ String dateString = " 2023-12-25 ";
+ // Trimming is not done by the parser, so this should fail
+ assertThrows(IllegalArgumentException.class, () -> new SqlDateTimeParser().parseDate(dateString));
+ }
+
+ @Test
+ void testParseDateExtractsOnlyDate() {
+ String dateString = "2023-12-25 14:30:45";
+ Date result = new SqlDateTimeParser().parseDate(dateString);
+
+ assertNotNull(result);
+ // Verify that only the date part is extracted, not time
+ assertEquals(Date.valueOf("2023-12-25"), result);
+
+ // Verify time component is not included
+ LocalDate localDate = result.toLocalDate();
+ assertEquals(2023, localDate.getYear());
+ assertEquals(12, localDate.getMonthValue());
+ assertEquals(25, localDate.getDayOfMonth());
+ }
+
+ @Test
+ void testParseMultipleFormatsForSameDate() {
+ String isoFormat = "2023-12-25";
+ String dateTimeFormat = "2023-12-25 12:00:00";
+ String isoDateTimeFormat = "2023-12-25T12:00:00";
+
+ Date result1 = new SqlDateTimeParser().parseDate(isoFormat);
+ Date result2 = new SqlDateTimeParser().parseDate(dateTimeFormat);
+ Date result3 = new SqlDateTimeParser().parseDate(isoDateTimeFormat);
+
+ // All should result in the same date
+ assertEquals(result1, result2);
+ assertEquals(result2, result3);
+ assertEquals(result1, result3);
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHandlerTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHandlerTest.java
index 82bd782728..af4acc0ac5 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHandlerTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlHandlerTest.java
@@ -4,10 +4,7 @@
import net.sf.jsqlparser.statement.Statement;
import org.evomaster.client.java.controller.api.dto.database.execution.SqlExecutionLogDto;
import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto;
-import org.evomaster.client.java.controller.api.dto.database.schema.ColumnDto;
-import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto;
-import org.evomaster.client.java.controller.api.dto.database.schema.TableDto;
-import org.evomaster.client.java.controller.api.dto.database.schema.TableIdDto;
+import org.evomaster.client.java.controller.api.dto.database.schema.*;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -201,6 +198,7 @@ private static ColumnDto createColumnDto(String tableName, String columnName, St
schema.tables.add(employeesTable);
schema.tables.add(departmentsTable);
schema.tables.add(locationsTable);
+ schema.databaseType = DatabaseType.POSTGRES;
return schema;
}
@@ -424,4 +422,57 @@ public void testGetSqlDistancesNoInsertions() {
assertNotEquals(0, distances.get(0).sqlDistanceWithMetrics.sqlDistance);
}
+ @Test
+ public void testExtractColumns() throws Exception {
+ String select = "SELECT id,name FROM Employees e WHERE e.income >1000;";
+ Statement stmt = CCJSqlParserUtil.parse(select);
+
+ final SqlHandler sqlHandler = new SqlHandler(null);
+ final DbInfoDto schema = createSchema();
+ sqlHandler.setSchema(schema);
+ Map> columnsInvolvedInWhere = sqlHandler.extractColumnsInvolvedInWhere(stmt);
+
+ assertEquals(1, columnsInvolvedInWhere.keySet().size());
+ final SqlTableId employeesTable = new SqlTableId(null,null,"employees");
+ assertTrue(columnsInvolvedInWhere.containsKey(employeesTable));
+
+ assertEquals(1, columnsInvolvedInWhere.get(employeesTable).size());
+ final SqlColumnId incomeColumn = new SqlColumnId("income");
+ assertTrue(columnsInvolvedInWhere.get(employeesTable).contains(incomeColumn));
+ }
+
+ @Test
+ public void testExtractColumnsWithSchema() throws Exception {
+ TableDto usertTableDto = new TableDto();
+ usertTableDto.id = new TableIdDto();
+ usertTableDto.id.schema = "public";
+ usertTableDto.id.name = "users";
+ usertTableDto.columns.add(createColumnDto("users", "user_id", "INTEGER"));
+ usertTableDto.columns.add(createColumnDto("users", "income", "INTEGER"));
+
+ DbInfoDto schema = new DbInfoDto();
+ schema.tables.add(usertTableDto);
+ schema.databaseType = DatabaseType.POSTGRES;
+
+ String select = "SELECT user_id FROM public.users u WHERE u.income >1000;";
+ Statement stmt = CCJSqlParserUtil.parse(select);
+
+ final SqlHandler sqlHandler = new SqlHandler(null);
+ sqlHandler.setSchema(schema);
+ Map> columnsInvolvedInWhere = sqlHandler.extractColumnsInvolvedInWhere(stmt);
+
+ assertEquals(1, columnsInvolvedInWhere.keySet().size());
+ /**
+ * The extraction of columns implemented in extractColumnsInvolvedInWhere() is legacy.
+ * Therefore, it does not keep the schema name when the table name is extracted.
+ */
+ final SqlTableId usersTableId = new SqlTableId(null, null, "users");
+ assertTrue(columnsInvolvedInWhere.containsKey(usersTableId));
+
+ assertEquals(1, columnsInvolvedInWhere.get(usersTableId).size());
+ final SqlColumnId incomeColumn = new SqlColumnId("income");
+ assertTrue(columnsInvolvedInWhere.get(usersTableId).contains(incomeColumn));
+ }
+
+
}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlSelectBuilderTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlSelectBuilderTest.java
new file mode 100644
index 0000000000..e4517f0a9e
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlSelectBuilderTest.java
@@ -0,0 +1,127 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SqlSelectBuilderTest {
+
+ @Test
+ void testBuildSelectWithSingleColumn() {
+ SqlTableId tableId = new SqlTableId(null, null, "users");
+ List columnIds = Collections.singletonList(new SqlColumnId("id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.H2, tableId, columnIds);
+
+ assertEquals("SELECT id FROM users", result);
+ }
+
+ @Test
+ void testBuildSelectWithMultipleColumns() {
+ SqlTableId tableId = new SqlTableId(null, null, "employees");
+ List columnIds = Arrays.asList(
+ new SqlColumnId("id"),
+ new SqlColumnId("name"),
+ new SqlColumnId("salary")
+ );
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.POSTGRES, tableId, columnIds);
+
+ assertEquals("SELECT id, name, salary FROM employees", result);
+ }
+
+ @Test
+ void testBuildSelectWithNullColumnList() {
+ SqlTableId tableId = new SqlTableId(null, null, "users");
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ SqlSelectBuilder.buildSelect(DatabaseType.H2, tableId, null);
+ });
+ }
+
+ @Test
+ void testBuildSelectWithEmptyColumnList() {
+ SqlTableId tableId = new SqlTableId(null, null, "users");
+ List columnIds = Collections.emptyList();
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ SqlSelectBuilder.buildSelect(DatabaseType.H2, tableId, columnIds);
+ });
+ }
+
+ @Test
+ void testBuildSelectWithSchemaName() {
+ SqlTableId tableId = new SqlTableId(null, "public", "customers");
+ List columnIds = Collections.singletonList(new SqlColumnId("email"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.POSTGRES, tableId, columnIds);
+
+ assertEquals("SELECT email FROM public.customers", result);
+ }
+
+ @Test
+ void testBuildSelectWithCatalogAndSchema() {
+ SqlTableId tableId = new SqlTableId("mydb", "public", "orders");
+ List columnIds = Collections.singletonList(new SqlColumnId("order_id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.POSTGRES, tableId, columnIds);
+
+ assertEquals("SELECT order_id FROM mydb.public.orders", result);
+ }
+
+ @Test
+ void testBuildSelectMySQLWithCatalog() {
+ SqlTableId tableId = new SqlTableId("mydb", "ignored_schema", "products");
+ List columnIds = Collections.singletonList(new SqlColumnId("product_id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.MYSQL, tableId, columnIds);
+
+ assertEquals("SELECT product_id FROM mydb.products", result);
+ }
+
+ @Test
+ void testBuildSelectMySQLWithSchemaOnly() {
+ SqlTableId tableId = new SqlTableId(null, "myschema", "categories");
+ List columnIds = Collections.singletonList(new SqlColumnId("category_id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.MYSQL, tableId, columnIds);
+
+ assertEquals("SELECT category_id FROM myschema.categories", result);
+ }
+
+ @Test
+ void testBuildSelectMySQLWithNoCatalogOrSchema() {
+ SqlTableId tableId = new SqlTableId(null, null, "inventory");
+ List columnIds = Collections.singletonList(new SqlColumnId("item_id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.MYSQL, tableId, columnIds);
+
+ assertEquals("SELECT item_id FROM inventory", result);
+ }
+
+ @Test
+ void testBuildSelectWithCatalogOnly() {
+ SqlTableId tableId = new SqlTableId("testdb", null, "logs");
+ List columnIds = Collections.singletonList(new SqlColumnId("timestamp"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.H2, tableId, columnIds);
+
+ assertEquals("SELECT timestamp FROM testdb.logs", result);
+ }
+
+ @Test
+ void testBuildSelectMSSQLWithSchemaAndCatalog() {
+ SqlTableId tableId = new SqlTableId("master", "dbo", "users");
+ List columnIds = Collections.singletonList(new SqlColumnId("user_id"));
+
+ String result = SqlSelectBuilder.buildSelect(DatabaseType.MS_SQL_SERVER, tableId, columnIds);
+
+ assertEquals("SELECT user_id FROM master.dbo.users", result);
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdParserTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdParserTest.java
new file mode 100644
index 0000000000..78eaf8ab76
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdParserTest.java
@@ -0,0 +1,74 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SqlTableIdParserTest {
+
+ @Test
+ void parseSingleName_returnsTableOnly() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("mytable", DatabaseType.POSTGRES);
+ assertNull(id.getCatalogName());
+ assertNull(id.getSchemaName());
+ assertEquals("mytable", id.getTableName());
+ }
+
+ @Test
+ void parseSchemaAndTable_returnsSchemaAndTable() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("myschema.mytable", DatabaseType.POSTGRES);
+ assertNull(id.getCatalogName());
+ assertEquals("myschema", id.getSchemaName());
+ assertEquals("mytable", id.getTableName());
+ }
+
+ @Test
+ void parseCatalogAndTableForMySQL_returnsCatalogAndTable() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("mycatalog.mytable", DatabaseType.MYSQL);
+ assertEquals("mycatalog", id.getCatalogName());
+ assertNull(id.getSchemaName());
+ assertEquals("mytable", id.getTableName());
+ }
+
+ @Test
+ void parseCatalogAndTableForMariaDB_returnsCatalogAndTable() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("mycatalog.mytable", DatabaseType.MARIADB);
+ assertEquals("mycatalog", id.getCatalogName());
+ assertNull(id.getSchemaName());
+ assertEquals("mytable", id.getTableName());
+ }
+
+ @Test
+ void parseCatalogSchemaTable_returnsAllParts() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("mycatalog.myschema.mytable", DatabaseType.POSTGRES);
+ assertEquals("mycatalog", id.getCatalogName());
+ assertEquals("myschema", id.getSchemaName());
+ assertEquals("mytable", id.getTableName());
+ }
+
+ @Test
+ void parseUppercaseParts_areLowerCased() {
+ SqlTableId id = SqlTableIdParser.parseFullyQualifiedTableName("CATALOG.SCHEMA.TABLE", DatabaseType.POSTGRES);
+ assertEquals("catalog", id.getCatalogName());
+ assertEquals("schema", id.getSchemaName());
+ assertEquals("table", id.getTableName());
+ }
+
+ @Test
+ void parseNull_throws() {
+ assertThrows(NullPointerException.class, () -> SqlTableIdParser.parseFullyQualifiedTableName(null, DatabaseType.POSTGRES));
+ }
+
+ @Test
+ void parseEmpty_throws() {
+ assertThrows(IllegalArgumentException.class, () -> SqlTableIdParser.parseFullyQualifiedTableName("", DatabaseType.POSTGRES));
+ }
+
+ @Test
+ void parseTooManyParts_throws() {
+ assertThrows(IllegalArgumentException.class, () -> SqlTableIdParser.parseFullyQualifiedTableName("a.b.c.d", DatabaseType.POSTGRES));
+ }
+
+}
+
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdTest.java
new file mode 100644
index 0000000000..574f488b76
--- /dev/null
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/SqlTableIdTest.java
@@ -0,0 +1,45 @@
+package org.evomaster.client.java.sql.internal;
+
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SqlTableIdTest {
+
+ @Test
+ void invalidCatalogName_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new SqlTableId("my.catalog", "my_schema", "my_table"));
+ }
+
+ @Test
+ void invalidSchemaName_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new SqlTableId("my_catalog", "my.schema", "my_table"));
+ }
+
+ @Test
+ void invalidTableName_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new SqlTableId("my_catalog", "my_schema", "my.table"));
+ }
+
+ @Test
+ void testBuildQualifiedTableName_Postgres() {
+ DatabaseType db = DatabaseType.POSTGRES;
+
+ assertEquals("table", new SqlTableId(null, null, "table").buildQualifiedTableName(db));
+ assertEquals("schema.table", new SqlTableId(null, "schema", "table").buildQualifiedTableName(db));
+ assertEquals("catalog.schema.table", new SqlTableId("catalog", "schema", "table").buildQualifiedTableName(db));
+ assertEquals("catalog.table", new SqlTableId("catalog", null, "table").buildQualifiedTableName(db));
+ }
+
+ @Test
+ void testBuildQualifiedTableName_MySQL() {
+ DatabaseType db = DatabaseType.MYSQL;
+
+ assertEquals("table", new SqlTableId(null, null, "table").buildQualifiedTableName(db));
+ assertEquals("schema.table", new SqlTableId(null, "schema", "table").buildQualifiedTableName(db));
+ assertEquals("catalog.table", new SqlTableId("catalog", "schema", "table").buildQualifiedTableName(db));
+ assertEquals("catalog.table", new SqlTableId("catalog", null, "table").buildQualifiedTableName(db));
+ }
+
+}
diff --git a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinderTest.java b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinderTest.java
index 58b2e71e0e..0960e476b1 100644
--- a/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinderTest.java
+++ b/client-java/sql/src/test/java/org/evomaster/client/java/sql/internal/TablesAndColumnsFinderTest.java
@@ -30,6 +30,7 @@ private static DbInfoDto createSchema() {
usersTable.id.name = "Users";
usersTable.columns.add(createColumnDto("name", "Users"));
usersTable.columns.add(createColumnDto("age", "Users"));
+ usersTable.columns.add(createColumnDto("active", "Users"));
TableDto employeesTable = new TableDto();
employeesTable.id = new TableIdDto();
@@ -63,6 +64,13 @@ private static DbInfoDto createSchema() {
dbBaseTable.columns.add(createColumnDto("id", "db_base"));
dbBaseTable.columns.add(createColumnDto("name", "db_base"));
+ TableDto agentsTable = new TableDto();
+ agentsTable.id = new TableIdDto();
+ agentsTable.id.schema = "public";
+ agentsTable.id.name = "agents";
+ agentsTable.columns.add(createColumnDto("name", "agents"));
+ agentsTable.columns.add(createColumnDto("age", "agents"));
+
schema.tables.add(usersTable);
schema.tables.add(employeesTable);
@@ -70,6 +78,7 @@ private static DbInfoDto createSchema() {
schema.tables.add(votingTable);
schema.tables.add(groupsTable);
schema.tables.add(dbBaseTable);
+ schema.tables.add(agentsTable);
return schema;
}
@@ -285,9 +294,10 @@ void findsTablesAndColumnsInCountQuery() throws JSQLParserException {
assertEquals(1, finder.getBaseTableReferences().size());
assertTrue(finder.containsColumnReferences(usersTableReference));
- assertEquals(2, finder.getColumnReferences(usersTableReference).size());
+ assertEquals(3, finder.getColumnReferences(usersTableReference).size());
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "age")));
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "name")));
+ assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "active")));
}
@Test
@@ -421,9 +431,10 @@ void findsTablesAndColumnsInSelectAllQuery() throws JSQLParserException {
assertEquals(1, finder.getBaseTableReferences().size());
assertTrue(finder.containsColumnReferences(usersTableReference));
- assertEquals(2, finder.getColumnReferences(usersTableReference).size());
+ assertEquals(3, finder.getColumnReferences(usersTableReference).size());
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "name")));
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "age")));
+ assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "active")));
}
@Test
@@ -445,7 +456,7 @@ void findsTablesAndColumnsInSelectAllFromDerivedTable() throws JSQLParserExcepti
}
@Test
- void findsTwoColumns() throws JSQLParserException {
+ void findsThreeColumns() throws JSQLParserException {
DbInfoDto schema = createSchema();
String sql = "SELECT * FROM Users WHERE name = 'joh' AND age IS NULL";
TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
@@ -458,9 +469,10 @@ void findsTwoColumns() throws JSQLParserException {
assertEquals(1, finder.getBaseTableReferences().size());
assertTrue(finder.containsColumnReferences(usersTableReference));
- assertEquals(2, finder.getColumnReferences(usersTableReference).size());
+ assertEquals(3, finder.getColumnReferences(usersTableReference).size());
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "age")));
assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "name")));
+ assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "active")));
}
@Test
@@ -640,4 +652,144 @@ void testMissingTables() throws JSQLParserException {
assertEquals(0, finder.getBaseTableReferences().size());
}
+
+ @Test
+ void testDeleteFromWithSubquery() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "DELETE FROM Employees WHERE department_id IN (SELECT id FROM Departments)";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ SqlBaseTableReference employeesTableReference = new SqlBaseTableReference("Employees");
+ SqlBaseTableReference departmentsTableReference = new SqlBaseTableReference("Departments");
+
+ assertEquals(2, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(employeesTableReference));
+ assertTrue(finder.containsColumnReferences(departmentsTableReference));
+
+ assertEquals(1, finder.getColumnReferences(employeesTableReference).size());
+ assertTrue(finder.getColumnReferences(employeesTableReference).contains(new SqlColumnReference(employeesTableReference, "department_id")));
+
+ assertEquals(1, finder.getColumnReferences(departmentsTableReference).size());
+ assertTrue(finder.getColumnReferences(departmentsTableReference).contains(new SqlColumnReference(departmentsTableReference, "id")));
+ }
+
+ @Test
+ void testCommonTableExpression() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "WITH EmployeeCTE AS (SELECT name, department_id FROM Employees WHERE department_id > 0) " +
+ "SELECT name FROM EmployeeCTE";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ SqlBaseTableReference employeesTableReference = new SqlBaseTableReference("Employees");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(employeesTableReference));
+
+ assertEquals(2, finder.getColumnReferences(employeesTableReference).size());
+ assertTrue(finder.getColumnReferences(employeesTableReference).contains(new SqlColumnReference(employeesTableReference, "name")));
+ assertTrue(finder.getColumnReferences(employeesTableReference).contains(new SqlColumnReference(employeesTableReference, "department_id")));
+ }
+
+
+ @Test
+ void testCommonTableExpressionWithWhere() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "WITH EmployeeCTE AS (SELECT name, department_id FROM Employees WHERE department_id > 0) " +
+ "SELECT name FROM EmployeeCTE empCTE WHERE empCTE.name='John'";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ SqlBaseTableReference employeesTableReference = new SqlBaseTableReference("Employees");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(employeesTableReference));
+
+ assertEquals(2, finder.getColumnReferences(employeesTableReference).size());
+ assertTrue(finder.getColumnReferences(employeesTableReference).contains(new SqlColumnReference(employeesTableReference, "name")));
+ assertTrue(finder.getColumnReferences(employeesTableReference).contains(new SqlColumnReference(employeesTableReference, "department_id")));
+ }
+
+ @Test
+ void testSelectWithExplicitSchema() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "SELECT name, age FROM public.agents WHERE age > 18";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ SqlBaseTableReference publicUsersTableReference = new SqlBaseTableReference(null, "public","agents");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(publicUsersTableReference));
+
+ assertEquals(2, finder.getColumnReferences(publicUsersTableReference).size());
+ assertTrue(finder.getColumnReferences(publicUsersTableReference).contains(new SqlColumnReference(publicUsersTableReference, "name")));
+ assertTrue(finder.getColumnReferences(publicUsersTableReference).contains(new SqlColumnReference(publicUsersTableReference, "age")));
+ }
+
+ @Test
+ void testSelectAllWithExplicitSchema() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "SELECT * FROM public.agents";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ statement.accept(finder);
+
+ SqlBaseTableReference publicUsersTableReference = new SqlBaseTableReference(null, "public","agents");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(publicUsersTableReference));
+
+ assertEquals(2, finder.getColumnReferences(publicUsersTableReference).size());
+ assertTrue(finder.getColumnReferences(publicUsersTableReference).contains(new SqlColumnReference(publicUsersTableReference, "name")));
+ assertTrue(finder.getColumnReferences(publicUsersTableReference).contains(new SqlColumnReference(publicUsersTableReference, "age")));
+ }
+
+
+ @Test
+ void testIsNull() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "SELECT name FROM Users WHERE age IS NULL";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+
+ statement.accept(finder);
+
+ SqlBaseTableReference usersTableReference = new SqlBaseTableReference("Users");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(usersTableReference));
+
+ assertEquals(2, finder.getColumnReferences(usersTableReference).size());
+ assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "age")));
+ assertTrue(finder.getColumnReferences(usersTableReference).contains(new SqlColumnReference(usersTableReference, "name")));
+ }
+
+ @Test
+ void testIsBoolean() throws JSQLParserException {
+ DbInfoDto schema = createSchema();
+ String sql = "SELECT name FROM Users WHERE active IS TRUE";
+ TablesAndColumnsFinder finder = new TablesAndColumnsFinder(schema);
+ Statement statement = CCJSqlParserUtil.parse(sql);
+
+ statement.accept(finder);
+
+ SqlBaseTableReference usersTableReference = new SqlBaseTableReference("Users");
+
+ assertEquals(1, finder.getBaseTableReferences().size());
+ assertTrue(finder.containsColumnReferences(usersTableReference));
+
+ // Expect: "active" (from WHERE) and "name" (from SELECT)
+ assertEquals(2, finder.getColumnReferences(usersTableReference).size());
+ assertTrue(finder.getColumnReferences(usersTableReference)
+ .contains(new SqlColumnReference(usersTableReference, "active")));
+ assertTrue(finder.getColumnReferences(usersTableReference)
+ .contains(new SqlColumnReference(usersTableReference, "name")));
+ }
+
}
diff --git a/core-tests/e2e-tests/spring/spring-graphql/src/test/kotlin/com/foo/graphql/db/SpringWithDbController.kt b/core-tests/e2e-tests/spring/spring-graphql/src/test/kotlin/com/foo/graphql/db/SpringWithDbController.kt
index b0c7a8042e..5e8d646edd 100644
--- a/core-tests/e2e-tests/spring/spring-graphql/src/test/kotlin/com/foo/graphql/db/SpringWithDbController.kt
+++ b/core-tests/e2e-tests/spring/spring-graphql/src/test/kotlin/com/foo/graphql/db/SpringWithDbController.kt
@@ -26,11 +26,21 @@ abstract class SpringWithDbController(applicationClass: Class<*>) : SpringContro
var dbconnection : Connection? = null
+ fun nextDbID(): Int {
+ if (dbID == Int.MAX_VALUE) {
+ dbID = 0
+ }
+ dbID++
+ return dbID
+ }
override fun startSut(): String {
//lot of problem if using same H2 instance. see:
//https://github.com/h2database/h2database/issues/227
- val rand = dbID++ //nextInt()
+ val currDbID = nextDbID() //nextInt()
+
+ // currDbID is always positive
+ val databaseName = "testdb_"+ currDbID
ctx = SpringApplication.run(applicationClass,
"--server.port=0",
@@ -40,7 +50,7 @@ abstract class SpringWithDbController(applicationClass: Class<*>) : SpringContro
* MODE=LEGACY can be removed when Spring-boot is upgraded to 2.6.4
* https://github.com/hibernate/hibernate-orm/pull/4524
*/
- "--spring.datasource.url=jdbc:h2:mem:testdb_"+rand+";DB_CLOSE_DELAY=-1;MODE=LEGACY",
+ "--spring.datasource.url=jdbc:h2:mem:${databaseName};DB_CLOSE_DELAY=-1;MODE=LEGACY",
"--spring.jpa.database-platform=" + H2Dialect::class.java.name,
"--spring.datasource.username=sa",
"--spring.datasource.password",
@@ -82,4 +92,4 @@ abstract class SpringWithDbController(applicationClass: Class<*>) : SpringContro
)
-}
\ No newline at end of file
+}
diff --git a/core-tests/e2e-tests/spring/spring-rest-h2-v1/src/test/java/com/foo/rest/examples/spring/db/SpringWithDbController.java b/core-tests/e2e-tests/spring/spring-rest-h2-v1/src/test/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
index db57f30dc2..0a67b58c15 100644
--- a/core-tests/e2e-tests/spring/spring-rest-h2-v1/src/test/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
+++ b/core-tests/e2e-tests/spring/spring-rest-h2-v1/src/test/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
@@ -37,8 +37,11 @@ public String startSut() {
//https://github.com/h2database/h2database/issues/227
int rand = Random.Default.nextInt();
+ // Avoid invalid database names such as "testdb_-1047301442"
+ String databaseName = "testdb_"+Math.abs(rand);
+
ctx = SpringApplication.run(applicationClass, "--server.port=0",
- "--spring.datasource.url=jdbc:h2:mem:testdb_"+rand+";DB_CLOSE_DELAY=-1;",
+ "--spring.datasource.url=jdbc:h2:mem:"+ databaseName +";DB_CLOSE_DELAY=-1;",
"--spring.jpa.database-platform=" + H2Dialect.class.getName(),
"--spring.datasource.username=sa",
"--spring.datasource.password",
diff --git a/core-tests/e2e-tests/spring/spring-rest-h2-v2/src/test/java/com/foo/spring/rest/h2/columntypes/H2ColumnTypesController.java b/core-tests/e2e-tests/spring/spring-rest-h2-v2/src/test/java/com/foo/spring/rest/h2/columntypes/H2ColumnTypesController.java
index 6a06e58288..efb3a62950 100644
--- a/core-tests/e2e-tests/spring/spring-rest-h2-v2/src/test/java/com/foo/spring/rest/h2/columntypes/H2ColumnTypesController.java
+++ b/core-tests/e2e-tests/spring/spring-rest-h2-v2/src/test/java/com/foo/spring/rest/h2/columntypes/H2ColumnTypesController.java
@@ -220,8 +220,11 @@ public String startSut() {
//https://github.com/h2database/h2database/issues/227
int rand = Random.Default.nextInt();
+ // Avoid invalid database names such as "testdb_-1047301442"
+ String databaseName = "testdb_" + Math.abs(rand);
+
ctx = SpringApplication.run(applicationClass, "--server.port=0",
- "--spring.datasource.url=jdbc:h2:mem:testdb_" + rand + ";DB_CLOSE_DELAY=-1;",
+ "--spring.datasource.url=jdbc:h2:mem:" + databaseName + ";DB_CLOSE_DELAY=-1;",
"--spring.jpa.database-platform=" + H2Dialect.class.getName(),
"--spring.datasource.username=sa",
"--spring.datasource.password",
diff --git a/core-tests/e2e-tests/spring/spring-rest-h2-z3solver/src/test/java/com/foo/spring/rest/h2/z3solver/Z3SolverController.java b/core-tests/e2e-tests/spring/spring-rest-h2-z3solver/src/test/java/com/foo/spring/rest/h2/z3solver/Z3SolverController.java
index 949c4d9bbb..8f10573889 100644
--- a/core-tests/e2e-tests/spring/spring-rest-h2-z3solver/src/test/java/com/foo/spring/rest/h2/z3solver/Z3SolverController.java
+++ b/core-tests/e2e-tests/spring/spring-rest-h2-z3solver/src/test/java/com/foo/spring/rest/h2/z3solver/Z3SolverController.java
@@ -56,8 +56,11 @@ public String startSut() {
// https://github.com/h2database/h2database/issues/227
int rand = Random.Default.nextInt();
+ // Avoid invalid database names such as "testdb_-1047301442"
+ String databaseName = "testdb_" + Math.abs(rand);
+
ctx = SpringApplication.run(applicationClass, "--server.port=0",
- "--spring.datasource.url=jdbc:h2:mem:testdb_" + rand + ";DB_CLOSE_DELAY=-1;",
+ "--spring.datasource.url=jdbc:h2:mem:" + databaseName + ";DB_CLOSE_DELAY=-1;",
"--spring.jpa.database-platform=" + H2Dialect.class.getName(),
"--spring.datasource.username=sa",
"--spring.datasource.password",
diff --git a/core-tests/e2e-tests/spring/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/db/SpringWithDbController.java b/core-tests/e2e-tests/spring/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/db/SpringWithDbController.java
index 59d79117bd..b0272c8686 100644
--- a/core-tests/e2e-tests/spring/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/db/SpringWithDbController.java
+++ b/core-tests/e2e-tests/spring/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/db/SpringWithDbController.java
@@ -34,9 +34,12 @@ public String startSut() {
int rand = Random.Default.nextInt();
+ // Avoid invalid database names such as "testdb_-1047301442"
+ String databaseName = "testdb_"+Math.abs(rand);
+
ctx = SpringApplication.run(applicationClass, new String[]{
"--server.port=0",
- "--spring.datasource.url=jdbc:h2:mem:testdb_"+rand+";DB_CLOSE_DELAY=-1;",
+ "--spring.datasource.url=jdbc:h2:mem:"+databaseName+";DB_CLOSE_DELAY=-1;",
"--spring.jpa.database-platform=" + H2Dialect.class.getName(),
"--spring.datasource.username=sa",
"--spring.datasource.password",
diff --git a/core-tests/jdk-8/spring-rest-openapi-v2-driver/src/main/java/com/foo/rest/examples/spring/db/SpringWithDbController.java b/core-tests/jdk-8/spring-rest-openapi-v2-driver/src/main/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
index aa9302f103..524d46d766 100644
--- a/core-tests/jdk-8/spring-rest-openapi-v2-driver/src/main/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
+++ b/core-tests/jdk-8/spring-rest-openapi-v2-driver/src/main/java/com/foo/rest/examples/spring/db/SpringWithDbController.java
@@ -42,10 +42,13 @@ public String startSut() {
//https://github.com/h2database/h2database/issues/227
int rand = new Random().nextInt();
+ // Avoid invalid database names such as "testdb_-1047301442"
+ String databaseName = "testdb_"+ Math.abs(rand);
+
List inputs = new ArrayList<>();
inputs.addAll(Arrays.asList(
"--server.port=0",
- "--spring.datasource.url=jdbc:h2:mem:testdb_"+rand+";DB_CLOSE_DELAY=-1;",
+ "--spring.datasource.url=jdbc:h2:mem:" + databaseName + ";DB_CLOSE_DELAY=-1;",
"--spring.jpa.database-platform=" + H2Dialect.class.getName(),
"--spring.datasource.username=sa",
"--spring.datasource.password",