Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat stonedb tlp #5

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
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
41 changes: 33 additions & 8 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ jobs:
run: |
mvn -Dtest=TestSQLiteQPG test

stonedb:
name: DBMS Tests (StoneDB)
stonedb-norec:
name: DBMS Tests (StoneDB NoRec)
runs-on: ubuntu-latest

steps:
Expand All @@ -385,12 +385,11 @@ jobs:
fetch-depth: 0
- name: Set up StoneDB
run: |
docker pull stoneatom/stonedb:v1.0.3
docker run -p 3306:3306 -itd -e MYSQL_ROOT_PASSWORD='123456' stoneatom/stonedb:v1.0.3
docker ps
ContainerID=$(docker ps --filter ancestor=stoneatom/stonedb:v1.0.3 --format "{{.ID}}")
docker run -p 3306:3306 -itd -e MYSQL_ROOT_PASSWORD='123456' stoneatom/stonedb
ContainerID=$(docker ps --filter ancestor=stoneatom/stonedb --format "{{.ID}}")
docker exec $ContainerID bash
sleep 180s
sleep 60s
docker exec $ContainerID sed -i "s/tianmu_insert_delayed=1/tianmu_insert_delayed=0/" /opt/stonedb57/install/my.cnf
docker exec $ContainerID /opt/stonedb57/install/bin/mysql -uroot -p123456 -e "CREATE USER 'sqlancer'@'%' IDENTIFIED WITH mysql_native_password BY 'sqlancer'; GRANT ALL PRIVILEGES ON *.* TO 'sqlancer'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;"
- name: Set up JDK 11
uses: actions/setup-java@v1.4.4
Expand All @@ -400,7 +399,33 @@ jobs:
run: mvn -B package -DskipTests=true
- name: StoneDB Tests
run: |
STONEDB_AVAILABLE=true mvn test -Dtest=TestStoneDB
STONEDB_AVAILABLE=true mvn test -Dtest=TestStoneDBNoRec

stonedb-tlp:
name: DBMS Tests (StoneDB TLP)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up StoneDB
run: |
docker run -p 3306:3306 -itd -e MYSQL_ROOT_PASSWORD='123456' stoneatom/stonedb
ContainerID=$(docker ps --filter ancestor=stoneatom/stonedb --format "{{.ID}}")
docker exec $ContainerID bash
sleep 60s
docker exec $ContainerID sed -i "s/tianmu_insert_delayed=1/tianmu_insert_delayed=0/" /opt/stonedb57/install/my.cnf
docker exec $ContainerID /opt/stonedb57/install/bin/mysql -uroot -p123456 -e "CREATE USER 'sqlancer'@'%' IDENTIFIED WITH mysql_native_password BY 'sqlancer'; GRANT ALL PRIVILEGES ON *.* TO 'sqlancer'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;"
- name: Set up JDK 11
uses: actions/setup-java@v1.4.4
with:
java-version: 11
- name: Build
run: mvn -B package -DskipTests=true
- name: StoneDB Tests
run: |
STONEDB_AVAILABLE=true mvn test -Dtest=TestStoneDBTLP

tidb:
name: DBMS Tests (TiDB)
Expand Down
1 change: 0 additions & 1 deletion src/sqlancer/stonedb/StoneDBErrors.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
public final class StoneDBErrors {
private StoneDBErrors() {
}

}
55 changes: 49 additions & 6 deletions src/sqlancer/stonedb/StoneDBOptions.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package sqlancer.stonedb;

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

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;

import sqlancer.DBMSSpecificOptions;
import sqlancer.OracleFactory;
import sqlancer.common.oracle.CompositeTestOracle;
import sqlancer.common.oracle.TestOracle;
import sqlancer.stonedb.StoneDBOptions.StoneDBOracleFactory;
import sqlancer.stonedb.StoneDBProvider.StoneDBGlobalState;
import sqlancer.stonedb.oracle.StoneDBAggregateOracle;
import sqlancer.stonedb.oracle.StoneDBNoRECOracle;
import sqlancer.stonedb.oracle.StoneDBTLPOracle;
import sqlancer.stonedb.oracle.StoneDBQueryPartitioningDistinctTester;
import sqlancer.stonedb.oracle.StoneDBQueryPartitioningGroupByTester;
import sqlancer.stonedb.oracle.StoneDBQueryPartitioningHavingTester;
import sqlancer.stonedb.oracle.StoneDBQueryPartitioningWhereTester;

@Parameters(separators = "=", commandDescription = "StoneDB (default host: " + StoneDBOptions.DEFAULT_HOST
+ ", default port: " + StoneDBOptions.DEFAULT_PORT + ")")
Expand All @@ -22,7 +27,7 @@ public class StoneDBOptions implements DBMSSpecificOptions<StoneDBOracleFactory>
public static final int DEFAULT_PORT = 3306;

