DBIx::Class::DeploymentHandler - DBIx::Class::Schema deployment for multiple versions.
sub _set_db_version {
my $self = shift;
my ($params) = @_;
$params ||= {};
my $version = $params->{version} ? $params->{version} : $self->schema_version;
my $vtable = $self->{vschema}->resultset('Table');
##############################################################################
# !!! NOTE !!!
##############################################################################
#
# The travesty below replaces the old nice timestamp format of %Y-%m-%d %H:%M:%S
# This is necessary since there are legitimate cases when upgrades can happen
# back to back within the same second. This breaks things since we relay on the
# ability to sort by the 'installed' value. The logical choice of an autoinc
# is not possible, as it will break multiple legacy installations. Also it is
# not possible to format the string sanely, as the column is a varchar(20).
# The 'v' character is added to the front of the string, so that any version
# formatted by this new function will sort _after_ any existing 200... strings.
my @tm = gettimeofday();
my @dt = gmtime ($tm[0]);
my $o = $vtable->create({
version => $version,
installed => sprintf("v%04d%02d%02d_%02d%02d%02d.%03.0f",
$dt[5] + 1900,
$dt[4] + 1,
$dt[3],
$dt[2],
$dt[1],
$dt[0],
$tm[1] / 1000, # convert to millisecs, format as up/down rounded int above
),
});
}
There are already a lot of different options for building your own DBICDH, we'll talk about the defaults (aka recommended) here.
The monotonic version handler uses integer only versions, which follow naturally per revision or release or whatever. So you first version would be 1, the second would be 2, etc.
The main benefit of monotonic is that it is perfectly simple and predictable. No issues of how versions are parsed or ordered or any of that.
The SQLT deploy method uses SQLT to generate SQL for installs, upgrades, and downgrades. In addition to that it does the actual running of SQL and Perl scripts and defines the directory layout for the scripts.
A Perl script used by SQLTDM is actually very similar to a .psgi script. It should just be an anonymous subref. Scripts for install, upgrade, and downgrade get the schema as their first argument. The initialize scripts are not passed any arguments.
Note: this is inspired by DBIx::Migration::Directories
$sql_migration_dir
|- _source
| `- deploy
| `- 1
| `- 001-auto.yml
|- SQLite
| |- downgrade
| | `- 2-1
| | `- 001-auto.sql
| |- initialize
| | `- 1
| | |- 001-create_database.pl
| | `- 002-create_users_and_permissions.pl
| |- deploy
| | `- 1
| | `- 001-auto.sql
| `- upgrade
| |- 1-2
| | `- 001-auto.sql
| `- 2-3
| `- 001-auto.sql
|- _common
|- downgrade
| `- 2-1
| `- 002-remove-customers.pl
`- upgrade
`- 1-2
`- 002-generate-customers.pl
Not a whole lot worth saying here. The main change from ::Versioned is that instead of that crazy date thing I pointed out above it uses a simple autoincrementing id.
The initialize stage, while not required, should vastly reduce the amount of manual intervention required for a deploy. Typical usage will probably be something like having it do CREATE DATABASE FOO as well as adding a few users to the database and whatnot.
Because initialize is such an open-ended stage, it currently only supports running perl scripts, instead of perl and sql like the rest of the stages.
# $migrations/MySQL/initialize/1/001_create_db.pl
sub {
use DBI;
my $user = 'root';
my $password = 'foo';
my $dsn = 'bar';
# you should probably
# prompt the user for
# at least the password
my $dbh = DBI->connect($dsn, $user, $password);
$dbh->do('CREATE DATABASE frewmbot')
or die 'Shucks howdy! FAIL:' . $@;
};
The install stage is where the schema gets deployed, as well as your version storage. In general this will be mostly automatic, unless you want to do something like prepopulate a users table.
# $migrations/_common/install/1/01_create_usr.pl
sub {
my $schema = shift;
$schema->resultset('User')->populate([
[qw( username password )],
[qw( frew test )],
]);
};
This stage is where your schema gets modified. If you are doing simple changes like adding/removing tables/columns it should be entirely automatic. If you are renaming things or transforming data you'll need to add some scripts yourself.
# $migrations/_common/upgrade/1-2/01_hash_pass.pl
sub {
my $schema = shift;
$_->update({
password => $_->get_column('password')
}) for $schema->resultset('User')->all;
};
package MyApp::Schema::DBICDHStorage;
use Moose;
extends
'DBIx::Class::DeploymentHandler' .
'::VersionStorage::Standard';
sub _build_version_rs {
$_[0]->schema->register_class(
__VERSION =>
'MyApp::Schema::DBICDHStorageResult'
);
$_[0]->schema->resultset('__VERSION')
}
no Moose;
__PACKAGE__->meta->make_immutable;
1;
package MyApp::Schema::DBICDHStorageResult;
use parent
'DBIx::Class::DeploymentHandler' .
'::VersionStorage::Standard::VersionResult';
__PACKAGE__->table('fl_bench_journal_versions');
1;
package MyApp::Schema::DeploymentHandler;
use Moose;
extends 'DBIx::Class::DeploymentHandler::Dad';
with 'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
interface_role => 'DBIx::Class::DeploymentHandler::HandlesDeploy',
class_name =>
'DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator',
delegate_name => 'deploy_method',
attributes_to_assume => ['schema'],
attributes_to_copy => [qw( databases script_directory sql_translator_args )],
},
'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
interface_role => 'DBIx::Class::DeploymentHandler::HandlesVersioning',
class_name => 'DBIx::Class::DeploymentHandler::VersionHandler::Monotonic',
delegate_name => 'version_handler',
attributes_to_assume => [qw( database_version schema_version to_version )],
},
'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
interface_role => 'DBIx::Class::DeploymentHandler::HandlesVersionStorage',
class_name => 'MyApp::Schema::DBICDHStorage',
delegate_name => 'version_storage',
attributes_to_assume => ['schema'],
};
with 'DBIx::Class::DeploymentHandler::WithReasonableDefaults';
sub prepare_version_storage_install {
$_[0]->prepare_resultsource_install(
$_[0]->version_storage->version_rs->result_source
);
}
sub install_version_storage {
$_[0]->install_resultsource(
$_[0]->version_storage->version_rs->result_source
);
}
sub prepare_install {
$_[0]->prepare_deploy;
$_[0]->prepare_version_storage_install;
}
no Moose;
__PACKAGE__->meta->make_immutable;
1;