Skip to content

Commit e4414df

Browse files
authored
Merge pull request #1261 from jinhuix/serialize-state-to-reproduce
Serialize StateToReproduce to JSON
2 parents 061a337 + 30d879a commit e4414df

File tree

15 files changed

+93
-6
lines changed

15 files changed

+93
-6
lines changed

src/sqlancer/Main.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.IOException;
66
import java.io.Writer;
77
import java.nio.file.Files;
8+
import java.nio.file.Path;
89
import java.text.DateFormat;
910
import java.text.SimpleDateFormat;
1011
import java.util.ArrayList;
@@ -78,6 +79,7 @@ public static final class StateLogger {
7879
public FileWriter currentFileWriter;
7980
private FileWriter queryPlanFileWriter;
8081
private FileWriter reduceFileWriter;
82+
private Path reproduceFilePath;
8183

8284
private static final List<String> INITIALIZED_PROVIDER_NAMES = new ArrayList<>();
8385
private final boolean logEachSelect;
@@ -127,7 +129,13 @@ public StateLogger(String databaseName, DatabaseProvider<?, ?, ?> provider, Main
127129
reduceFileDir.mkdir();
128130
}
129131
this.reduceFile = new File(reduceFileDir, databaseName + "-reduce.log");
130-
132+
}
133+
if (options.serializeReproduceState()) {
134+
File reproduceFileDir = new File(dir, "reproduce");
135+
if (!reproduceFileDir.exists()) {
136+
reproduceFileDir.mkdir();
137+
}
138+
reproduceFilePath = new File(reproduceFileDir, databaseName + ".ser").toPath();
131139
}
132140
this.databaseProvider = provider;
133141
}
@@ -341,6 +349,10 @@ private String removeNamesFromQueryPlans(String queryPlan) {
341349
result = result.replaceAll("i[0-9]+", "i0"); // Avoid duplicate indexes
342350
return result + "\n";
343351
}
352+
353+
public Path getReproduceFilePath() {
354+
return reproduceFilePath;
355+
}
344356
}
345357

