diff --git a/src/nl/andrewlalis/DatabaseHelper.java b/src/nl/andrewlalis/DatabaseHelper.java
index 61ebf2c..9aaea02 100644
--- a/src/nl/andrewlalis/DatabaseHelper.java
+++ b/src/nl/andrewlalis/DatabaseHelper.java
@@ -11,7 +11,7 @@ import java.util.List;
import static nl.andrewlalis.Window.*;
-public class DatabaseHelper {
+class DatabaseHelper {
private String host;
private int port;
@@ -19,7 +19,7 @@ public class DatabaseHelper {
private String password;
private Window window;
- public DatabaseHelper(String host, int port, String user, String password, Window window) {
+ DatabaseHelper(String host, int port, String user, String password, Window window) {
this.host = host;
this.port = port;
this.user = user;
@@ -27,7 +27,7 @@ public class DatabaseHelper {
this.window = window;
}
- public void executeSQLComparison(String initializationSQL, String templateSQL, String testingSQL) {
+ void executeSQLComparison(String initializationSQL, String templateSQL, String testingSQL) {
// Run the database code in a separate thread to update the UI quickly.
Thread t = new Thread(() -> {
// Setup both databases.
@@ -37,26 +37,26 @@ public class DatabaseHelper {
"DROP DATABASE " + DB_TESTING + ";";
String createDatabases = "CREATE DATABASE " + DB_TEMPLATE + "; " +
"CREATE DATABASE " + DB_TESTING + ";";
- this.executeQueries("", dropDatabases);
- this.executeQueries("", createDatabases);
+ this.executeQueries("", dropDatabases, false);
+ this.executeQueries("", createDatabases, false);
this.window.unindentOutput();
// Run initialization script on each database.
this.window.appendOutput("Running initialization SQL on databases...");
this.window.indentOutput();
- this.executeQueries(DB_TEMPLATE, initializationSQL);
- this.executeQueries(DB_TESTING, initializationSQL);
+ this.executeQueries(DB_TEMPLATE, initializationSQL, false);
+ this.executeQueries(DB_TESTING, initializationSQL, false);
this.window.unindentOutput();
// TESTING SQL HERE
// Template-specific output.
this.window.setOutputChannel(OUTPUT_TEMPLATE);
- ExecutionLog templateLog = this.executeQueries(DB_TEMPLATE, templateSQL);
+ ExecutionLog templateLog = this.executeQueries(DB_TEMPLATE, templateSQL, true);
// Testing-specific output.
this.window.setOutputChannel(OUTPUT_TESTING);
- ExecutionLog testingLog = this.executeQueries(DB_TESTING, testingSQL);
+ ExecutionLog testingLog = this.executeQueries(DB_TESTING, testingSQL, true);
// Output results.
this.window.setOutputChannel(OUTPUT_GENERAL);
@@ -65,13 +65,30 @@ public class DatabaseHelper {
t.start();
}
+// private void listDatabases() {
+// try {
+// PreparedStatement ps = connection
+// .prepareStatement("SELECT datname FROM pg_database WHERE datistemplate = false;");
+// ResultSet rs = ps.executeQuery();
+// while (rs.next()) {
+// System.out.println(rs.getString(1));
+// }
+// rs.close();
+// ps.close();
+//
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+
/**
* Executes possibly many queries which are contained in one string.
* @param database The database name to connect to, or an empty string to connect to the user's database.
* @param queriesString The string of queries.
+ * @param safe Whether the queries should be checked for safety.
* @return The execution log from this series of queries.
*/
- public ExecutionLog executeQueries(String database, String queriesString) {
+ private ExecutionLog executeQueries(String database, String queriesString, boolean safe) {
ExecutionLog executionLog = new ExecutionLog();
String url = String.format(
"jdbc:postgresql://%s:%4d/%s?user=%s&password=%s",
@@ -93,7 +110,11 @@ public class DatabaseHelper {
for (String query : queries) {
try {
- executionLog.recordAction(executeQuery(query, st));
+ if (!safe || isQuerySafe(query)) {
+ executionLog.recordAction(executeQuery(query, st));
+ } else {
+ window.appendOutput("Blocked execution of unsafe query: " + query);
+ }
} catch (SQLException e) {
window.appendOutput("Exception while executing statement: " + e.getMessage());
}
@@ -152,6 +173,17 @@ public class DatabaseHelper {
return strings;
}
+ /**
+ * Determines if the given query is safe to run.
+ * @param query The query to run.
+ * @return True if this query is safe, or false if it would cause damage to the system.
+ */
+ private static boolean isQuerySafe(String query) {
+ String upper = query.trim().toUpperCase();
+ return !upper.startsWith("CREATE DATABASE")
+ && !upper.startsWith("DROP DATABASE");
+ }
+
/**
* Determines if an SQL string is a query (it should return a result set)
* @param str The string to check.
diff --git a/src/nl/andrewlalis/Main.java b/src/nl/andrewlalis/Main.java
index fe7077e..e3d6d94 100644
--- a/src/nl/andrewlalis/Main.java
+++ b/src/nl/andrewlalis/Main.java
@@ -4,7 +4,7 @@ import javax.swing.*;
public class Main {
- public static final String APPLICATION_NAME = "SQL-Assesser";
+ private static final String APPLICATION_NAME = "SQL-Assesser";
public static void main(String[] args) {
Window window = new Window(APPLICATION_NAME);
diff --git a/src/nl/andrewlalis/Window.form b/src/nl/andrewlalis/Window.form
index 6230ccd..2c08ab2 100644
--- a/src/nl/andrewlalis/Window.form
+++ b/src/nl/andrewlalis/Window.form
@@ -60,6 +60,12 @@
+
+
+
+
+
+
@@ -105,6 +111,12 @@
+
+
+
+
+
+
@@ -210,6 +222,7 @@
+
diff --git a/src/nl/andrewlalis/Window.java b/src/nl/andrewlalis/Window.java
index 51e882b..0e08e54 100644
--- a/src/nl/andrewlalis/Window.java
+++ b/src/nl/andrewlalis/Window.java
@@ -1,9 +1,11 @@
package nl.andrewlalis;
+import nl.andrewlalis.util.FileLoader;
+
import javax.swing.*;
-import javax.xml.crypto.Data;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.text.DefaultCaret;
+import java.io.IOException;
public class Window extends JFrame {
private JPanel mainPanel;
@@ -26,27 +28,34 @@ public class Window extends JFrame {
private JButton loadTemplateFromFileButton;
private JButton loadTestingFromFileButton;
private JButton loadInitializationFromFileButton;
+ private JButton clearTestingButton;
+ private JButton clearTemplateButton;
- public static final int OUTPUT_GENERAL = 0;
- public static final int OUTPUT_TEMPLATE = 1;
- public static final int OUTPUT_TESTING = 2;
+ static final int OUTPUT_GENERAL = 0;
+ static final int OUTPUT_TEMPLATE = 1;
+ static final int OUTPUT_TESTING = 2;
- public static final String DB_TEMPLATE = "sql_assess_template";
- public static final String DB_TESTING = "sql_assess_testing";
+ static final String DB_TEMPLATE = "sql_assess_template";
+ static final String DB_TESTING = "sql_assess_testing";
private int outputChannel;
private int outputIndent;
- public Window(String applicationName) {
+ Window(String applicationName) {
super(applicationName);
+ // Setup autoscrolling on text areas.
+ DefaultCaret caret = (DefaultCaret) this.outputTextArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+
+ // Setup default SQL values.
+ this.fillDefaultSQL();
+
this.setOutputChannel(OUTPUT_GENERAL);
this.setContentPane(mainPanel);
- executeButton.addActionListener(actionEvent -> {
- this.executeSQL();
- });
+ executeButton.addActionListener(actionEvent -> this.executeSQL());
clearOutputButton.addActionListener(actionEvent -> {
this.templateOutputTextArea.setText(null);
@@ -54,9 +63,11 @@ public class Window extends JFrame {
this.outputTextArea.setText(null);
});
- loadInitializationFromFileButton.addActionListener(actionEvent -> {
-
- });
+ loadInitializationFromFileButton.addActionListener(actionEvent -> this.fillSQLFromFileChooser(this.initializationTextArea));
+ loadTemplateFromFileButton.addActionListener(actionEvent -> this.fillSQLFromFileChooser(this.templateTextArea));
+ loadTestingFromFileButton.addActionListener(actionEvent -> this.fillSQLFromFileChooser(this.testingTextArea));
+ clearTemplateButton.addActionListener(actionEvent -> this.templateTextArea.setText(null));
+ clearTestingButton.addActionListener(actionEvent -> this.testingTextArea.setText(null));
}
/**
@@ -119,4 +130,27 @@ public class Window extends JFrame {
break;
}
}
+
+ /**
+ * Fills the input elements from the SQL packaged with this application.
+ */
+ private void fillDefaultSQL() {
+ try {
+ this.initializationTextArea.setText(FileLoader.readResource("initialization.sql"));
+ this.templateTextArea.setText(FileLoader.readResource("template.sql"));
+ this.testingTextArea.setText(FileLoader.readResource("example_test.sql"));
+ } catch (IOException e) {
+ this.appendOutput("Could not load default SQL resources.");
+ e.printStackTrace();
+ }
+ }
+
+ private void fillSQLFromFileChooser(JTextArea textArea) {
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setFileFilter(new FileNameExtensionFilter("SQL", "sql"));
+ int result = fileChooser.showOpenDialog(this);
+ if (result == JFileChooser.APPROVE_OPTION) {
+ textArea.setText(FileLoader.readFile(fileChooser.getSelectedFile()));
+ }
+ }
}
diff --git a/src/nl/andrewlalis/log/ExecutionLog.java b/src/nl/andrewlalis/log/ExecutionLog.java
index c27f0c4..11bbe26 100644
--- a/src/nl/andrewlalis/log/ExecutionLog.java
+++ b/src/nl/andrewlalis/log/ExecutionLog.java
@@ -18,7 +18,7 @@ public class ExecutionLog {
this.actions.add(action);
}
- public List getActions() {
+ private List getActions() {
return this.actions;
}
diff --git a/src/nl/andrewlalis/util/FileLoader.java b/src/nl/andrewlalis/util/FileLoader.java
new file mode 100644
index 0000000..18bc554
--- /dev/null
+++ b/src/nl/andrewlalis/util/FileLoader.java
@@ -0,0 +1,51 @@
+package nl.andrewlalis.util;
+
+import java.io.*;
+
+/**
+ * Helps with loading files.
+ */
+public class FileLoader {
+
+ /**
+ * Reads a resource file from the classpath, and returns its contents as a string.
+ * @param classPath The class path of the resource.
+ * @return The string containing the contents of the file.
+ * @throws IOException If an error occurred or the file could not be read.
+ */
+ public static String readResource(String classPath) throws IOException {
+ String newLine = System.getProperty("line.separator");
+ InputStream is = FileLoader.class.getClassLoader().getResourceAsStream(classPath);
+ if (is == null) {
+ throw new IOException("Could not open input stream to resource.");
+ }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder result = new StringBuilder();
+ boolean flag = false;
+ for (String line; (line = reader.readLine()) != null; ) {
+ result.append(flag? newLine: "").append(line);
+ flag = true;
+ }
+ return result.toString();
+ }
+
+ /**
+ * Reads a file into a string.
+ * @param selectedFile The file object, as it is often selected by a JFileChooser.
+ * @return The string containing the file, or an empty string.
+ */
+ public static String readFile(File selectedFile) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(selectedFile))) {
+ String line;
+ StringBuilder sb = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ sb.append('\n');
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}