fixed broken regex when reading sql files
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema / Versioned.pm
CommitLineData
c9d2e0a2 1package DBIx::Class::Version::Table;
2use base 'DBIx::Class';
3use strict;
4use warnings;
5
6__PACKAGE__->load_components(qw/ Core/);
7__PACKAGE__->table('SchemaVersions');
8
9__PACKAGE__->add_columns
10 ( 'Version' => {
11 'data_type' => 'VARCHAR',
12 'is_auto_increment' => 0,
13 'default_value' => undef,
14 'is_foreign_key' => 0,
15 'name' => 'Version',
16 'is_nullable' => 0,
17 'size' => '10'
18 },
19 'Installed' => {
20 'data_type' => 'VARCHAR',
21 'is_auto_increment' => 0,
22 'default_value' => undef,
23 'is_foreign_key' => 0,
24 'name' => 'Installed',
25 'is_nullable' => 0,
26 'size' => '20'
27 },
28 );
29__PACKAGE__->set_primary_key('Version');
30
31package DBIx::Class::Version;
32use base 'DBIx::Class::Schema';
33use strict;
34use warnings;
35
36__PACKAGE__->register_class('Table', 'DBIx::Class::Version::Table');
37
38
39# ---------------------------------------------------------------------------
40package DBIx::Class::Schema::Versioned;
41
42use strict;
43use warnings;
44use base 'DBIx::Class';
45use POSIX 'strftime';
46use Data::Dumper;
47
48__PACKAGE__->mk_classdata('_filedata');
49__PACKAGE__->mk_classdata('upgrade_directory');
8795fefb 50__PACKAGE__->mk_classdata('backup_directory');
f925f7cb 51__PACKAGE__->mk_classdata('do_backup');
c9d2e0a2 52
42416a0b 53sub schema_version {
54 my ($self) = @_;
55 my $class = ref($self)||$self;
56 my $version;
57 {
58 no strict 'refs';
59 $version = ${"${class}::VERSION"};
60 }
61 return $version;
62}
63
a2800991 64sub connection {
65 my $self = shift;
66 $self->next::method(@_);
67 $self->_on_connect;
68 return $self;
69}
70
737416a4 71sub _on_connect
c9d2e0a2 72{
73 my ($self) = @_;
e6129e56 74 $self->{vschema} = DBIx::Class::Version->connect(@{$self->storage->connect_info()});
e2c0df8e 75
0d865134 76 my $pversion = $self->get_db_version();
77
42416a0b 78 if($pversion eq $self->schema_version)
c9d2e0a2 79 {
80 warn "This version is already installed\n";
81 return 1;
82 }
83
c9d2e0a2 84 if(!$pversion)
85 {
0d865134 86 warn "Your DB is currently unversioned. Please call upgrade on your schema to sync the DB.\n";
c9d2e0a2 87 return 1;
88 }
89
90 my $file = $self->ddl_filename(
91 $self->storage->sqlt_type,
92 $self->upgrade_directory,
42416a0b 93 $self->schema_version
c9d2e0a2 94 );
95 if(!$file)
96 {
97 # No upgrade path between these two versions
98 return 1;
99 }
100
c9d2e0a2 101
102 ## Don't do this yet, do only on command?
103 ## If we do this later, where does the Version table get updated??
42416a0b 104 warn "Versions out of sync. This is " . $self->schema_version .
c9d2e0a2 105 ", your database contains version $pversion, please call upgrade on your Schema.\n";
c9d2e0a2 106}
107
e6129e56 108sub get_db_version
109{
110 my ($self, $rs) = @_;
111
112 my $vtable = $self->{vschema}->resultset('Table');
0d865134 113 return 0 unless ($self->_source_exists($vtable));
114
e6129e56 115 my $psearch = $vtable->search(undef,
116 { select => [
117 { 'max' => 'Installed' },
118 ],
119 as => ['maxinstall'],
120 })->first;
f925f7cb 121 my $pversion = $vtable->search({ Installed => $psearch->get_column('maxinstall'),
e6129e56 122 })->first;
123 $pversion = $pversion->Version if($pversion);
124 return $pversion;
125}
126
a2800991 127sub _source_exists
c9d2e0a2 128{
129 my ($self, $rs) = @_;
130
131 my $c = eval {
132 $rs->search({ 1, 0 })->count;
133 };
134 return 0 if $@ || !defined $c;
135
136 return 1;
137}
138
139sub backup
140{
141 my ($self) = @_;
142 ## Make each ::DBI::Foo do this
8795fefb 143 $self->storage->backup($self->backup_directory());
c9d2e0a2 144}
145
146sub upgrade
147{
148 my ($self) = @_;
0d865134 149 my $db_version = $self->get_db_version();
c9d2e0a2 150
1d48fcff 151 my %driver_to_db_map = (
152 'mysql' => 'MySQL'
153 );
0d865134 154 if (!$db_version) {
0d865134 155 my $db = $driver_to_db_map{$self->storage->dbh->{Driver}->{Name}};
156 unless ($db) {
157 print "Sorry, this is an unsupported DB\n";
158 return;
159 }
160
161 require SQL::Translator;
162 require SQL::Translator::Diff;
0d865134 163 my $db_tr = SQL::Translator->new({
164 add_drop_table => 1,
165 parser => 'DBI',
bcd1448c 166 parser_args => { dbh => $self->storage->dbh }
0d865134 167 });
168
1d48fcff 169 $db_tr->producer($db);
0d865134 170 my $dbic_tr = SQL::Translator->new;
171 $dbic_tr->parser('SQL::Translator::Parser::DBIx::Class');
172 $dbic_tr = $self->storage->configure_sqlt($dbic_tr, $db);
173 $dbic_tr->data($self);
174 $dbic_tr->producer($db);
175
bcd1448c 176 $db_tr->schema->name('db_schema');
177 $dbic_tr->schema->name('dbic_schema');
0d865134 178
179 # is this really necessary?
180 foreach my $tr ($db_tr, $dbic_tr) {
181 my $data = $tr->data;
182 $tr->parser->($tr, $$data);
183 }
1d48fcff 184
0d865134 185 my $diff = SQL::Translator::Diff::schema_diff($db_tr->schema, $db,
186 $dbic_tr->schema, $db,
da153fe5 187 { caseopt => 1 });
0d865134 188
189 my $filename = $self->ddl_filename(
1d48fcff 190 $db,
0d865134 191 $self->upgrade_directory,
192 $self->schema_version,
193 'PRE',
194 );
195 my $file;
196 if(!open($file, ">$filename"))
197 {
198 $self->throw_exception("Can't open $filename for writing ($!)");
199 next;
200 }
201 print $file $diff;
202 close($file);
203
bcd1448c 204 # create versions table
205 $self->{vschema}->deploy;
206
207 print "WARNING: There may be differences between your DB and your DBIC schema. Please review and if necessary run the SQL in $filename to sync your DB.\n";
0d865134 208 } else {
1d48fcff 209 if ($db_version eq $self->schema_version) {
210 print "Upgrade not necessary\n";
211 return;
212 }
213
0d865134 214 my $file = $self->ddl_filename(
b98d9e8a 215 $self->storage->sqlt_type,
216 $self->upgrade_directory,
217 $self->schema_version,
0d865134 218 $db_version,
b98d9e8a 219 );
220
0d865134 221 if(!-f $file)
222 {
223 warn "Upgrade not possible, no upgrade file found ($file)\n";
224 return;
225 }
226
227 my $fh;
228 open $fh, "<$file" or warn("Can't open upgrade file, $file ($!)");
da153fe5 229 my @data = split(/\n/, join('', <$fh>));
230 @data = grep(!/^--/, @data);
231 @data = split(/;/, join('', @data));
0d865134 232 close($fh);
233 @data = grep { $_ && $_ !~ /^-- / } @data;
234 @data = grep { $_ !~ /^(BEGIN TRANACTION|COMMIT)/m } @data;
235
236 $self->_filedata(\@data);
237 $self->backup() if($self->do_backup);
238
239 $self->txn_do(sub {
240 $self->do_upgrade();
241 });
b98d9e8a 242 }
243
e6129e56 244 my $vtable = $self->{vschema}->resultset('Table');
245 $vtable->create({ Version => $self->schema_version,
246 Installed => strftime("%Y-%m-%d %H:%M:%S", gmtime())
247 });
f925f7cb 248
e6129e56 249}
c9d2e0a2 250
e6129e56 251sub do_upgrade
252{
253 my ($self) = @_;
254
255 ## overridable sub, per default just run all the commands.
c9d2e0a2 256 $self->run_upgrade(qr/create/i);
257 $self->run_upgrade(qr/alter table .*? add/i);
258 $self->run_upgrade(qr/alter table .*? (?!drop)/i);
259 $self->run_upgrade(qr/alter table .*? drop/i);
260 $self->run_upgrade(qr/drop/i);
c9d2e0a2 261}
262
c9d2e0a2 263sub run_upgrade
264{
265 my ($self, $stm) = @_;
266# print "Reg: $stm\n";
267 my @statements = grep { $_ =~ $stm } @{$self->_filedata};
268# print "Statements: ", join("\n", @statements), "\n";
269 $self->_filedata([ grep { $_ !~ /$stm/i } @{$self->_filedata} ]);
270
271 for (@statements)
272 {
70f39278 273 $self->storage->debugobj->query_start($_) if $self->storage->debug;
c9d2e0a2 274 $self->storage->dbh->do($_) or warn "SQL was:\n $_";
70f39278 275 $self->storage->debugobj->query_end($_) if $self->storage->debug;
c9d2e0a2 276 }
277
278 return 1;
279}
280
e2c0df8e 2811;
282
c9d2e0a2 283=head1 NAME
284
7d9fbacf 285DBIx::Class::Schema::Versioned - DBIx::Class::Schema plugin for Schema upgrades
c9d2e0a2 286
287=head1 SYNOPSIS
288
289 package Library::Schema;
290 use base qw/DBIx::Class::Schema/;
291 # load Library::Schema::CD, Library::Schema::Book, Library::Schema::DVD
292 __PACKAGE__->load_classes(qw/CD Book DVD/);
293
294 __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Versioned/);
295 __PACKAGE__->upgrade_directory('/path/to/upgrades/');
8795fefb 296 __PACKAGE__->backup_directory('/path/to/backups/');
c9d2e0a2 297
298 sub backup
299 {
300 my ($self) = @_;
301 # my special backup process
302 }
303
304 sub upgrade
305 {
306 my ($self) = @_;
307
308 ## overridable sub, per default just runs all the commands.
309
310 $self->run_upgrade(qr/create/i);
311 $self->run_upgrade(qr/alter table .*? add/i);
312 $self->run_upgrade(qr/alter table .*? (?!drop)/i);
313 $self->run_upgrade(qr/alter table .*? drop/i);
314 $self->run_upgrade(qr/drop/i);
315 $self->run_upgrade(qr//i);
316 }
317
318=head1 DESCRIPTION
319
320This module is a component designed to extend L<DBIx::Class::Schema>
321classes, to enable them to upgrade to newer schema layouts. To use this
322module, you need to have called C<create_ddl_dir> on your Schema to
323create your upgrade files to include with your delivery.
324
325A table called I<SchemaVersions> is created and maintained by the
326module. This contains two fields, 'Version' and 'Installed', which
327contain each VERSION of your Schema, and the date+time it was installed.
328
329If you would like to influence which levels of version change need
330upgrades in your Schema, you can override the method C<ddl_filename>
331in L<DBIx::Class::Schema>. Return a false value if there is no upgrade
332path between the two versions supplied. By default, every change in
333your VERSION is regarded as needing an upgrade.
334
8795fefb 335The actual upgrade is called manually by calling C<upgrade> on your
336schema object. Code is run at connect time to determine whether an
337upgrade is needed, if so, a warning "Versions out of sync" is
338produced.
339
c9d2e0a2 340NB: At the moment, SQLite upgrading is rather spotty, as SQL::Translator::Diff
341returns SQL statements that SQLite does not support.
342
343
344=head1 METHODS
345
346=head2 backup
347
348This is an overwritable method which is called just before the upgrade, to
349allow you to make a backup of the database. Per default this method attempts
350to call C<< $self->storage->backup >>, to run the standard backup on each
351database type.
352
e6129e56 353This method should return the name of the backup file, if appropriate..
c9d2e0a2 354
355=head2 upgrade
356
e6129e56 357This is the main upgrade method which calls the overridable do_upgrade and
358also handles the backups and updating of the SchemaVersion table.
359
360=head2 do_upgrade
361
c9d2e0a2 362This is an overwritable method used to run your upgrade. The freeform method
363allows you to run your upgrade any way you please, you can call C<run_upgrade>
364any number of times to run the actual SQL commands, and in between you can
365sandwich your data upgrading. For example, first run all the B<CREATE>
366commands, then migrate your data from old to new tables/formats, then
367issue the DROP commands when you are finished.
368
369=head2 run_upgrade
370
371 $self->run_upgrade(qr/create/i);
372
373Runs a set of SQL statements matching a passed in regular expression. The
374idea is that this method can be called any number of times from your
375C<upgrade> method, running whichever commands you specify via the
376regex in the parameter.
377
8795fefb 378=head2 upgrade_directory
379
380Use this to set the directory your upgrade files are stored in.
381
382=head2 backup_directory
383
384Use this to set the directory you want your backups stored in.
385
42416a0b 386=head2 schema_version
387
388Returns the current schema class' $VERSION; does -not- use $schema->VERSION
389since that varies in results depending on if version.pm is installed, and if
390so the perl or XS versions. If you want this to change, bug the version.pm
391author to make vpp and vxs behave the same.
392
c9d2e0a2 393=head1 AUTHOR
394
395Jess Robinson <castaway@desert-island.demon.co.uk>