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