package sqlancer; import java.io.Closeable; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; import sqlancer.common.query.Query; public class StateToReproduce implements Serializable { private static final long serialVersionUID = 1L; private List> statements = new ArrayList<>(); private final String databaseName; private transient DatabaseProvider, ?, ?> databaseProvider; public String databaseVersion; protected long seedValue; String exception; public transient OracleRunReproductionState localState; public StateToReproduce(String databaseName, DatabaseProvider, ?, ?> databaseProvider) { this.databaseName = databaseName; this.databaseProvider = databaseProvider; } public String getException() { return exception; } public String getDatabaseName() { return databaseName; } public String getDatabaseVersion() { return databaseVersion; } public DatabaseProvider, ?, ?> getDatabaseProvider() { return databaseProvider; } /** * Logs the statement string without executing the corresponding statement. * * @param queryString * the query string to be logged */ public void logStatement(String queryString) { if (queryString == null) { throw new IllegalArgumentException(); } logStatement(databaseProvider.getLoggableFactory().getQueryForStateToReproduce(queryString)); } /** * Logs the statement without executing it. * * @param query * the query to be logged */ public void logStatement(Query> query) { if (query == null) { throw new IllegalArgumentException(); } statements.add(query); } public List> getStatements() { return Collections.unmodifiableList(statements); } /** * @deprecated */ @Deprecated public void commentStatements() { for (int i = 0; i < statements.size(); i++) { Query> statement = statements.get(i); Query> newQuery = databaseProvider.getLoggableFactory().commentOutQuery(statement); statements.set(i, newQuery); } } public long getSeedValue() { return seedValue; } /** * Returns a local state in which a test oracle can save useful information about a single run. If the local state * is closed without indicating access to it, the local statements will be added to the global state. * * @return the local state for logging */ public OracleRunReproductionState getLocalState() { return localState; } /** * State information that is logged if the test oracle finds a bug or if an exception is thrown. */ public class OracleRunReproductionState implements Closeable { private final List> statements = new ArrayList<>(); private boolean success; public OracleRunReproductionState() { StateToReproduce.this.localState = this; } public void executedWithoutError() { this.success = true; } public void log(String s) { statements.add(databaseProvider.getLoggableFactory().getQueryForStateToReproduce(s)); } @Override public void close() { if (!success) { StateToReproduce.this.statements.addAll(statements); } } } public OracleRunReproductionState createLocalState() { return new OracleRunReproductionState(); } public void serialize(Path path) { try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(path))) { oos.writeObject(this); } catch (IOException e) { throw new AssertionError(e); } } public static StateToReproduce deserialize(Path path) { try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(path))) { return (StateToReproduce) ois.readObject(); } catch (IOException | ClassNotFoundException e) { throw new AssertionError(e); } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(this.databaseProvider != null ? this.databaseProvider.getDBMSName() : null); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); String dbmsName = (String) in.readObject(); DatabaseProvider, ?, ?> provider = null; if (dbmsName != null) { List> providers = Main.getDBMSProviders(); for (DatabaseProvider, ?, ?> p : providers) { if (p.getDBMSName().equals(dbmsName)) { provider = p; break; } } } this.databaseProvider = provider; } public void setStatements(List> statements) { this.statements = statements; } }