[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