diff --git a/dba/update-rscds-database b/dba/update-rscds-database deleted file mode 100755 index 3a5475d4..00000000 --- a/dba/update-rscds-database +++ /dev/null @@ -1,363 +0,0 @@ -#!/usr/bin/perl -w -# -# Update the DAViCal database by repeatedly applying patches to it -# in the correct order. -# - -use strict; - -use DBI; -use POSIX qw(floor); -use Getopt::Long qw(:config permute); # allow mixed args. - -# Options variables -my $debug = 0; -my $dbname = "rscds"; -my $dbport = 5432; -my $dbuser = ""; -my $dbpass = ""; -my $dbhost = ""; -my $appuser = "general"; -my $helpmeplease = 0; -my $apply_patches = 1; -my $revoke_list = ""; -my $force_owner = ""; -my $config_file = "config/administration.yml"; - -my $dbadir = $0; -$dbadir =~ s#/[^/]*$##; -my $patchdir = $dbadir . "/patches"; - -# -# We look in a few places for the config file. First relative to -# where we are, then relative to the code we are running, then we -# start to look in absolute locations. Then we give up :-) -if ( ! -f $config_file ) { - $config_file = $0; - $config_file =~ s{[^/]+/update-[a-z]+-database}{config/administration.yml}; -} -if ( ! -f $config_file ) { - $config_file = "/etc/davical/administration.yml"; -} -if ( -f $config_file ) { - use YAML qw( LoadFile ); - - my ($ycfg) = LoadFile($config_file); - $dbuser = $ycfg->{'admin_db_user'} if ( defined($ycfg->{'admin_db_user'})); - $dbpass = $ycfg->{'admin_db_pass'} if ( defined($ycfg->{'admin_db_pass'})); - $dbhost = $ycfg->{'admin_db_host'} if ( defined($ycfg->{'admin_db_host'})); - $dbname = $ycfg->{'admin_db_name'} if ( defined($ycfg->{'admin_db_name'})); - $dbport = $ycfg->{'admin_db_port'} if ( defined($ycfg->{'admin_db_port'})); - $appuser = $ycfg->{'app_db_user'} if ( defined($ycfg->{'app_db_user'})); -} - -GetOptions ('debug!' => \$debug, - 'dbname=s' => \$dbname, - 'dbuser=s' => \$dbuser, - 'dbpass=s' => \$dbpass, - 'dbport=s' => \$dbport, - 'dbhost=s' => \$dbhost, - 'appuser=s' => \$appuser, - 'patch!' => \$apply_patches, - 'owner=s' => \$force_owner, - 'revoke=s' => \$revoke_list, - 'help' => \$helpmeplease ); - -show_usage() if ( $helpmeplease ); - -$revoke_list = ", ". $revoke_list if ( $revoke_list ne "" ); - - -############################################################ -# Open database connection. Note that the standard PostgreSQL -# environment variables will also work with DBD::Pg. -############################################################ -my $dsn = "dbi:Pg:dbname=$dbname"; -$dsn .= ";host=$dbhost" if ( "$dbhost" ne "" ); -$dsn .= ";port=$dbport" if ( $dbport != 5432 ); - -print "Using database: $dbuser".'%'.$dbpass.'@'.$dsn."\n" if ( $debug ); - -my $current_revision; -my $last_results = ''; # Will hold the last SQL result from applying a patch - -if ( $apply_patches ) { - $current_revision = get_current_revision(); - printf( "The database is currently at revision %d.%d.%d.\n", $current_revision->{'schema_major'}, $current_revision->{'schema_minor'}, $current_revision->{'schema_patch'} ); - - opendir( PATCHDIR, $patchdir ) or die "Can't open patch directory $patchdir"; - my @patches = grep { /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)\.sql$/ } readdir(PATCHDIR); - closedir(PATCHDIR); - - @patches = sort { compare_revisions(revision_hash($a),revision_hash($b), 1); } @patches; - - my $applied = 0; - - for ( my $i=0; $i <= $#patches; $i++ ) { - printf( "Looking at patches[%d] (%s)\n", $i, $patches[$i]) if ( $debug ); - if ( compare_revisions(revision_hash($patches[$i]),$current_revision) > 0 ) { - print "Applying patch $patches[$i] ... "; - if ( !apply_patch( $patches[$i] ) ) { - # Skip to the end unless the next patch is an alternate for the same version. - if ( defined($patches[$i+1]) && compare_revisions(revision_hash($patches[$i]),revision_hash($patches[$i+1])) == 0 ) { - print "failed. Attempting next alternative.\n"; - $applied--; - } - else { - print "failed!\n$last_results ==> No further patches will be attempted!\n"; - last; - } - } - else { - print "succeeded.\n"; - } - $applied++; - } - else { - print "Patch $patches[$i] has already been applied.\n" if ( $debug ); - } - } - - if ( $applied ) { - print "Successfully applied $applied patches.\n"; - } - else { - print "No patches were applied.\n"; - } -} - -# Ensure the locales data is up to date -apply_sql_file( $dbadir, "supported_locales.sql" ); -print "Supported locales updated.\n"; - -# Ensure the functions are up to date -apply_sql_file( $dbadir, "caldav_functions.sql" ); -print "CalDAV functions updated.\n"; - -apply_sql_file( $dbadir, "rrule_functions.sql" ); -print "RRULE functions updated.\n"; - -# Ensure the permissions are up to date -apply_permissions( $dbadir, "appuser_permissions.txt" ); -print "Database permissions updated.\n"; - -# The End! -exit 0; - - - - -############################################################ -# Revision Hash - we either have a single parameter, -# which is of the form "1.2.3" or we have three parameters. -############################################################ -sub revision_hash { - my $rev = +{ 'schema_major', => 0, 'schema_minor' => 0, 'schema_patch' => 0, 'alternative' => '0' }; - my $first = shift; - return $rev unless ( defined($first) ); - if ( $first =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)([^0-9]|$)/ ) { - $rev->{'schema_major'} = $1; - $rev->{'schema_minor'} = $2; - $rev->{'schema_patch'} = $3; - $rev->{'alternative'} = $4; - } - else { - $rev->{'schema_major'} = $first; - $rev->{'schema_minor'} = shift; - $rev->{'schema_patch'} = shift; - $rev->{'alternative'} = '0'; - } - return $rev; -} - - -############################################################ -# Compare revisions -############################################################ -sub compare_revisions { - my $a = shift; - my $b = shift; - my $test_alt = shift; - - return -1 if ( $a->{'schema_major'} < $b->{'schema_major'} ); - return 1 if ( $a->{'schema_major'} > $b->{'schema_major'} ); - - return -1 if ( $a->{'schema_minor'} < $b->{'schema_minor'} ); - return 1 if ( $a->{'schema_minor'} > $b->{'schema_minor'} ); - - return -1 if ( $a->{'schema_patch'} < $b->{'schema_patch'} ); - return 1 if ( $a->{'schema_patch'} > $b->{'schema_patch'} ); - - if ( defined($test_alt) ) { - return -1 if ( $a->{'alternative'} lt $b->{'alternative'} ); - return 1 if ( $a->{'alternative'} gt $b->{'alternative'} ); - } - - return 0; - -} - - - -############################################################ -# Get the current revision -############################################################ -sub get_current_revision { - - my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 0 } ) or die "Can't connect to database $dbname"; - - my $current_revision = $dbh->prepare( <errstr; - SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1 -EOQ - - if ( $current_revision->execute() ) { - my $revision = $current_revision->fetchrow_hashref(); - undef $current_revision; - $dbh->disconnect; - return $revision; - } - else { - die "ERROR: Cannot read current revision from database."; - } - -} - - - -############################################################ -# Apply a DB Patch File -############################################################ -sub apply_patch { - - my $patch = shift; - - apply_sql_file( $patchdir, $patch ); - - $current_revision = get_current_revision(); - if ( compare_revisions($current_revision,revision_hash($patch)) != 0 ) { - printf( "Failed to apply revision %s to the database!\n", $patch ) if ( $debug ); - return 0; - } - return 1; # Success -} - - - -############################################################ -# Apply SQL File -# Note that this stuffs the password into an environment -# variable, which isn't ideal. If you use a .pgpass you -# can bypass that issue, but you still need it on the command -# line for this program until I get a patch from someone. -############################################################ -sub apply_sql_file { - - my $sqldir = shift; - my $sqlfile = shift; - - my @psql_opts = ( "psql", "-q", "-f", $sqldir."/".$sqlfile ); - push @psql_opts, "-h", $dbhost if ( $dbhost ne "" ); - push @psql_opts, "-p", "$dbport" if ( $dbport != 5432 ); - push @psql_opts, "-U", $dbuser if ( $dbuser ne "" ); - push @psql_opts, $dbname; # It seems that FreeBSD has a poorer argument parsing library so non-option arguments must be last - $ENV{'PGPASS'} = $dbpass if ( $dbpass ne "" ); - - my $command = join ' ', @psql_opts; - $last_results = `$command 2>&1 1>/dev/null`; - - $last_results =~ s/^.*WARNING: there is no transaction in progress\s$//m; - $last_results =~ s/^.*NOTICE: //m; -} - - - -############################################################ -# Apply database permissions from file -############################################################ -sub apply_permissions { - - my $sqldir = shift; - my $permsfile = shift; - - open PERMS, '<', $sqldir."/".$permsfile; - my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 1 } ) or die "Can't connect to database $dbname"; - - my $sql; - my $current_grant; - - while( ) { - next if ( /^\s*(#|--)/ ); - - /^\s*GRANT\s+(\S.*)\s*$/i && do { - $current_grant = $1; - }; - - /^\s*ON\s+(\S.*)\s*$/i && do { - defined($current_grant) or die "No GRANT before ON in $permsfile\n"; - my $doohickey = $1; - - if ( $revoke_list ne "" ) { - # TODO: we should really loop through the revoke_list so that a single non-existent - # user doesn't cause this whole statement to fail. - $sql = sprintf( "REVOKE ALL ON %s FROM %s %s", $doohickey, $appuser, $revoke_list ); - print $sql, "\n" if ( $debug ); - $dbh->do($sql); - } - - $sql = sprintf( "GRANT %s on %s to %s", $current_grant, $doohickey, $appuser ); - print $sql, "\n" if ( $debug ); - $dbh->do($sql); - - if ( $force_owner ne "" ) { - if ( $doohickey =~ /_seq$/ ) { - $sql = sprintf( "GRANT ALL on %s to %s", $doohickey, $force_owner ); - } - else { - $sql = sprintf( "ALTER TABLE %s OWNER to %s", $doohickey, $force_owner ); - } - print $sql, "\n" if ( $debug ); - $dbh->do($sql); - } - }; - - } - close(PERMS); - $dbh->disconnect; -} - - - -############################################################ -# Tell the nice user how we do things. Short and sweet. -############################################################ -sub show_usage { - print <