consolidate multiple ALTER TABLE statements for efficiency, modernize deb packaging...
[DBIx-DBSchema.git] / DBSchema / Table.pm
index b19e7ef..01382ef 100644 (file)
@@ -4,13 +4,13 @@ use strict;
 use vars qw($VERSION $DEBUG %create_params);
 use Carp;
 #use Exporter;
-use DBIx::DBSchema::_util qw(_load_driver _dbh);
-use DBIx::DBSchema::Column 0.07;
+use DBIx::DBSchema::_util qw(_load_driver _dbh _parse_opt);
+use DBIx::DBSchema::Column 0.14;
 use DBIx::DBSchema::Index;
 use DBIx::DBSchema::ColGroup::Unique;
 use DBIx::DBSchema::ColGroup::Index;
 
-$VERSION = '0.05';
+$VERSION = '0.08';
 $DEBUG = 0;
 
 =head1 NAME
@@ -87,10 +87,11 @@ Creates a new DBIx::DBSchema::Table object.  The preferred usage is to pass a
 hash reference of named parameters.
 
   {
-    name        => TABLE_NAME,
-    primary_key => PRIMARY_KEY,
-    columns     => COLUMNS,
-    indices     => INDICES,
+    name          => TABLE_NAME,
+    primary_key   => PRIMARY_KEY,
+    columns       => COLUMNS,
+    indices       => INDICES,
+    local_options => OPTIONS,
     #deprecated# unique => UNIQUE,
     #deprecated# index  => INDEX,
   }
@@ -100,6 +101,8 @@ empty).  COLUMNS is a reference to an array of DBIx::DBSchema::Column objects
 (see L<DBIx::DBSchema::Column>).  INDICES is a reference to an array of 
 DBIx::DBSchema::Index objects (see L<DBIx::DBSchema::Index>), or a hash
 reference of index names (keys) and DBIx::DBSchema::Index objects (values).
+OPTIONS is a scalar of database-specific table options, such as "WITHOUT OIDS"
+for Pg or "TYPE=InnoDB" for mysql.
 
 Deprecated options:
 
@@ -346,6 +349,21 @@ sub name {
   }
 }
 
+=item local_options [ OPTIONS ]
+
+Returns or sets the database-specific table options string.
+
+=cut
+
+sub local_options {
+  my($self,$value)=@_;
+  if ( defined($value) ) {
+    $self->{local_options} = $value;
+  } else {
+    defined $self->{local_options} ? $self->{local_options} : '';
+  }
+}
+
 =item primary_key [ PRIMARY_KEY ]
 
 Returns or sets the primary key.
@@ -377,11 +395,18 @@ Returns or sets the DBIx::DBSchema::ColGroup::Unique object.
 
 =cut
 
