Removed logic for equality of query actions for now.

This commit is contained in:
Andrew Lalis 2019-02-22 21:29:33 +01:00 committed by andrewlalis
parent d6e84f47e7
commit 305ea4211c
4 changed files with 97 additions and 115 deletions

View File

@ -1,5 +1,6 @@
package nl.andrewlalis; package nl.andrewlalis;
import nl.andrewlalis.log.ExecutionAction;
import nl.andrewlalis.log.ExecutionLog; import nl.andrewlalis.log.ExecutionLog;
import nl.andrewlalis.log.QueryAction; import nl.andrewlalis.log.QueryAction;
import nl.andrewlalis.log.UpdateAction; import nl.andrewlalis.log.UpdateAction;
@ -8,6 +9,8 @@ import java.sql.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static nl.andrewlalis.Window.*;
public class DatabaseHelper { public class DatabaseHelper {
private String host; private String host;
@ -16,24 +19,60 @@ public class DatabaseHelper {
private String password; private String password;
private Window window; private Window window;
private ExecutionLog executionLog;
public DatabaseHelper(String host, int port, String user, String password, Window window) { public DatabaseHelper(String host, int port, String user, String password, Window window) {
this.host = host; this.host = host;
this.port = port; this.port = port;
this.user = user; this.user = user;
this.password = password; this.password = password;
this.window = window; this.window = window;
}
this.executionLog = new ExecutionLog(); public 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.
this.window.appendOutput("Dropping old databases and re-creating them...");
this.window.indentOutput();
String dropDatabases = "DROP DATABASE " + DB_TEMPLATE + "; " +
"DROP DATABASE " + DB_TESTING + ";";
String createDatabases = "CREATE DATABASE " + DB_TEMPLATE + "; " +
"CREATE DATABASE " + DB_TESTING + ";";
this.executeQueries("", dropDatabases);
this.executeQueries("", createDatabases);
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.window.unindentOutput();
// TESTING SQL HERE
// Template-specific output.
this.window.setOutputChannel(OUTPUT_TEMPLATE);
ExecutionLog templateLog = this.executeQueries(DB_TEMPLATE, templateSQL);
// Testing-specific output.
this.window.setOutputChannel(OUTPUT_TESTING);
ExecutionLog testingLog = this.executeQueries(DB_TESTING, testingSQL);
// Output results.
this.window.setOutputChannel(OUTPUT_GENERAL);
this.window.appendOutput("Execution test result: " + templateLog.equals(testingLog));
});
t.start();
} }
/** /**
* Executes possibly many queries which are contained in one string. * 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 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 queriesString The string of queries.
* @return The execution log from this series of queries.
*/ */
public void executeQueries(String database, String queriesString) { public ExecutionLog executeQueries(String database, String queriesString) {
ExecutionLog executionLog = new ExecutionLog();
String url = String.format( String url = String.format(
"jdbc:postgresql://%s:%4d/%s?user=%s&password=%s", "jdbc:postgresql://%s:%4d/%s?user=%s&password=%s",
host, host,
@ -54,7 +93,7 @@ public class DatabaseHelper {
for (String query : queries) { for (String query : queries) {
try { try {
executeQuery(query, st); executionLog.recordAction(executeQuery(query, st));
} catch (SQLException e) { } catch (SQLException e) {
window.appendOutput("Exception while executing statement: " + e.getMessage()); window.appendOutput("Exception while executing statement: " + e.getMessage());
} }
@ -67,27 +106,30 @@ public class DatabaseHelper {
window.appendOutput("Unexpected SQL Exception occurred. URL:\n" + url + "\n\tException: " + e.getMessage() + "\n\tSQL State: " + e.getSQLState()); window.appendOutput("Unexpected SQL Exception occurred. URL:\n" + url + "\n\tException: " + e.getMessage() + "\n\tSQL State: " + e.getSQLState());
window.setOutputChannel(previousChannel); window.setOutputChannel(previousChannel);
} }
return executionLog;
} }
/** /**
* Executes a single query and outputs the results. * Executes a single query and outputs the results.
* @param query The query to execute. Must be only one query in the string. * @param query The query to execute. Must be only one query in the string.
* @param statement The statement used to execute the query. * @param statement The statement used to execute the query.
* @return The execution action which was done by executing this query.
*/ */
private void executeQuery(String query, Statement statement) throws SQLException { private ExecutionAction executeQuery(String query, Statement statement) throws SQLException {
if (isSQLStatementQuery(query)) { if (isSQLStatementQuery(query)) {
// A result set is expected. // A result set is expected.
window.appendOutput("Executing query:\n" + query); window.appendOutput("Executing query:\n" + query);
QueryAction action = new QueryAction(statement.executeQuery(query)); QueryAction action = new QueryAction(statement.executeQuery(query));
window.appendOutput(action.toString()); window.appendOutput(action.toString());
this.executionLog.recordAction(action); return action;
} else { } else {
// A result set is not expected. // A result set is not expected.
window.appendOutput("Executing update:\n" + query); window.appendOutput("Executing update:\n" + query);
UpdateAction action = new UpdateAction(statement.executeUpdate(query), query); UpdateAction action = new UpdateAction(statement.executeUpdate(query), query);
window.appendOutput(action.toString()); window.appendOutput(action.toString());
this.executionLog.recordAction(action); return action;
} }
} }

View File

@ -1,6 +1,7 @@
package nl.andrewlalis; package nl.andrewlalis;
import javax.swing.*; import javax.swing.*;
import javax.xml.crypto.Data;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@ -69,37 +70,8 @@ public class Window extends JFrame {
String password = this.passwordTextField.getText(); String password = this.passwordTextField.getText();
String initialization = this.initializationTextArea.getText(); String initialization = this.initializationTextArea.getText();
// Run the database code in a separate thread to update the UI quickly. DatabaseHelper helper = new DatabaseHelper(host, port, user, password, this);
Thread t = new Thread(() -> { helper.executeSQLComparison(initialization, this.templateTextArea.getText(), this.testingTextArea.getText());
DatabaseHelper dbHelper = new DatabaseHelper(host, port, user, password, this);
// Setup both databases.
this.appendOutput("Dropping old databases and re-creating them...");
this.indentOutput();
String dropDatabases = "DROP DATABASE " + DB_TEMPLATE + "; " +
"DROP DATABASE " + DB_TESTING + ";";
String createDatabases = "CREATE DATABASE " + DB_TEMPLATE + "; " +
"CREATE DATABASE " + DB_TESTING + ";";
dbHelper.executeQueries("", dropDatabases);
dbHelper.executeQueries("", createDatabases);
this.unindentOutput();
// Run initialization script on each database.
this.appendOutput("Running initialization SQL on databases...");
this.indentOutput();
dbHelper.executeQueries(DB_TEMPLATE, initialization);
dbHelper.executeQueries(DB_TESTING, initialization);
this.unindentOutput();
// Template-specific output.
this.setOutputChannel(OUTPUT_TEMPLATE);
dbHelper.executeQueries(DB_TEMPLATE, this.templateTextArea.getText());
// Testing-specific output.
this.setOutputChannel(OUTPUT_TESTING);
dbHelper.executeQueries(DB_TESTING, this.testingTextArea.getText());
});
t.start();
} }
int getOutputChannel() { int getOutputChannel() {

View File

@ -31,15 +31,17 @@ public class ExecutionLog {
ExecutionLog otherLog = (ExecutionLog) other; ExecutionLog otherLog = (ExecutionLog) other;
if (otherLog.getActions().size() != this.getActions().size()) { if (otherLog.getActions().size() != this.getActions().size()) {
System.out.println("Size difference in logs.");
return false; return false;
} }
List<ExecutionAction> otherLogActions = otherLog.getActions(); List<ExecutionAction> otherLogActions = otherLog.getActions();
for (int i = 0; i < this.getActions().size(); i++) { for (int i = 0; i < this.getActions().size(); i++) {
if (!this.getActions().get(i).equals(otherLogActions.get(i))) { ExecutionAction myAction = this.getActions().get(i);
return false; ExecutionAction theirAction = otherLogActions.get(i);
} System.out.println("My action: " + myAction + "\nTheir action: " + theirAction);
System.out.println("\tEqual? " + myAction.equals(theirAction));
} }
return true; return true;

View File

@ -3,97 +3,58 @@ package nl.andrewlalis.log;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* An action in which a query result set is returned. * An action in which a query result set is returned. Note that SCROLL_INSENSITIVE statements must be used, otherwise
* an SQL exception will be thrown at each attempt to go through the result set.
*/ */
public class QueryAction extends ExecutionAction { public class QueryAction extends ExecutionAction {
private String[] columns; private ResultSet resultSet;
private String[][] values;
public QueryAction(ResultSet resultSet) throws SQLException { public QueryAction(ResultSet resultSet) {
// Read the columns into this object's memory. this.resultSet = resultSet;
ResultSetMetaData metaData = resultSet.getMetaData();
this.columns = new String[metaData.getColumnCount()];
for (int i = 0; i < metaData.getColumnCount(); i++) {
columns[i] = metaData.getColumnName(i + 1);
}
resultSet.absolute(1);// Ensure that this result set cursor is at the beginning.
// Read the rows into this object's memory.
List<String[]> rows = new ArrayList<>();
while (resultSet.next()) {
String[] row = new String[columns.length];
for (int i = 0; i < columns.length; i++) {
row[i] = resultSet.getString(i + 1);
}
rows.add(row);
}
this.values = new String[rows.size()][];
rows.toArray(this.values);
}
public String[] getColumns() {
return this.columns;
}
public String[][] getValues() {
return this.values;
} }
/**
* The algorithm to determine if two query sets are equivalent is as follows:
* If all of the values of one column contain all of the values of another column, then these two columns must
* almost certainly represent the same value, even if in the wrong order.
* @param other The other object to check equality with.
* @return True if the two query sets are equivalent, or false otherwise.
*/
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (!(other instanceof QueryAction)) { if (!(other instanceof QueryAction)) {
return false; return false;
} }
QueryAction action = (QueryAction) other; QueryAction otherAction = (QueryAction) other;
if (action.getColumns().length != this.columns.length || action.getValues().length != this.values.length) {
return false;
}
for (int i = 0; i < this.values.length; i++) {
Map<String, String> thisColumnValues = new HashMap<>();
Map<String, String> otherColumnValues = new HashMap<>();
for (int k = 0; k < this.values[i].length; k++) {
thisColumnValues.put(this.columns[k], this.values[i][k]);
otherColumnValues.put(action.getColumns()[k], action.getValues()[i][k]);
}
for (String column : this.columns) {
if (thisColumnValues.get(column).equals(otherColumnValues.get(column))) {
return false;
}
}
}
return true; return true;
} }
@Override @Override
public String toString() { public String toString() {
// First build a list of columns. try {
this.resultSet.absolute(1);
ResultSetMetaData metaData = this.resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
StringBuilder sb = new StringBuilder("Query Result:\n\tColumns: ("); StringBuilder sb = new StringBuilder("Query Result:\n\tColumns: (");
for (int i = 0; i < this.columns.length; i++) { for (int i = 0; i < columnCount; i++) {
sb.append(this.columns[i]); sb.append(metaData.getColumnName(i + 1));
if (i < this.columns.length - 1) { if (i < columnCount - 1) {
sb.append(", "); sb.append(", ");
} }
} }
sb.append(")\n\tValues:\n"); sb.append(")\n\tValues:\n");
// Then build a list of the rows. while (this.resultSet.next()) {
for (int i = 0; i < this.values.length; i++) {
sb.append("\t("); sb.append("\t(");
for (int k = 0; k < this.values[i].length; k++) { for (int i = 0; i < columnCount; i++) {
sb.append(this.values[i][k]); sb.append(this.resultSet.getString(i + 1));
if (k < this.values[i].length - 1) { if (i < columnCount - 1) {
sb.append(", "); sb.append(", ");
} }
} }
@ -101,6 +62,11 @@ public class QueryAction extends ExecutionAction {
} }
return sb.toString(); return sb.toString();
} catch (SQLException e) {
e.printStackTrace();
return "SQLException; Please use a SCROLL_INSENSITIVE statement when executing the query.";
}
} }
} }