explicitly disconnect if created_dbh
[DBIx-DBSchema.git] / DBSchema / Table.pm
1 package DBIx::DBSchema::Table;
2
3 use strict;
4 use vars qw(@ISA %create_params);
5 #use Carp;
6 use Exporter;
7 use DBIx::DBSchema::Column;
8 use DBIx::DBSchema::ColGroup::Unique;
9 use DBIx::DBSchema::ColGroup::Index;
10
11 #@ISA = qw(Exporter);
12 @ISA = qw();
13
14 =head1 NAME
15
16 DBIx::DBSchema::Table - Table objects
17
18 =head1 SYNOPSIS
19
20   use DBIx::DBSchema::Table;
21
22   $table = new DBIx::DBSchema::Table (
23     "table_name",
24     "primary_key",
25     $dbix_dbschema_colgroup_unique_object,
26     $dbix_dbschema_colgroup_index_object,
27     @dbix_dbschema_column_objects,
28   );
29
30   $table->addcolumn ( $dbix_dbschema_column_object );
31
32   $table_name = $table->name;
33   $table->name("table_name");
34
35   $primary_key = $table->primary_key;
36   $table->primary_key("primary_key");
37
38   $dbix_dbschema_colgroup_unique_object = $table->unique;
39   $table->unique( $dbix_dbschema__colgroup_unique_object );
40
41   $dbix_dbschema_colgroup_index_object = $table->index;
42   $table->index( $dbix_dbschema_colgroup_index_object );
43
44   @column_names = $table->columns;
45
46   $dbix_dbschema_column_object = $table->column("column");
47
48   #preferred
49   @sql_statements = $table->sql_create_table $dbh;
50   @sql_statements = $table->sql_create_table $datasrc, $username, $password;
51
52   #possible problems
53   @sql_statements = $table->sql_create_table $datasrc;
54   @sql_statements = $table->sql_create_table;
55
56 =head1 DESCRIPTION
57
58 DBIx::DBSchema::Table objects represent a single database table.
59
60 =head1 METHODS
61
62 =over 4
63
64 =item new [ TABLE_NAME [ , PRIMARY_KEY [ , UNIQUE [ , INDEX [ , COLUMN... ] ] ] ] ]
65
66 Creates a new DBIx::DBSchema::Table object.  TABLE_NAME is the name of the
67 table.  PRIMARY_KEY is the primary key (may be empty).  UNIQUE is a
68 DBIx::DBSchema::ColGroup::Unique object (see
69 L<DBIx::DBSchema::ColGroup::Unique>).  INDEX is a
70 DBIx::DBSchema::ColGroup::Index object (see
71 L<DBIx::DBSchema::ColGroup::Index>).  The rest of the arguments should be
72 DBIx::DBSchema::Column objects (see L<DBIx::DBSchema::Column>).
73
74 =cut
75
76 sub new {
77   my($proto,$name,$primary_key,$unique,$index,@columns)=@_;
78
79   my(%columns) = map { $_->name, $_ } @columns;
80   my(@column_order) = map { $_->name } @columns;
81
82   #check $primary_key, $unique and $index to make sure they are $columns ?
83   # (and sanity check?)
84
85   my $class = ref($proto) || $proto;
86   my $self = {
87     'name'         => $name,
88     'primary_key'  => $primary_key,
89     'unique'       => $unique,
90     'index'        => $index,
91     'columns'      => \%columns,
92     'column_order' => \@column_order,
93   };
94
95   bless ($self, $class);
96
97 }
98
99 =item new_odbc DATABASE_HANDLE TABLE_NAME
100
101 Creates a new DBIx::DBSchema::Table object from the supplied DBI database
102 handle for the specified table.  This uses the experimental DBI type_info
103 method to create a table with standard (ODBC) SQL column types that most
104 closely correspond to any non-portable column types.   Use this to import a
105 schema that you wish to use with many different database engines.  Although
106 primary key and (unique) index information will only be imported from databases
107 with DBIx::DBSchema::DBD drivers (currently MySQL and PostgreSQL), import of
108 column names and attributes *should* work for any database.
109
110 =cut
111
112 %create_params = (
113 #  undef             => sub { '' },
114   ''                => sub { '' },
115   'max length'      => sub { $_[0]->{PRECISION}->[$_[1]]; },
116   'precision,scale' =>
117     sub { $_[0]->{PRECISION}->[$_[1]]. ','. $_[0]->{SCALE}->[$_[1]]; }
118 );
119
120 sub new_odbc {
121   my( $proto, $dbh, $name) = @_;
122   my $driver = DBIx::DBSchema::_load_driver($dbh);
123   my $sth = _null_sth($dbh, $name);
124   my $sthpos = 0;
125   $proto->new (
126     $name,
127     scalar(eval "DBIx::DBSchema::DBD::$driver->primary_key(\$dbh, \$name)"),
128     DBIx::DBSchema::ColGroup::Unique->new(
129       $driver
130        ? [values %{eval "DBIx::DBSchema::DBD::$driver->unique(\$dbh, \$name)"}]
131        : []
132     ),
133     DBIx::DBSchema::ColGroup::Index->new(
134       $driver
135       ? [ values %{eval "DBIx::DBSchema::DBD::$driver->index(\$dbh, \$name)"} ]
136       : []
137     ),
138     map { 
139       my $type_info = scalar($dbh->type_info($sth->{TYPE}->[$sthpos]))
140         or die "DBI::type_info ". $dbh->{Driver}->{Name}. " driver ".
141                "returned no results for type ".  $sth->{TYPE}->[$sthpos];
142       new DBIx::DBSchema::Column
143           $_,
144           $type_info->{'TYPE_NAME'},
145           #"SQL_". uc($type_info->{'TYPE_NAME'}),
146           $sth->{NULLABLE}->[$sthpos],
147           &{ $create_params{ $type_info->{CREATE_PARAMS} } }( $sth, $sthpos++ ),          $driver && #default
148             ${ [
149               eval "DBIx::DBSchema::DBD::$driver->column(\$dbh, \$name, \$_)"
150             ] }[4]
151           # DB-local
152     } @{$sth->{NAME}}
153   );
154 }
155
156 =item new_native DATABASE_HANDLE TABLE_NAME
157
158 Creates a new DBIx::DBSchema::Table object from the supplied DBI database
159 handle for the specified table.  This uses database-native methods to read the
160 schema, and will preserve any non-portable column types.  The method is only
161 available if there is a DBIx::DBSchema::DBD for the corresponding database
162 engine (currently, MySQL and PostgreSQL).
163
164 =cut
165
166 sub new_native {
167   my( $proto, $dbh, $name) = @_;
168   my $driver = DBIx::DBSchema::_load_driver($dbh);
169   $proto->new (
170     $name,
171     scalar(eval "DBIx::DBSchema::DBD::$driver->primary_key(\$dbh, \$name)"),
172     DBIx::DBSchema::ColGroup::Unique->new(
173       [ values %{eval "DBIx::DBSchema::DBD::$driver->unique(\$dbh, \$name)"} ]
174     ),
175     DBIx::DBSchema::ColGroup::Index->new(
176       [ values %{eval "DBIx::DBSchema::DBD::$driver->index(\$dbh, \$name)"} ]
177     ),
178     map {
179       DBIx::DBSchema::Column->new( @{$_} )
180     } eval "DBIx::DBSchema::DBD::$driver->columns(\$dbh, \$name)"
181   );
182 }
183
184 =item addcolumn COLUMN
185
186 Adds this DBIx::DBSchema::Column object. 
187
188 =cut
189
190 sub addcolumn {
191   my($self,$column)=@_;
192   ${$self->{'columns'}}{$column->name}=$column; #sanity check?
193   push @{$self->{'column_order'}}, $column->name;
194 }
195
196 =item name [ TABLE_NAME ]
197
198 Returns or sets the table name.
199
200 =cut
201
202 sub name {
203   my($self,$value)=@_;
204   if ( defined($value) ) {
205     $self->{name} = $value;
206   } else {
207     $self->{name};
208   }
209 }
210
211 =item primary_key [ PRIMARY_KEY ]
212
213 Returns or sets the primary key.
214
215 =cut
216
217 sub primary_key {
218   my($self,$value)=@_;
219   if ( defined($value) ) {
220     $self->{primary_key} = $value;
221   } else {
222     #$self->{primary_key};
223     #hmm.  maybe should untaint the entire structure when it comes off disk 
224     # cause if you don't trust that, ?
225     $self->{primary_key} =~ /^(\w*)$/ 
226       #aah!
227       or die "Illegal primary key: ", $self->{primary_key};
228     $1;
229   }
230 }
231
232 =item unique [ UNIQUE ]
233
234 Returns or sets the DBIx::DBSchema::ColGroup::Unique object.
235
236 =cut
237
238 sub unique { 
239   my($self,$value)=@_;
240   if ( defined($value) ) {
241     $self->{unique} = $value;
242   } else {
243     $self->{unique};
244   }
245 }
246
247 =item index [ INDEX ]
248
249 Returns or sets the DBIx::DBSchema::ColGroup::Index object.
250
251 =cut
252
253 sub index { 
254   my($self,$value)=@_;
255   if ( defined($value) ) {
256     $self->{'index'} = $value;
257   } else {
258     $self->{'index'};
259   }
260 }
261
262 =item columns
263
264 Returns a list consisting of the names of all columns.
265
266 =cut
267
268 sub columns {
269   my($self)=@_;
270   #keys %{$self->{'columns'}};
271   #must preserve order
272   @{ $self->{'column_order'} };
273 }
274
275 =item column COLUMN_NAME
276
277 Returns the column object (see L<DBIx::DBSchema::Column>) for the specified
278 COLUMN_NAME.
279
280 =cut
281
282 sub column {
283   my($self,$column)=@_;
284   $self->{'columns'}->{$column};
285 }
286
287 =item sql_create_table [ DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ] ]
288
289 Returns a list of SQL statments to create this table.
290
291 The data source can be specified by passing an open DBI database handle, or by
292 passing the DBI data source name, username and password.  
293
294 Although the username and password are optional, it is best to call this method
295 with a database handle or data source including a valid username and password -
296 a DBI connection will be opened and the quoting and type mapping will be more
297 reliable.
298
299 If passed a DBI data source (or handle) such as `DBI:mysql:database', will use
300 MySQL-specific syntax.  PostgreSQL is also supported (requires no special
301 syntax).  Non-standard syntax for other engines (if applicable) may also be
302 supported in the future.
303
304 =cut
305
306
307 sub sql_create_table { 
308   my($self, $dbh) = (shift, shift);
309
310   my $created_dbh = 0;
311   unless ( ref($dbh) || ! @_ ) {
312     $dbh = DBI->connect( $dbh, @_ ) or die $DBI::errstr;
313     my $gratuitous = $DBI::errstr; #surpress superfluous `used only once' error
314     $created_dbh = 1;
315   }
316   #false laziness: nicked from DBSchema::_load_driver
317   my $driver;
318   if ( ref($dbh) ) {
319     $driver = $dbh->{Driver}->{Name};
320   } else {
321     my $discard = $dbh;
322     $discard =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i #nicked from DBI->connect
323                         or '' =~ /()/; # ensure $1 etc are empty if match fails
324     $driver = $1 or die "can't parse data source: $dbh";
325   }
326   #eofalse
327
328   my(@columns)=map { $self->column($_)->line($dbh) } $self->columns;
329   push @columns, "PRIMARY KEY (". $self->primary_key. ")"
330     if $self->primary_key;
331   if ( $driver eq 'mysql' ) { #yucky mysql hack
332     push @columns, map "UNIQUE ($_)", $self->unique->sql_list;
333     push @columns, map "INDEX ($_)", $self->index->sql_list;
334   }
335
336   my @r =
337     "CREATE TABLE ". $self->name. " (\n  ". join(",\n  ", @columns). "\n)\n",
338     ( map {
339       my($index) = $self->name. "__". $_ . "_index";
340       $index =~ s/,\s*/_/g;
341       "CREATE UNIQUE INDEX $index ON ". $self->name. " ($_)\n"
342     } $self->unique->sql_list ),
343     ( map {
344       my($index) = $self->name. "__". $_ . "_index";
345       $index =~ s/,\s*/_/g;
346       "CREATE INDEX $index ON ". $self->name. " ($_)\n"
347     } $self->index->sql_list ),
348   ;  
349   $dbh->disconnect if $created_dbh;
350   @r;
351 }
352
353 #
354
355 sub _null_sth {
356   my($dbh, $table) = @_;
357   my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0")
358     or die $dbh->errstr;
359   $sth->execute or die $sth->errstr;
360   $sth;
361 }
362
363 =back
364
365 =head1 AUTHOR
366
367 Ivan Kohler <ivan-dbix-dbschema@420.am>
368
369 =head1 COPYRIGHT
370
371 Copyright (c) 2000 Ivan Kohler
372 Copyright (c) 2000 Mail Abuse Prevention System LLC
373 All rights reserved.
374 This program is free software; you can redistribute it and/or modify it under
375 the same terms as Perl itself.
376
377 =head1 BUGS
378
379 sql_create_table() has database-specific foo that probably ought to be
380 abstracted into the DBIx::DBSchema::DBD:: modules.
381
382 Some of the logic in new_odbc might be better abstracted into Column.pm etc.
383
384 =head1 SEE ALSO
385
386 L<DBIx::DBSchema>, L<DBIx::DBSchema::ColGroup::Unique>,
387 L<DBIx::DBSchema::ColGroup::Index>, L<DBIx::DBSchema::Column>, L<DBI>
388
389 =cut
390
391 1;
392