@Parameter(names = "--oracle")
public List<StoneDBOracleFactory> oracles = Arrays.asList(StoneDBOracleFactory.NOREC);
public List<StoneDBOracleFactory> oracles = List.of(StoneDBOracleFactory.NOREC);

public enum StoneDBOracleFactory implements OracleFactory<StoneDBGlobalState> {
NOREC {
Expand All @@ -31,10 +36,48 @@ public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) thr
return new StoneDBNoRECOracle(globalState);
}
},
TLP {
QUERY_PARTITIONING {
@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws Exception {
return new StoneDBTLPOracle(globalState);
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
List<TestOracle<StoneDBGlobalState>> oracles = new ArrayList<>();
oracles.add(new StoneDBQueryPartitioningWhereTester(globalState));
oracles.add(new StoneDBQueryPartitioningHavingTester(globalState));
oracles.add(new StoneDBAggregateOracle(globalState));
oracles.add(new StoneDBQueryPartitioningDistinctTester(globalState));
oracles.add(new StoneDBQueryPartitioningGroupByTester(globalState));
return new CompositeTestOracle<>(oracles, globalState);
}
},
HAVING {
@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
return new StoneDBQueryPartitioningHavingTester(globalState);
}
},
WHERE {
@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
return new StoneDBQueryPartitioningWhereTester(globalState);
}
},
GROUP_BY {
@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
return new StoneDBQueryPartitioningGroupByTester(globalState);
}
},
AGGREGATE {

@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
return new StoneDBAggregateOracle(globalState);
}

},
DISTINCT {
@Override
public TestOracle<StoneDBGlobalState> create(StoneDBGlobalState globalState) throws SQLException {
return new StoneDBQueryPartitioningDistinctTester(globalState);
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/sqlancer/stonedb/gen/StoneDBExpressionGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ protected List<Node<StoneDBExpression>> generateColumns() {
return new ArrayList<>(set);
}

// https://stonedb.io/docs/SQL-reference/functions/aggregate-functions/
public enum StoneDBAggregateFunction {
MAX(1), MIN(1), AVG(1), COUNT(1), FIRST(1), SUM(1);

Expand Down Expand Up @@ -317,4 +318,14 @@ public String getTextRepresentation() {
}

}

public NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> generateAggregateAndArgs(
StoneDBAggregateFunction aggregateFunction) {
return new NewFunctionNode<>(generateExpressions(aggregateFunction.getNrArgs()), aggregateFunction);
}

public Node<StoneDBExpression> generateAggregate() {
StoneDBAggregateFunction aggrFunc = StoneDBAggregateFunction.getRandom();
return generateAggregateAndArgs(aggrFunc);
}
}
168 changes: 168 additions & 0 deletions src/sqlancer/stonedb/oracle/StoneDBAggregateOracle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package sqlancer.stonedb.oracle;

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

import sqlancer.ComparatorHelper;
import sqlancer.IgnoreMeException;
import sqlancer.Randomly;
import sqlancer.common.ast.newast.NewAliasNode;
import sqlancer.common.ast.newast.NewFunctionNode;
import sqlancer.common.ast.newast.NewUnaryPostfixOperatorNode;
import sqlancer.common.ast.newast.NewUnaryPrefixOperatorNode;
import sqlancer.common.ast.newast.Node;
import sqlancer.common.query.SQLQueryAdapter;
import sqlancer.common.query.SQLancerResultSet;
import sqlancer.stonedb.StoneDBProvider.StoneDBGlobalState;
import sqlancer.stonedb.StoneDBSchema.StoneDBCompositeDataType;
import sqlancer.stonedb.StoneDBSchema.StoneDBDataType;
import sqlancer.stonedb.StoneDBToStringVisitor;
import sqlancer.stonedb.ast.StoneDBExpression;
import sqlancer.stonedb.ast.StoneDBSelect;
import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBAggregateFunction;
import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBCastOperation;
import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBUnaryPostfixOperator;
import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBUnaryPrefixOperator;

public class StoneDBAggregateOracle extends StoneDBQueryPartitioningBase {

public StoneDBAggregateOracle(StoneDBGlobalState state) {
super(state);
}

@Override
public void check() throws Exception {
super.check();

StoneDBAggregateFunction aggregateFunction = Randomly.fromOptions(StoneDBAggregateFunction.values());
NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> aggregate = gen
.generateAggregateAndArgs(aggregateFunction);

List<Node<StoneDBExpression>> fetchColumns = new ArrayList<>();
fetchColumns.add(aggregate);
select.setFetchColumns(fetchColumns);

if (Randomly.getBooleanWithRatherLowProbability()) {
select.setOrderByExpressions(gen.generateOrderBys());
}

String originalQuery = StoneDBToStringVisitor.asString(select);
String originalResult = getAggregateResult(originalQuery);

String metamorphicQuery = createMetamorphicUnionQuery(select, aggregate, select.getFromList());
String metamorphicResult = getAggregateResult(metamorphicQuery);

String line1 = "--" + originalQuery + ";";
String line2 = "--" + originalResult + ";";
String line3 = "--" + originalResult + ";";
String line4 = "--" + metamorphicResult + ";";
String output = String.join(System.lineSeparator(), line1, line2, line3, line4);
state.getState().getLocalState().log(output);

if (originalResult == null && metamorphicResult != null
|| originalResult != null && (!originalResult.contentEquals(metamorphicResult)
&& !ComparatorHelper.isEqualDouble(originalResult, metamorphicResult))) {
throw new AssertionError("aggregate result mismatch!" + System.lineSeparator() + output);
}
}

private String createMetamorphicUnionQuery(StoneDBSelect select,
NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> aggregate,
List<Node<StoneDBExpression>> from) {
String metamorphicQuery;
Node<StoneDBExpression> whereClause = gen.generateExpression();

Node<StoneDBExpression> negatedClause = new NewUnaryPrefixOperatorNode<>(whereClause,
StoneDBUnaryPrefixOperator.NOT);
Node<StoneDBExpression> notNullClause = new NewUnaryPostfixOperatorNode<>(whereClause,
StoneDBUnaryPostfixOperator.IS_NULL);
List<Node<StoneDBExpression>> mappedAggregate = mapped(aggregate);
StoneDBSelect leftSelect = getSelect(mappedAggregate, from, whereClause, select.getJoinList());
StoneDBSelect middleSelect = getSelect(mappedAggregate, from, negatedClause, select.getJoinList());
StoneDBSelect rightSelect = getSelect(mappedAggregate, from, notNullClause, select.getJoinList());
metamorphicQuery = "SELECT " + getOuterAggregateFunction(aggregate) + " FROM (";
metamorphicQuery += StoneDBToStringVisitor.asString(leftSelect) + " UNION ALL "
+ StoneDBToStringVisitor.asString(middleSelect) + " UNION ALL "
+ StoneDBToStringVisitor.asString(rightSelect);
metamorphicQuery += ") as result";
return metamorphicQuery;
}

private String getAggregateResult(String queryString) throws SQLException {
String resultString;
SQLQueryAdapter q = new SQLQueryAdapter(queryString, errors);
try (SQLancerResultSet result = q.executeAndGet(state)) {
if (result == null) {
throw new IgnoreMeException();
}
if (!result.next()) {
resultString = null;
} else {
resultString = result.getString(1);
}
return resultString;
} catch (SQLException e) {
if (!e.getMessage().contains("Not implemented type")) {
throw new AssertionError(queryString, e);
} else {
throw new IgnoreMeException();
}
}
}

private List<Node<StoneDBExpression>> mapped(
NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> aggregate) {

StoneDBCastOperation count;
switch (aggregate.getFunc()) {
case COUNT:
case MAX:
case MIN:
case SUM:
return aliasArgs(List.of(aggregate));
case AVG:
NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> sum = new NewFunctionNode<>(
aggregate.getArgs(), StoneDBAggregateFunction.SUM);
count = new StoneDBCastOperation(new NewFunctionNode<>(aggregate.getArgs(), StoneDBAggregateFunction.COUNT),
new StoneDBCompositeDataType(StoneDBDataType.DECIMAL).getPrimitiveDataType());
return aliasArgs(Arrays.asList(sum, count));
default:
throw new AssertionError(aggregate.getFunc());
}
}

private List<Node<StoneDBExpression>> aliasArgs(List<Node<StoneDBExpression>> originalAggregateArgs) {
List<Node<StoneDBExpression>> args = new ArrayList<>();
int i = 0;
for (Node<StoneDBExpression> expr : originalAggregateArgs) {
args.add(new NewAliasNode<StoneDBExpression>(expr, "agg" + i++));
}
return args;
}

private String getOuterAggregateFunction(NewFunctionNode<StoneDBExpression, StoneDBAggregateFunction> aggregate) {
switch (aggregate.getFunc()) {
case AVG:
return "SUM(agg0)/SUM(agg1)";
case COUNT:
return StoneDBAggregateFunction.SUM + "(agg0)";
default:
return aggregate.getFunc().toString() + "(agg0)";
}
}

private StoneDBSelect getSelect(List<Node<StoneDBExpression>> aggregates, List<Node<StoneDBExpression>> fromList,
Node<StoneDBExpression> whereClause, List<Node<StoneDBExpression>> joinList) {
StoneDBSelect select = new StoneDBSelect();
select.setFetchColumns(aggregates);
select.setFromList(fromList);
select.setWhereClause(whereClause);
select.setJoinList(joinList);
if (Randomly.getBooleanWithSmallProbability()) {
select.setGroupByExpressions(gen.generateExpressions(Randomly.smallNumber() + 1));
}
return select;
}
}
Loading