sqlancer.duckdb.test.DuckDBNoRECOracle Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sqlancer Show documentation
Show all versions of sqlancer Show documentation
SQLancer finds logic bugs in Database Management Systems through automatic testing
package sqlancer.duckdb.test;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import sqlancer.IgnoreMeException;
import sqlancer.Randomly;
import sqlancer.SQLConnection;
import sqlancer.common.ast.newast.ColumnReferenceNode;
import sqlancer.common.ast.newast.NewPostfixTextNode;
import sqlancer.common.ast.newast.Node;
import sqlancer.common.ast.newast.TableReferenceNode;
import sqlancer.common.oracle.NoRECBase;
import sqlancer.common.oracle.TestOracle;
import sqlancer.common.query.SQLQueryAdapter;
import sqlancer.common.query.SQLancerResultSet;
import sqlancer.duckdb.DuckDBErrors;
import sqlancer.duckdb.DuckDBProvider.DuckDBGlobalState;
import sqlancer.duckdb.DuckDBSchema;
import sqlancer.duckdb.DuckDBSchema.DuckDBColumn;
import sqlancer.duckdb.DuckDBSchema.DuckDBCompositeDataType;
import sqlancer.duckdb.DuckDBSchema.DuckDBDataType;
import sqlancer.duckdb.DuckDBSchema.DuckDBTable;
import sqlancer.duckdb.DuckDBSchema.DuckDBTables;
import sqlancer.duckdb.DuckDBToStringVisitor;
import sqlancer.duckdb.ast.DuckDBExpression;
import sqlancer.duckdb.ast.DuckDBJoin;
import sqlancer.duckdb.ast.DuckDBSelect;
import sqlancer.duckdb.gen.DuckDBExpressionGenerator;
import sqlancer.duckdb.gen.DuckDBExpressionGenerator.DuckDBCastOperation;
public class DuckDBNoRECOracle extends NoRECBase implements TestOracle {
private final DuckDBSchema s;
public DuckDBNoRECOracle(DuckDBGlobalState globalState) {
super(globalState);
this.s = globalState.getSchema();
DuckDBErrors.addExpressionErrors(errors);
}
@Override
public void check() throws SQLException {
DuckDBTables randomTables = s.getRandomTableNonEmptyTables();
List columns = randomTables.getColumns();
DuckDBExpressionGenerator gen = new DuckDBExpressionGenerator(state).setColumns(columns);
Node randomWhereCondition = gen.generateExpression();
List tables = randomTables.getTables();
List> tableList = tables.stream()
.map(t -> new TableReferenceNode(t)).collect(Collectors.toList());
List> joins = DuckDBJoin.getJoins(tableList, state);
int secondCount = getSecondQuery(tableList.stream().collect(Collectors.toList()), randomWhereCondition, joins);
int firstCount = getFirstQueryCount(con, tableList.stream().collect(Collectors.toList()), columns,
randomWhereCondition, joins);
if (firstCount == -1 || secondCount == -1) {
throw new IgnoreMeException();
}
if (firstCount != secondCount) {
throw new AssertionError(
optimizedQueryString + "; -- " + firstCount + "\n" + unoptimizedQueryString + " -- " + secondCount);
}
}
private int getSecondQuery(List> tableList, Node randomWhereCondition,
List> joins) throws SQLException {
DuckDBSelect select = new DuckDBSelect();
// select.setGroupByClause(groupBys);
// DuckDBExpression isTrue = DuckDBPostfixOperation.create(randomWhereCondition,
// PostfixOperator.IS_TRUE);
Node asText = new NewPostfixTextNode<>(new DuckDBCastOperation(
new NewPostfixTextNode(randomWhereCondition,
" IS NOT NULL AND " + DuckDBToStringVisitor.asString(randomWhereCondition)),
new DuckDBCompositeDataType(DuckDBDataType.INT, 8)), "as count");
select.setFetchColumns(Arrays.asList(asText));
select.setFromList(tableList);
// select.setSelectType(SelectType.ALL);
select.setJoinList(joins);
int secondCount = 0;
unoptimizedQueryString = "SELECT SUM(count) FROM (" + DuckDBToStringVisitor.asString(select) + ") as res";
errors.add("canceling statement due to statement timeout");
SQLQueryAdapter q = new SQLQueryAdapter(unoptimizedQueryString, errors);
SQLancerResultSet rs;
try {
rs = q.executeAndGetLogged(state);
} catch (Exception e) {
throw new AssertionError(unoptimizedQueryString, e);
}
if (rs == null) {
return -1;
}
if (rs.next()) {
secondCount += rs.getLong(1);
}
rs.close();
return secondCount;
}
private int getFirstQueryCount(SQLConnection con, List> tableList,
List columns, Node randomWhereCondition, List> joins)
throws SQLException {
DuckDBSelect select = new DuckDBSelect();
// select.setGroupByClause(groupBys);
// DuckDBAggregate aggr = new DuckDBAggregate(
List> allColumns = columns.stream()
.map((c) -> new ColumnReferenceNode(c)).collect(Collectors.toList());
// DuckDBAggregateFunction.COUNT);
// select.setFetchColumns(Arrays.asList(aggr));
select.setFetchColumns(allColumns);
select.setFromList(tableList);
select.setWhereClause(randomWhereCondition);
if (Randomly.getBooleanWithSmallProbability()) {
select.setOrderByExpressions(new DuckDBExpressionGenerator(state).setColumns(columns).generateOrderBys());
}
// select.setSelectType(SelectType.ALL);
select.setJoinList(joins);
int firstCount = 0;
try (Statement stat = con.createStatement()) {
optimizedQueryString = DuckDBToStringVisitor.asString(select);
if (options.logEachSelect()) {
logger.writeCurrent(optimizedQueryString);
}
try (ResultSet rs = stat.executeQuery(optimizedQueryString)) {
while (rs.next()) {
firstCount++;
}
}
} catch (SQLException e) {
throw new IgnoreMeException();
}
return firstCount;
}
}