Minor cookbook fix (two adjacent examples were mixed up)
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema / Versioned.pm
index 9416583..fa403ea 100644 (file)
@@ -1,40 +1,66 @@
-package DBIx::Class::Version::Table;
+package # Hide from PAUSE
+  DBIx::Class::Version::Table;
 use base 'DBIx::Class';
 use strict;
 use warnings;
 
 __PACKAGE__->load_components(qw/ Core/);
-__PACKAGE__->table('SchemaVersions');
+__PACKAGE__->table('dbix_class_schema_versions');
 
 __PACKAGE__->add_columns
-    ( 'Version' => {
+    ( 'version' => {
         'data_type' => 'VARCHAR',
         'is_auto_increment' => 0,
         'default_value' => undef,
         'is_foreign_key' => 0,
-        'name' => 'Version',
+        'name' => 'version',
         'is_nullable' => 0,
         'size' => '10'
         },
-      'Installed' => {
+      'installed' => {
           'data_type' => 'VARCHAR',
           'is_auto_increment' => 0,
           'default_value' => undef,
           'is_foreign_key' => 0,
-          'name' => 'Installed',
+          'name' => 'installed',
           'is_nullable' => 0,
           'size' => '20'
           },
       );
+__PACKAGE__->set_primary_key('version');
+
+package # Hide from PAUSE
+  DBIx::Class::Version::TableCompat;
+use base 'DBIx::Class';
+__PACKAGE__->load_components(qw/ Core/);
+__PACKAGE__->table('SchemaVersions');
+
+__PACKAGE__->add_columns
+    ( 'Version' => {
+        'data_type' => 'VARCHAR',
+        },
+      'Installed' => {
+          'data_type' => 'VARCHAR',
+          },
+      );
 __PACKAGE__->set_primary_key('Version');
 
-package DBIx::Class::Version;
+package # Hide from PAUSE
+  DBIx::Class::Version;
 use base 'DBIx::Class::Schema';
 use strict;
 use warnings;
 
 __PACKAGE__->register_class('Table', 'DBIx::Class::Version::Table');
 
+package # Hide from PAUSE
+  DBIx::Class::VersionCompat;
+use base 'DBIx::Class::Schema';
+use strict;
+use warnings;
+
+__PACKAGE__->register_class('TableCompat', 'DBIx::Class::Version::TableCompat');
+
 
 # ---------------------------------------------------------------------------
 
@@ -61,7 +87,7 @@ classes, to enable them to upgrade to newer schema layouts. To use this
 module, you need to have called C<create_ddl_dir> on your Schema to
 create your upgrade files to include with your delivery.
 
-A table called I<SchemaVersions> is created and maintained by the
+A table called I<dbix_class_schema_versions> is created and maintained by the
 module. This contains two fields, 'Version' and 'Installed', which
 contain each VERSION of your Schema, and the date+time it was installed.
 
@@ -126,7 +152,7 @@ sub schema_version {
 =head2 get_db_version
 
 Returns the version that your database is currently at. This is determined by the values in the
-SchemaVersions table that $self->upgrade writes to.
+dbix_class_schema_versions table that $self->upgrade writes to.
 
 =cut
 
@@ -135,18 +161,12 @@ sub get_db_version
     my ($self, $rs) = @_;
 
     my $vtable = $self->{vschema}->resultset('Table');
-    return 0 unless ($self->_source_exists($vtable));
-
-    my $psearch = $vtable->search(undef, 
-                                    { select => [
-                                                 { 'max' => 'Installed' },
-                                                 ],
-                                          as => ['maxinstall'],
-                                      })->first;
-    my $pversion = $vtable->search({ Installed => $psearch->get_column('maxinstall'),
-                                })->first;
-    $pversion = $pversion->Version if($pversion);
-    return $pversion;
+    my $version = 0;
+    eval {
+      my $stamp = $vtable->get_column('installed')->max;
+      $version = $vtable->search({ installed => $stamp })->first->version;
+    };
+    return $version;
 }
 
 sub _source_exists
@@ -170,6 +190,8 @@ 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
@@ -179,7 +201,7 @@ sub backup
     $self->storage->backup($self->backup_directory());
 }
 
-# is this just a waste of time?
+# is this just a waste of time? if not then merge with DBI.pm
 sub _create_db_to_schema_diff {
   my $self = shift;
 
@@ -193,8 +215,10 @@ sub _create_db_to_schema_diff {
     return;
   }
 
-  require SQL::Translator;
-  require SQL::Translator::Diff;
+  eval 'require SQL::Translator "0.09"';
+  if ($@) {
+    $self->throw_exception("SQL::Translator 0.09 required");
+  }
 
   my $db_tr = SQL::Translator->new({ 
                                     add_drop_table => 1, 
@@ -257,7 +281,7 @@ sub upgrade
 
   # db unversioned
   unless ($db_version) {
-    # set version in SchemaVersions table, can't actually upgrade as we don 't know what version the DB is at
+    # set version in dbix_class_schema_versions table, can't actually upgrade as we don 't know what version the DB is at
     $self->_create_db_to_schema_diff() if ($self->do_diff_on_init);
 
     # create versions table and version row
@@ -272,6 +296,12 @@ sub upgrade
     return;
   }
 
+  # strangely the first time this is called can
+  # differ to subsequent times. so we call it 
+  # here to be sure.
+  # XXX - just fix it
+  $self->storage->sqlt_type;
+  
   my $upgrade_file = $self->ddl_filename(
                                          $self->storage->sqlt_type,
                                          $self->upgrade_directory,
@@ -289,7 +319,7 @@ sub upgrade
   $self->backup() if($self->do_backup);
   $self->txn_do(sub { $self->do_upgrade() });
 
-  # set row in SchemaVersions table
+  # set row in dbix_class_schema_versions table
   $self->_set_db_version;
 }
 
@@ -297,8 +327,8 @@ sub _set_db_version {
   my $self = shift;
 
   my $vtable = $self->{vschema}->resultset('Table');
-  $vtable->create({ Version => $self->schema_version,
-                      Installed => strftime("%Y-%m-%d %H:%M:%S", gmtime())
+  $vtable->create({ version => $self->schema_version,
+                      installed => strftime("%Y-%m-%d %H:%M:%S", gmtime())
                       });
 
 }
@@ -309,10 +339,12 @@ sub _read_sql_file {
 
   my $fh;
   open $fh, "<$file" or warn("Can't open upgrade file, $file ($!)");
-  my @data = split(/[;\n]/, join('', <$fh>));
+  my @data = split(/\n/, join('', <$fh>));
+  @data = grep(!/^--/, @data);
+  @data = split(/;/, join('', @data));
   close($fh);
   @data = grep { $_ && $_ !~ /^-- / } @data;
-  @data = grep { $_ !~ /^(BEGIN TRANACTION|COMMIT)/m } @data;
+  @data = grep { $_ !~ /^(BEGIN TRANSACTION|COMMIT)/m } @data;
   return \@data;
 }
 
@@ -323,22 +355,16 @@ allows you to run your upgrade any way you please, you can call C<run_upgrade>
 any number of times to run the actual SQL commands, and in between you can
 sandwich your data upgrading. For example, first run all the B<CREATE>
 commands, then migrate your data from old to new tables/formats, then 
-issue the DROP commands when you are finished.
-
-Will run the whole file as it is by default.
+issue the DROP commands when you are finished. Will run the whole file as it is by default.
 
 =cut
 
 sub do_upgrade
 {
-    my ($self) = @_;
+  my ($self) = @_;
 
-    ## overridable sub, per default just run all the commands.
-    $self->run_upgrade(qr/create/i);
-    $self->run_upgrade(qr/alter table .*? add/i);
-    $self->run_upgrade(qr/alter table .*? (?!drop)/i);
-    $self->run_upgrade(qr/alter table .*? drop/i);
-    $self->run_upgrade(qr/drop/i);
+  # just run all the commands (including inserts) in order                                                        
+  $self->run_upgrade(qr/.*?/);
 }
 
 =head2 run_upgrade
@@ -371,23 +397,57 @@ sub run_upgrade
     return 1;
 }
 
+=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 env var DBIC_NO_VERSION_CHECK or alternatively you can set the ignore_version attr in the forth arg 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;
+  $self->_on_connect($_[3]);
   return $self;
 }
 
 sub _on_connect
 {
-  my ($self) = @_;
+  my ($self, $args) = @_;
+
+  $args = {} unless $args;
   $self->{vschema} = DBIx::Class::Version->connect(@{$self->storage->connect_info()});
+  my $vtable = $self->{vschema}->resultset('Table');
+
+  # check for legacy versions table and move to new if exists
+  my $vschema_compat = DBIx::Class::VersionCompat->connect(@{$self->storage->connect_info()});
+  unless ($self->_source_exists($vtable)) {
+    my $vtable_compat = $vschema_compat->resultset('TableCompat');
+    if ($self->_source_exists($vtable_compat)) {
+      $self->{vschema}->deploy;
+      map { $vtable->create({ installed => $_->Installed, version => $_->Version }) } $vtable_compat->all;
+      $self->storage->dbh->do("DROP TABLE " . $vtable_compat->result_source->from);
+    }
+  }
 
+  # useful when connecting from scripts etc
+  return if ($args->{ignore_version} || ($ENV{DBIC_NO_VERSION_CHECK} && !exists $args->{ignore_version}));
   my $pversion = $self->get_db_version();
 
   if($pversion eq $self->schema_version)
     {
-        warn "This version is already installed\n";
+#         warn "This version is already installed\n";
         return 1;
     }