346358
public static class QueryManager<C extends SQLancerDBConnection> {
@@ -461,6 +473,9 @@ public void run() throws Exception {
461473
throw new AssertionError(e);
462474
}
463475

476+
if (options.serializeReproduceState() && reproducer != null) {
477+
stateToRepro.serialize(logger.getReproduceFilePath());
478+
}
464479
if (options.reduceAST() && !options.useReducer()) {
465480
throw new AssertionError("To reduce AST, use-reducer option must be enabled first");
466481
}
@@ -675,6 +690,9 @@ private boolean run(MainOptions options, ExecutorService execService,
675690
executor.getStateToReproduce().exception = reduce.getMessage();
676691
executor.getLogger().logFileWriter = null;
677692
executor.getLogger().logException(reduce, executor.getStateToReproduce());
693+
if (options.serializeReproduceState()) {
694+
executor.getStateToReproduce().serialize(executor.getLogger().getReproduceFilePath());
695+
}
678696
return false;
679697
} finally {
680698
try {

src/sqlancer/MainOptions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ public class MainOptions {
123123
@Parameter(names = "--database-prefix", description = "The prefix used for each database created")
124124
private String databasePrefix = "database"; // NOPMD
125125

126+
@Parameter(names = "--serialize-reproduce-state", description = "Serialize the state to reproduce")
127+
private boolean serializeReproduceState = false; // NOPMD
128+
126129
@Parameter(names = "--use-reducer", description = "EXPERIMENTAL Attempt to reduce queries using a simple reducer")
127130
private boolean useReducer = false; // NOPMD
128131

@@ -304,6 +307,10 @@ public boolean performConnectionTest() {
304307
return useConnectionTest;
305308
}
306309

310+
public boolean serializeReproduceState() {
311+
return serializeReproduceState;
312+
}
313+
307314
public boolean useReducer() {
308315
return useReducer;
309316
}

src/sqlancer/StateToReproduce.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package sqlancer;
22

33
import java.io.Closeable;
4+
import java.io.IOException;
5+
import java.io.ObjectInputStream;
6+
import java.io.ObjectOutputStream;
7+
import java.io.Serializable;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
410
import java.util.ArrayList;
511
import java.util.Collections;
612
import java.util.List;
713

814
import sqlancer.common.query.Query;
915

10-
public class StateToReproduce {
16+
public class StateToReproduce implements Serializable {
17+
private static final long serialVersionUID = 1L;
1118

1219
private List<Query<?>> statements = new ArrayList<>();
1320

1421
private final String databaseName;
1522

16-
private final DatabaseProvider<?, ?, ?> databaseProvider;
23+
private transient DatabaseProvider<?, ?, ?> databaseProvider;
1724

1825
public String databaseVersion;
1926

@@ -131,6 +138,45 @@ public OracleRunReproductionState createLocalState() {
131138
return new OracleRunReproductionState();
132139
}
133140

141+
public void serialize(Path path) {
142+
try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(path))) {
143+
oos.writeObject(this);
144+
} catch (IOException e) {
145+
throw new AssertionError(e);
146+
}
147+
}
148+
149+
public static StateToReproduce deserialize(Path path) {
150+
try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(path))) {
151+
return (StateToReproduce) ois.readObject();
152+
} catch (IOException | ClassNotFoundException e) {
153+
throw new AssertionError(e);
154+
}
155+
}
156+
157+
private void writeObject(ObjectOutputStream out) throws IOException {
158+
out.defaultWriteObject();
159+
160+
out.writeObject(this.databaseProvider != null ? this.databaseProvider.getDBMSName() : null);
161+
}
162+
163+
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
164+
in.defaultReadObject();
165+
String dbmsName = (String) in.readObject();
166+
167+
DatabaseProvider<?, ?, ?> provider = null;
168+
if (dbmsName != null) {
169+
List<DatabaseProvider<?, ?, ?>> providers = Main.getDBMSProviders();
170+
for (DatabaseProvider<?, ?, ?> p : providers) {
171+
if (p.getDBMSName().equals(dbmsName)) {
172+
provider = p;
173+
break;
174+
}
175+
}
176+
}
177+
this.databaseProvider = provider;
178+
}
179+
134180
public void setStatements(List<Query<?>> statements) {
135181
this.statements = statements;
136182
}

src/sqlancer/cnosdb/query/CnosDBOtherQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import sqlancer.common.query.ExpectedErrors;
77

88
public class CnosDBOtherQuery extends CnosDBQueryAdapter {
9+
private static final long serialVersionUID = 1L;
10+
911
public CnosDBOtherQuery(String query, ExpectedErrors errors) {
1012
super(query, errors);
1113
}

src/sqlancer/cnosdb/query/CnosDBQueryAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sqlancer.common.query.Query;
66

77
public abstract class CnosDBQueryAdapter extends Query<CnosDBConnection> {
8+
private static final long serialVersionUID = 1L;
89

910
String query;
1011
ExpectedErrors errors;

src/sqlancer/cnosdb/query/CnosDBSelectQuery.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sqlancer.common.query.SQLancerResultSet;
88

99
public class CnosDBSelectQuery extends CnosDBQueryAdapter {
10+
private static final long serialVersionUID = 1L;
1011
CnosDBResultSet resultSet;
1112

1213
public CnosDBSelectQuery(String query, ExpectedErrors errors) {
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package sqlancer.common.log;
22

3-
public interface Loggable {
3+
import java.io.Serializable;
4+
5+
public interface Loggable extends Serializable {
46
String getLogString();
57
}

src/sqlancer/common/log/LoggedString.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sqlancer.common.log;
22

33
public class LoggedString implements Loggable {
4+
private static final long serialVersionUID = 1L;
45

56
private final String loggedString;
67

src/sqlancer/common/query/ExpectedErrors.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package sqlancer.common.query;
22

3+
import java.io.Serializable;
34
import java.util.Arrays;
45
import java.util.Collection;
56
import java.util.HashSet;
@@ -11,7 +12,8 @@
1112
* result in an error "UNIQUE constraint violated" when it attempts to insert a duplicate value in a column declared as
1213
* UNIQUE.
1314
*/
14-
public class ExpectedErrors {
15+
public class ExpectedErrors implements Serializable {
16+
private static final long serialVersionUID = 1L;
1517

1618
private final Set<String> errors;
1719
private final Set<Pattern> regexes;

src/sqlancer/common/query/Query.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sqlancer.common.log.Loggable;
66

77
public abstract class Query<C extends SQLancerDBConnection> implements Loggable {
8+
private static final long serialVersionUID = 1L;
89

910
/**
1011
* Gets the query string, which is guaranteed to be terminated with a semicolon.

0 commit comments

Comments
 (0)