[Jifty-commit] r3290 - in jifty/trunk: lib/Jifty lib/Jifty/Script
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Thu May 24 00:14:10 EDT 2007
Author: jesse
Date: Thu May 24 00:14:06 2007
New Revision: 3290
Added:
jifty/trunk/lib/Jifty/Schema.pm
Modified:
jifty/trunk/ (props changed)
jifty/trunk/lib/Jifty/Script/Schema.pm
Log:
r57067 at pinglin: jesse | 2007-05-23 23:56:08 -0400
* Incremental extraction of schema management from Jifty::Script::Schema
Added: jifty/trunk/lib/Jifty/Schema.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Schema.pm Thu May 24 00:14:06 2007
@@ -0,0 +1,266 @@
+use warnings;
+use strict;
+
+package Jifty::Schema;
+use base qw/Jifty::Object/;
+use SQL::ReservedWords;
+
+Jifty::Module::Pluggable->import(
+ require => 1,
+ search_path => ["SQL::ReservedWords"],
+ sub_name => '_sql_dialects',
+);
+
+our %_SQL_RESERVED = ();
+our @_SQL_RESERVED_OVERRIDE = qw(value);
+foreach my $dialect ( 'SQL::ReservedWords', &_sql_dialects ) {
+ foreach my $word ( $dialect->words ) {
+ push @{ $_SQL_RESERVED{ lc($word) } }, $dialect->reserved_by($word);
+ }
+}
+
+# XXX TODO: QUESTIONABLE ENGINEERING DECISION
+# The SQL standard forbids columns named 'value', but just about everone on the planet
+# actually supports it. Rather than going about scaremongering, we choose
+# not to warn people about columns named 'value'
+
+delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
+
+
+
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self, $class;
+ return $self;
+
+}
+
+
+sub serialize_current_schema {
+ my $self = shift;
+
+ my @models = $self->model_classes;
+ my $serialized_models = {};
+ foreach my $model (@models) {
+ $serialized_models->{$model->_class_name} = $model->serialize_metadata;
+ }
+
+ return $serialized_models;
+
+}
+
+
+
+
+sub upgrade_schema {
+ my $self = shift;
+
+
+ # load the database schema version
+
+ # hashref
+ my $old_tables = $self->current_db_schema;
+
+ # hashref
+ my $new_tables = $self->new_db_schema;
+
+ my $add_tables = {};
+ my $remove_tables ={};
+ my $add_columns = {};
+ my $remove_columns = {};
+
+ # diff the current schema version and the database schema version
+ foreach my $table ( keys %$old_tables ) {
+ unless ( $new_tables->{$table} ) {
+ $remove_tables->{$table} = $old_tables->{$table};
+ next;
+ }
+
+ foreach my $column ( @{ $old_tables->{$table}->columns } ) {
+
+ # if the column isn't in the new table as well, then mark it for deletion
+ unless ( $new_tables->{$table}->column($column) ) {
+ push @{ $remove_columns->{$table} }, $column;
+ }
+
+ # XXX TODO: compare the column definitions and alter them if necessary
+
+ }
+ }
+
+ foreach my $table ( keys %$new_tables ) {
+ unless ( $old_tables->{$table} ) {
+ $add_tables->{$table} = $new_tables->{$table};
+ next;
+ }
+
+ foreach my $column ( @{ $new_tables->{$table}->columns } ) {
+
+ # if the column isn't in the old table as well, then mark it for addition
+ unless ( $old_tables->{$table}->column($column) ) {
+ push @{ $add_columns->{$table} }, $column;
+ }
+
+ # XXX TODO: compare the column definitions and alter them if necessary
+
+ }
+ }
+
+ # Run all "Rename" rules
+ $self->run_upgrade_rules('before_all_renames');
+ my $table_renames = Jifty->upgrade->table_renames;
+ my $column_renames = Jifty->upgrade->column_renames;
+ $self->run_upgrade_rules('after_column_renames');
+
+ $self->_add_tables($add_tables);
+ $self->_add_columns($add_columns);
+ $self->_drop_tables($remove_tables);
+ $self->_drop_columns($remove_columns);
+
+
+}
+
+
+sub _add_tables {
+ my $self = shift;
+ my $add_tables = shift;
+
+
+ # add all new tables
+ $self->run_upgrade_rules('before_table_adds');
+ foreach my $table ( values %$add_tables ) {
+ $self->run_upgrade_rules( 'before_add_table_' . $table );
+ $add_tables->{$table}->create_table_in_db();
+ $self->run_upgrade_rules( 'after_add_table_' . $table );
+ }
+ $self->run_upgrade_rules('after_table_adds');
+}
+
+
+sub _add_columns {
+ my $self = shift;
+ my $add_columns = shift;
+
+ $self->run_upgrade_rules('before_column_adds');
+ foreach my $table ( values %$add_columns ) {
+ $self->run_upgrade_rules( 'before_add_columns_to_table_' . $table );
+ my @cols = @{ $add_columns->{$table} };
+ foreach my $col (@cols) {
+ $self->run_upgrade_rules( 'before_add_column_' . $col->name . '_to_table_' . $table );
+ $add_columns->{$table}->add_column_in_db($col);
+ $self->run_upgrade_rules( 'after_add_column_' . $col->name . '_to_table_' . $table );
+ }
+ $self->run_upgrade_rules( 'after_add_columns_to_table_' . $table );
+ }
+ $self->run_upgrade_rules('after_add_columns');
+
+}
+
+
+
+
+sub _drop_tables {
+ my $self =shift;
+ my $remove_tables = shift;
+
+
+ $self->run_upgrade_rules('before_drop_tables');
+
+ foreach my $table ( values %$remove_tables ) {
+ $self->run_upgrade_rules( 'before_drop_table_' . $table );
+ $remove_tables->{$table}->drop_table_in_db();
+ $self->run_upgrade_rules( 'after_drop_table_' . $table );
+ }
+ $self->run_upgrade_rules('after_drop_tables');
+
+}
+
+sub _drop_columns {
+ my $self = shift;
+ my $remove_columns = shift;
+
+ $self->run_upgrade_rules('before_drop_columns');
+
+ foreach my $table ( values %$remove_columns ) {
+ $self->run_upgrade_rules( 'before_drop_columns_from_' . $table );
+ my @cols = @{ $remove_columns->{$table} };
+ foreach my $col (@cols) {
+ $self->run_upgrade_rules( 'before_drop_column' . $col->name . '_from_' . $table );
+ $remove_columns->{$table}->drop_column_in_db($col);
+ $self->run_upgrade_rules( 'after_drop_column_' . $col->name . '_from_' . $table );
+ }
+ $self->run_upgrade_rules( 'after_drop_columns_from_' . $table );
+ }
+ $self->run_upgrade_rules('after_drop_columns');
+
+}
+
+
+=head2 run_upgrade_rules rule_name
+
+This method runs all upgrade rules for the rule named C<rule_name>.
+
+=cut
+
+sub run_upgrade_rules {
+ my $self = shift;
+ my $rule_name = shift;
+
+ my $upgrade_object = Jifty->app_class('Upgrade');
+ $upgrade_object->call_trigger($rule_name);
+}
+
+
+
+sub _check_reserved {
+ my $self = shift;
+ my $model = shift;
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+ foreach my $col ( $model->columns ) {
+ if ( exists $_SQL_RESERVED{ lc( $col->name ) } ) {
+ $log->error(
+ $model . ": "
+ . $col->name
+ . " is a reserved word in these SQL dialects: "
+ . join( ', ',
+ _classify_reserved_words( @{ $_SQL_RESERVED{ lc( $col->name ) } } ) )
+ );
+ }
+ }
+}
+
+sub _classify_reserved_words {
+ my %dbs;
+
+ # Guess names of databases + their versions by breaking on last space,
+ # e.g., "SQL Server 7" is ("SQL Server", "7"), not ("SQL", "Server 7").
+ push @{ $dbs{ $_->[0] } }, $_->[1]
+ for map { [ split /\s+(?!.*\s)/, $_, 2 ] } @_;
+ return
+ map { join " ", $_, __parenthesize_sql_variants( @{ $dbs{$_} } ) } sort keys %dbs;
+}
+
+sub __parenthesize_sql_variants {
+ if ( not defined $_[0] ) { return () }
+ if ( @_ == 1 ) { return $_[0] }
+ return "(" . ( join ", ", @_ ) . ")";
+}
+
+
+
+sub connect_to_db_for_management {
+ my $handle = Jifty::Handle->new();
+
+ my $driver = Jifty->config->framework('Database')->{'Driver'};
+
+ # Everything but the template1 database is assumed
+ my %connect_args;
+ $connect_args{'database'} = 'template1' if ( $handle->isa("Jifty::DBI::Handle::Pg") );
+ $connect_args{'database'} = '' if ( $handle->isa("Jifty::DBI::Handle::mysql") );
+ $handle->connect(%connect_args);
+ return $handle;
+}
+
+
+1;
Modified: jifty/trunk/lib/Jifty/Script/Schema.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Script/Schema.pm (original)
+++ jifty/trunk/lib/Jifty/Script/Schema.pm Thu May 24 00:14:06 2007
@@ -8,28 +8,7 @@
use version;
use Jifty::DBI::SchemaGenerator;
use Jifty::Config;
-use SQL::ReservedWords;
-
-Jifty::Module::Pluggable->import(
- require => 1,
- search_path => ["SQL::ReservedWords"],
- sub_name => '_sql_dialects',
-);
-
-our %_SQL_RESERVED = ();
-our @_SQL_RESERVED_OVERRIDE = qw(value);
-foreach my $dialect ( 'SQL::ReservedWords', &_sql_dialects ) {
- foreach my $word ( $dialect->words ) {
- push @{ $_SQL_RESERVED{ lc($word) } }, $dialect->reserved_by($word);
- }
-}
-
-# XXX TODO: QUESTIONABLE ENGINEERING DECISION
-# The SQL standard forbids columns named 'value', but just about everone on the planet
-# actually supports it. Rather than going about scaremongering, we choose
-# not to warn people about columns named 'value'
-
-delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
+use Jifty::Schema;
=head2 options
@@ -64,7 +43,14 @@
$self->print_help();
$self->setup_environment();
- $self->probe_database_existence();
+ $self->probe_database_existence(
+
+ create_database => $self->{create_database},
+ drop_database => $self->{drop_database},
+ setup_tables => $self->{setup_tables},
+ create_all_tables => $self->{create_all_tables}
+
+ );
$self->manage_database_existence();
$self->prepare_model_classes();
if ( $self->{create_all_tables} ) {
@@ -142,8 +128,16 @@
sub probe_database_existence {
my $self = shift;
+ my %args = (
+ create_database => undef,
+ drop_database => undef,
+ setup_tables => undef,
+ create_all_tables => undef,
+ @_
+ );
+
my $no_handle = 0;
- if ( $self->{'create_database'} or $self->{'drop_database'} ) {
+ if ( $args{'create_database'} or $args{'drop_database'} ) {
$no_handle = 1;
}
@@ -155,17 +149,17 @@
if ( $@ =~ /doesn't match (application schema|running jifty) version/i ) {
# We found an out-of-date DB. Upgrade it
- $self->{setup_tables} = 1;
+ $args{setup_tables} = 1;
} elsif ( $@ =~ /no version in the database/i ) {
# No version table. Assume the DB is empty.
- $self->{create_all_tables} = 1;
+ $args{create_all_tables} = 1;
} elsif ( $@ =~ /database .*? does not exist/i
or $@ =~ /unknown database/i ) {
# No database exists; we'll need to make one and fill it up
- $self->{create_database} = 1;
- $self->{create_all_tables} = 1;
+ $args{create_database} = 1;
+ $args{create_all_tables} = 1;
} elsif ($@) {
# Some other unexpected error; rethrow it
@@ -173,12 +167,12 @@
}
# Setting up tables requires creating the DB if we just dropped it
- $self->{create_database} = 1
- if $self->{drop_database} and $self->{setup_tables};
+ $args{create_database} = 1
+ if $args{drop_database} and $args{setup_tables};
# Setting up tables on a just-created DB is the same as setting them all up
- $self->{create_all_tables} = 1
- if $self->{create_database} and $self->{setup_tables};
+ $args{create_all_tables} = 1
+ if $args{create_database} and $args{setup_tables};
# Give us some kind of handle if we don't have one by now
Jifty->handle( Jifty::Handle->new() ) unless Jifty->handle;
@@ -246,6 +240,8 @@
my $appv = version->new( Jifty->config->framework('Database')->{'Version'} );
my $jiftyv = version->new( $Jifty::VERSION );
+ my $schema = Jifty::Schema->new();
+
for my $model ( @models) {
# TODO XXX FIXME:
@@ -257,7 +253,7 @@
}
$log->info("Using $model, as it appears to be new.");
- $self->_check_reserved($model)
+ $schema->_check_reserved($model)
unless ( $self->{'ignore_reserved'}
or !Jifty->config->framework('Database')->{'CheckSchema'} );
@@ -466,84 +462,43 @@
sub manage_database_existence {
my $self = shift;
- my $handle = $self->_connect_to_db_for_management();
+ my $handle = Jifty::Schema->connect_to_db_for_management();
+
+ if ( $self->{print} ) {
if ( $self->{'drop_database'} ) {
- $handle->drop_database($self->{'print'} ? 'print' : 'execute');
- }
+ $handle->drop_database('print');
+ }
+ if ( $self->{'create_database'} ) {
+ $handle->create_database('print');
+ }
+ return;
+ } else {
- if ( $self->{'create_database'} ) {
- $handle->create_database($self->{'print'} ? 'print' : 'execute');
- }
+ if ( $self->{'drop_database'} ) {
+ $handle->drop_database('execute');
+ }
- $handle->disconnect;
+ if ( $self->{'create_database'} ) {
+ $handle->create_database('execute');
+ }
- # If we drop and didn't re-create, then don't reconnect
- if ( $self->{'drop_database'} and not $self->{'create_database'} ) {
- return;
- }
+ $handle->disconnect;
+
+ # If we drop and didn't re-create, then don't reconnect
+ if ( $self->{'drop_database'} and not $self->{'create_database'} ) {
+ return;
+ }
- # Likewise if we didn't get a connection before, and we're just
- # printing, the connect below will fail
- elsif ( $self->{'print'}
- and not( Jifty->handle and Jifty->handle->dbh->ping ) ) {
- return;
- } else {
$self->_reinit_handle();
}
}
-sub _connect_to_db_for_management {
- my $handle = Jifty::Handle->new();
-
- my $driver = Jifty->config->framework('Database')->{'Driver'};
-
- # Everything but the template1 database is assumed
- my %connect_args;
- $connect_args{'database'} = 'template1' if ( $handle->isa("Jifty::DBI::Handle::Pg") );
- $connect_args{'database'} = '' if ( $handle->isa("Jifty::DBI::Handle::mysql") );
- $handle->connect(%connect_args);
- return $handle;
-}
-
sub _reinit_handle {
Jifty->handle( Jifty::Handle->new() );
Jifty->handle->connect();
}
-sub __parenthesize {
- if ( not defined $_[0] ) { return () }
- if ( @_ == 1 ) { return $_[0] }
- return "(" . ( join ", ", @_ ) . ")";
-}
-
-sub _classify {
- my %dbs;
-
- # Guess names of databases + their versions by breaking on last space,
- # e.g., "SQL Server 7" is ("SQL Server", "7"), not ("SQL", "Server 7").
- push @{ $dbs{ $_->[0] } }, $_->[1]
- for map { [ split /\s+(?!.*\s)/, $_, 2 ] } @_;
- return
- map { join " ", $_, __parenthesize( @{ $dbs{$_} } ) } sort keys %dbs;
-}
-
-sub _check_reserved {
- my $self = shift;
- my $model = shift;
- my $log = Log::Log4perl->get_logger("SchemaTool");
- foreach my $col ( $model->columns ) {
- if ( exists $_SQL_RESERVED{ lc( $col->name ) } ) {
- $log->error(
- $model . ": "
- . $col->name
- . " is a reserved word in these SQL dialects: "
- . join( ', ',
- _classify( @{ $_SQL_RESERVED{ lc( $col->name ) } } ) )
- );
- }
- }
-}
sub _exec_sql {
my $sql = shift;
More information about the Jifty-commit
mailing list