-sub unique { 
-  my($self,$value)=@_;
+sub unique {
+    my $self = shift;
 
-  carp ref($self). "->unique method is deprecated; see ->indices";
-  #croak ref($self). "->unique method is deprecated; see ->indices";
+    carp ref($self) . "->unique method is deprecated; see ->indices";
+    #croak ref($self). "->unique method is deprecated; see ->indices";
+
+    $self->_unique(@_);
+}
+
+sub _unique {
+
+  my ($self,$value)=@_;
 
   if ( defined($value) ) {
     $self->{unique} = $value;
@@ -401,11 +426,18 @@ Returns or sets the DBIx::DBSchema::ColGroup::Index object.
 =cut
 
 sub index { 
-  my($self,$value)=@_;
+  my $self = shift;
 
   carp ref($self). "->index method is deprecated; see ->indices";
   #croak ref($self). "->index method is deprecated; see ->indices";
 
+  $self->_index(@_);
+}
+
+
+sub _index {
+  my($self,$value)=@_;
+
   if ( defined($value) ) {
     $self->{'index'} = $value;
   } else {
@@ -517,10 +549,11 @@ sub sql_create_table {
   my $indexnum = 1;
 
   my @r = (
-    "CREATE TABLE ". $self->name. " (\n  ". join(",\n  ", @columns). "\n)\n"
+    "CREATE TABLE ". $self->name. " (\n  ". join(",\n  ", @columns). "\n)\n".
+    $self->local_options
   );
 
-  if ( $self->unique ) {
+  if ( $self->_unique ) {
 
     warn "WARNING: DBIx::DBSchema::Table object for ". $self->name.
          " table has deprecated (non-named) unique indices\n";
@@ -534,7 +567,7 @@ sub sql_create_table {
 
   }
 
-  if ( $self->index ) {
+  if ( $self->_index ) {
 
     warn "WARNING: DBIx::DBSchema::Table object for ". $self->name.
          " table has deprecated (non-named) indices\n";
@@ -562,57 +595,67 @@ sub sql_create_table {
 Returns a list of SQL statements to alter this table so that it is identical
 to the provided table, also a DBIx::DBSchema::Table object.
 
- #Optionally, the data source can be specified by passing an open DBI database
- #handle, or by passing the DBI data source name, username and password.  
- #
- #If passed a DBI data source (or handle) such as `DBI:Pg:dbname=database', will
- #use PostgreSQL-specific syntax.  Non-standard syntax for other engines (if
- #applicable) may also be supported in the future.
- #
- #If not passed a data source (or handle), or if there is no driver for the
- #specified database, will attempt to use generic SQL syntax.
+The data source can be specified by passing an open DBI database handle, or by
+passing the DBI data source name, username and password.  
+
+Although the username and password are optional, it is best to call this method
+with a database handle or data source including a valid username and password -
+a DBI connection will be opened and used to check the database version as well
+as for more reliable quoting and type mapping.  Note that the database
+connection will be used passively, B<not> to actually run the CREATE
+statements.
+
+If passed a DBI data source (or handle) such as `DBI:mysql:database' or
+`DBI:Pg:dbname=database', will use syntax specific to that database engine.
+Currently supported databases are MySQL and PostgreSQL.
+
+If not passed a data source (or handle), or if there is no driver for the
+specified database, will attempt to use generic SQL syntax.
 
 =cut
 
 #gosh, false laziness w/DBSchema::sql_update_schema
 
 sub sql_alter_table {
-  my( $self, $new, $dbh ) = ( shift, shift, _dbh(@_) );
+  my($self, $opt, $new, $dbh) = ( shift, _parse_opt(\@_), shift, _dbh(@_) );
 
   my $driver = _load_driver($dbh);
 
   my $table = $self->name;
 
+  my @at = ();
   my @r = ();
   my @r_later = ();
   my $tempnum = 1;
 
   ###
-  # columns
+  # columns (add/alter)
   ###
 
   foreach my $column ( $new->columns ) {
 
     if ( $self->column($column) )  {
-
       warn "  $table.$column exists\n" if $DEBUG > 1;
 
-      push @r,
-        $self->column($column)->sql_alter_column( $new->column($column), $dbh );
+      my ($alter_table, $sql) = 
+        $self->column($column)->sql_alter_column( $new->column($column),
+                                                  $dbh,
+                                                  $opt,
+                                                );
+      push @at, @$alter_table;
+      push @r, @$sql;
 
     } else {
-  
       warn "column $table.$column does not exist.\n" if $DEBUG > 1;
 
-      push @r,
-        $new->column($column)->sql_add_column( $dbh );
+      my ($alter_table, $sql) = $new->column($column)->sql_add_column( $dbh );
+      push @at, @$alter_table;
+      push @r, @$sql;
   
     }
   
   }
 
-  #should eventually drop columns not in $new...
-  
   ###
   # indices
   ###
@@ -669,6 +712,20 @@ sub sql_alter_table {
     warn "creating new index $table.$new\n" if $DEBUG > 1;
     push @r, $new_indices{$new}->sql_create_index($table);
   }
+
+  ###
+  # columns (drop)
+  ###
+
+  foreach my $column ( grep !$new->column($_), $self->columns ) {
+
+    warn "column $table.$column should be dropped.\n" if $DEBUG;
+
+    push @at, $self->column($column)->sql_drop_column( $dbh );
+
+  }
+
+  unshift @r, "ALTER TABLE $table ", join(', ', @at) if @at;
   
   ###
   # return the statements
@@ -712,7 +769,7 @@ with no indices.
 
 Copyright (c) 2000-2007 Ivan Kohler
 Copyright (c) 2000 Mail Abuse Prevention System LLC
-Copyright (c) 2007 Freeside Internet Services, Inc.
+Copyright (c) 2007-2013 Freeside Internet Services, Inc.
 All rights reserved.
 This program is free software; you can redistribute it and/or modify it under
 the same terms as Perl itself.
@@ -730,8 +787,6 @@ the object after sql_create_table, make a copy beforehand.
 
 Some of the logic in new_odbc might be better abstracted into Column.pm etc.
 
-sql_alter_table ought to drop columns not in $new
-
 Add methods to get and set specific indices, by name? (like column COLUMN_NAME)
 
 indices method should be a setter, not just a getter?