From 9efc91f82fcbeb199d628fe0db95a0dff53bf72f Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 23 Feb 2019 07:25:43 +0100 Subject: [PATCH] Added query result equality algorithm. --- src/nl/andrewlalis/DatabaseHelper.java | 11 +- src/nl/andrewlalis/Window.form | 6 +- src/nl/andrewlalis/log/ExecutionLog.java | 3 + src/nl/andrewlalis/log/QueryAction.java | 123 ++++++++++++++++++++++- 4 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/nl/andrewlalis/DatabaseHelper.java b/src/nl/andrewlalis/DatabaseHelper.java index a2fdb99..61ebf2c 100644 --- a/src/nl/andrewlalis/DatabaseHelper.java +++ b/src/nl/andrewlalis/DatabaseHelper.java @@ -121,7 +121,7 @@ public class DatabaseHelper { // A result set is expected. window.appendOutput("Executing query:\n" + query); - QueryAction action = new QueryAction(statement.executeQuery(query)); + QueryAction action = new QueryAction(statement.executeQuery(query), isQueryOrdered(query)); window.appendOutput(action.toString()); return action; } else { @@ -162,4 +162,13 @@ public class DatabaseHelper { return upper.startsWith("SELECT"); } + /** + * Determines if a query is ordered by something. + * @param query The query to check. + * @return True if the query makes use of the 'ORDER BY' clause. + */ + private static boolean isQueryOrdered(String query) { + return query.toUpperCase().contains("ORDER BY"); + } + } diff --git a/src/nl/andrewlalis/Window.form b/src/nl/andrewlalis/Window.form index f693ee9..6230ccd 100644 --- a/src/nl/andrewlalis/Window.form +++ b/src/nl/andrewlalis/Window.form @@ -44,7 +44,7 @@ - + @@ -89,7 +89,7 @@ - + @@ -356,7 +356,7 @@ - + diff --git a/src/nl/andrewlalis/log/ExecutionLog.java b/src/nl/andrewlalis/log/ExecutionLog.java index e4e8d95..c27f0c4 100644 --- a/src/nl/andrewlalis/log/ExecutionLog.java +++ b/src/nl/andrewlalis/log/ExecutionLog.java @@ -42,6 +42,9 @@ public class ExecutionLog { ExecutionAction theirAction = otherLogActions.get(i); System.out.println("My action: " + myAction + "\nTheir action: " + theirAction); System.out.println("\tEqual? " + myAction.equals(theirAction)); + if (!myAction.equals(theirAction)) { + return false; + } } return true; diff --git a/src/nl/andrewlalis/log/QueryAction.java b/src/nl/andrewlalis/log/QueryAction.java index d30d7e9..a21bcc1 100644 --- a/src/nl/andrewlalis/log/QueryAction.java +++ b/src/nl/andrewlalis/log/QueryAction.java @@ -3,6 +3,8 @@ package nl.andrewlalis.log; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; /** * An action in which a query result set is returned. Note that SCROLL_INSENSITIVE statements must be used, otherwise @@ -11,9 +13,11 @@ import java.sql.SQLException; public class QueryAction extends ExecutionAction { private ResultSet resultSet; + private boolean isOrdered; - public QueryAction(ResultSet resultSet) { + public QueryAction(ResultSet resultSet, boolean isOrdered) { this.resultSet = resultSet; + this.isOrdered = isOrdered; } /** @@ -31,9 +35,124 @@ public class QueryAction extends ExecutionAction { QueryAction otherAction = (QueryAction) other; - return true; + try { + ResultSetMetaData myMetaData = this.resultSet.getMetaData(); + ResultSetMetaData theirMetaData = otherAction.resultSet.getMetaData(); + + if (myMetaData.getColumnCount() != theirMetaData.getColumnCount()) { + return false; + } + + int columnCount = myMetaData.getColumnCount(); + + List myColumnsQueue = new ArrayList<>(columnCount); + List theirColumnsQueue = new ArrayList<>(columnCount); + for (int i = 1; i <= columnCount; i++) { + myColumnsQueue.add(i); + theirColumnsQueue.add(i); + } + + while (!myColumnsQueue.isEmpty()) { + + System.out.println(myColumnsQueue); + System.out.println(theirColumnsQueue); + + // Pop the first column value. + int myColumn = myColumnsQueue.remove(0); + System.out.println("Testing my column " + myColumn); + + // Find a column in their columns which has the same type as this one. + boolean columnMatchFound = false; + int failedAttempts = 0; + while (!theirColumnsQueue.isEmpty() && failedAttempts < myColumnsQueue.size() + 1) { + int theirColumn = theirColumnsQueue.remove(0); + System.out.println("\tWith their column " + theirColumn); + // Check if this column is of a compatible type. + if (myMetaData.getColumnType(myColumn) == theirMetaData.getColumnType(theirColumn)) { + System.out.println("\t\t+ Column type matches."); + // Now check if the data inside is a match. + List myValues = new ArrayList<>(); + List theirValues = new ArrayList<>(); + this.resultSet.beforeFirst(); + otherAction.resultSet.beforeFirst(); + + // Iterate until one of the result sets goes past the last row. + while (true) { + this.resultSet.next(); + otherAction.resultSet.next(); + if (this.resultSet.isAfterLast() || otherAction.resultSet.isAfterLast()) { + break; + } + // Collect this row's values for both my column and their column. + myValues.add(this.resultSet.getString(myColumn)); + theirValues.add(otherAction.resultSet.getString(theirColumn)); + } + // Check if both row counts are the same; i.e. both reach the end at the same time. + if (this.resultSet.isAfterLast() != otherAction.resultSet.isAfterLast()) { + System.out.println("\t\t- Result sets have differing row counts!"); + return false; + } + + // Compare the values until an error. + boolean isMatch = true; + int index = 0; + for (String value : myValues) { + // If either query action is ordered, then require this. + if (this.isOrdered || otherAction.isOrdered) { + if (!(myValues.get(index).equals(theirValues.get(index)))) { + System.out.println("\t\t- Their column (" + theirColumn + ") does not contain my column's (" + myColumn + ") value of " + value); + isMatch = false; + break; + } + } else { + if (!theirValues.contains(value)) { + System.out.println("\t\t- Their column (" + theirColumn + ") does not contain my column's (" + myColumn + ") value of " + value); + isMatch = false; + break; + } + } + index++; + } + + // If the column data matches. + if (isMatch) { + System.out.println("\t\t+ Columns Match! Ensuring my column " + myColumn +" and their column " + theirColumn + " are not added back to the queues."); + // Leave the loop knowing we found a column, without adding it back. + columnMatchFound = true; + break; + } else { + System.out.println("\t\t- Columns do not match."); + // No column was found to match, so add it back to their queue. + theirColumnsQueue.add(theirColumn); + failedAttempts++; + } + } else { + System.out.println("\t\t- Column types do not match."); + theirColumnsQueue.add(theirColumn); + failedAttempts++; + } + } + + // columnMatchFound == true when a column match has been found somewhere in other result set. + if (!columnMatchFound) { + System.out.println("\t\t- Could not find a matching column for my column " + myColumn); + return false; + } + } + + // If we manage to get to the end without failing, return true. + return true; + + } catch (SQLException e) { + e.printStackTrace(); + return false; + } } + /** + * Translates the result set into a listing of column names and queries. + * @return The string representation of this QueryAction. + */ @Override public String toString() { try {