Jifty::Handle - A database handle class for Jifty


Jifty documentation Contained in the Jifty distribution.

Index


Code Index:

NAME

Top

Jifty::Handle -- A database handle class for Jifty

DESCRIPTION

Top

A wrapper around Jifty::DBI::Handle which is aware of versions in the database

METHODS

Top

new PARAMHASH

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.

canonical_database_name

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.

connect ARGS

Like Jifty::DBI's connect method but pulls the name of the database from the current Jifty::Config.

check_schema_version [pretend => 0|1]

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.

create_database MODE

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.

drop_database MODE

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.

AUTHOR

Top

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;