Skip to content

Commit

Permalink
Add support for Presto - common classes (sqlancer#884)
Browse files Browse the repository at this point in the history
To implement support for Presto some changes in common
classes are required.

## Changes in common classes:
1. class sqlancer.MainOptions - added global parameters :
 * canonicalizeString (boolean) - presto doesn't support JDBC
     queries with ";" at the end of statement
 * compareResultsContent (boolean) - comparing content of
     VARBINARY columns fails

2. class sqlancer.ComparatorHelper - compare result based on parameter

```
    boolean compare = state.getOptions().compareResultsContent();
    if (compare && !firstHashSet.equals(secondHashSet)) {
```

3. sqlancer.common.query.SQLancerResultSet - added method:

```
    public String getType(int i) throws SQLException {
    return rs.getMetaData().getColumnTypeName(i);
    }
```

4. sqlancer.common.query.SQLQueryAdapter : added constructor

```
    public SQLQueryAdapter(String query, ExpectedErrors expectedErrors,
        boolean couldAffectSchema, boolean canonicalizeString) {

```
  • Loading branch information
branimir-vujicic authored Aug 24, 2023
1 parent 706ac16 commit f852f8c
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 7 deletions.
6 changes: 4 additions & 2 deletions src/sqlancer/ComparatorHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public static List<String> getResultSetFirstColumnAsString(String queryString, E
e.printStackTrace();
}
}
SQLQueryAdapter q = new SQLQueryAdapter(queryString, errors);
boolean canonicalizeString = state.getOptions().canonicalizeSqlString();
SQLQueryAdapter q = new SQLQueryAdapter(queryString, errors, true, canonicalizeString);
List<String> resultSet = new ArrayList<>();
SQLancerResultSet result = null;
try {
Expand Down Expand Up @@ -106,7 +107,8 @@ public static void assumeResultSetsAreEqual(List<String> resultSet, List<String>
Set<String> firstHashSet = new HashSet<>(resultSet);
Set<String> secondHashSet = new HashSet<>(secondResultSet);

if (!firstHashSet.equals(secondHashSet)) {
boolean validateResultSizeOnly = state.getOptions().validateResultSizeOnly();
if (!validateResultSizeOnly && !firstHashSet.equals(secondHashSet)) {
Set<String> firstResultSetMisses = new HashSet<>(firstHashSet);
firstResultSetMisses.removeAll(secondHashSet);
Set<String> secondResultSetMisses = new HashSet<>(secondHashSet);
Expand Down
14 changes: 14 additions & 0 deletions src/sqlancer/MainOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ public class MainOptions {
@Parameter(names = "--ast-reducer-max-time", description = "EXPERIMENTAL Maximum time duration (secs) the statement reducer will do")
private long maxStatementReduceTime = NO_REDUCE_LIMIT; // NOPMD

@Parameter(names = "--validate-result-size-only", description = "Should validate result size only and skip comparing content of the result set ", arity = 1)
private boolean validateResultSizeOnly = false; // NOPMD

@Parameter(names = "--canonicalize-sql-strings", description = "Should canonicalize query string (add ';' at the end", arity = 1)
private boolean canonicalizeSqlString = true; // NOPMD

public int getMaxExpressionDepth() {
return maxExpressionDepth;
}
Expand Down Expand Up @@ -322,4 +328,12 @@ public long getMaxASTReduceTime() {
return maxASTReduceTime;
}

public boolean validateResultSizeOnly() {
return validateResultSizeOnly;
}

public boolean canonicalizeSqlString() {
return canonicalizeSqlString;
}

}
11 changes: 10 additions & 1 deletion src/sqlancer/common/query/SQLQueryAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ private static boolean guessAffectSchemaFromQuery(String query) {
}

public SQLQueryAdapter(String query, ExpectedErrors expectedErrors, boolean couldAffectSchema) {
this.query = canonicalizeString(query);
this(query, expectedErrors, couldAffectSchema, true);
}

public SQLQueryAdapter(String query, ExpectedErrors expectedErrors, boolean couldAffectSchema,
boolean canonicalizeString) {
if (canonicalizeString) {
this.query = canonicalizeString(query);
} else {
this.query = query;
}
this.expectedErrors = expectedErrors;
this.couldAffectSchema = couldAffectSchema;
checkQueryString();
Expand Down
4 changes: 4 additions & 0 deletions src/sqlancer/common/query/SQLancerResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public long getLong(int i) throws SQLException {
return rs.getLong(i);
}

public String getType(int i) throws SQLException {
return rs.getMetaData().getColumnTypeName(i);
}

public void registerEpilogue(Runnable runnableEpilogue) {
this.runnableEpilogue = runnableEpilogue;
}
Expand Down
26 changes: 22 additions & 4 deletions test/sqlancer/TestComparatorHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,37 @@

import static org.junit.jupiter.api.Assertions.assertThrowsExactly;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.junit.jupiter.api.Test;

import sqlancer.h2.H2Options;
import sqlancer.h2.H2Schema;

public class TestComparatorHelper {
// TODO: Implement tests for the other ComparatorHelper methods

// TODO: create test state that not depends on specific database
final SQLGlobalState<H2Options, H2Schema> state = new SQLGlobalState<H2Options, H2Schema>() {

@Override
protected H2Schema readSchema() throws SQLException {
return H2Schema.fromConnection(getConnection(), getDatabaseName());
}

@Override
public MainOptions getOptions() {
return new MainOptions();
}
};

@Test
public void testAssumeResultSetsAreEqualWithEqualSets() {
List<String> r1 = Arrays.asList("a", "b", "c");
List<String> r2 = Arrays.asList("a", "b", "c");
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), null);
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), state);

}

Expand All @@ -26,7 +44,7 @@ public void testAssumeResultSetsAreEqualWithUnequalLengthSets() {
// line occurs before AssertionError is thrown, but it's good enough as an indicator that one of the Exceptions
// is raised
assertThrowsExactly(NullPointerException.class, () -> {
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), null);
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), state);
});
}

Expand All @@ -38,15 +56,15 @@ public void testAssumeResultSetsAreEqualWithUnequalValueSets() {
// line occurs before AssertionError is thrown, but it's good enough as an indicator that one of the Exceptions
// is raised
assertThrowsExactly(NullPointerException.class, () -> {
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), null);
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), state);
});
}

@Test
public void testAssumeResultSetsAreEqualWithCanonicalizationRule() {
List<String> r1 = Arrays.asList("a", "b", "c");
List<String> r2 = Arrays.asList("a", "b", "d");
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), null, (String s) -> {
ComparatorHelper.assumeResultSetsAreEqual(r1, r2, "", Arrays.asList(""), state, (String s) -> {
return s.equals("d") ? "c" : s;
});
}
Expand Down

0 comments on commit f852f8c

Please sign in to comment.