Skip to content

Commit

Permalink
Rework Line Ending Normalization => Hash Rules
Browse files Browse the repository at this point in the history
The existing implementation did not account for mac line endings and did not normalize new hashes to be consistent.

This change does three things:
 - All future hashes are computed with line ending normalized to unix
 - Compares hashes with the unnormalized and the unix normalized (accounts for old hashes and new)
 - Compares the hash value with the line endings normalized to windows, unix, or mac

Any existing hash in the database that was calculated from a file with any consistent line ending should be pronounced equal. The only
case that we are not going to handle is if the existing hash was calculated from a file with mixed line endings and the line endings have
changed.
  • Loading branch information
BiggerNoise committed Oct 9, 2017
1 parent d5678b4 commit 82649eb
Showing 1 changed file with 22 additions and 18 deletions.
40 changes: 22 additions & 18 deletions product/roundhouse/migrators/DefaultDatabaseMigrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace roundhouse.migrators
using infrastructure.logging;
using sqlsplitters;
using Environment = roundhouse.environments.Environment;
using System.Linq;

public sealed class DefaultDatabaseMigrator : DatabaseMigrator
{
Expand Down Expand Up @@ -256,7 +257,7 @@ public IEnumerable<string> get_statements_to_run(string sql_to_run)
public void record_script_in_scripts_run_table(string script_name, string sql_to_run, bool run_this_script_once, long version_id)
{
Log.bound_to(this).log_a_debug_event_containing("Recording {0} script ran on {1} - {2}.", script_name, database.server_name, database.database_name);
database.insert_script_run(script_name, sql_to_run, create_hash(sql_to_run), run_this_script_once, version_id);
database.insert_script_run(script_name, sql_to_run, create_hash(sql_to_run, true), run_this_script_once, version_id);
}

public void record_script_in_scripts_run_errors_table(string script_name, string sql_to_run, string sql_erroneous_part, string error_message, string repository_version, string repository_path)
Expand All @@ -265,9 +266,12 @@ public void record_script_in_scripts_run_errors_table(string script_name, string
database.insert_script_run_error(script_name, sql_to_run, sql_erroneous_part, error_message, repository_version, repository_path);
}

private string create_hash(string sql_to_run)
private string create_hash(string sql_to_run, bool normalizeEndings)
{
return crypto_provider.hash(sql_to_run.Replace(@"'", @"''"));
var input = sql_to_run.Replace(@"'", @"''");
if (normalizeEndings)
input = input.Replace(WindowsLineEnding, UnixLineEnding).Replace(MacLineEnding, UnixLineEnding);
return crypto_provider.hash(input);
}

public bool this_is_an_every_time_script(string script_name, bool run_this_script_every_time)
Expand Down Expand Up @@ -316,9 +320,13 @@ private bool this_script_has_changed_since_last_run(string script_name, string s

if (string.IsNullOrEmpty(old_text_hash)) return true;

string new_text_hash = create_hash(sql_to_run);

bool hash_is_same = hashes_are_equal(new_text_hash, old_text_hash);
// These check hashes from before the normalization change and after
// The change does result in a different hash that will not be the result of
// any sore of file change and therefore should not be logged.
bool hash_is_same =
hashes_are_equal(create_hash(sql_to_run, true), old_text_hash) || // New hash
hashes_are_equal(create_hash(sql_to_run, false), old_text_hash); // Old hash

if (!hash_is_same)
{
Expand All @@ -338,22 +346,18 @@ private bool hashes_are_equal(string new_text_hash, string old_text_hash)
return string.Compare(old_text_hash, new_text_hash, true) == 0;
}

private const string WindowsLineEnding = "\r\n";
private const string UnixLineEnding = "\n";
private const string MacLineEnding = "\r";

private bool have_same_hash_ignoring_platform(string sql_to_run, string old_text_hash)
{
// check with unix and windows line endings
const string line_ending_windows = "\r\n";
const string line_ending_unix = "\n";
string new_text_hash = create_hash(sql_to_run.Replace(line_ending_windows, line_ending_unix));
bool hash_is_same = hashes_are_equal(new_text_hash, old_text_hash);

if (!hash_is_same)
{
// try other way around
new_text_hash = create_hash(sql_to_run.Replace(line_ending_unix, line_ending_windows));
hash_is_same = hashes_are_equal(new_text_hash, old_text_hash);
}
var lineEndingVariations = new List<string> {WindowsLineEnding, UnixLineEnding, MacLineEnding};

return hash_is_same;
return lineEndingVariations.Any(variation => {
var normalized_sql = lineEndingVariations.Aggregate(sql_to_run, (norm, ending) => norm.Replace(ending, variation));
return hashes_are_equal(create_hash(normalized_sql, false), old_text_hash);
});
}

private bool this_script_should_run(string script_name, string sql_to_run, bool run_this_script_once, bool run_this_script_every_time)
Expand Down

0 comments on commit 82649eb

Please sign in to comment.