package sqlancer; import java.util.ArrayList; import java.util.List; import sqlancer.common.query.Query; public class StatementExecutor, A extends AbstractAction> { private final G globalState; private final A[] actions; private final ActionMapper mapping; private final AfterQueryAction queryConsumer; @FunctionalInterface public interface AfterQueryAction { void notify(Query> q) throws Exception; } @FunctionalInterface public interface ActionMapper { int map(T globalState, A action); } public StatementExecutor(G globalState, A[] actions, ActionMapper mapping, AfterQueryAction queryConsumer) { this.globalState = globalState; this.actions = actions.clone(); this.mapping = mapping; this.queryConsumer = queryConsumer; } @SuppressWarnings("unchecked") public void executeStatements() throws Exception { Randomly r = globalState.getRandomly(); int[] nrRemaining = new int[actions.length]; List availableActions = new ArrayList<>(); int total = 0; for (int i = 0; i < actions.length; i++) { A action = actions[i]; int nrPerformed = mapping.map(globalState, action); if (nrPerformed != 0) { availableActions.add(action); } nrRemaining[i] = nrPerformed; total += nrPerformed; } while (total != 0) { A nextAction = null; int selection = r.getInteger(0, total); int previousRange = 0; int i; for (i = 0; i < nrRemaining.length; i++) { if (previousRange <= selection && selection < previousRange + nrRemaining[i]) { nextAction = actions[i]; break; } else { previousRange += nrRemaining[i]; } } assert nextAction != null; assert nrRemaining[i] > 0; nrRemaining[i]--; @SuppressWarnings("rawtypes") Query query = null; try { boolean success; int nrTries = 0; do { query = nextAction.getQuery(globalState); success = globalState.executeStatement(query); } while (nextAction.canBeRetried() && !success && nrTries++ < globalState.getOptions().getNrStatementRetryCount()); } catch (IgnoreMeException ignored) { } if (query != null && query.couldAffectSchema()) { globalState.updateSchema(); queryConsumer.notify(query); } total--; } } }