diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 02e9582c..ffd98169 100755 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -9,6 +9,7 @@ use DBI; use Scalar::Util qw(weaken isweak); use Data::Dumper; use Time::HiRes qw(gettimeofday tv_interval); +use SQL::Parser; our $VERSION = "3.0.0"; my $THIS_FILE = "Database.pm"; @@ -5965,82 +5966,130 @@ sub _split_query my $public_query = ""; my $history_query = ""; - my $type = ""; - my $schema = ""; - my $table = ""; - foreach my $line (split/\n/, $query) + # Find out if we're doing an INSERT or UPDATE, which schema we're writing to, and if we have or need + # to inject the change_uuid. + my $parser = SQL::Parser->new(); + my $schema = "public"; + my $table = ""; + my $command = ""; + my $change_date = ""; + my $change_uuid = ""; + my $success = $parser->parse($query); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { success => $success }}); + if ($success) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); - if (not $type) + #print Dumper $parser->structure; + $table = $parser->structure->{org_table_names}->[0]; + $command = $parser->structure->{command}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + table => $table, + command => $command, + }}); + if ($table =~ /^(.*?)\.(.*)$/) { - if ($line =~ /INSERT INTO/i) - { - $type = "insert"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { type => $type }}); - } - elsif ($line =~ /UPDATE/i) - { - $type = "update"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { type => $type }}); - } + $table = $1; + $schema = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + table => $table, + schema => $schema, + }}); } - if (($type) && (not $table)) + + for (my $i = 0; $i < @{$parser->structure->{column_defs}}; $i++) { - # The table could be the next line, or it could be after the INSERT or UPDATE. - if ($type eq "insert") + my $column_name = $parser->structure->{column_defs}->[$i]->{value}; + my $column_value = $parser->structure->{'values'}->[0]->[$i]->{value}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + column_name => $column_name, + column_value => $column_value, + }}); + if ($column_name eq "change_date") { - if ($line =~ /INSERT INTO (.*?) \(/i) - { - $table = $anvil->Words->clean_spaces({string => $1}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }}); - } - elsif ($line !~ /INSERT INTO/i) - { - $table = $anvil->Words->clean_spaces({string => $line}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }}); - } + $change_date = $column_value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { change_date => $change_date }}); } - elsif ($type eq "update") + elsif ($column_name eq "change_uuid") { - if ($line =~ /UPDATE (.*?) /i) - { - $table = $anvil->Words->clean_spaces({string => $1}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }}); - } - elsif ($line !~ /UPDATE/i) - { - $table = $anvil->Words->clean_spaces({string => $line}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }}); - } + $change_uuid = $column_value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { change_uuid => $change_uuid }}); } } - if ($table) + } + else + { + print "Failed to parse: [".$query."]\n"; + } + + # Inject the $change_uuid, if needed. + my $new_query = ""; + if ($change_uuid) + { + # Just strait copy the query to new_query + $new_query = $query; + } + else + { + my $column_end_seen = 0; + $change_uuid = $anvil->Get->uuid({debug => $debug}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { change_uuid => $change_uuid }}); + + foreach my $line (split/\n/, $query) { - if ($table =~ /(.*?)\.(.*)$/) + if ((not $column_end_seen) && ($command eq "INSERT") && ($line =~ /\)/)) { - $schema = $1; - $table = $2; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - schema => $schema, - table => $table, - }}); + $column_end_seen = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ">> line" => $line }}); + $line =~ s/\)/, change_uuid = '$change_uuid')/; + $line =~ s/ ,/,/gs; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "<< line" => $line }}); } - else + elsif ((not $column_end_seen) && ($command eq "UPDATE") && ($line =~ /WHERE/i)) { - $schema = "public"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { schema => $schema }}); + $column_end_seen = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ">> line" => $line }}); + $line =~ s/WHERE/, change_uuid = '$change_uuid' WHERE/gs; + $line =~ s/ ,/,/gs; + $line =~ s/ WHERE $/ \nWHERE /; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "<< line" => $line }}); } + $new_query .= $line."\n"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_query => $new_query }}); } - last if $table; + $new_query =~ s/\n$//gs; + + if ($new_query =~ /\n, change_uuid/gs) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ">> new_query" => $new_query }}); + $new_query =~ s/\n, change_uuid/, \n change_uuid/gs; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "<< new_query" => $new_query }}); + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_query => $new_query }}); + } + + if ($schema eq "history") + { + # Nothing more to do. + $history_query = $new_query; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { history_query => $history_query }}); + } + else + { + # Create a copy and prepend 'history.' to the table name + $public_query = $new_query; + $history_query = $new_query; + $history_query =~ s/ $table/ history.$table/gs; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + public_query => $public_query, + history_query => $history_query, + }}); } - # If the query is not an INSERT or UPDATE, we're done. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - type => $type, - schema => $schema, - table => $table, + query => $query, + public_query => $public_query, + history_query => $history_query, }}); - return($public_query, $history_query); } diff --git a/notes b/notes index 560f63c6..b6026479 100644 --- a/notes +++ b/notes @@ -1,3 +1,5 @@ +What if I simply add a flag to Database->write to refresh 'sys::database::timestamp' and not round it? Then things like 'update_job' could ask for the date to be updated and keep all the records unique? + DB stuff; Dump; diff --git a/rpm/SPECS/anvil.spec b/rpm/SPECS/anvil.spec index 2f689590..d9c8dc17 100644 --- a/rpm/SPECS/anvil.spec +++ b/rpm/SPECS/anvil.spec @@ -43,6 +43,7 @@ Requires: perl-Log-Journald Requires: perl-Net-SSH2 Requires: perl-NetAddr-IP Requires: perl-Proc-Simple +Requires: perl-SQL-Statement Requires: perl-Sys-Syslog Requires: perl-Time-HiRes Requires: perl-UUID-Tiny @@ -394,8 +395,8 @@ firewall-cmd --add-service=postgresql --permanent %changelog -* Madison Kelly 3.0-17 -- Added 'perl-UUID-Tiny' to core dependencies. +* Madison Kelly 3.0-17 +- Added 'perl-UUID-Tiny' and 'perl-SQL-Statement' to core dependencies. * Fri Sep 14 2018 Madison Kelly 3.0-16 - Added htop as a -core dependency.