/
NikolayIvkin
/
sqlancer2
Обзор
Документация
Войти
/
NikolayIvkin
/
sqlancer2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/sqlancer/mysql/MySQLProvider.java
191 строка
7 KB
Manuel Rigger
Use a ServiceLoader to pick up database testing implementations
20 янв 2022, 00:22
20 янв 2022, 00:22
f278315
Код
Авторство
О чём код?
package sqlancer.mysql; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import com.google.auto.service.AutoService; import sqlancer.AbstractAction; import sqlancer.DatabaseProvider; import sqlancer.IgnoreMeException; import sqlancer.MainOptions; import sqlancer.Randomly; import sqlancer.SQLConnection; import sqlancer.SQLProviderAdapter; import sqlancer.StatementExecutor; import sqlancer.common.DBMSCommon; import sqlancer.common.query.SQLQueryAdapter; import sqlancer.common.query.SQLQueryProvider; import sqlancer.mysql.gen.MySQLAlterTable; import sqlancer.mysql.gen.MySQLDeleteGenerator; import sqlancer.mysql.gen.MySQLDropIndex; import sqlancer.mysql.gen.MySQLInsertGenerator; import sqlancer.mysql.gen.MySQLSetGenerator; import sqlancer.mysql.gen.MySQLTableGenerator; import sqlancer.mysql.gen.MySQLTruncateTableGenerator; import sqlancer.mysql.gen.admin.MySQLFlush; import sqlancer.mysql.gen.admin.MySQLReset; import sqlancer.mysql.gen.datadef.MySQLIndexGenerator; import sqlancer.mysql.gen.tblmaintenance.MySQLAnalyzeTable; import sqlancer.mysql.gen.tblmaintenance.MySQLCheckTable; import sqlancer.mysql.gen.tblmaintenance.MySQLChecksum; import sqlancer.mysql.gen.tblmaintenance.MySQLOptimize; import sqlancer.mysql.gen.tblmaintenance.MySQLRepair; @AutoService(DatabaseProvider.class) public class MySQLProvider extends SQLProviderAdapter<MySQLGlobalState, MySQLOptions> { public MySQLProvider() { super(MySQLGlobalState.class, MySQLOptions.class); } enum Action implements AbstractAction<MySQLGlobalState> { SHOW_TABLES((g) -> new SQLQueryAdapter("SHOW TABLES")), // INSERT(MySQLInsertGenerator::insertRow), // SET_VARIABLE(MySQLSetGenerator::set), // REPAIR(MySQLRepair::repair), // OPTIMIZE(MySQLOptimize::optimize), // CHECKSUM(MySQLChecksum::checksum), // CHECK_TABLE(MySQLCheckTable::check), // ANALYZE_TABLE(MySQLAnalyzeTable::analyze), // FLUSH(MySQLFlush::create), RESET(MySQLReset::create), CREATE_INDEX(MySQLIndexGenerator::create), // ALTER_TABLE(MySQLAlterTable::create), // TRUNCATE_TABLE(MySQLTruncateTableGenerator::generate), // SELECT_INFO((g) -> new SQLQueryAdapter( "select TABLE_NAME, ENGINE from information_schema.TABLES where table_schema = '" + g.getDatabaseName() + "'")), // CREATE_TABLE((g) -> { // TODO refactor String tableName = DBMSCommon.createTableName(g.getSchema().getDatabaseTables().size()); return MySQLTableGenerator.generate(g, tableName); }), // DELETE(MySQLDeleteGenerator::delete), // DROP_INDEX(MySQLDropIndex::generate); private final SQLQueryProvider<MySQLGlobalState> sqlQueryProvider; Action(SQLQueryProvider<MySQLGlobalState> sqlQueryProvider) { this.sqlQueryProvider = sqlQueryProvider; } @Override public SQLQueryAdapter getQuery(MySQLGlobalState globalState) throws Exception { return sqlQueryProvider.getQuery(globalState); } } private static int mapActions(MySQLGlobalState globalState, Action a) { Randomly r = globalState.getRandomly(); int nrPerformed = 0; switch (a) { case DROP_INDEX: nrPerformed = r.getInteger(0, 2); break; case SHOW_TABLES: nrPerformed = r.getInteger(0, 1); break; case CREATE_TABLE: nrPerformed = r.getInteger(0, 1); break; case INSERT: nrPerformed = r.getInteger(0, globalState.getOptions().getMaxNumberInserts()); break; case REPAIR: nrPerformed = r.getInteger(0, 1); break; case SET_VARIABLE: nrPerformed = r.getInteger(0, 5); break; case CREATE_INDEX: nrPerformed = r.getInteger(0, 5); break; case FLUSH: nrPerformed = Randomly.getBooleanWithSmallProbability() ? r.getInteger(0, 1) : 0; break; case OPTIMIZE: // seems to yield low CPU utilization nrPerformed = Randomly.getBooleanWithSmallProbability() ? r.getInteger(0, 1) : 0; break; case RESET: // affects the global state, so do not execute nrPerformed = globalState.getOptions().getNumberConcurrentThreads() == 1 ? r.getInteger(0, 1) : 0; break; case CHECKSUM: case CHECK_TABLE: case ANALYZE_TABLE: nrPerformed = r.getInteger(0, 2); break; case ALTER_TABLE: nrPerformed = r.getInteger(0, 5); break; case TRUNCATE_TABLE: nrPerformed = r.getInteger(0, 2); break; case SELECT_INFO: nrPerformed = r.getInteger(0, 10); break; case DELETE: nrPerformed = r.getInteger(0, 10); break; default: throw new AssertionError(a); } return nrPerformed; } @Override public void generateDatabase(MySQLGlobalState globalState) throws Exception { while (globalState.getSchema().getDatabaseTables().size() < Randomly.smallNumber() + 1) { String tableName = DBMSCommon.createTableName(globalState.getSchema().getDatabaseTables().size()); SQLQueryAdapter createTable = MySQLTableGenerator.generate(globalState, tableName); globalState.executeStatement(createTable); } StatementExecutor<MySQLGlobalState, Action> se = new StatementExecutor<>(globalState, Action.values(), MySQLProvider::mapActions, (q) -> { if (globalState.getSchema().getDatabaseTables().isEmpty()) { throw new IgnoreMeException(); } }); se.executeStatements(); } @Override public SQLConnection createDatabase(MySQLGlobalState globalState) throws SQLException { String username = globalState.getOptions().getUserName(); String password = globalState.getOptions().getPassword(); String host = globalState.getOptions().getHost(); int port = globalState.getOptions().getPort(); if (host == null) { host = MySQLOptions.DEFAULT_HOST; } if (port == MainOptions.NO_SET_PORT) { port = MySQLOptions.DEFAULT_PORT; } String databaseName = globalState.getDatabaseName(); globalState.getState().logStatement("DROP DATABASE IF EXISTS " + databaseName); globalState.getState().logStatement("CREATE DATABASE " + databaseName); globalState.getState().logStatement("USE " + databaseName); String url = String.format("jdbc:mysql://%s:%d?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true", host, port); Connection con = DriverManager.getConnection(url, username, password); try (Statement s = con.createStatement()) { s.execute("DROP DATABASE IF EXISTS " + databaseName); } try (Statement s = con.createStatement()) { s.execute("CREATE DATABASE " + databaseName); } try (Statement s = con.createStatement()) { s.execute("USE " + databaseName); } return new SQLConnection(con); } @Override public String getDBMSName() { return "mysql"; } }