use strict; use warnings; use Try::Tiny; use Zonemaster::Backend::Config; my $config = Zonemaster::Backend::Config->load_config(); my %patch = ( mysql => \&patch_db_mysql, postgresql => \&patch_db_postgresql, sqlite => \&patch_db_sqlite, ); my $db_engine = $config->DB_engine; if ( $db_engine =~ /^(MySQL|PostgreSQL|SQLite)$/ ) { $patch{ lc $db_engine }(); } else { die "Unknown database engine configured: $db_engine\n"; } sub patch_db_mysql { use Zonemaster::Backend::DB::MySQL; my $db = Zonemaster::Backend::DB::MySQL->from_config( $config ); my $dbh = $db->dbh; # add table constraints $dbh->do( 'ALTER TABLE users ADD CONSTRAINT UNIQUE (username)' ); $dbh->do( 'ALTER TABLE test_results ADD CONSTRAINT UNIQUE (hash_id)' ); # update columns names, data type and default value $dbh->do( 'ALTER TABLE test_results MODIFY COLUMN id BIGINT AUTO_INCREMENT' ); $dbh->do( 'ALTER TABLE test_results CHANGE COLUMN creation_time created_at DATETIME NOT NULL' ); $dbh->do( 'ALTER TABLE test_results CHANGE COLUMN test_start_time started_at DATETIME DEFAULT NULL' ); $dbh->do( 'ALTER TABLE test_results CHANGE COLUMN test_end_time ended_at DATETIME DEFAULT NULL' ); $dbh->do( 'ALTER TABLE batch_jobs CHANGE COLUMN creation_time created_at DATETIME NOT NULL' ); $dbh->{AutoCommit} = 0; try { # normalize "domain" column $dbh->do( q[ UPDATE test_results SET domain = LOWER(domain) WHERE CAST(domain AS BINARY) RLIKE '[A-Z]' ] ); $dbh->do( q[ UPDATE test_results SET domain = '.' WHERE domain = '..' OR domain = '...' OR domain = '....' ] ); $dbh->do( q[ UPDATE test_results SET domain = TRIM( TRAILING '.' FROM domain ) WHERE domain != '.' AND domain LIKE '%.' ] ); $dbh->commit(); } catch { print( "Could not upgrade database: " . $_ ); eval { $dbh->rollback() }; }; } sub patch_db_postgresql { use Zonemaster::Backend::DB::PostgreSQL; my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); my $dbh = $db->dbh; $dbh->{AutoCommit} = 0; try { # update sequence data type to BIGINT $dbh->do( 'ALTER SEQUENCE test_results_id_seq AS BIGINT' ); $dbh->do( 'ALTER TABLE test_results ALTER COLUMN id SET DATA TYPE BIGINT' ); # remove default value for "creation_time" $dbh->do( 'ALTER TABLE test_results ALTER COLUMN creation_time DROP DEFAULT' ); $dbh->do( 'ALTER TABLE batch_jobs ALTER COLUMN creation_time DROP DEFAULT' ); # rename columns $dbh->do( 'ALTER TABLE test_results RENAME COLUMN creation_time TO created_at' ); $dbh->do( 'ALTER TABLE test_results RENAME COLUMN test_start_time TO started_at' ); $dbh->do( 'ALTER TABLE test_results RENAME COLUMN test_end_time TO ended_at' ); $dbh->do( 'ALTER TABLE batch_jobs RENAME COLUMN creation_time TO created_at' ); # add table constraints $dbh->do( 'ALTER TABLE test_results ADD UNIQUE (hash_id)' ); $dbh->do( 'ALTER TABLE users ADD UNIQUE (username)' ); # normalize "domain" column $dbh->do( q[ UPDATE test_results SET domain = LOWER(domain) WHERE domain != LOWER(domain) ] ); $dbh->do( q[ UPDATE test_results SET domain = '.' WHERE domain = '..' OR domain = '...' OR domain = '....' ] ); $dbh->do( q[ UPDATE test_results SET domain = RTRIM(domain, '.') WHERE domain != '.' AND domain LIKE '%.' ] ); $dbh->commit(); } catch { print( "Could not upgrade database: " . $_ ); eval { $dbh->rollback() }; }; } sub patch_db_sqlite { use Zonemaster::Backend::DB::SQLite; my $db = Zonemaster::Backend::DB::SQLite->from_config( $config ); my $dbh = $db->dbh; $dbh->{AutoCommit} = 0; # since we change the default value for a column, the whole table needs to # be recreated # 1. rename the table to "