Document create_ddl_dir method
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
CommitLineData
a02675cd 1package DBIx::Class::Schema;
2
3use strict;
4use warnings;
aa562407 5
701da8c4 6use Carp::Clan qw/^DBIx::Class/;
a02675cd 7
41a6f8c0 8use base qw/DBIx::Class/;
a02675cd 9
0dc79249 10__PACKAGE__->mk_classdata('class_mappings' => {});
11__PACKAGE__->mk_classdata('source_registrations' => {});
1e10a11d 12__PACKAGE__->mk_classdata('storage_type' => '::DBI');
d7156e50 13__PACKAGE__->mk_classdata('storage');
a02675cd 14
c2da098a 15=head1 NAME
16
17DBIx::Class::Schema - composable schemas
18
19=head1 SYNOPSIS
20
24d67825 21 package Library::Schema;
c2da098a 22 use base qw/DBIx::Class::Schema/;
bab77431 23
24d67825 24 # load Library::Schema::CD, Library::Schema::Book, Library::Schema::DVD
25 __PACKAGE__->load_classes(qw/CD Book DVD/);
c2da098a 26
24d67825 27 package Library::Schema::CD;
03312470 28 use base qw/DBIx::Class/;
77254782 29 __PACKAGE__->load_components(qw/PK::Auto Core/); # for example
24d67825 30 __PACKAGE__->table('cd');
c2da098a 31
5d9076f2 32 # Elsewhere in your code:
24d67825 33 my $schema1 = Library::Schema->connect(
a3d93194 34 $dsn,
35 $user,
36 $password,
24d67825 37 { AutoCommit => 0 },
a3d93194 38 );
bab77431 39
24d67825 40 my $schema2 = Library::Schema->connect($coderef_returning_dbh);
c2da098a 41
24d67825 42 # fetch objects using Library::Schema::DVD
43 my $resultset = $schema1->resultset('DVD')->search( ... );
44 my @dvd_objects = $schema2->resultset('DVD')->search( ... );
c2da098a 45
46=head1 DESCRIPTION
47
a3d93194 48Creates database classes based on a schema. This is the recommended way to
49use L<DBIx::Class> and allows you to use more than one concurrent connection
50with your classes.
429bd4f1 51
03312470 52NB: If you're used to L<Class::DBI> it's worth reading the L</SYNOPSIS>
2053ab2a 53carefully, as DBIx::Class does things a little differently. Note in
03312470 54particular which module inherits off which.
55
c2da098a 56=head1 METHODS
57
87c4e602 58=head2 register_class
59
27f01d1f 60=over 4
61
ebc77b53 62=item Arguments: $moniker, $component_class
27f01d1f 63
64=back
076652e8 65
71f9df37 66Registers a class which isa DBIx::Class::ResultSourceProxy. Equivalent to
2053ab2a 67calling:
66d9ef6b 68
181a28f4 69 $schema->register_source($moniker, $component_class->result_source_instance);
076652e8 70
c2da098a 71=cut
72
a02675cd 73sub register_class {
0dc79249 74 my ($self, $moniker, $to_register) = @_;
75 $self->register_source($moniker => $to_register->result_source_instance);
74b92d9a 76}
77
87c4e602 78=head2 register_source
79
27f01d1f 80=over 4
81
ebc77b53 82=item Arguments: $moniker, $result_source
27f01d1f 83
84=back
076652e8 85
82b01c38 86Registers the L<DBIx::Class::ResultSource> in the schema with the given
87moniker.
076652e8 88
89=cut
90
0dc79249 91sub register_source {
92 my ($self, $moniker, $source) = @_;
93 my %reg = %{$self->source_registrations};
94 $reg{$moniker} = $source;
95 $self->source_registrations(\%reg);
96 $source->schema($self);
97 if ($source->result_class) {
98 my %map = %{$self->class_mappings};
99 $map{$source->result_class} = $moniker;
100 $self->class_mappings(\%map);
101 }
75d07914 102}
a02675cd 103
bfb2bd4f 104=head2 class
105
27f01d1f 106=over 4
82b01c38 107
ebc77b53 108=item Arguments: $moniker
27f01d1f 109
d601dc88 110=item Return Value: $classname
27f01d1f 111
112=back
82b01c38 113
2053ab2a 114Retrieves the result class name for the given moniker. For example:
82b01c38 115
116 my $class = $schema->class('CD');
bfb2bd4f 117
118=cut
119
120sub class {
0dc79249 121 my ($self, $moniker) = @_;
122 return $self->source($moniker)->result_class;
bfb2bd4f 123}
124
ea20d0fd 125=head2 source
126
27f01d1f 127=over 4
128
ebc77b53 129=item Arguments: $moniker
27f01d1f 130
d601dc88 131=item Return Value: $result_source
82b01c38 132
27f01d1f 133=back
82b01c38 134
24d67825 135 my $source = $schema->source('Book');
ea20d0fd 136
82b01c38 137Returns the L<DBIx::Class::ResultSource> object for the registered moniker.
ea20d0fd 138
139=cut
140
141sub source {
0dc79249 142 my ($self, $moniker) = @_;
143 my $sreg = $self->source_registrations;
144 return $sreg->{$moniker} if exists $sreg->{$moniker};
145
146 # if we got here, they probably passed a full class name
147 my $mapped = $self->class_mappings->{$moniker};
701da8c4 148 $self->throw_exception("Can't find source for ${moniker}")
0dc79249 149 unless $mapped && exists $sreg->{$mapped};
150 return $sreg->{$mapped};
ea20d0fd 151}
152
0dc79249 153=head2 sources
154
27f01d1f 155=over 4
156
d601dc88 157=item Return Value: @source_monikers
27f01d1f 158
159=back
82b01c38 160
161Returns the source monikers of all source registrations on this schema.
2053ab2a 162For example:
82b01c38 163
164 my @source_monikers = $schema->sources;
0dc79249 165
166=cut
167
168sub sources { return keys %{shift->source_registrations}; }
169
ea20d0fd 170=head2 resultset
171
27f01d1f 172=over 4
173
ebc77b53 174=item Arguments: $moniker
27f01d1f 175
d601dc88 176=item Return Value: $result_set
82b01c38 177
27f01d1f 178=back
82b01c38 179
24d67825 180 my $rs = $schema->resultset('DVD');
ea20d0fd 181
82b01c38 182Returns the L<DBIx::Class::ResultSet> object for the registered moniker.
ea20d0fd 183
184=cut
185
186sub resultset {
0dc79249 187 my ($self, $moniker) = @_;
188 return $self->source($moniker)->resultset;
ea20d0fd 189}
190
87c4e602 191=head2 load_classes
192
27f01d1f 193=over 4
194
195=item Arguments: @classes?, { $namespace => [ @classes ] }+
196
197=back
076652e8 198
82b01c38 199With no arguments, this method uses L<Module::Find> to find all classes under
200the schema's namespace. Otherwise, this method loads the classes you specify
201(using L<use>), and registers them (using L</"register_class">).
076652e8 202
2053ab2a 203It is possible to comment out classes with a leading C<#>, but note that perl
204will think it's a mistake (trying to use a comment in a qw list), so you'll
205need to add C<no warnings 'qw';> before your load_classes call.
5ce32fc1 206
2053ab2a 207Example:
82b01c38 208
209 My::Schema->load_classes(); # loads My::Schema::CD, My::Schema::Artist,
75d07914 210 # etc. (anything under the My::Schema namespace)
82b01c38 211
212 # loads My::Schema::CD, My::Schema::Artist, Other::Namespace::Producer but
213 # not Other::Namespace::LinerNotes nor My::Schema::Track
214 My::Schema->load_classes(qw/ CD Artist #Track /, {
215 Other::Namespace => [qw/ Producer #LinerNotes /],
216 });
217
076652e8 218=cut
219
a02675cd 220sub load_classes {
5ce32fc1 221 my ($class, @params) = @_;
bab77431 222
5ce32fc1 223 my %comps_for;
bab77431 224
5ce32fc1 225 if (@params) {
226 foreach my $param (@params) {
227 if (ref $param eq 'ARRAY') {
228 # filter out commented entries
229 my @modules = grep { $_ !~ /^#/ } @$param;
bab77431 230
5ce32fc1 231 push (@{$comps_for{$class}}, @modules);
232 }
233 elsif (ref $param eq 'HASH') {
234 # more than one namespace possible
235 for my $comp ( keys %$param ) {
236 # filter out commented entries
237 my @modules = grep { $_ !~ /^#/ } @{$param->{$comp}};
238
239 push (@{$comps_for{$comp}}, @modules);
240 }
241 }
242 else {
243 # filter out commented entries
244 push (@{$comps_for{$class}}, $param) if $param !~ /^#/;
245 }
246 }
247 } else {
41a6f8c0 248 eval "require Module::Find;";
bc0c9800 249 $class->throw_exception(
250 "No arguments to load_classes and couldn't load Module::Find ($@)"
251 ) if $@;
252 my @comp = map { substr $_, length "${class}::" }
253 Module::Find::findallmod($class);
5ce32fc1 254 $comps_for{$class} = \@comp;
41a6f8c0 255 }
5ce32fc1 256
e6efde04 257 my @to_register;
258 {
259 no warnings qw/redefine/;
260 local *Class::C3::reinitialize = sub { };
261 foreach my $prefix (keys %comps_for) {
262 foreach my $comp (@{$comps_for{$prefix}||[]}) {
263 my $comp_class = "${prefix}::${comp}";
264 eval "use $comp_class"; # If it fails, assume the user fixed it
265 if ($@) {
75d07914 266 $comp_class =~ s/::/\//g;
3b24f6ea 267 die $@ unless $@ =~ /Can't locate.+$comp_class\.pm\sin\s\@INC/;
75d07914 268 warn $@ if $@;
e6efde04 269 }
bab77431 270
271 $comp_class->source_name($comp) unless $comp_class->source_name;
272
273 push(@to_register, [ $comp_class->source_name, $comp_class ]);
bfb2bd4f 274 }
5ce32fc1 275 }
a02675cd 276 }
e6efde04 277 Class::C3->reinitialize;
278
279 foreach my $to (@to_register) {
280 $class->register_class(@$to);
281 # if $class->can('result_source_instance');
282 }
a02675cd 283}
284
87c4e602 285=head2 compose_connection
286
27f01d1f 287=over 4
288
ebc77b53 289=item Arguments: $target_namespace, @db_info
429bd4f1 290
d601dc88 291=item Return Value: $new_schema
27f01d1f 292
293=back
076652e8 294
2053ab2a 295Calls L<DBIx::Class::Schema/"compose_namespace"> to the target namespace,
296calls L<DBIx::Class::Schema/connection> with @db_info on the new schema,
297then injects the L<DBix::Class::ResultSetProxy> component and a
298resultset_instance classdata entry on all the new classes, in order to support
82b01c38 299$target_namespaces::$class->search(...) method calls.
300
301This is primarily useful when you have a specific need for class method access
302to a connection. In normal usage it is preferred to call
303L<DBIx::Class::Schema/connect> and use the resulting schema object to operate
304on L<DBIx::Class::ResultSet> objects with L<DBIx::Class::Schema/resultset> for
305more information.
54540863 306
076652e8 307=cut
308
a02675cd 309sub compose_connection {
ea20d0fd 310 my ($self, $target, @info) = @_;
80c90f5d 311 my $base = 'DBIx::Class::ResultSetProxy';
8ef144ff 312 eval "require ${base};";
bc0c9800 313 $self->throw_exception
314 ("No arguments to load_classes and couldn't load ${base} ($@)")
315 if $@;
be381829 316
317 if ($self eq $target) {
318 # Pathological case, largely caused by the docs on early C::M::DBIC::Plain
319 foreach my $moniker ($self->sources) {
320 my $source = $self->source($moniker);
321 my $class = $source->result_class;
322 $self->inject_base($class, $base);
323 $class->mk_classdata(resultset_instance => $source->resultset);
324 $class->mk_classdata(class_resolver => $self);
325 }
50041f3c 326 $self->connection(@info);
be381829 327 return $self;
328 }
329
66d9ef6b 330 my $schema = $self->compose_namespace($target, $base);
ecceadff 331 {
332 no strict 'refs';
333 *{"${target}::schema"} = sub { $schema };
334 }
335
66d9ef6b 336 $schema->connection(@info);
0dc79249 337 foreach my $moniker ($schema->sources) {
338 my $source = $schema->source($moniker);
339 my $class = $source->result_class;
340 #warn "$moniker $class $source ".$source->storage;
8c49f629 341 $class->mk_classdata(result_source_instance => $source);
ea20d0fd 342 $class->mk_classdata(resultset_instance => $source->resultset);
66d9ef6b 343 $class->mk_classdata(class_resolver => $schema);
bfb2bd4f 344 }
345 return $schema;
e678398e 346}
347
77254782 348=head2 compose_namespace
349
27f01d1f 350=over 4
351
352=item Arguments: $target_namespace, $additional_base_class?
82b01c38 353
d601dc88 354=item Return Value: $new_schema
27f01d1f 355
356=back
13765dad 357
82b01c38 358For each L<DBIx::Class::ResultSource> in the schema, this method creates a
359class in the target namespace (e.g. $target_namespace::CD,
360$target_namespace::Artist) that inherits from the corresponding classes
361attached to the current schema.
77254782 362
82b01c38 363It also attaches a corresponding L<DBIx::Class::ResultSource> object to the
364new $schema object. If C<$additional_base_class> is given, the new composed
365classes will inherit from first the corresponding classe from the current
366schema then the base class.
367
2053ab2a 368For example, for a schema with My::Schema::CD and My::Schema::Artist classes,
82b01c38 369
370 $schema->compose_namespace('My::DB', 'Base::Class');
371 print join (', ', @My::DB::CD::ISA) . "\n";
372 print join (', ', @My::DB::Artist::ISA) ."\n";
373
2053ab2a 374will produce the output
82b01c38 375
376 My::Schema::CD, Base::Class
377 My::Schema::Artist, Base::Class
77254782 378
379=cut
380
e678398e 381sub compose_namespace {
66d9ef6b 382 my ($self, $target, $base) = @_;
383 my %reg = %{ $self->source_registrations };
11b78bd6 384 my %target;
385 my %map;
66d9ef6b 386 my $schema = $self->clone;
e9100ff7 387 {
388 no warnings qw/redefine/;
389 local *Class::C3::reinitialize = sub { };
390 foreach my $moniker ($schema->sources) {
391 my $source = $schema->source($moniker);
392 my $target_class = "${target}::${moniker}";
393 $self->inject_base(
394 $target_class => $source->result_class, ($base ? $base : ())
395 );
396 $source->result_class($target_class);
397 }
b7951443 398 }
e9100ff7 399 Class::C3->reinitialize();
11b78bd6 400 {
401 no strict 'refs';
1edaf6fe 402 foreach my $meth (qw/class source resultset/) {
403 *{"${target}::${meth}"} =
404 sub { shift->schema->$meth(@_) };
405 }
11b78bd6 406 }
bfb2bd4f 407 return $schema;
b7951443 408}
409
87c4e602 410=head2 setup_connection_class
411
27f01d1f 412=over 4
413
ebc77b53 414=item Arguments: $target, @info
27f01d1f 415
416=back
076652e8 417
82b01c38 418Sets up a database connection class to inject between the schema and the
419subclasses that the schema creates.
429bd4f1 420
076652e8 421=cut
422
b7951443 423sub setup_connection_class {
424 my ($class, $target, @info) = @_;
63e9583a 425 $class->inject_base($target => 'DBIx::Class::DB');
426 #$target->load_components('DB');
b7951443 427 $target->connection(@info);
428}
429
87c4e602 430=head2 connection
431
27f01d1f 432=over 4
433
ebc77b53 434=item Arguments: @args
66d9ef6b 435
d601dc88 436=item Return Value: $new_schema
27f01d1f 437
438=back
82b01c38 439
440Instantiates a new Storage object of type
441L<DBIx::Class::Schema/"storage_type"> and passes the arguments to
442$storage->connect_info. Sets the connection in-place on the schema. See
443L<DBIx::Class::Storage::DBI/"connect_info"> for more information.
66d9ef6b 444
445=cut
446
447sub connection {
448 my ($self, @info) = @_;
e59d3e5b 449 return $self if !@info && $self->storage;
1e10a11d 450 my $storage_class = $self->storage_type;
451 $storage_class = 'DBIx::Class::Storage'.$storage_class
452 if $storage_class =~ m/^::/;
8ef144ff 453 eval "require ${storage_class};";
bc0c9800 454 $self->throw_exception(
455 "No arguments to load_classes and couldn't load ${storage_class} ($@)"
456 ) if $@;
66d9ef6b 457 my $storage = $storage_class->new;
458 $storage->connect_info(\@info);
459 $self->storage($storage);
460 return $self;
461}
462
87c4e602 463=head2 connect
464
27f01d1f 465=over 4
466
ebc77b53 467=item Arguments: @info
66d9ef6b 468
d601dc88 469=item Return Value: $new_schema
27f01d1f 470
471=back
82b01c38 472
473This is a convenience method. It is equivalent to calling
474$schema->clone->connection(@info). See L</connection> and L</clone> for more
475information.
66d9ef6b 476
477=cut
478
08b515f1 479sub connect { shift->clone->connection(@_) }
480
481=head2 txn_begin
482
82b01c38 483Begins a transaction (does nothing if AutoCommit is off). Equivalent to
484calling $schema->storage->txn_begin. See
485L<DBIx::Class::Storage::DBI/"txn_begin"> for more information.
08b515f1 486
487=cut
488
489sub txn_begin { shift->storage->txn_begin }
490
491=head2 txn_commit
492
82b01c38 493Commits the current transaction. Equivalent to calling
494$schema->storage->txn_commit. See L<DBIx::Class::Storage::DBI/"txn_commit">
495for more information.
08b515f1 496
497=cut
498
499sub txn_commit { shift->storage->txn_commit }
500
501=head2 txn_rollback
502
82b01c38 503Rolls back the current transaction. Equivalent to calling
504$schema->storage->txn_rollback. See
505L<DBIx::Class::Storage::DBI/"txn_rollback"> for more information.
08b515f1 506
507=cut
508
509sub txn_rollback { shift->storage->txn_rollback }
66d9ef6b 510
a62cf8d4 511=head2 txn_do
512
27f01d1f 513=over 4
514
ebc77b53 515=item Arguments: C<$coderef>, @coderef_args?
82b01c38 516
d601dc88 517=item Return Value: The return value of $coderef
27f01d1f 518
519=back
a62cf8d4 520
82b01c38 521Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
522returning its result (if any). If an exception is caught, a rollback is issued
523and the exception is rethrown. If the rollback fails, (i.e. throws an
524exception) an exception is thrown that includes a "Rollback failed" message.
a62cf8d4 525
526For example,
527
24d67825 528 my $author_rs = $schema->resultset('Author')->find(1);
a62cf8d4 529
530 my $coderef = sub {
24d67825 531 my ($author, @titles) = @_;
a62cf8d4 532
533 # If any one of these fails, the entire transaction fails
24d67825 534 $author->create_related('books', {
535 title => $_
536 }) foreach (@titles);
a62cf8d4 537
24d67825 538 return $author->books;
a62cf8d4 539 };
540
541 my $rs;
542 eval {
24d67825 543 $rs = $schema->txn_do($coderef, $author_rs, qw/Night Day It/);
a62cf8d4 544 };
545
546 if ($@) {
547 my $error = $@;
548 if ($error =~ /Rollback failed/) {
549 die "something terrible has happened!";
550 } else {
551 deal_with_failed_transaction();
a62cf8d4 552 }
553 }
554
82b01c38 555In a nested transaction (calling txn_do() from within a txn_do() coderef) only
556the outermost transaction will issue a L<DBIx::Class::Schema/"txn_commit"> on
557the Schema's storage, and txn_do() can be called in void, scalar and list
558context and it will behave as expected.
a62cf8d4 559
560=cut
561
562sub txn_do {
563 my ($self, $coderef, @args) = @_;
564
171dadd7 565 ref $self or $self->throw_exception
566 ('Cannot execute txn_do as a class method');
567 ref $coderef eq 'CODE' or $self->throw_exception
568 ('$coderef must be a CODE reference');
a62cf8d4 569
570 my (@return_values, $return_value);
571
572 $self->txn_begin; # If this throws an exception, no rollback is needed
573
e7f2b7d5 574 my $wantarray = wantarray; # Need to save this since the context
75d07914 575 # inside the eval{} block is independent
576 # of the context that called txn_do()
a62cf8d4 577 eval {
82b01c38 578
24d67825 579 # Need to differentiate between scalar/list context to allow for
580 # returning a list in scalar context to get the size of the list
a62cf8d4 581 if ($wantarray) {
eeb34228 582 # list context
a62cf8d4 583 @return_values = $coderef->(@args);
eeb34228 584 } elsif (defined $wantarray) {
585 # scalar context
a62cf8d4 586 $return_value = $coderef->(@args);
eeb34228 587 } else {
588 # void context
589 $coderef->(@args);
a62cf8d4 590 }
591 $self->txn_commit;
592 };
593
594 if ($@) {
595 my $error = $@;
596
597 eval {
598 $self->txn_rollback;
599 };
600
601 if ($@) {
602 my $rollback_error = $@;
603 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
604 $self->throw_exception($error) # propagate nested rollback
75d07914 605 if $rollback_error =~ /$exception_class/;
a62cf8d4 606
bc0c9800 607 $self->throw_exception(
608 "Transaction aborted: $error. Rollback failed: ${rollback_error}"
609 );
a62cf8d4 610 } else {
611 $self->throw_exception($error); # txn failed but rollback succeeded
612 }
613 }
614
615 return $wantarray ? @return_values : $return_value;
616}
617
66d9ef6b 618=head2 clone
619
27f01d1f 620=over 4
621
d601dc88 622=item Return Value: $new_schema
27f01d1f 623
624=back
82b01c38 625
66d9ef6b 626Clones the schema and its associated result_source objects and returns the
627copy.
628
629=cut
630
631sub clone {
632 my ($self) = @_;
633 my $clone = bless({ (ref $self ? %$self : ()) }, ref $self || $self);
634 foreach my $moniker ($self->sources) {
635 my $source = $self->source($moniker);
636 my $new = $source->new($source);
637 $clone->register_source($moniker => $new);
638 }
639 return $clone;
640}
641
87c4e602 642=head2 populate
643
27f01d1f 644=over 4
645
ebc77b53 646=item Arguments: $moniker, \@data;
27f01d1f 647
648=back
a37a4697 649
650Populates the source registered with the given moniker with the supplied data.
82b01c38 651@data should be a list of listrefs -- the first containing column names, the
652second matching values.
653
654i.e.,
a37a4697 655
24d67825 656 $schema->populate('Artist', [
657 [ qw/artistid name/ ],
658 [ 1, 'Popular Band' ],
659 [ 2, 'Indie Band' ],
a62cf8d4 660 ...
661 ]);
a37a4697 662
663=cut
664
665sub populate {
666 my ($self, $name, $data) = @_;
667 my $rs = $self->resultset($name);
668 my @names = @{shift(@$data)};
84e3c114 669 my @created;
a37a4697 670 foreach my $item (@$data) {
671 my %create;
672 @create{@names} = @$item;
84e3c114 673 push(@created, $rs->create(\%create));
a37a4697 674 }
84e3c114 675 return @created;
a37a4697 676}
677
5160b401 678=head2 throw_exception
701da8c4 679
75d07914 680=over 4
82b01c38 681
ebc77b53 682=item Arguments: $message
82b01c38 683
684=back
685
686Throws an exception. Defaults to using L<Carp::Clan> to report errors from
687user's perspective.
701da8c4 688
689=cut
690
691sub throw_exception {
692 my ($self) = shift;
693 croak @_;
694}
695
ec6704d4 696=head2 deploy (EXPERIMENTAL)
1c339d71 697
82b01c38 698=over 4
699
ebc77b53 700=item Arguments: $sqlt_args
82b01c38 701
702=back
703
704Attempts to deploy the schema to the current storage using L<SQL::Translator>.
ec6704d4 705
706Note that this feature is currently EXPERIMENTAL and may not work correctly
707across all databases, or fully handle complex relationships.
1c339d71 708
709=cut
710
711sub deploy {
cb561d1a 712 my ($self, $sqltargs) = @_;
1c339d71 713 $self->throw_exception("Can't deploy without storage") unless $self->storage;
cb561d1a 714 $self->storage->deploy($self, undef, $sqltargs);
1c339d71 715}
716
c0f61310 717=head2 create_ddl_dir (EXPERIMENTAL)
718
719=over 4
720
721=item Arguments: \@databases, $version, $directory, $sqlt_args
722
723=back
724
725Creates an SQL file based on the Schema, for each of the specified
726database types, in the given directory.
727
728Note that this feature is currently EXPERIMENTAL and may not work correctly
729across all databases, or fully handle complex relationships.
730
731=cut
732
e673f011 733sub create_ddl_dir
734{
735 my $self = shift;
736
737 $self->throw_exception("Can't create_ddl_dir without storage") unless $self->storage;
738 $self->storage->create_ddl_dir($self, @_);
739}
740
741sub ddl_filename
742{
743 my ($self, $type, $dir, $version) = @_;
744
745 my $filename = ref($self);
746 $filename =~ s/^.*:://;
747 $filename = "$dir$filename-$version-$type.sql";
748
749 return $filename;
750}
751
a02675cd 7521;
c2da098a 753
c2da098a 754=head1 AUTHORS
755
daec44b8 756Matt S. Trout <mst@shadowcatsystems.co.uk>
c2da098a 757
758=head1 LICENSE
759
760You may distribute this code under the same terms as Perl itself.
761
762=cut
763