| Jifty documentation | Contained in the Jifty distribution. |
Jifty::Handle -- A database handle class for Jifty
A wrapper around Jifty::DBI::Handle which is aware of versions in the database
This class method instantiates a new Jifty::Handle object. This object deals with database handles for the system. After it is created, it will be a subclass of Jifty::DBI::Handle.
Returns the canonical name of the application's database (the actual name that will
be given to the database driver). This name is a lower-case version of the Database
argument in the Database section of the framework config.
For SQLite databases (where the database name is actually a filename), this also converts a relative path into an absolute path based at the application root.
Like Jifty::DBI's connect method but pulls the name of the database from the current Jifty::Config.
Make sure that we have a recent enough database schema. If we don't, then error out.
If pretend = 1> is passed, then any auto-upgrade action this might take is
dry-run only.
MODE is either "print" or "execute".
This method either prints the commands necessary to create the database or actually creates it, depending on the value of MODE. Returns undef on failure.
MODE is either "print" or "execute".
This method either prints the commands necessary to drop the database or actually drops it, depending on the value of MODE. Returns undef on failure.
Various folks at BestPractical Solutions, LLC.
| Jifty documentation | Contained in the Jifty distribution. |
use warnings; use strict; package Jifty::Handle;
use Jifty::Util; our @ISA;
# Setup database handle based on config data sub new { my $class = shift; my $driver = Jifty->config->framework('Database')->{'Driver'}; if ( $driver eq 'Oracle' ) { $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8"; $ENV{'NLS_NCHAR'} = "AL32UTF8"; } # We do this to avoid Jifty::DBI::Handle's magic reblessing, because # it breaks subclass methods. my $driver_class = "Jifty::DBI::Handle::" . $driver; Jifty::Util->require($driver_class); die "No such handle class as $driver_class. ", "Check your spelling and check that your Jifty installation and ", "related modules (especially Jifty::DBI) are up to date." unless $driver_class->can('isa'); unshift @ISA, $driver_class; return $class->SUPER::new(); }
sub canonical_database_name { my $self_or_class = shift; my $db_config = Jifty->config->framework('Database'); # XXX TODO consider canonicalizing to all-lowercase, once there are no # legacy databases my $db = $db_config->{'Database'}; if ( $db_config->{'Driver'} =~ /SQLite/ ) { $db = Jifty::Util->absolute_path($db); # ':' is not allowed in a (esp.) Win32 path. # however, this may break someone's existing setup, so # added existence check (Shawn's advice) $db =~ s{::}{-}g unless -e $db; } return $db; }
sub connect { my $self = shift; my %args = (@_); my %db_config = ( %{ Jifty->config->framework('Database') }, Database => $self->canonical_database_name ); my %lc_db_config; # Skip the non-dsn keys, but not anything else for ( grep { !/^autoupgrade|checkschema|version|forwardcompatible|recordbaseclass|attributes$/i } keys %db_config ) { $lc_db_config{ lc($_) } = $db_config{$_}; } $self->SUPER::connect( %lc_db_config, %args ); $self->{db_config} = { %lc_db_config, %args }; $self->dbh->{LongReadLen} = Jifty->config->framework('MaxAttachmentSize') || '10000000'; # setup attributes my $attributes = Jifty->config->framework('Database')->{Attributes} || {}; for ( keys %$attributes ) { $self->dbh->{ lc($_) } = $attributes->{$_}; } }
sub check_schema_version { my $self = shift; my %opts = ( pretend => 0, @_ ); require Jifty::Model::Metadata; my $autoup = delete Jifty->config->framework('Database')->{'AutoUpgrade'}; # Application db version check { my $appv = Jifty->config->framework('Database')->{'Version'}; my $dbv = $self->_fetch_dbv; unless (defined $dbv) { my $error = "Application schema has no version in the database.\n"; # we can just create it and we'll be up to date if ( $autoup ) { warn $error; warn "Automatically creating your database.\n"; $self->_create_original_database(); return 1; } die "${error}Please run `bin/jifty schema --setup` to create the database.\n"; } unless ( version->new($appv) == version->new($dbv) ) { # if app version is older than db version, but we are still compatible my $compat = delete Jifty->config->framework('Database') ->{'ForwardCompatible'} || $appv; if ( version->new($appv) > version->new($dbv) || version->new($compat) < version->new($dbv) ) { my $error = "Application schema version in database ($dbv) doesn't match application schema version ($appv)\n"; if ( $autoup ) { warn $error; warn "Automatically upgrading your database to match the current application schema.\n"; $self->_upgrade_schema('print' => $opts{pretend}); } else { die "${error}Please run `bin/jifty schema --setup` to upgrade the database.\n"; } } } } # Jifty db version check { # If we got here, the application had a version (somehow) so # this is an upgrade. If $dbv is undef, it's because it's # from before when the _jifty_metadata table existed. my $dbv = version->new( Jifty::Model::Metadata->load("jifty_db_version") || '0.60426' ); my $appv = version->new($Jifty::VERSION); unless ( $appv == $dbv ) { my $error = "Internal jifty schema version in database ($dbv) doesn't match running jifty version ($appv)\n"; if ($autoup) { warn $error; warn "Automatically upgrading your database to match the current Jifty schema\n"; $self->_upgrade_schema('print' => $opts{pretend}); } else { die "${error}Please run `bin/jifty schema --setup` to upgrade the database.\n"; } } } # Plugin version check for my $plugin ( Jifty->plugins ) { my $plugin_class = ref $plugin; my $dbv = Jifty::Model::Metadata->load( $plugin_class . '_db_version' ); my $appv = version->new( $plugin->version ); if ( not defined $dbv ) { my $error = "$plugin_class plugin isn't installed in database\n"; if ($autoup) { warn $error; warn "Automatically upgrading your database to match the current plugin schema\n"; $self->_upgrade_schema('print' => $opts{pretend}); } else { die "${error}Please run `bin/jifty schema --setup` to upgrade the database.\n"; } } elsif (version->new($dbv) < $appv) { my $error = "$plugin_class plugin version in database ($dbv) doesn't match running plugin version ($appv)\n"; if ($autoup) { warn $error; warn "Automatically upgrading your database to match the current plugin schema\n"; $self->_upgrade_schema('print' => $opts{pretend}); } else { die "${error}Please run `bin/jifty schema --setup` to upgrade the database.\n"; } } } }
sub create_database { my $self = shift; my $mode = shift || 'execute'; my $database = $self->canonical_database_name; my $driver = Jifty->config->framework('Database')->{'Driver'}; my $query = "CREATE DATABASE $database"; $query .= " TEMPLATE template0" if $driver =~ /Pg/; if ( $mode eq 'print' ) { print "$query;\n"; } elsif ( $driver =~ /SQLite/ ) { return 1; # Kinda an optimistic hack } else { return $self->simple_query($query); } }
sub drop_database { my $self = shift; my $mode = shift || 'execute'; my $database = $self->canonical_database_name; my $driver = Jifty->config->framework('Database')->{'Driver'}; if ( $mode eq 'print' ) { print "DROP DATABASE $database;\n"; } elsif ( $driver =~ /SQLite/ ) { # Win32 complains when you try to unlink open DB $self->disconnect if $^O eq 'MSWin32'; return unlink($database); } else { local $SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /exist|couldn't execute/i }; return $self->simple_query("DROP DATABASE $database"); } } sub _create_original_database { my $self = shift; my $hack = {}; require Jifty::Script::Schema; bless $hack, "Jifty::Script::Schema"; $hack->create_all_tables; # reconnect for consistency # SQLite complains about the schema being changed $self->disconnect; $self->connect; } sub _upgrade_schema { my $self = shift; my $hack = { @_ }; require Jifty::Script::Schema; bless $hack, "Jifty::Script::Schema"; $hack->run_upgrades; } sub _fetch_dbv { my $self = shift; my $dbv = Jifty::Model::Metadata->load("application_db_version"); return $dbv if defined $dbv; # First layer of backwards compatibility -- it used to be in _db_version eval { local $SIG{__WARN__} = sub { }; my @v = Jifty->handle->fetch_result( "SELECT major, minor, rev FROM _db_version"); return join( ".", @v ) if @v == 3; }; # Second layer -- it was also called the 'key' column, not the data_key column eval { local $SIG{__WARN__} = sub { }; return Jifty->handle->fetch_result( "SELECT value FROM _jifty_metadata WHERE key = 'application_db_version'" ); }; # most likely no database exists return undef; }
1;