- more schema update stuff:
[DBIx-DBSchema.git] / DBSchema.pm
1 package DBIx::DBSchema;
2
3 use strict;
4 use vars qw(@ISA $VERSION $DEBUG);
5 #use Exporter;
6 use Storable;
7 use DBIx::DBSchema::_util qw(_load_driver _dbh);
8 use DBIx::DBSchema::Table;
9 use DBIx::DBSchema::Column;
10 use DBIx::DBSchema::ColGroup::Unique;
11 use DBIx::DBSchema::ColGroup::Index;
12
13 #@ISA = qw(Exporter);
14 @ISA = ();
15
16 $VERSION = "0.31";
17 $DEBUG = 0;
18
19 =head1 NAME
20
21 DBIx::DBSchema - Database-independent schema objects
22
23 =head1 SYNOPSIS
24
25   use DBIx::DBSchema;
26
27   $schema = new DBIx::DBSchema @dbix_dbschema_table_objects;
28   $schema = new_odbc DBIx::DBSchema $dbh;
29   $schema = new_odbc DBIx::DBSchema $dsn, $user, $pass;
30   $schema = new_native DBIx::DBSchema $dbh;
31   $schema = new_native DBIx::DBSchema $dsn, $user, $pass;
32
33   $schema->save("filename");
34   $schema = load DBIx::DBSchema "filename";
35
36   $schema->addtable($dbix_dbschema_table_object);
37
38   @table_names = $schema->tables;
39
40   $DBIx_DBSchema_table_object = $schema->table("table_name");
41
42   @sql = $schema->sql($dbh);
43   @sql = $schema->sql($dsn, $username, $password);
44   @sql = $schema->sql($dsn); #doesn't connect to database - less reliable
45
46   $perl_code = $schema->pretty_print;
47   %hash = eval $perl_code;
48   use DBI qw(:sql_types); $schema = pretty_read DBIx::DBSchema \%hash;
49
50 =head1 DESCRIPTION
51
52 DBIx::DBSchema objects are collections of DBIx::DBSchema::Table objects and
53 represent a database schema.
54
55 This module implements an OO-interface to database schemas.  Using this module,
56 you can create a database schema with an OO Perl interface.  You can read the
57 schema from an existing database.  You can save the schema to disk and restore
58 it a different process.  Most importantly, DBIx::DBSchema can write SQL
59 CREATE statements statements for different databases from a single source.
60
61 Currently supported databases are MySQL and PostgreSQL.  Sybase support is
62 partially implemented.  DBIx::DBSchema will attempt to use generic SQL syntax
63 for other databases.  Assistance adding support for other databases is
64 welcomed.  See L<DBIx::DBSchema::DBD>, "Driver Writer's Guide and Base Class".
65
66 =head1 METHODS
67
68 =over 4
69
70 =item new TABLE_OBJECT, TABLE_OBJECT, ...
71
72 Creates a new DBIx::DBSchema object.
73
74 =cut
75
76 sub new {
77   my($proto, @tables) = @_;
78   my %tables = map  { $_->name, $_ } @tables; #check for duplicates?
79
80   my $class = ref($proto) || $proto;
81   my $self = {
82     'tables' => \%tables,
83   };
84
85   bless ($self, $class);
86
87 }
88
89 =item new_odbc DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
90
91 Creates a new DBIx::DBSchema object from an existing data source, which can be
92 specified by passing an open DBI database handle, or by passing the DBI data
93 source name, username, and password.  This uses the experimental DBI type_info
94 method to create a schema with standard (ODBC) SQL column types that most
95 closely correspond to any non-portable column types.  Use this to import a
96 schema that you wish to use with many different database engines.  Although
97 primary key and (unique) index information will only be read from databases
98 with DBIx::DBSchema::DBD drivers (currently MySQL and PostgreSQL), import of
99 column names and attributes *should* work for any database.  Note that this
100 method only uses "ODBC" column types; it does not require or use an ODBC
101 driver.
102
103 =cut
104
105 sub new_odbc {
106   my($proto, $dbh) = ( shift, _dbh(@_) );
107   $proto->new(
108     map { new_odbc DBIx::DBSchema::Table $dbh, $_ } _tables_from_dbh($dbh)
109   );
110 }
111
112 =item new_native DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
113
114 Creates a new DBIx::DBSchema object from an existing data source, which can be
115 specified by passing an open DBI database handle, or by passing the DBI data
116 source name, username and password.  This uses database-native methods to read
117 the schema, and will preserve any non-portable column types.  The method is
118 only available if there is a DBIx::DBSchema::DBD for the corresponding database engine (currently, MySQL and PostgreSQL).
119
120 =cut
121
122 sub new_native {
123   my($proto, $dbh) = (shift, _dbh(@_) );
124   $proto->new(
125     map { new_native DBIx::DBSchema::Table ( $dbh, $_ ) } _tables_from_dbh($dbh)
126   );
127 }
128
129 =item load FILENAME
130
131 Loads a DBIx::DBSchema object from a file.
132
133 =cut
134
135 sub load {
136   my($proto,$file)=@_; #use $proto ?
137
138   my $self;
139
140   #first try Storable
141   eval { $self = Storable::retrieve($file); };
142
143   if ( $@ && $@ =~ /not.*storable/i ) { #then try FreezeThaw
144     eval "use FreezeThaw;";
145     die $@ if $@;
146     open(FILE,"<$file") or die "Can't open $file: $!";
147     my $string = join('',<FILE>);
148     close FILE or die "Can't close $file: $!";
149     ($self) = FreezeThaw::thaw($string);
150   }
151
152   $self;
153
154 }
155
156 =item save FILENAME
157
158 Saves a DBIx::DBSchema object to a file.
159
160 =cut
161
162 sub save {
163   #my($self, $file) = @_;
164   Storable::nstore(@_);
165 }
166
167 =item addtable TABLE_OBJECT
168
169 Adds the given DBIx::DBSchema::Table object to this DBIx::DBSchema.
170
171 =cut
172
173 sub addtable {
174   my($self,$table)=@_;
175   $self->{'tables'}->{$table->name} = $table; #check for dupliates?
176 }
177
178 =item tables 
179
180 Returns a list of the names of all tables.
181
182 =cut
183
184 sub tables {
185   my($self)=@_;
186   keys %{$self->{'tables'}};
187 }
188
189 =item table TABLENAME
190
191 Returns the specified DBIx::DBSchema::Table object.
192
193 =cut
194
195 sub table {
196   my($self,$table)=@_;
197   $self->{'tables'}->{$table};
198 }
199
200 =item sql [ DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ] ]
201
202 Returns a list of SQL `CREATE' statements for this schema.
203
204 The data source can be specified by passing an open DBI database handle, or by
205 passing the DBI data source name, username and password.  
206
207 Although the username and password are optional, it is best to call this method
208 with a database handle or data source including a valid username and password -
209 a DBI connection will be opened and the quoting and type mapping will be more
210 reliable.
211
212 If passed a DBI data source (or handle) such as `DBI:mysql:database' or
213 `DBI:Pg:dbname=database', will use syntax specific to that database engine.
214 Currently supported databases are MySQL and PostgreSQL.
215
216 If not passed a data source (or handle), or if there is no driver for the
217 specified database, will attempt to use generic SQL syntax.
218
219 =cut
220
221 sub sql {
222   my($self, $dbh) = ( shift, _dbh(@_) );
223   map { $self->table($_)->sql_create_table($dbh); } $self->tables;
224 }
225
226 =item sql_update_schema PROTOTYPE_SCHEMA [ DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ] ]
227
228 Returns a list of SQL statements to update this schema so that it is idential
229 to the provided prototype schema, also a DBIx::DBSchema object.
230
231  #Optionally, the data source can be specified by passing an open DBI database
232  #handle, or by passing the DBI data source name, username and password.  
233  #
234  #If passed a DBI data source (or handle) such as `DBI:mysql:database' or
235  #`DBI:Pg:dbname=database', will use syntax specific to that database engine.
236  #Currently supported databases are MySQL and PostgreSQL.
237  #
238  #If not passed a data source (or handle), or if there is no driver for the
239  #specified database, will attempt to use generic SQL syntax.
240
241 Right now this method knows how to add new tables and alter existing tables.
242 It doesn't know how to drop tables yet.
243
244 See L<DBIx::DBSchema::Table/sql_alter_table>,
245 L<DBIx::DBSchema::Column/sql_add_coumn> and
246 L<DBIx::DBSchema::Column/sql_alter_column> for additional specifics and
247 limitations.
248
249 =cut
250
251 #gosh, false laziness w/DBSchema::Table::sql_alter_schema
252
253 sub sql_update_schema {
254   my($self, $new, $dbh) = ( shift, shift, _dbh(@_) );
255
256   my @r = ();
257
258   foreach my $table ( $new->tables ) {
259   
260     if ( $self->table($table) ) {
261   
262       warn "$table exists\n" if $DEBUG > 1;
263
264       push @r,
265         $self->table($table)->sql_alter_table( $new->table($table), $dbh );
266
267     } else {
268   
269       warn "table $table does not exist.\n" if $DEBUG;
270
271       push @r, 
272         $new->table($table)->sql_create_table( $dbh );
273   
274     }
275   
276   }
277
278   # should eventually drop tables not in $new
279
280   warn join("\n", @r). "\n"
281     if $DEBUG;
282
283   @r;
284   
285 }
286
287 =item update_schema PROTOTYPE_SCHEMA, DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ]
288
289 Same as sql_update_schema, except actually runs the SQL commands to update
290 the schema.  Throws a fatal error if any statement fails.
291
292 =cut
293
294 sub update_schema {
295   my($self, $new, $dbh) = ( shift, shift, _dbh(@_) );
296
297   foreach my $statement ( $self->sql_update_schema( $new, $dbh ) ) {
298     $dbh->do( $statement )
299       or die "Error: ". $dbh->errstr. "\n executing: $statement";
300   }
301
302 }
303
304 =item pretty_print
305
306 Returns the data in this schema as Perl source, suitable for assigning to a
307 hash.
308
309 =cut
310
311 sub pretty_print {
312   my($self) = @_;
313   join("},\n\n",
314     map {
315       my $table = $_;
316       "'$table' => {\n".
317         "  'columns' => [\n".
318           join("", map { 
319                          #cant because -w complains about , in qw()
320                          # (also biiiig problems with empty lengths)
321                          #"    qw( $_ ".
322                          #$self->table($table)->column($_)->type. " ".
323                          #( $self->table($table)->column($_)->null ? 'NULL' : 0 ). " ".
324                          #$self->table($table)->column($_)->length. " ),\n"
325                          "    '$_', ".
326                          "'". $self->table($table)->column($_)->type. "', ".
327                          "'". $self->table($table)->column($_)->null. "', ". 
328                          "'". $self->table($table)->column($_)->length. "', ".
329                          "'". $self->table($table)->column($_)->default. "', ".
330                          "'". $self->table($table)->column($_)->local. "',\n"
331                        } $self->table($table)->columns
332           ).
333         "  ],\n".
334         "  'primary_key' => '". $self->table($table)->primary_key. "',\n".
335         "  'unique' => [ ". join(', ',
336           map { "[ '". join("', '", @{$_}). "' ]" }
337             @{$self->table($table)->unique->lol_ref}
338           ).  " ],\n".
339         "  'index' => [ ". join(', ',
340           map { "[ '". join("', '", @{$_}). "' ]" }
341             @{$self->table($table)->index->lol_ref}
342           ). " ],\n"
343         #"  'index' => [ ".    " ],\n"
344     } $self->tables
345   ). "}\n";
346 }
347
348 =cut
349
350 =item pretty_read HASHREF
351
352 Creates a schema as specified by a data structure such as that created by
353 B<pretty_print> method.
354
355 =cut
356
357 sub pretty_read {
358   my($proto, $href) = @_;
359   my $schema = $proto->new( map {  
360     my(@columns);
361     while ( @{$href->{$_}{'columns'}} ) {
362       push @columns, DBIx::DBSchema::Column->new(
363         splice @{$href->{$_}{'columns'}}, 0, 6
364       );
365     }
366     DBIx::DBSchema::Table->new(
367       $_,
368       $href->{$_}{'primary_key'},
369       DBIx::DBSchema::ColGroup::Unique->new($href->{$_}{'unique'}),
370       DBIx::DBSchema::ColGroup::Index->new($href->{$_}{'index'}),
371       @columns,
372     );
373   } (keys %{$href}) );
374 }
375
376 # private subroutines
377
378 sub _tables_from_dbh {
379   my($dbh) = @_;
380   my $driver = _load_driver($dbh);
381   my $db_catalog =
382     scalar(eval "DBIx::DBSchema::DBD::$driver->default_db_catalog");
383   my $db_schema  =
384     scalar(eval "DBIx::DBSchema::DBD::$driver->default_db_schema");
385   my $sth = $dbh->table_info($db_catalog, $db_schema, '', 'TABLE')
386     or die $dbh->errstr;
387   #map { $_->{TABLE_NAME} } grep { $_->{TABLE_TYPE} eq 'TABLE' }
388   #  @{ $sth->fetchall_arrayref({ TABLE_NAME=>1, TABLE_TYPE=>1}) };
389   map { $_->[0] } grep { $_->[1] =~ /^TABLE$/i }
390     @{ $sth->fetchall_arrayref([2,3]) };
391 }
392
393 =back
394
395 =head1 AUTHORS
396
397 Ivan Kohler <ivan-dbix-dbschema@420.am>
398
399 Charles Shapiro <charles.shapiro@numethods.com> and Mitchell Friedman
400 <mitchell.friedman@numethods.com> contributed the start of a Sybase driver.
401
402 Daniel Hanks <hanksdc@about-inc.com> contributed the Oracle driver.
403
404 Jesse Vincent contributed the SQLite driver.
405
406 =head1 CONTRIBUTIONS
407
408 Contributions are welcome!  I'm especially keen on any interest in the first
409 three items/projects below under BUGS.
410
411 =head1 COPYRIGHT
412
413 Copyright (c) 2000-2006 Ivan Kohler
414 Copyright (c) 2000 Mail Abuse Prevention System LLC
415 All rights reserved.
416 This program is free software; you can redistribute it and/or modify it under
417 the same terms as Perl itself.
418
419 =head1 BUGS
420
421 Indices are not stored by name.  Index representation could use an overhaul.
422
423 Multiple primary keys are not yet supported.
424
425 Foreign keys and other constraints are not yet supported.
426
427 Eventually it would be nice to have additional transformations (deleted,
428 modified columns, added/modified/indices (probably need em named first),
429 added/deleted tables
430
431 Need to port and test with additional databases
432
433 Each DBIx::DBSchema object should have a name which corresponds to its name
434 within the SQL database engine (DBI data source).
435
436 pretty_print is actually pretty ugly.
437
438 Perhaps pretty_read should eval column types so that we can use DBI
439 qw(:sql_types) here instead of externally.
440
441 sql CREATE TABLE output should convert integers
442 (i.e. use DBI qw(:sql_types);) to local types using DBI->type_info plus a hash
443 to fudge things
444
445 sql_update_schema doesn't drop tables yet.
446
447 =head1 SEE ALSO
448
449 L<DBIx::DBSchema::Table>, L<DBIx::DBSchema::ColGroup>,
450 L<DBIx::DBSchema::ColGroup::Unique>, L<DBIx::DBSchema::ColGroup::Index>,
451 L<DBIx::DBSchema::Column>, L<DBIx::DBSchema::DBD>,
452 L<DBIx::DBSchema::DBD::mysql>, L<DBIx::DBSchema::DBD::Pg>, L<FS::Record>,
453 L<DBI>
454
455 =cut
456
457 1;
458