Skip to content

Commit b0354de

Browse files
committed
Provide a useful error message when SQLancer fails to connect to the DBMS
1 parent bd4a455 commit b0354de

4 files changed

Lines changed: 53 additions & 11 deletions

File tree

src/sqlancer/Main.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,13 @@ public O getCommand() {
316316
return command;
317317
}
318318

319+
public void testConnection() throws SQLException {
320+
G state = getInitializedGlobalState(options.getRandomSeed());
321+
try (Connection con = provider.createDatabase(state)) {
322+
return;
323+
}
324+
}
325+
319326
public void run() throws SQLException {
320327
G state = createGlobalState();
321328
stateToRepro = provider.getStateToReproduce(databaseName);
@@ -350,6 +357,20 @@ public void run() throws SQLException {
350357
}
351358
}
352359

360+
private G getInitializedGlobalState(long seed) {
361+
G state = createGlobalState();
362+
stateToRepro = provider.getStateToReproduce(databaseName);
363+
stateToRepro.seedValue = seed;
364+
state.setState(stateToRepro);
365+
logger = new StateLogger(databaseName, provider, options);
366+
Randomly r = new Randomly(seed);
367+
state.setRandomly(r);
368+
state.setDatabaseName(databaseName);
369+
state.setMainOptions(options);
370+
state.setDmbsSpecificOptions(command);
371+
return state;
372+
}
373+
353374
public StateLogger getLogger() {
354375
return logger;
355376
}
@@ -393,6 +414,10 @@ public DBMSExecutor<G, O> getDBMSExecutor(String databaseName, Randomly r) {
393414
}
394415
}
395416

417+
public DatabaseProvider<G, O> getProvider() {
418+
return provider;
419+
}
420+
396421
}
397422

398423
public static int executeMain(String... args) throws AssertionError {
@@ -448,6 +473,19 @@ private String formatInteger(long intValue) {
448473

449474
ExecutorService execService = Executors.newFixedThreadPool(options.getNumberConcurrentThreads());
450475
DBMSExecutorFactory<?, ?> executorFactory = nameToProvider.get(jc.getParsedCommand());
476+
477+
if (options.performConnectionTest()) {
478+
try {
479+
executorFactory.getDBMSExecutor(options.getDatabasePrefix() + "connectiontest", new Randomly())
480+
.testConnection();
481+
} catch (SQLException e) {
482+
System.err.println(
483+
"SQLancer failed creating a test database, indicating that SQLancer might have failed connecting to the DBMS. In order to change the username and password, you can use the --username and --password options. Currently, SQLancer does not yet support passing a host and port (see https://github.com/sqlancer/sqlancer/issues/95).\n\n");
484+
e.printStackTrace();
485+
return options.getErrorExitCode();
486+
}
487+
}
488+
451489
for (int i = 0; i < options.getTotalNumberTries(); i++) {
452490
final String databaseName = options.getDatabasePrefix() + i;
453491
final long seed;

src/sqlancer/MainOptions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public class MainOptions {
8585
@Parameter(names = "--use-constant-caching", description = "Specifies whether constants should be cached and re-used with a certain probability")
8686
private boolean useConstantCaching = true; // NOPMD
8787

88+
@Parameter(names = "--use-connection-test", description = "Test whether the DBMS is accessible before trying to connect using multiple threads")
89+
private boolean useConnectionTest = true; // NOPMD
90+
8891
@Parameter(names = "--constant-cache-size", description = "Specifies the size of the constant cache. This option only takes effect when constant caching is enabled")
8992
private int constantCacheSize = 100; // NOPMD
9093

@@ -204,4 +207,8 @@ public String getDatabasePrefix() {
204207
return databasePrefix;
205208
}
206209

210+
public boolean performConnectionTest() {
211+
return useConnectionTest;
212+
}
213+
207214
}

src/sqlancer/postgres/PostgresGlobalState.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.sql.Statement;
77
import java.util.ArrayList;
88
import java.util.Arrays;
9+
import java.util.Collections;
910
import java.util.HashMap;
1011
import java.util.List;
1112
import java.util.Map;
@@ -19,9 +20,9 @@ public class PostgresGlobalState extends GlobalState<PostgresOptions, PostgresSc
1920
public static final char STABLE = 's';
2021
public static final char VOLATILE = 'v';
2122

22-
private List<String> operators;
23-
private List<String> collates;
24-
private List<String> opClasses;
23+
private List<String> operators = Collections.emptyList();
24+
private List<String> collates = Collections.emptyList();
25+
private List<String> opClasses = Collections.emptyList();
2526
// store and allow filtering by function volatility classifications
2627
private final Map<String, Character> functionsAndTypes = new HashMap<>();
2728
private List<Character> allowedFunctionTypes = Arrays.asList(IMMUTABLE, STABLE, VOLATILE);

src/sqlancer/postgres/PostgresProvider.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ public class PostgresProvider extends ProviderAdapter<PostgresGlobalState, Postg
5050
*/
5151
public static boolean generateOnlyKnown;
5252

53-
private PostgresGlobalState globalState;
54-
5553
protected String entryURL;
5654
protected String username;
5755
protected String password;
@@ -244,7 +242,7 @@ public Connection createDatabase(PostgresGlobalState globalState) throws SQLExce
244242
Connection con = DriverManager.getConnection("jdbc:" + entryURL, username, password);
245243
globalState.getState().logStatement(String.format("\\c %s;", entryDatabaseName));
246244
globalState.getState().logStatement("DROP DATABASE IF EXISTS " + databaseName);
247-
createDatabaseCommand = getCreateDatabaseCommand(databaseName, con, globalState);
245+
createDatabaseCommand = getCreateDatabaseCommand(globalState);
248246
globalState.getState().logStatement(createDatabaseCommand);
249247
try (Statement s = con.createStatement()) {
250248
s.execute("DROP DATABASE IF EXISTS " + databaseName);
@@ -297,7 +295,7 @@ protected void prepareTables(PostgresGlobalState globalState) throws SQLExceptio
297295
globalState.executeStatement(new QueryAdapter("SET SESSION statement_timeout = 5000;\n"));
298296
}
299297

300-
private String getCreateDatabaseCommand(String databaseName, Connection con, PostgresGlobalState state) {
298+
private String getCreateDatabaseCommand(PostgresGlobalState state) {
301299
StringBuilder sb = new StringBuilder();
302300
sb.append("CREATE DATABASE " + databaseName + " ");
303301
if (Randomly.getBoolean() && ((PostgresOptions) state.getDmbsSpecificOptions()).testCollations) {
@@ -307,10 +305,8 @@ private String getCreateDatabaseCommand(String databaseName, Connection con, Pos
307305
sb.append("' ");
308306
}
309307
for (String lc : Arrays.asList("LC_COLLATE", "LC_CTYPE")) {
310-
if (Randomly.getBoolean()) {
311-
globalState = new PostgresGlobalState();
312-
globalState.setConnection(con);
313-
sb.append(String.format(" %s = '%s'", lc, Randomly.fromList(globalState.getCollates())));
308+
if (!state.getCollates().isEmpty() && Randomly.getBoolean()) {
309+
sb.append(String.format(" %s = '%s'", lc, Randomly.fromList(state.getCollates())));
314310
}
315311
}
316312
sb.append(" TEMPLATE template0");

0 commit comments

Comments
 (0)