DBIx-Class

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Schema/Versioned.pm  view on Meta::CPAN

  );
  my $sql_dir = './sql';
  my $version = $schema->schema_version();
  $schema->create_ddl_dir( 'MySQL', $version, $sql_dir, $preversion );

Then your upgrade script might look like so:

  use strict;
  use MyApp::Schema;

  my $schema = MyApp::Schema->connect(
    $dsn,
    $user,
    $password,
  );

  if (!$schema->get_db_version()) {
    # schema is unversioned
    $schema->deploy();
  } else {
    $schema->upgrade();
  }

The script above assumes that if the database is unversioned then it is empty
and we can safely deploy the DDL to it. However things are not always so simple.

if you want to initialise a pre-existing database where the DDL is not the same
as the DDL for your current schema version then you will need a diff which
converts the database's DDL to the current DDL. The best way to do this is
to get a dump of the database schema (without data) and save that in your
SQL directory as version 0.000 (the filename must be as with
L<DBIx::Class::Schema/ddl_filename>) then create a diff using your create DDL
script given above from version 0.000 to the current version. Then hand check
and if necessary edit the resulting diff to ensure that it will apply. Once you have
done all that you can do this:

  if (!$schema->get_db_version()) {
    # schema is unversioned
    $schema->install("0.000");
  }

  # this will now apply the 0.000 to current version diff
  $schema->upgrade();

In the case of an unversioned database the above code will create the
dbix_class_schema_versions table and write version 0.000 to it, then
upgrade will then apply the diff we talked about creating in the previous paragraph
and then you're good to go.

=cut

package DBIx::Class::Schema::Versioned;

use strict;
use warnings;
use base 'DBIx::Class::Schema';

use DBIx::Class::Carp;
use Time::HiRes qw/gettimeofday/;
use Try::Tiny;
use Scalar::Util 'weaken';
use namespace::clean;

__PACKAGE__->mk_classdata('_filedata');
__PACKAGE__->mk_classdata('upgrade_directory');
__PACKAGE__->mk_classdata('backup_directory');
__PACKAGE__->mk_classdata('do_backup');
__PACKAGE__->mk_classdata('do_diff_on_init');


=head1 METHODS

=head2 upgrade_directory

Use this to set the directory your upgrade files are stored in.

=head2 backup_directory

Use this to set the directory you want your backups stored in (note that backups
are disabled by default).

=cut

=head2 install

=over 4

=item Arguments: $db_version

=back

Call this to initialise a previously unversioned database. The table 'dbix_class_schema_versions' will be created which will be used to store the database version.

Takes one argument which should be the version that the database is currently at. Defaults to the return value of L</schema_version>.

See L</GETTING STARTED> for more details.

=cut

sub install
{
  my ($self, $new_version) = @_;

  # must be called on a fresh database
  if ($self->get_db_version()) {
      $self->throw_exception("A versioned schema has already been deployed, try upgrade instead.\n");
  }

  # default to current version if none passed
  $new_version ||= $self->schema_version();

  if ($new_version) {
    # create versions table and version row
    $self->{vschema}->deploy;
    $self->_set_db_version({ version => $new_version });
  }
}

=head2 deploy

Same as L<DBIx::Class::Schema/deploy> but also calls C<install>.

lib/DBIx/Class/Schema/Versioned.pm  view on Meta::CPAN

               ->next;
    };
    return $version || 0;
}

=head2 schema_version

Returns the current schema class' $VERSION

=cut

=head2 backup

This is an overwritable method which is called just before the upgrade, to
allow you to make a backup of the database. Per default this method attempts
to call C<< $self->storage->backup >>, to run the standard backup on each
database type.

This method should return the name of the backup file, if appropriate..

This method is disabled by default. Set $schema->do_backup(1) to enable it.

=cut

sub backup
{
    my ($self) = @_;
    ## Make each ::DBI::Foo do this
    $self->storage->backup($self->backup_directory());
}

=head2 connection

Overloaded method. This checks the DBIC schema version against the DB version and
warns if they are not the same or if the DB is unversioned. It also provides
compatibility between the old versions table (SchemaVersions) and the new one
(dbix_class_schema_versions).

To avoid the checks on connect, set the environment var DBIC_NO_VERSION_CHECK or alternatively you can set the ignore_version attr in the forth argument like so:

  my $schema = MyApp::Schema->connect(
    $dsn,
    $user,
    $password,
    { ignore_version => 1 },
  );

=cut

sub connection {
  my $self = shift;
  $self->next::method(@_);
  $self->_on_connect();
  return $self;
}

sub _on_connect
{
  my ($self) = @_;

  weaken (my $w_storage = $self->storage );

  $self->{vschema} = DBIx::Class::Version->connect(
    sub { $w_storage->dbh },

    # proxy some flags from the main storage
    { map { $_ => $w_storage->$_ } qw( unsafe ) },
  );
  my $conn_attrs = $w_storage->_dbic_connect_attributes || {};

  my $vtable = $self->{vschema}->resultset('Table');

  # useful when connecting from scripts etc
  return if ($conn_attrs->{ignore_version} || ($ENV{DBIC_NO_VERSION_CHECK} && !exists $conn_attrs->{ignore_version}));

  # check for legacy versions table and move to new if exists
  unless ($self->_source_exists($vtable)) {
    my $vtable_compat = DBIx::Class::VersionCompat->connect(sub { $w_storage->dbh })->resultset('TableCompat');
    if ($self->_source_exists($vtable_compat)) {
      $self->{vschema}->deploy;
      map { $vtable->new_result({ installed => $_->Installed, version => $_->Version })->insert } $vtable_compat->all;
      $self->storage->_get_dbh->do("DROP TABLE " . $vtable_compat->result_source->from);
    }
  }

  my $pversion = $self->get_db_version();

  if($pversion eq $self->schema_version)
    {
        #carp "This version is already installed";
        return 1;
    }

  if(!$pversion)
    {
        carp "Your DB is currently unversioned. Please call upgrade on your schema to sync the DB.";
        return 1;
    }

  carp "Versions out of sync. This is " . $self->schema_version .
    ", your database contains version $pversion, please call upgrade on your Schema.";
}

# is this just a waste of time? if not then merge with DBI.pm
sub _create_db_to_schema_diff {
  my $self = shift;

  my %driver_to_db_map = (
                          'mysql' => 'MySQL'
                         );

  my $db = $driver_to_db_map{$self->storage->dbh->{Driver}->{Name}};
  unless ($db) {
    print "Sorry, this is an unsupported DB\n";
    return;
  }

  unless (DBIx::Class::Optional::Dependencies->req_ok_for ('deploy')) {
    $self->throw_exception("Unable to proceed without " . DBIx::Class::Optional::Dependencies->req_missing_for ('deploy') );
  }



( run in 0.616 second using v1.01-cache-2.11-cpan-39bf76dae61 )