use strict;
use vars qw(@ISA $VERSION);
#use Exporter;
-#use Carp qw(verbose);
+use Carp qw(confess);
use DBI;
-use FreezeThaw qw(freeze thaw cmpStr);
+use Storable;
use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column;
+use DBIx::DBSchema::ColGroup::Unique;
+use DBIx::DBSchema::ColGroup::Index;
#@ISA = qw(Exporter);
@ISA = ();
-$VERSION = "0.1";
+$VERSION = "0.25";
=head1 NAME
$DBIx_DBSchema_table_object = $schema->table("table_name");
- $sql_string = $schema->sql($dsn);
+ @sql = $schema->sql($dbh);
+ @sql = $schema->sql($dsn, $username, $password);
+ @sql = $schema->sql($dsn); #doesn't connect to database - less reliable
$perl_code = $schema->pretty_print;
%hash = eval $perl_code;
- $schema = pretty_read DBIx::DBSchema \%hash;
+ use DBI qw(:sql_types); $schema = pretty_read DBIx::DBSchema \%hash;
=head1 DESCRIPTION
DBIx::DBSchema objects are collections of DBIx::DBSchema::Table objects and
represent a database schema.
+This module implements an OO-interface to database schemas. Using this module,
+you can create a database schema with an OO Perl interface. You can read the
+schema from an existing database. You can save the schema to disk and restore
+it a different process. Most importantly, DBIx::DBSchema can write SQL
+CREATE statements statements for different databases from a single source.
+
+Currently supported databases are MySQL and PostgreSQL. Sybase support is
+partially implemented. DBIx::DBSchema will attempt to use generic SQL syntax
+for other databases. Assistance adding support for other databases is
+welcomed. See L<DBIx::DBSchema::DBD>, "Driver Writer's Guide and Base Class".
+
=head1 METHODS
=over 4
}
-=item new_odbc DATABASE_HANDLE || DATA_SOURCE USERNAME PASSWORD [ ATTR ]
+=item new_odbc DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
Creates a new DBIx::DBSchema object from an existing data source, which can be
specified by passing an open DBI database handle, or by passing the DBI data
schema that you wish to use with many different database engines. Although
primary key and (unique) index information will only be read from databases
with DBIx::DBSchema::DBD drivers (currently MySQL and PostgreSQL), import of
-column names and attributes *should* work for any database.
+column names and attributes *should* work for any database. Note that this
+method only uses "ODBC" column types; it does not require or use an ODBC
+driver.
=cut
);
}
-=item new_native DATABASE_HANDLE || DATA_SOURCE USERNAME PASSWORD [ ATTR ]
+=item new_native DATABASE_HANDLE | DATA_SOURCE USERNAME PASSWORD [ ATTR ]
Creates a new DBIx::DBSchema object from an existing data source, which can be
specified by passing an open DBI database handle, or by passing the DBI data
sub load {
my($proto,$file)=@_; #use $proto ?
- open(FILE,"<$file") or die "Can't open $file: $!";
- my($string)=join('',<FILE>); #can $string have newlines? pry not?
- close FILE or die "Can't close $file: $!";
- my($self)=thaw $string;
- #no bless needed?
+
+ my $self;
+
+ #first try Storable
+ eval { $self = Storable::retrieve($file); };
+
+ if ( $@ && $@ =~ /not.*storable/i ) { #then try FreezeThaw
+ eval "use FreezeThaw;";
+ die $@ if $@;
+ open(FILE,"<$file") or die "Can't open $file: $!";
+ my $string = join('',<FILE>);
+ close FILE or die "Can't close $file: $!";
+ ($self) = FreezeThaw::thaw($string);
+ }
+
$self;
+
}
=item save FILENAME
=cut
sub save {
- my($self,$file)=@_;
- my($string)=freeze $self;
- open(FILE,">$file") or die "Can't open $file: $!";
- print FILE $string;
- close FILE or die "Can't close file: $!";
- my($check_self)=thaw $string;
- die "Verify error: Can't freeze and thaw dbdef $self"
- if (cmpStr($self,$check_self));
+ #my($self, $file) = @_;
+ Storable::nstore(@_);
}
=item addtable TABLE_OBJECT
$self->{'tables'}->{$table};
}
-=item sql_string [ DATASRC ]
+=item sql [ DATABASE_HANDLE | DATA_SOURCE [ USERNAME PASSWORD [ ATTR ] ] ]
Returns a list of SQL `CREATE' statements for this schema.
-If passed a DBI data source such as `DBI:mysql:database' or
+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 the quoting and type mapping will be more
+reliable.
+
+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 if there is no driver for the specified
-database, will attempt to use generic SQL syntax.
+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
-sub sql_string {
- my($self, $datasrc) = @_;
- map { $self->table($_)->sql_create_table($datasrc); } $self->tables;
+sub sql {
+ my($self, $dbh) = (shift, shift);
+ my $created_dbh = 0;
+ unless ( ref($dbh) || ! @_ ) {
+ $dbh = DBI->connect( $dbh, @_ ) or die $DBI::errstr;
+ $created_dbh = 1;
+ }
+ my @r = map { $self->table($_)->sql_create_table($dbh); } $self->tables;
+ $dbh->disconnect if $created_dbh;
+ @r;
}
=item pretty_print
"'$table' => {\n".
" 'columns' => [\n".
join("", map {
+ #cant because -w complains about , in qw()
+ # (also biiiig problems with empty lengths)
+ #" qw( $_ ".
+ #$self->table($table)->column($_)->type. " ".
+ #( $self->table($table)->column($_)->null ? 'NULL' : 0 ). " ".
+ #$self->table($table)->column($_)->length. " ),\n"
" '$_', ".
"'". $self->table($table)->column($_)->type. "', ".
- "'". $self->table($table)->column($_)->null. "', ".
- "'". $self->table($table)->column($_)->length. "'\n"
+ "'". $self->table($table)->column($_)->null. "', ".
+ "'". $self->table($table)->column($_)->length. "', ".
+ "'". $self->table($table)->column($_)->default. "', ".
+ "'". $self->table($table)->column($_)->local. "',\n"
} $self->table($table)->columns
).
" ],\n".
). " ],\n"
#" 'index' => [ ". " ],\n"
} $self->tables
- ), "}\n";
+ ). "}\n";
}
=cut
=cut
sub pretty_read {
- die "unimplemented (pull from fs-setup)";
- my($proto) = @_;
+ my($proto, $href) = @_;
+ my $schema = $proto->new( map {
+ my(@columns);
+ while ( @{$href->{$_}{'columns'}} ) {
+ push @columns, DBIx::DBSchema::Column->new(
+ splice @{$href->{$_}{'columns'}}, 0, 6
+ );
+ }
+ DBIx::DBSchema::Table->new(
+ $_,
+ $href->{$_}{'primary_key'},
+ DBIx::DBSchema::ColGroup::Unique->new($href->{$_}{'unique'}),
+ DBIx::DBSchema::ColGroup::Index->new($href->{$_}{'index'}),
+ @columns,
+ );
+ } (keys %{$href}) );
}
# private subroutines
sub _load_driver {
my($dbh) = @_;
- my $driver = $dbh->{Driver}->{Name};
+ my $driver;
+ if ( ref($dbh) ) {
+ $driver = $dbh->{Driver}->{Name};
+ } else {
+ $dbh =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i #nicked from DBI->connect
+ or '' =~ /()/; # ensure $1 etc are empty if match fails
+ $driver = $1 or confess "can't parse data source: $dbh";
+ }
+
#require "DBIx/DBSchema/DBD/$driver.pm";
#$driver;
- eval 'require "DBIx/DBSchema/DBD/$driver.pm"' and $driver;
+ eval 'require "DBIx/DBSchema/DBD/$driver.pm"' and $driver or die $@;
}
sub _tables_from_dbh {
Ivan Kohler <ivan-dbix-dbschema@420.am>
+Charles Shapiro <charles.shapiro@numethods.com> and Mitchell Friedman
+<mitchell.friedman@numethods.com> contributed the start of a Sybase driver.
+
=head1 COPYRIGHT
-Copyright (c) 2000 Ivan Kohler
+Copyright (c) 2000-2005 Ivan Kohler
Copyright (c) 2000 Mail Abuse Prevention System LLC
All rights reserved.
This program is free software; you can redistribute it and/or modify it under
Each DBIx::DBSchema object should have a name which corresponds to its name
within the SQL database engine (DBI data source).
-pretty_print is atrocious.
+pretty_print is actually pretty ugly.
+
+Perhaps pretty_read should eval column types so that we can use DBI
+qw(:sql_types) here instead of externally.
=head1 SEE ALSO
L<DBIx::DBSchema::Table>, L<DBIx::DBSchema::ColGroup>,
L<DBIx::DBSchema::ColGroup::Unique>, L<DBIx::DBSchema::ColGroup::Index>,
-L<DBIx::DBSchema::Column>, L<DBIx::DBSchema::DBD>, L<DBIx::DBSchema::mysql>,
-L<DBIx::DBSchema::Pg>, L<FS::Record>, L<DBI>
+L<DBIx::DBSchema::Column>, L<DBIx::DBSchema::DBD>,
+L<DBIx::DBSchema::DBD::mysql>, L<DBIx::DBSchema::DBD::Pg>, L<FS::Record>,
+L<DBI>
=cut