get rid of use of uninitialized value errors
[DBIx-DBSchema.git] / DBSchema.pm
1 package DBIx::DBSchema;
2
3 use strict;
4 use vars qw(@ISA $VERSION);
5 #use Exporter;
6 use Carp qw(confess);
7 use DBI;
8 use FreezeThaw qw(freeze thaw cmpStr);
9 use DBIx::DBSchema::Table;
10 use DBIx::DBSchema::Column;
11 use DBIx::DBSchema::ColGroup::Unique;
12 use DBIx::DBSchema::ColGroup::Index;
13
14 #@ISA = qw(Exporter);
15 @ISA = ();
16
17 $VERSION = "0.13";
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.  DBIx::DBSchema will
62 attempt to use generic SQL syntax for other databases.  Assistance adding
63 support for other databases is welcomed.
64
65 =head1 METHODS
66
67 =over 4
68
69 =item new TABLE_OBJECT, TABLE_OBJECT, ...
70
71 Creates a new DBIx::DBSchema object.
72
73 =cut
74
75 sub new {
76   my($proto, @tables) = @_;
77   my %tables = map  { $_->name, $_ } @tables; #check for duplicates?
78
79   my $class = ref($proto) || $proto;
80   my $self = {
81     'tables' => \%tables,
82   };
83
84   bless ($self, $class);
85
86 }
87
88 =item new_odbc DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
89
90 Creates a new DBIx::DBSchema object from an existing data source, which can be
91 specified by passing an open DBI database handle, or by passing the DBI data
92 source name, username, and password.  This uses the experimental DBI type_info
93 method to create a schema with standard (ODBC) SQL column types that most
94 closely correspond to any non-portable column types.  Use this to import a
95 schema that you wish to use with many different database engines.  Although
96 primary key and (unique) index information will only be read from databases
97 with DBIx::DBSchema::DBD drivers (currently MySQL and PostgreSQL), import of
98 column names and attributes *should* work for any database.
99
100 =cut
101
102 sub new_odbc {
103   my($proto, $dbh) = (shift, shift);
104   $dbh = DBI->connect( $dbh, @_ ) or die $DBI::errstr unless ref($dbh);
105   $proto->new(
106     map { new_odbc DBIx::DBSchema::Table $dbh, $_ } _tables_from_dbh($dbh)
107   );
108 }
109
110 =item new_native DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
111
112 Creates a new DBIx::DBSchema object from an existing data source, which can be
113 specified by passing an open DBI database handle, or by passing the DBI data
114 source name, username and password.  This uses database-native methods to read
115 the schema, and will preserve any non-portable column types.  The method is
116 only available if there is a DBIx::DBSchema::DBD for the corresponding database engine (currently, MySQL and PostgreSQL).
117
118 =cut
119
120 sub new_native {
121   my($proto, $dbh) = (shift, shift);
122   $dbh = DBI->connect( $dbh, @_ ) or die $DBI::errstr unless ref($dbh);
123   $proto->new(
124     map { new_native DBIx::DBSchema::Table ( $dbh, $_ ) } _tables_from_dbh($dbh)
125   );
126 }
127
128 =item load FILENAME
129
130 Loads a DBIx::DBSchema object from a file.
131
132 =cut
133
134 sub load {
135   my($proto,$file)=@_; #use $proto ?
136   open(FILE,"<$file") or die "Can't open $file: $!";
137   my($string)=join('',<FILE>); #can $string have newlines?  pry not?
138   close FILE or die "Can't close $file: $!";
139   my($self)=thaw $string;
140   #no bless needed?
141   $self;
142 }
143
144 =item save FILENAME
145
146 Saves a DBIx::DBSchema object to a file.
147
148 =cut
149
150 sub save {
151   my($self,$file)=@_;
152   my($string)=freeze $self;
153   open(FILE,">$file") or die "Can't open $file: $!";
154   print FILE $string;
155   close FILE or die "Can't close file: $!";
156   my($check_self)=thaw $string;
157   die "Verify error: Can't freeze and thaw dbdef $self"
158     if (cmpStr($self,$check_self));
159 }
160
161 =item addtable TABLE_OBJECT
162
163 Adds the given DBIx::DBSchema::Table object to this DBIx::DBSchema.
164
165 =cut
166
167 sub addtable {
168   my($self,$table)=@_;
169   $self->{'tables'}->{$table->name} = $table; #check for dupliates?
170 }
171
172 =item tables 
173
174 Returns a list of the names of all tables.
175
176 =cut
177
178 sub tables {
179   my($self)=@_;
180   keys %{$self->{'tables'}};
181 }
182
183 =item table TABLENAME
184
185 Returns the specified DBIx::DBSchema::Table object.
186
187 =cut
188
189 sub table {
190   my($self,$table)=@_;
191   $self->{'tables'}->{$table};
192 }
193
194 =item sql [ DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ] ]
195
196 Returns a list of SQL `CREATE' statements for this schema.
197
198 The data source can be specified by passing an open DBI database handle, or by
199 passing the DBI data source name, username and password.  
200
201 Although the username and password are optional, it is best to call this method
202 with a database handle or data source including a valid username and password -
203 a DBI connection will be opened and the quoting and type mapping will be more
204 reliable.
205
206 If passed a DBI data source (or handle) such as `DBI:mysql:database' or
207 `DBI:Pg:dbname=database', will use syntax specific to that database engine.
208 Currently supported databases are MySQL and PostgreSQL.
209
210 If not passed a data source (or handle), or if there is no driver for the
211 specified database, will attempt to use generic SQL syntax.
212
213 =cut
214
215 sub sql {
216   my($self, $dbh) = (shift, shift);
217   $dbh = DBI->connect( $dbh, @_ ) or die $DBI::errstr
218     unless ref($dbh) || ! @_;
219   map { $self->table($_)->sql_create_table($dbh); } $self->tables;
220 }
221
222 =item pretty_print
223
224 Returns the data in this schema as Perl source, suitable for assigning to a
225 hash.
226
227 =cut
228
229 sub pretty_print {
230   my($self) = @_;
231   join("},\n\n",
232     map {
233       my $table = $_;
234       "'$table' => {\n".
235         "  'columns' => [\n".
236           join("", map { 
237                          #cant because -w complains about , in qw()
238                          # (also biiiig problems with empty lengths)
239                          #"    qw( $_ ".
240                          #$self->table($table)->column($_)->type. " ".
241                          #( $self->table($table)->column($_)->null ? 'NULL' : 0 ). " ".
242                          #$self->table($table)->column($_)->length. " ),\n"
243                          "    '$_', ".
244                          "'". $self->table($table)->column($_)->type. "', ".
245                          "'". $self->table($table)->column($_)->null. "', ". 
246                          "'". $self->table($table)->column($_)->length. "', ".
247                          "'". $self->table($table)->column($_)->default. "', ".
248                          "'". $self->table($table)->column($_)->local. "',\n"
249                        } $self->table($table)->columns
250           ).
251         "  ],\n".
252         "  'primary_key' => '". $self->table($table)->primary_key. "',\n".
253         "  'unique' => [ ". join(', ',
254           map { "[ '". join("', '", @{$_}). "' ]" }
255             @{$self->table($table)->unique->lol_ref}
256           ).  " ],\n".
257         "  'index' => [ ". join(', ',
258           map { "[ '". join("', '", @{$_}). "' ]" }
259             @{$self->table($table)->index->lol_ref}
260           ). " ],\n"
261         #"  'index' => [ ".    " ],\n"
262     } $self->tables
263   ), "}\n";
264 }
265
266 =cut
267
268 =item pretty_read HASHREF
269
270 Creates a schema as specified by a data structure such as that created by
271 B<pretty_print> method.
272
273 =cut
274
275 sub pretty_read {
276   my($proto, $href) = @_;
277   my $schema = $proto->new( map {  
278     my(@columns);
279     while ( @{$href->{$_}{'columns'}} ) {
280       push @columns, DBIx::DBSchema::Column->new(
281         splice @{$href->{$_}{'columns'}}, 0, 6
282       );
283     }
284     DBIx::DBSchema::Table->new(
285       $_,
286       $href->{$_}{'primary_key'},
287       DBIx::DBSchema::ColGroup::Unique->new($href->{$_}{'unique'}),
288       DBIx::DBSchema::ColGroup::Index->new($href->{$_}{'index'}),
289       @columns,
290     );
291   } (keys %{$href}) );
292 }
293
294 # private subroutines
295
296 sub _load_driver {
297   my($dbh) = @_;
298   my $driver;
299   if ( ref($dbh) ) {
300     $driver = $dbh->{Driver}->{Name};
301   } else {
302     $dbh =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i #nicked from DBI->connect
303                         or '' =~ /()/; # ensure $1 etc are empty if match fails
304     $driver = $1 or confess "can't parse data source: $dbh";
305   }
306
307   #require "DBIx/DBSchema/DBD/$driver.pm";
308   #$driver;
309   eval 'require "DBIx/DBSchema/DBD/$driver.pm"' and $driver;
310 }
311
312 sub _tables_from_dbh {
313   my($dbh) = @_;
314   my $sth = $dbh->table_info or die $dbh->errstr;
315   #map { $_->{TABLE_NAME} } grep { $_->{TABLE_TYPE} eq 'TABLE' }
316   #  @{ $sth->fetchall_arrayref({ TABLE_NAME=>1, TABLE_TYPE=>1}) };
317   map { $_->[0] } grep { $_->[1] =~ /^TABLE$/i }
318     @{ $sth->fetchall_arrayref([2,3]) };
319 }
320
321 =back
322
323 =head1 AUTHOR
324
325 Ivan Kohler <ivan-dbix-dbschema@420.am>
326
327 =head1 COPYRIGHT
328
329 Copyright (c) 2000 Ivan Kohler
330 Copyright (c) 2000 Mail Abuse Prevention System LLC
331 All rights reserved.
332 This program is free software; you can redistribute it and/or modify it under
333 the same terms as Perl itself.
334
335 =head1 BUGS
336
337 Each DBIx::DBSchema object should have a name which corresponds to its name
338 within the SQL database engine (DBI data source).
339
340 pretty_print is actually pretty ugly.
341
342 Perhaps pretty_read should eval column types so that we can use DBI
343 qw(:sql_types) here instead of externally.
344
345 =head1 SEE ALSO
346
347 L<DBIx::DBSchema::Table>, L<DBIx::DBSchema::ColGroup>,
348 L<DBIx::DBSchema::ColGroup::Unique>, L<DBIx::DBSchema::ColGroup::Index>,
349 L<DBIx::DBSchema::Column>, L<DBIx::DBSchema::DBD>, L<DBIx::DBSchema::mysql>,
350 L<DBIx::DBSchema::Pg>, L<FS::Record>, L<DBI>
351
352 =cut
353
354 1;
355