Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 82 additions & 5 deletions src/test/java/com/jayant/JDBCDriverComparisonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.jayant.testparams.DatabaseMetaDataTestParams;
import com.jayant.testparams.ResultSetMetaDataTestParams;
import com.jayant.testparams.ResultSetTestParams;
import com.jayant.testparams.StatementTestParams;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
Expand Down Expand Up @@ -42,6 +43,12 @@ public class JDBCDriverComparisonTest {
// ResultSets for Thrift vs SEA comparison
private static ResultSet ossThriftResultSet;
private static ResultSet ossSeaResultSet2;
// Statements for Old vs SEA comparison
private static Statement oldDriverStatement;
private static Statement ossSeaStatement1;
// Statements for Thrift vs SEA comparison
private static Statement ossThriftStatement;
private static Statement ossSeaStatement2;

@BeforeAll
static void setup() throws Exception {
Expand Down Expand Up @@ -89,6 +96,12 @@ static void setup() throws Exception {
ossThriftResultSet =
ossThriftConnection.createStatement().executeQuery(queryResultSetTypesTable);
ossSeaResultSet2 = ossSeaConnection.createStatement().executeQuery(queryResultSetTypesTable);

// Create separate Statements for each comparison pair
oldDriverStatement = oldDriverConnection.createStatement();
ossSeaStatement1 = ossSeaConnection.createStatement();
ossThriftStatement = ossThriftConnection.createStatement();
ossSeaStatement2 = ossSeaConnection.createStatement();
}

@AfterAll
Expand Down Expand Up @@ -174,7 +187,38 @@ private static Stream<Arguments> withResultSetPairs(Stream<Arguments> baseProvid
return combined.stream();
}

@ParameterizedTest
/**
* Helper method for tests that need Statements. Prepends Statement pair info (name, stmt1, stmt2)
* to original arguments.
*/
private static Stream<Arguments> withStatementPairs(Stream<Arguments> baseProvider) {
List<Arguments> base = baseProvider.collect(Collectors.toList());
List<Arguments> combined = new ArrayList<>();

// Define Statement pairs - each with separate instances to avoid reuse
Object[][] statementPairs = {
{"Old(2.7.6) vs OSS-SEA", oldDriverStatement, ossSeaStatement1},
{"OSS-Thrift vs OSS-SEA", ossThriftStatement, ossSeaStatement2}
};

// Combine each pair with each base argument
for (Object[] pair : statementPairs) {
for (Arguments arg : base) {
Object[] originalArgs = arg.get();
// Create new array: [name, stmt1, stmt2, ...originalArgs]
Object[] newArgs = new Object[3 + originalArgs.length];
newArgs[0] = pair[0]; // comparison name
newArgs[1] = pair[1]; // statement 1
newArgs[2] = pair[2]; // statement 2
System.arraycopy(originalArgs, 0, newArgs, 3, originalArgs.length);
combined.add(Arguments.of(newArgs));
}
}

return combined.stream();
}

@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideSQLQueries")
@DisplayName("Compare SQL Query Results")
void compareSQLQueryResults(
Expand All @@ -197,7 +241,7 @@ void compareSQLQueryResults(
});
}

@ParameterizedTest
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideMetadataMethods")
@DisplayName("Compare Metadata API Results")
void compareMetadataResults(
Expand Down Expand Up @@ -227,7 +271,7 @@ void compareMetadataResults(
});
}

@ParameterizedTest
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideResultSetMethods")
@DisplayName("Compare ResultSet API Results")
void compareResultSetResults(
Expand All @@ -254,7 +298,7 @@ void compareResultSetResults(
});
}

@ParameterizedTest
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideResultSetMetaDataMethods")
@DisplayName("Compare ResultSetMetaData API Results")
void compareResultSetMetaDataResults(
Expand Down Expand Up @@ -283,7 +327,7 @@ void compareResultSetMetaDataResults(
});
}

@ParameterizedTest
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideConnectionMethods")
@DisplayName("Compare Connection API Results")
void compareConnectionResults(
Expand All @@ -310,6 +354,33 @@ void compareConnectionResults(
});
}

@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideStatementMethods")
@DisplayName("Compare Statement API Results")
void compareStatementResults(
String comparisonName, Statement stmt1, Statement stmt2, String methodName, Object[] args) {
assertDoesNotThrow(
() -> {
Object result1 = ReflectionUtils.executeMethod(stmt1, methodName, args);
Object result2 = ReflectionUtils.executeMethod(stmt2, methodName, args);

ComparisonResult result =
ResultSetComparator.compare(
"Statement [" + comparisonName + "]", methodName, args, result1, result2);
reporter.addResult(result);

if (result.hasDifferences()) {
System.err.println(
"["
+ comparisonName
+ "] Differences found in Statement results for method: "
+ methodName);
System.err.println("Args: " + getStringForArgs(args));
System.err.println(result);
}
});
}

