2019-04-06 19:08:36 +00:00
|
|
|
package Travelynx::Command::database;
|
|
|
|
use Mojo::Base 'Mojolicious::Command';
|
|
|
|
|
|
|
|
use DateTime;
|
|
|
|
|
|
|
|
has description => 'Initialize or upgrade database layout';
|
|
|
|
|
|
|
|
has usage => sub { shift->extract_usage };
|
|
|
|
|
|
|
|
sub get_schema_version {
|
|
|
|
my ($dbh) = @_;
|
|
|
|
for my $entry (
|
|
|
|
$dbh->selectall_array(qq{select version from schema_version}) )
|
|
|
|
{
|
|
|
|
return $entry->[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub initialize_db {
|
|
|
|
my ($dbh) = @_;
|
|
|
|
return $dbh->do(
|
|
|
|
qq{
|
|
|
|
create table schema_version (
|
|
|
|
version integer primary key
|
|
|
|
);
|
|
|
|
create table users (
|
|
|
|
id serial not null primary key,
|
|
|
|
name varchar(64) not null unique,
|
|
|
|
status smallint not null,
|
|
|
|
public_level smallint not null,
|
|
|
|
email varchar(256),
|
|
|
|
token varchar(80),
|
|
|
|
password text,
|
|
|
|
registered_at timestamptz not null,
|
|
|
|
last_login timestamptz not null,
|
|
|
|
deletion_requested timestamptz
|
|
|
|
);
|
|
|
|
create table stations (
|
|
|
|
id serial not null primary key,
|
|
|
|
ds100 varchar(16) not null unique,
|
|
|
|
name varchar(64) not null unique
|
|
|
|
);
|
|
|
|
create table user_actions (
|
|
|
|
id serial not null primary key,
|
|
|
|
user_id integer not null references users (id),
|
|
|
|
action_id smallint not null,
|
|
|
|
station_id int references stations (id),
|
|
|
|
action_time timestamptz not null,
|
2019-04-09 16:37:21 +00:00
|
|
|
edited not null,
|
2019-04-06 19:08:36 +00:00
|
|
|
train_type varchar(16),
|
|
|
|
train_line varchar(16),
|
|
|
|
train_no varchar(16),
|
|
|
|
train_id varchar(128),
|
|
|
|
sched_time timestamptz,
|
|
|
|
real_time timestamptz,
|
|
|
|
route text,
|
|
|
|
messages text
|
|
|
|
);
|
|
|
|
create table pending_mails (
|
|
|
|
email varchar(256) not null primary key,
|
|
|
|
num_tries smallint not null,
|
|
|
|
last_try timestamptz not null
|
|
|
|
);
|
|
|
|
create table tokens (
|
|
|
|
user_id integer not null references users (id),
|
|
|
|
type smallint not null,
|
|
|
|
token varchar(80) not null,
|
|
|
|
primary key (user_id, type)
|
|
|
|
);
|
2019-04-09 16:37:21 +00:00
|
|
|
insert into schema_version values (2);
|
2019-04-06 19:08:36 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-04-07 14:55:35 +00:00
|
|
|
my @migrations = (
|
|
|
|
|
|
|
|
# v0 -> v1
|
|
|
|
sub {
|
|
|
|
my ($dbh) = @_;
|
|
|
|
return $dbh->do(
|
|
|
|
qq{
|
|
|
|
alter table user_actions
|
|
|
|
add column edited smallint;
|
|
|
|
drop table if exists monthly_stats;
|
|
|
|
create table journey_stats (
|
|
|
|
user_id integer not null references users (id),
|
|
|
|
year smallint not null,
|
|
|
|
month smallint not null,
|
|
|
|
data jsonb not null,
|
|
|
|
primary key (user_id, year, month)
|
|
|
|
);
|
|
|
|
update schema_version set version = 1;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
2019-04-09 16:37:21 +00:00
|
|
|
|
|
|
|
# v1 -> v2
|
|
|
|
sub {
|
|
|
|
my ($dbh) = @_;
|
|
|
|
return $dbh->do(
|
|
|
|
qq{
|
|
|
|
update user_actions set edited = 0;
|
|
|
|
alter table user_actions
|
|
|
|
alter column edited set not null;
|
|
|
|
update schema_version set version = 2;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
2019-04-07 14:55:35 +00:00
|
|
|
);
|
2019-04-06 19:08:36 +00:00
|
|
|
|
|
|
|
sub run {
|
|
|
|
my ( $self, $command ) = @_;
|
|
|
|
|
|
|
|
my $dbh = $self->app->dbh;
|
|
|
|
|
|
|
|
if ( $command eq 'setup' ) {
|
|
|
|
$dbh->begin_work;
|
|
|
|
if ( initialize_db($dbh) ) {
|
|
|
|
$dbh->commit;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$dbh->rollback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif ( $command eq 'migrate' ) {
|
|
|
|
$dbh->begin_work;
|
|
|
|
my $schema_version = get_schema_version($dbh);
|
|
|
|
say "Found travelynx schema v${schema_version}";
|
|
|
|
if ( $schema_version == @migrations ) {
|
|
|
|
say "Database layout is up-to-date";
|
|
|
|
}
|
|
|
|
for my $i ( $schema_version .. $#migrations ) {
|
|
|
|
printf( "Updating to v%d ...\n", $i + 1 );
|
2019-04-07 14:55:35 +00:00
|
|
|
if ( not $migrations[$i]($dbh) ) {
|
2019-04-06 19:08:36 +00:00
|
|
|
say "Aborting migration; rollback to v${schema_version}";
|
|
|
|
$dbh->rollback;
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
2019-04-07 14:55:35 +00:00
|
|
|
if ( get_schema_version($dbh) == @migrations ) {
|
2019-04-06 19:08:36 +00:00
|
|
|
$dbh->commit;
|
|
|
|
}
|
|
|
|
}
|
2019-04-07 12:18:56 +00:00
|
|
|
elsif ( $command eq 'has-current-schema' ) {
|
2019-04-07 14:55:35 +00:00
|
|
|
if ( get_schema_version($dbh) == @migrations ) {
|
2019-04-07 12:18:56 +00:00
|
|
|
say "yes";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
say "no";
|
|
|
|
}
|
|
|
|
}
|
2019-04-06 19:08:36 +00:00
|
|
|
else {
|
|
|
|
$self->help;
|
|
|
|
}
|
|
|
|
|
|
|
|
$dbh->disconnect;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|
|
|
__END__
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
2019-04-07 12:18:56 +00:00
|
|
|
Usage: index.pl database <setup|migrate|has-current-schema>
|
2019-04-06 19:08:36 +00:00
|
|
|
|
|
|
|
Upgrades the database layout to the latest schema.
|
|
|
|
|
|
|
|
Recommended workflow:
|
|
|
|
> systemctl stop travelynx
|
|
|
|
> TRAVELYNX_DB_HOST=... TRAVELYNX_DB_NAME=... TRAVELYNX_DB_USER=... \
|
|
|
|
TRAVELYNX_DB_PASSWORD=... perl index.pl migrate
|
|
|
|
> systemctl start travelynx
|