private static Stream<Arguments> provideSQLQueries() {
Stream<Arguments> base =
Stream.of(
Expand Down Expand Up @@ -343,6 +414,12 @@ private static Stream<Arguments> provideConnectionMethods() {
return withConnectionPairs(base);
}

private static Stream<Arguments> provideStatementMethods() {
StatementTestParams params = new StatementTestParams();
Stream<Arguments> base = ReflectionUtils.provideMethodsForClass(Statement.class, params);
return withStatementPairs(base);
}

private static URL extractJarToTemp(String jarName, Path tempDir) {
try {
try (InputStream in = JDBCDriverComparisonTest.class.getResourceAsStream("/" + jarName)) {
Expand Down
12 changes: 8 additions & 4 deletions src/test/java/com/jayant/ResultSetComparator.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ public static ComparisonResult compare(
if (result1 instanceof ResultSet && result2 instanceof ResultSet) {
ResultSet rs1 = (ResultSet) result1;
ResultSet rs2 = (ResultSet) result2;
// Compare metadata
result.metadataDifferences = compareMetadata(rs1.getMetaData(), rs2.getMetaData());
try {
// Compare metadata
result.metadataDifferences = compareMetadata(rs1.getMetaData(), rs2.getMetaData());

// Compare data
result.dataDifferences = compareData(rs1, rs2);
// Compare data
result.dataDifferences = compareData(rs1, rs2);
} catch (SQLException e) {
result.dataDifferences.add("ResultSet iteration error: " + e.getMessage());
}
} else if (!(result1 instanceof ResultSet) && !(result2 instanceof ResultSet)) {
// Both are not of type ResultSet
if (result1 == null || !resultIsSame(result1, result2)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,87 @@ public Map<Map.Entry<String, Integer>, Set<Object[]>> getFunctionToArgsMap() {
Map.entry("getAttributes", 4),
new String[] {"main", "tpcds_sf100_delta", "%", "%"});

// Cross-catalog tests: null catalog (match all catalogs)
putInMapForKey(
functionToArgsMap,
Map.entry("getTables", 4),
new String[] {null, "tpcds_sf100_delta", "%", null});
putInMapForKey(
functionToArgsMap,
Map.entry("getTablePrivileges", 3),
new String[] {null, "tpcds_sf100_delta", "%"});
putInMapForKey(functionToArgsMap, Map.entry("getSchemas", 2), new String[] {null, "tpcds_%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getColumns", 4),
new String[] {null, "tpcds_sf100_delta", "catalog_sales", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getPseudoColumns", 4),
new String[] {null, "tpcds_sf100_delta", "catalog_sales", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getColumnPrivileges", 4),
new String[] {null, "tpcds_sf100_delta", "catalog_sales", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getVersionColumns", 3),
new String[] {null, "tpcds_sf100_delta", "catalog_sales"});
putInMapForKey(
functionToArgsMap,
Map.entry("getFunctions", 3),
new String[] {null, "tpcds_sf100_delta", "aggregate"});
putInMapForKey(
functionToArgsMap,
Map.entry("getFunctionColumns", 4),
new String[] {null, "tpcds_sf100_delta", "aggregate", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getProcedures", 3),
new String[] {null, "tpcds_sf100_delta", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getProcedureColumns", 4),
new String[] {null, "tpcds_sf100_delta", "%", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getPrimaryKeys", 3),
new String[] {null, "oss_jdbc_tests", "test_result_set_types"});
putInMapForKey(
functionToArgsMap,
Map.entry("getImportedKeys", 3),
new String[] {null, "tpcds_sf100_delta", "catalog_sales"});
putInMapForKey(
functionToArgsMap,
Map.entry("getExportedKeys", 3),
new String[] {null, "tpcds_sf100_delta", "catalog_sales"});
putInMapForKey(
functionToArgsMap,
Map.entry("getCrossReference", 6),
new String[] {
null, "tpcds_sf100_delta", "catalog_sales", null, "tpcds_sf100_delta", "catalog_sales"
});
putInMapForKey(
functionToArgsMap,
Map.entry("getIndexInfo", 5),
new Object[] {null, "tpcds_sf100_delta", "catalog_sales", true, false});
putInMapForKey(
functionToArgsMap,
Map.entry("getUDTs", 4),
new String[] {null, "tpcds_sf100_delta", "%", null});
putInMapForKey(
functionToArgsMap,
Map.entry("getSuperTypes", 3),
new String[] {null, "tpcds_sf100_delta", "%"});
putInMapForKey(
functionToArgsMap,
Map.entry("getSuperTables", 3),
new String[] {null, "tpcds_sf100_delta", "catalog_sales"});
putInMapForKey(
functionToArgsMap,
Map.entry("getAttributes", 4),
new String[] {null, "tpcds_sf100_delta", "%", "%"});

// Methods for ResultSet concurrency and visibility
for (Integer type : getResultSetTypes()) {
putInMapForKey(
Expand Down Expand Up @@ -129,6 +210,11 @@ public Map<Map.Entry<String, Integer>, Set<Object[]>> getFunctionToArgsMap() {
functionToArgsMap,
Map.entry("getBestRowIdentifier", 5),
new Object[] {"main", "tpcds_sf100_delta", "catalog_sales", i, true});
// Cross-catalog: null catalog
putInMapForKey(
functionToArgsMap,
Map.entry("getBestRowIdentifier", 5),
new Object[] {null, "tpcds_sf100_delta", "catalog_sales", i, true});
}
for (Integer i : getResultSetHoldability()) {
putInMapForKey(
Expand Down
92 changes: 92 additions & 0 deletions src/test/java/com/jayant/testparams/StatementTestParams.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.jayant.testparams;

import static com.jayant.testparams.ParamUtils.putInMapForKey;

import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;

public class StatementTestParams implements TestParams {

@Override
public Set<Map.Entry<String, Integer>> getAcceptedKnownDiffs() {
Set<Map.Entry<String, Integer>> set = new HashSet<>();

// Do not close the shared statement
set.add(Map.entry("close", 0));

// Cancel needs an active query
set.add(Map.entry("cancel", 0));

// Void side effects on shared object
set.add(Map.entry("clearWarnings", 0));
set.add(Map.entry("closeOnCompletion", 0));
set.add(Map.entry("clearBatch", 0));

// Returns object references (not comparable across drivers)
set.add(Map.entry("getConnection", 0));

// Execution methods - tested via SQL query comparator tests
set.add(Map.entry("executeQuery", 1));
set.add(Map.entry("executeUpdate", 1));
set.add(Map.entry("execute", 1));
set.add(Map.entry("executeLargeUpdate", 1));
set.add(Map.entry("executeUpdate", 2));
set.add(Map.entry("execute", 2));
set.add(Map.entry("executeLargeUpdate", 2));

// Batch operations - DML side effects
set.add(Map.entry("executeBatch", 0));
set.add(Map.entry("executeLargeBatch", 0));
set.add(Map.entry("addBatch", 1));

// Not implemented / throws
set.add(Map.entry("setCursorName", 1));

// Driver-specific wrapper methods
set.add(Map.entry("unwrap", 1));
set.add(Map.entry("isWrapperFor", 1));

return set;
}

@Override
public Map<Map.Entry<String, Integer>, Set<Object[]>> getFunctionToArgsMap() {
Map<Map.Entry<String, Integer>, Set<Object[]>> functionToArgsMap = new HashMap<>();

// SQL quoting methods
putInMapForKey(
functionToArgsMap, Map.entry("enquoteLiteral", 1), new Object[] {"test's value"});
putInMapForKey(
functionToArgsMap, Map.entry("enquoteIdentifier", 2), new Object[] {"my column", true});
putInMapForKey(
functionToArgsMap, Map.entry("enquoteIdentifier", 2), new Object[] {"simple", false});
putInMapForKey(functionToArgsMap, Map.entry("isSimpleIdentifier", 1), new Object[] {"simple"});
putInMapForKey(
functionToArgsMap, Map.entry("isSimpleIdentifier", 1), new Object[] {"has space"});
putInMapForKey(
functionToArgsMap, Map.entry("enquoteNCharLiteral", 1), new Object[] {"test string"});

// Setter methods (compare void returns + catch exception differences)
putInMapForKey(functionToArgsMap, Map.entry("setMaxRows", 1), new Object[] {10});
putInMapForKey(functionToArgsMap, Map.entry("setLargeMaxRows", 1), new Object[] {10L});
putInMapForKey(functionToArgsMap, Map.entry("setMaxFieldSize", 1), new Object[] {100});
putInMapForKey(functionToArgsMap, Map.entry("setQueryTimeout", 1), new Object[] {30});
putInMapForKey(functionToArgsMap, Map.entry("setFetchSize", 1), new Object[] {100});
putInMapForKey(
functionToArgsMap,
Map.entry("setFetchDirection", 1),
new Object[] {ResultSet.FETCH_FORWARD});
putInMapForKey(functionToArgsMap, Map.entry("setPoolable", 1), new Object[] {false});
putInMapForKey(functionToArgsMap, Map.entry("setEscapeProcessing", 1), new Object[] {true});
putInMapForKey(functionToArgsMap, Map.entry("setEscapeProcessing", 1), new Object[] {false});

// getMoreResults with flag
putInMapForKey(
functionToArgsMap,
Map.entry("getMoreResults", 1),
new Object[] {Statement.CLOSE_CURRENT_RESULT});

return functionToArgsMap;
}
}
Loading