fix unique detection in DB2 for multiple schemas (RT#39622)
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Base.pm
CommitLineData
996be9ee 1package DBIx::Class::Schema::Loader::Base;
2
3use strict;
4use warnings;
65e705c3 5use base qw/Class::Accessor::Grouped Class::C3::Componentised/;
f170d55b 6use namespace::autoclean;
996be9ee 7use Class::C3;
fa994d3c 8use Carp::Clan qw/^DBIx::Class/;
996be9ee 9use DBIx::Class::Schema::Loader::RelBuilder;
10use Data::Dump qw/ dump /;
11use POSIX qw//;
dd03ee1a 12use File::Spec qw//;
419a2eeb 13use Cwd qw//;
7cab3ab7 14use Digest::MD5 qw//;
22270947 15use Lingua::EN::Inflect::Number qw//;
ecf930e6 16use Lingua::EN::Inflect::Phrase qw//;
af31090c 17use File::Temp qw//;
18use Class::Unload;
8048320c 19use Class::Inspector ();
f170d55b 20use Data::Dumper::Concise;
21use Scalar::Util 'looks_like_number';
b0d2b300 22use File::Slurp 'slurp';
996be9ee 23require DBIx::Class;
24
5afd3e72 25our $VERSION = '0.06001';
32f784fc 26
3d95f9ff 27__PACKAGE__->mk_group_ro_accessors('simple', qw/
996be9ee 28 schema
29 schema_class
30
31 exclude
32 constraint
33 additional_classes
34 additional_base_classes
35 left_base_classes
36 components
37 resultset_components
59cfa251 38 skip_relationships
0ca61324 39 skip_load_external
996be9ee 40 moniker_map
b639d969 41 custom_column_info
996be9ee 42 inflect_singular
43 inflect_plural
44 debug
45 dump_directory
d65cda9e 46 dump_overwrite
28b4691d 47 really_erase_my_files
f44ecc2f 48 resultset_namespace
49 default_resultset_class
9c9c2f2b 50 schema_base_class
51 result_base_class
639a1367 52 overwrite_modifications
996be9ee 53
c8c27020 54 relationship_attrs
55
996be9ee 56 db_schema
57 _tables
58 classes
f53dcdf0 59 _upgrading_classes
996be9ee 60 monikers
106a976a 61 dynamic
a8d229ff 62 naming
42e785fa 63 datetime_timezone
64 datetime_locale
73099af4 65 config_file
71a6e88a 66 loader_class
65e705c3 67/);
68
996be9ee 69
3d95f9ff 70__PACKAGE__->mk_group_accessors('simple', qw/
01012543 71 version_to_dump
1c95b304 72 schema_version_to_dump
f53dcdf0 73 _upgrading_from
f22644d7 74 _upgrading_from_load_classes
a4b94090 75 _downgrading_to_load_classes
540a8149 76 _rewriting_result_namespace
f22644d7 77 use_namespaces
540a8149 78 result_namespace
492dce8d 79 generate_pod
43b982ea 80 pod_comment_mode
81 pod_comment_spillover_length
01012543 82/);
83
996be9ee 84=head1 NAME
85
86DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
87
88=head1 SYNOPSIS
89
90See L<DBIx::Class::Schema::Loader>
91
92=head1 DESCRIPTION
93
94This is the base class for the storage-specific C<DBIx::Class::Schema::*>
95classes, and implements the common functionality between them.
96
97=head1 CONSTRUCTOR OPTIONS
98
99These constructor options are the base options for
29ddb54c 100L<DBIx::Class::Schema::Loader/loader_options>. Available constructor options are:
3953cbee 101
59cfa251 102=head2 skip_relationships
996be9ee 103
59cfa251 104Skip setting up relationships. The default is to attempt the loading
105of relationships.
996be9ee 106
0ca61324 107=head2 skip_load_external
108
109Skip loading of other classes in @INC. The default is to merge all other classes
110with the same name found in @INC into the schema file we are creating.
111
9a95164d 112=head2 naming
113
ecf930e6 114Static schemas (ones dumped to disk) will, by default, use the new-style
9a95164d 115relationship names and singularized Results, unless you're overwriting an
ecf930e6 116existing dump made by an older version of L<DBIx::Class::Schema::Loader>, in
117which case the backward compatible RelBuilder will be activated, and the
118appropriate monikerization used.
9a95164d 119
120Specifying
121
ecf930e6 122 naming => 'current'
9a95164d 123
124will disable the backward-compatible RelBuilder and use
125the new-style relationship names along with singularized Results, even when
126overwriting a dump made with an earlier version.
127
128The option also takes a hashref:
129
ecf930e6 130 naming => { relationships => 'v6', monikers => 'v6' }
a8d229ff 131
132The keys are:
133
134=over 4
135
136=item relationships
137
138How to name relationship accessors.
139
140=item monikers
141
142How to name Result classes.
143
144=back
9a95164d 145
146The values can be:
147
148=over 4
149
150=item current
151
ecf930e6 152Latest style, whatever that happens to be.
153
154=item v4
155
156Unsingularlized monikers, C<has_many> only relationships with no _id stripping.
9a95164d 157
158=item v5
159
ecf930e6 160Monikers singularized as whole words, C<might_have> relationships for FKs on
161C<UNIQUE> constraints, C<_id> stripping for belongs_to relationships.
9a95164d 162
ecf930e6 163Some of the C<_id> stripping edge cases in C<0.05003> have been reverted for
164the v5 RelBuilder.
165
166=item v6
9a95164d 167
ecf930e6 168All monikers and relationships inflected using L<Lingua::EN::Inflect::Phrase>,
169more aggressive C<_id> stripping from relationships.
170
171In general, there is very little difference between v5 and v6 schemas.
9a95164d 172
173=back
174
175Dynamic schemas will always default to the 0.04XXX relationship names and won't
176singularize Results for backward compatibility, to activate the new RelBuilder
177and singularization put this in your C<Schema.pm> file:
178
179 __PACKAGE__->naming('current');
180
181Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
182next major version upgrade:
183
184 __PACKAGE__->naming('v5');
185
492dce8d 186=head2 generate_pod
187
188By default POD will be generated for columns and relationships, using database
7f2de014 189metadata for the text if available and supported.
190
191Reading database metadata (e.g. C<COMMENT ON TABLE some_table ...>) is only
192supported for Postgres right now.
492dce8d 193
194Set this to C<0> to turn off all POD generation.
195
43b982ea 196=head2 pod_comment_mode
197
f7976fea 198Controls where table comments appear in the generated POD. Smaller table
199comments are appended to the C<NAME> section of the documentation, and larger
200ones are inserted into C<DESCRIPTION> instead. You can force a C<DESCRIPTION>
201section to be generated with the comment always, only use C<NAME>, or choose
202the length threshold at which the comment is forced into the description.
43b982ea 203
34896b5e 204=over 4
205
206=item name
207
208Use C<NAME> section only.
209
210=item description
211
212Force C<DESCRIPTION> always.
213
214=item auto
215
216Use C<DESCRIPTION> if length > L</pod_comment_spillover_length>, this is the
217default.
218
219=back
43b982ea 220
221=head2 pod_comment_spillover_length
222
223When pod_comment_mode is set to C<auto>, this is the length of the comment at
224which it will be forced into a separate description section.
225
226The default is C<60>
227
c8c27020 228=head2 relationship_attrs
229
230Hashref of attributes to pass to each generated relationship, listed
231by type. Also supports relationship type 'all', containing options to
232pass to all generated relationships. Attributes set for more specific
233relationship types override those set in 'all'.
234
235For example:
236
237 relationship_attrs => {
238 all => { cascade_delete => 0 },
239 has_many => { cascade_delete => 1 },
240 },
241
242will set the C<cascade_delete> option to 0 for all generated relationships,
243except for C<has_many>, which will have cascade_delete as 1.
244
245NOTE: this option is not supported if v4 backward-compatible naming is
246set either globally (naming => 'v4') or just for relationships.
247
996be9ee 248=head2 debug
249
250If set to true, each constructive L<DBIx::Class> statement the loader
251decides to execute will be C<warn>-ed before execution.
252
d65cda9e 253=head2 db_schema
254
255Set the name of the schema to load (schema in the sense that your database
256vendor means it). Does not currently support loading more than one schema
257name.
258
996be9ee 259=head2 constraint
260
261Only load tables matching regex. Best specified as a qr// regex.
262
263=head2 exclude
264
265Exclude tables matching regex. Best specified as a qr// regex.
266
267=head2 moniker_map
268
8f9d7ce5 269Overrides the default table name to moniker translation. Can be either
270a hashref of table keys and moniker values, or a coderef for a translator
996be9ee 271function taking a single scalar table name argument and returning
272a scalar moniker. If the hash entry does not exist, or the function
273returns a false value, the code falls back to default behavior
274for that table name.
275
9cc8e7e1 276The default behavior is to singularize the table name, and: C<join '', map
277ucfirst, split /[\W_]+/, lc $table>, which is to say: lowercase everything,
278split up the table name into chunks anywhere a non-alpha-numeric character
279occurs, change the case of first letter of each chunk to upper case, and put
280the chunks back together. Examples:
996be9ee 281
282 Table Name | Moniker Name
283 ---------------------------
284 luser | Luser
285 luser_group | LuserGroup
48b8d687 286 luser-opts | LuserOpt
996be9ee 287
288=head2 inflect_plural
289
290Just like L</moniker_map> above (can be hash/code-ref, falls back to default
291if hash key does not exist or coderef returns false), but acts as a map
292for pluralizing relationship names. The default behavior is to utilize
293L<Lingua::EN::Inflect::Number/to_PL>.
294
295=head2 inflect_singular
296
297As L</inflect_plural> above, but for singularizing relationship names.
298Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
299
9c9c2f2b 300=head2 schema_base_class
301
302Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
303
304=head2 result_base_class
305
2229729e 306Base class for your table classes (aka result classes). Defaults to
307'DBIx::Class::Core'.
9c9c2f2b 308
996be9ee 309=head2 additional_base_classes
310
311List of additional base classes all of your table classes will use.
312
313=head2 left_base_classes
314
315List of additional base classes all of your table classes will use
316that need to be leftmost.
317
318=head2 additional_classes
319
320List of additional classes which all of your table classes will use.
321
322=head2 components
323
324List of additional components to be loaded into all of your table
325classes. A good example would be C<ResultSetManager>.
326
327=head2 resultset_components
328
8f9d7ce5 329List of additional ResultSet components to be loaded into your table
996be9ee 330classes. A good example would be C<AlwaysRS>. Component
331C<ResultSetManager> will be automatically added to the above
332C<components> list if this option is set.
333
f44ecc2f 334=head2 use_namespaces
335
f22644d7 336This is now the default, to go back to L<DBIx::Class::Schema/load_classes> pass
337a C<0>.
338
f44ecc2f 339Generate result class names suitable for
340L<DBIx::Class::Schema/load_namespaces> and call that instead of
341L<DBIx::Class::Schema/load_classes>. When using this option you can also
342specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
343C<resultset_namespace>, C<default_resultset_class>), and they will be added
344to the call (and the generated result class names adjusted appropriately).
345
996be9ee 346=head2 dump_directory
347
348This option is designed to be a tool to help you transition from this
349loader to a manually-defined schema when you decide it's time to do so.
350
351The value of this option is a perl libdir pathname. Within
352that directory this module will create a baseline manual
353L<DBIx::Class::Schema> module set, based on what it creates at runtime
354in memory.
355
356The created schema class will have the same classname as the one on
357which you are setting this option (and the ResultSource classes will be
7cab3ab7 358based on this name as well).
996be9ee 359
8f9d7ce5 360Normally you wouldn't hard-code this setting in your schema class, as it
996be9ee 361is meant for one-time manual usage.
362
363See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
364recommended way to access this functionality.
365
d65cda9e 366=head2 dump_overwrite
367
28b4691d 368Deprecated. See L</really_erase_my_files> below, which does *not* mean
369the same thing as the old C<dump_overwrite> setting from previous releases.
370
371=head2 really_erase_my_files
372
7cab3ab7 373Default false. If true, Loader will unconditionally delete any existing
374files before creating the new ones from scratch when dumping a schema to disk.
375
376The default behavior is instead to only replace the top portion of the
377file, up to and including the final stanza which contains
378C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
379leaving any customizations you placed after that as they were.
380
28b4691d 381When C<really_erase_my_files> is not set, if the output file already exists,
7cab3ab7 382but the aforementioned final stanza is not found, or the checksum
383contained there does not match the generated contents, Loader will
384croak and not touch the file.
d65cda9e 385
28b4691d 386You should really be using version control on your schema classes (and all
387of the rest of your code for that matter). Don't blame me if a bug in this
388code wipes something out when it shouldn't have, you've been warned.
389
639a1367 390=head2 overwrite_modifications
391
392Default false. If false, when updating existing files, Loader will
393refuse to modify any Loader-generated code that has been modified
394since its last run (as determined by the checksum Loader put in its
395comment lines).
396
397If true, Loader will discard any manual modifications that have been
398made to Loader-generated code.
399
400Again, you should be using version control on your schema classes. Be
401careful with this option.
402
3a368709 403=head2 custom_column_info
404
d67d058e 405Hook for adding extra attributes to the
406L<column_info|DBIx::Class::ResultSource/column_info> for a column.
407
408Must be a coderef that returns a hashref with the extra attributes.
409
410Receives the table name, column name and column_info.
411
412For example:
413
414 custom_column_info => sub {
415 my ($table_name, $column_name, $column_info) = @_;
416
417 if ($column_name eq 'dog' && $column_info->{default_value} eq 'snoopy') {
418 return { is_snoopy => 1 };
419 }
420 },
3a368709 421
d67d058e 422This attribute can also be used to set C<inflate_datetime> on a non-datetime
423column so it also receives the L</datetime_timezone> and/or L</datetime_locale>.
3a368709 424
42e785fa 425=head2 datetime_timezone
426
d67d058e 427Sets the timezone attribute for L<DBIx::Class::InflateColumn::DateTime> for all
428columns with the DATE/DATETIME/TIMESTAMP data_types.
42e785fa 429
430=head2 datetime_locale
431
d67d058e 432Sets the locale attribute for L<DBIx::Class::InflateColumn::DateTime> for all
433columns with the DATE/DATETIME/TIMESTAMP data_types.
42e785fa 434
73099af4 435=head1 config_file
436
437File in Perl format, which should return a HASH reference, from which to read
438loader options.
439
996be9ee 440=head1 METHODS
441
442None of these methods are intended for direct invocation by regular
d67d058e 443users of L<DBIx::Class::Schema::Loader>. Some are proxied via
444L<DBIx::Class::Schema::Loader>.
996be9ee 445
446=cut
447
2a8e93e9 448my $CURRENT_V = 'v6';
8048320c 449
c5df7397 450my @CLASS_ARGS = qw(
8048320c 451 schema_base_class result_base_class additional_base_classes
452 left_base_classes additional_classes components resultset_components
453);
66afce69 454
996be9ee 455# ensure that a peice of object data is a valid arrayref, creating
456# an empty one or encapsulating whatever's there.
457sub _ensure_arrayref {
458 my $self = shift;
459
460 foreach (@_) {
461 $self->{$_} ||= [];
462 $self->{$_} = [ $self->{$_} ]
463 unless ref $self->{$_} eq 'ARRAY';
464 }
465}
466
467=head2 new
468
469Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
470by L<DBIx::Class::Schema::Loader>.
471
472=cut
473
474sub new {
475 my ( $class, %args ) = @_;
476
477 my $self = { %args };
478
479 bless $self => $class;
480
73099af4 481 if (my $config_file = $self->config_file) {
482 my $config_opts = do $config_file;
483
484 croak "Error reading config from $config_file: $@" if $@;
485
486 croak "Config file $config_file must be a hashref" unless ref($config_opts) eq 'HASH';
487
488 while (my ($k, $v) = each %$config_opts) {
489 $self->{$k} = $v unless exists $self->{$k};
490 }
491 }
492
996be9ee 493 $self->_ensure_arrayref(qw/additional_classes
494 additional_base_classes
495 left_base_classes
496 components
497 resultset_components
498 /);
499
8048320c 500 $self->_validate_class_args;
501
996be9ee 502 push(@{$self->{components}}, 'ResultSetManager')
503 if @{$self->{resultset_components}};
504
505 $self->{monikers} = {};
506 $self->{classes} = {};
f53dcdf0 507 $self->{_upgrading_classes} = {};
996be9ee 508
996be9ee 509 $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
510 $self->{schema} ||= $self->{schema_class};
511
28b4691d 512 croak "dump_overwrite is deprecated. Please read the"
513 . " DBIx::Class::Schema::Loader::Base documentation"
514 if $self->{dump_overwrite};
515
af31090c 516 $self->{dynamic} = ! $self->{dump_directory};
79193756 517 $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
af31090c 518 TMPDIR => 1,
519 CLEANUP => 1,
520 );
521
79193756 522 $self->{dump_directory} ||= $self->{temp_directory};
523
01012543 524 $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
1c95b304 525 $self->schema_version_to_dump($DBIx::Class::Schema::Loader::VERSION);
01012543 526
66afce69 527 if ((not ref $self->naming) && defined $self->naming) {
9cc8e7e1 528 my $naming_ver = $self->naming;
a8d229ff 529 $self->{naming} = {
530 relationships => $naming_ver,
531 monikers => $naming_ver,
532 };
533 }
534
66afce69 535 if ($self->naming) {
536 for (values %{ $self->naming }) {
c5df7397 537 $_ = $CURRENT_V if $_ eq 'current';
66afce69 538 }
539 }
540 $self->{naming} ||= {};
541
d67d058e 542 if ($self->custom_column_info && ref $self->custom_column_info ne 'CODE') {
543 croak 'custom_column_info must be a CODE ref';
544 }
545
7824616e 546 $self->_check_back_compat;
9c465d2c 547
f22644d7 548 $self->use_namespaces(1) unless defined $self->use_namespaces;
492dce8d 549 $self->generate_pod(1) unless defined $self->generate_pod;
43b982ea 550 $self->pod_comment_mode('auto') unless defined $self->pod_comment_mode;
551 $self->pod_comment_spillover_length(60) unless defined $self->pod_comment_spillover_length;
f22644d7 552
7824616e 553 $self;
554}
af31090c 555
7824616e 556sub _check_back_compat {
557 my ($self) = @_;
e8ad6491 558
a8d229ff 559# dynamic schemas will always be in 0.04006 mode, unless overridden
106a976a 560 if ($self->dynamic) {
fb3bb595 561# just in case, though no one is likely to dump a dynamic schema
1c95b304 562 $self->schema_version_to_dump('0.04006');
a8d229ff 563
66afce69 564 if (not %{ $self->naming }) {
565 warn <<EOF unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
566
567Dynamic schema detected, will run in 0.04006 mode.
568
569Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
570to disable this warning.
a0e0a56a 571
805dbe0a 572Also consider setting 'use_namespaces => 1' if/when upgrading.
573
a0e0a56a 574See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
575details.
66afce69 576EOF
577 }
f53dcdf0 578 else {
579 $self->_upgrading_from('v4');
580 }
66afce69 581
a8d229ff 582 $self->naming->{relationships} ||= 'v4';
583 $self->naming->{monikers} ||= 'v4';
584
805dbe0a 585 if ($self->use_namespaces) {
586 $self->_upgrading_from_load_classes(1);
587 }
588 else {
589 $self->use_namespaces(0);
590 }
f22644d7 591
01012543 592 return;
593 }
594
595# otherwise check if we need backcompat mode for a static schema
7824616e 596 my $filename = $self->_get_dump_filename($self->schema_class);
597 return unless -e $filename;
598
599 open(my $fh, '<', $filename)
600 or croak "Cannot open '$filename' for reading: $!";
601
540a8149 602 my $load_classes = 0;
603 my $result_namespace = '';
f22644d7 604
7824616e 605 while (<$fh>) {
f22644d7 606 if (/^__PACKAGE__->load_classes;/) {
607 $load_classes = 1;
540a8149 608 } elsif (/result_namespace => '([^']+)'/) {
609 $result_namespace = $1;
805dbe0a 610 } elsif (my ($real_ver) =
611 /^# Created by DBIx::Class::Schema::Loader v(\d+\.\d+)/) {
612
613 if ($load_classes && (not defined $self->use_namespaces)) {
614 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
615
a1a91c42 616'load_classes;' static schema detected, turning off 'use_namespaces'.
805dbe0a 617
618Set the 'use_namespaces' attribute or the SCHEMA_LOADER_BACKCOMPAT environment
619variable to disable this warning.
620
621See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
622details.
623EOF
624 $self->use_namespaces(0);
625 }
626 elsif ($load_classes && $self->use_namespaces) {
805dbe0a 627 $self->_upgrading_from_load_classes(1);
628 }
540a8149 629 elsif ((not $load_classes) && defined $self->use_namespaces
630 && (not $self->use_namespaces)) {
a4b94090 631 $self->_downgrading_to_load_classes(
632 $result_namespace || 'Result'
633 );
634 }
540a8149 635 elsif ((not defined $self->use_namespaces)
636 || $self->use_namespaces) {
637 if (not $self->result_namespace) {
638 $self->result_namespace($result_namespace || 'Result');
639 }
640 elsif ($result_namespace ne $self->result_namespace) {
641 $self->_rewriting_result_namespace(
642 $result_namespace || 'Result'
643 );
644 }
645 }
a8d229ff 646
a8d229ff 647 # XXX when we go past .0 this will need fixing
648 my ($v) = $real_ver =~ /([1-9])/;
649 $v = "v$v";
650
c5df7397 651 last if $v eq $CURRENT_V || $real_ver =~ /^0\.\d\d999/;
a0e0a56a 652
653 if (not %{ $self->naming }) {
654 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
655
656Version $real_ver static schema detected, turning on backcompat mode.
657
658Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
659to disable this warning.
660
2a8e93e9 661See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 if upgrading
662from version 0.04006.
a0e0a56a 663EOF
664 }
f53dcdf0 665 else {
666 $self->_upgrading_from($v);
68d49e50 667 last;
f53dcdf0 668 }
a0e0a56a 669
a8d229ff 670 $self->naming->{relationships} ||= $v;
671 $self->naming->{monikers} ||= $v;
672
a0e0a56a 673 $self->schema_version_to_dump($real_ver);
674
7824616e 675 last;
676 }
677 }
678 close $fh;
996be9ee 679}
680
8048320c 681sub _validate_class_args {
682 my $self = shift;
683 my $args = shift;
684
c5df7397 685 foreach my $k (@CLASS_ARGS) {
8048320c 686 next unless $self->$k;
687
688 my @classes = ref $self->$k eq 'ARRAY' ? @{ $self->$k } : $self->$k;
689 foreach my $c (@classes) {
690 # components default to being under the DBIx::Class namespace unless they
691 # are preceeded with a '+'
692 if ( $k =~ m/components$/ && $c !~ s/^\+// ) {
693 $c = 'DBIx::Class::' . $c;
694 }
695
696 # 1 == installed, 0 == not installed, undef == invalid classname
697 my $installed = Class::Inspector->installed($c);
698 if ( defined($installed) ) {
699 if ( $installed == 0 ) {
700 croak qq/$c, as specified in the loader option "$k", is not installed/;
701 }
702 } else {
703 croak qq/$c, as specified in the loader option "$k", is an invalid class name/;
704 }
705 }
706 }
707}
708
419a2eeb 709sub _find_file_in_inc {
710 my ($self, $file) = @_;
711
712 foreach my $prefix (@INC) {
af31090c 713 my $fullpath = File::Spec->catfile($prefix, $file);
714 return $fullpath if -f $fullpath
281d0f3e 715 # abs_path throws on Windows for nonexistant files
716 and eval { Cwd::abs_path($fullpath) } ne
717 (eval { Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) } || '');
419a2eeb 718 }
719
720 return;
721}
722
fb3bb595 723sub _class_path {
f96ef30f 724 my ($self, $class) = @_;
725
726 my $class_path = $class;
727 $class_path =~ s{::}{/}g;
728 $class_path .= '.pm';
729
fb3bb595 730 return $class_path;
731}
732
733sub _find_class_in_inc {
734 my ($self, $class) = @_;
735
736 return $self->_find_file_in_inc($self->_class_path($class));
737}
738
a4b94090 739sub _rewriting {
740 my $self = shift;
741
742 return $self->_upgrading_from
743 || $self->_upgrading_from_load_classes
540a8149 744 || $self->_downgrading_to_load_classes
745 || $self->_rewriting_result_namespace
746 ;
a4b94090 747}
748
b24cb177 749sub _rewrite_old_classnames {
750 my ($self, $code) = @_;
751
a4b94090 752 return $code unless $self->_rewriting;
b24cb177 753
754 my %old_classes = reverse %{ $self->_upgrading_classes };
755
756 my $re = join '|', keys %old_classes;
757 $re = qr/\b($re)\b/;
758
68d49e50 759 $code =~ s/$re/$old_classes{$1} || $1/eg;
b24cb177 760
761 return $code;
762}
763
fb3bb595 764sub _load_external {
765 my ($self, $class) = @_;
766
0ca61324 767 return if $self->{skip_load_external};
768
ffc705f3 769 # so that we don't load our own classes, under any circumstances
770 local *INC = [ grep $_ ne $self->dump_directory, @INC ];
771
fb3bb595 772 my $real_inc_path = $self->_find_class_in_inc($class);
f96ef30f 773
ffc705f3 774 my $old_class = $self->_upgrading_classes->{$class}
a4b94090 775 if $self->_rewriting;
ffc705f3 776
777 my $old_real_inc_path = $self->_find_class_in_inc($old_class)
778 if $old_class && $old_class ne $class;
779
780 return unless $real_inc_path || $old_real_inc_path;
781
782 if ($real_inc_path) {
783 # If we make it to here, we loaded an external definition
784 warn qq/# Loaded external class definition for '$class'\n/
785 if $self->debug;
786
787 open(my $fh, '<', $real_inc_path)
788 or croak "Failed to open '$real_inc_path' for reading: $!";
b24cb177 789 my $code = do { local $/; <$fh> };
ffc705f3 790 close($fh)
791 or croak "Failed to close $real_inc_path: $!";
b24cb177 792 $code = $self->_rewrite_old_classnames($code);
ffc705f3 793
794 if ($self->dynamic) { # load the class too
795 # kill redefined warnings
502b65d4 796 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
ffc705f3 797 local $SIG{__WARN__} = sub {
502b65d4 798 $warn_handler->(@_)
799 unless $_[0] =~ /^Subroutine \S+ redefined/;
ffc705f3 800 };
b24cb177 801 eval $code;
ffc705f3 802 die $@ if $@;
803 }
b24cb177 804
805 $self->_ext_stmt($class,
806 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
807 .qq|# They are now part of the custom portion of this file\n|
808 .qq|# for you to hand-edit. If you do not either delete\n|
809 .qq|# this section or remove that file from \@INC, this section\n|
810 .qq|# will be repeated redundantly when you re-create this\n|
e770e9ce 811 .qq|# file again via Loader! See skip_load_external to disable\n|
812 .qq|# this feature.\n|
b24cb177 813 );
814 chomp $code;
815 $self->_ext_stmt($class, $code);
816 $self->_ext_stmt($class,
817 qq|# End of lines loaded from '$real_inc_path' |
818 );
996be9ee 819 }
106a976a 820
ffc705f3 821 if ($old_real_inc_path) {
822 open(my $fh, '<', $old_real_inc_path)
823 or croak "Failed to open '$old_real_inc_path' for reading: $!";
824 $self->_ext_stmt($class, <<"EOF");
825
30a4c064 826# These lines were loaded from '$old_real_inc_path',
827# based on the Result class name that would have been created by an 0.04006
828# version of the Loader. For a static schema, this happens only once during
e770e9ce 829# upgrade. See skip_load_external to disable this feature.
ffc705f3 830EOF
b24cb177 831
b0d2b300 832 my $code = slurp $old_real_inc_path;
b24cb177 833 $code = $self->_rewrite_old_classnames($code);
834
ffc705f3 835 if ($self->dynamic) {
836 warn <<"EOF";
837
838Detected external content in '$old_real_inc_path', a class name that would have
839been used by an 0.04006 version of the Loader.
840
841* PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
842new name of the Result.
843EOF
844 # kill redefined warnings
502b65d4 845 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
ffc705f3 846 local $SIG{__WARN__} = sub {
502b65d4 847 $warn_handler->(@_)
848 unless $_[0] =~ /^Subroutine \S+ redefined/;
ffc705f3 849 };
ffc705f3 850 eval $code;
851 die $@ if $@;
852 }
853
b24cb177 854 chomp $code;
855 $self->_ext_stmt($class, $code);
ffc705f3 856 $self->_ext_stmt($class,
857 qq|# End of lines loaded from '$old_real_inc_path' |
858 );
9e8033c1 859 }
996be9ee 860}
861
862=head2 load
863
864Does the actual schema-construction work.
865
866=cut
867
868sub load {
869 my $self = shift;
870
bfb43060 871 $self->_load_tables(
872 $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude })
873 );
b97c2c1e 874}
875
876=head2 rescan
877
a60b5b8d 878Arguments: schema
879
b97c2c1e 880Rescan the database for newly added tables. Does
a60b5b8d 881not process drops or changes. Returns a list of
882the newly added table monikers.
883
884The schema argument should be the schema class
885or object to be affected. It should probably
886be derived from the original schema_class used
887during L</load>.
b97c2c1e 888
889=cut
890
891sub rescan {
a60b5b8d 892 my ($self, $schema) = @_;
893
894 $self->{schema} = $schema;
7824616e 895 $self->_relbuilder->{schema} = $schema;
b97c2c1e 896
897 my @created;
bfb43060 898 my @current = $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude });
899 foreach my $table (@current) {
b97c2c1e 900 if(!exists $self->{_tables}->{$table}) {
901 push(@created, $table);
902 }
903 }
904
c39e3507 905 my $loaded = $self->_load_tables(@created);
a60b5b8d 906
c39e3507 907 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 908}
909
7824616e 910sub _relbuilder {
66afce69 911 no warnings 'uninitialized';
7824616e 912 my ($self) = @_;
3fed44ca 913
914 return if $self->{skip_relationships};
915
a8d229ff 916 if ($self->naming->{relationships} eq 'v4') {
917 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
918 return $self->{relbuilder} ||=
919 DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
920 $self->schema, $self->inflect_plural, $self->inflect_singular
921 );
922 }
ecf930e6 923 elsif ($self->naming->{relationships} eq 'v5') {
924 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05;
925 return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05->new (
926 $self->schema,
927 $self->inflect_plural,
928 $self->inflect_singular,
929 $self->relationship_attrs,
930 );
931 }
a8d229ff 932
ecf930e6 933 return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
934 $self->schema,
935 $self->inflect_plural,
936 $self->inflect_singular,
937 $self->relationship_attrs,
7824616e 938 );
939}
940
b97c2c1e 941sub _load_tables {
942 my ($self, @tables) = @_;
943
b97c2c1e 944 # Save the new tables to the tables list
a60b5b8d 945 foreach (@tables) {
946 $self->{_tables}->{$_} = 1;
947 }
f96ef30f 948
af31090c 949 $self->_make_src_class($_) for @tables;
27305cc1 950
27305cc1 951 # sanity-check for moniker clashes
952 my $inverse_moniker_idx;
953 for (keys %{$self->monikers}) {
954 push @{$inverse_moniker_idx->{$self->monikers->{$_}}}, $_;
955 }
956
957 my @clashes;
958 for (keys %$inverse_moniker_idx) {
959 my $tables = $inverse_moniker_idx->{$_};
960 if (@$tables > 1) {
961 push @clashes, sprintf ("tables %s reduced to the same source moniker '%s'",
962 join (', ', map { "'$_'" } @$tables),
963 $_,
964 );
965 }
966 }
967
968 if (@clashes) {
969 die 'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
970 . 'Either change the naming style, or supply an explicit moniker_map: '
971 . join ('; ', @clashes)
972 . "\n"
973 ;
974 }
975
976
f96ef30f 977 $self->_setup_src_meta($_) for @tables;
978
e8ad6491 979 if(!$self->skip_relationships) {
181cc907 980 # The relationship loader needs a working schema
af31090c 981 $self->{quiet} = 1;
79193756 982 local $self->{dump_directory} = $self->{temp_directory};
106a976a 983 $self->_reload_classes(\@tables);
e8ad6491 984 $self->_load_relationships($_) for @tables;
af31090c 985 $self->{quiet} = 0;
79193756 986
987 # Remove that temp dir from INC so it doesn't get reloaded
ffc705f3 988 @INC = grep $_ ne $self->dump_directory, @INC;
e8ad6491 989 }
990
f96ef30f 991 $self->_load_external($_)
75451704 992 for map { $self->classes->{$_} } @tables;
f96ef30f 993
106a976a 994 # Reload without unloading first to preserve any symbols from external
995 # packages.
996 $self->_reload_classes(\@tables, 0);
996be9ee 997
5223f24a 998 # Drop temporary cache
999 delete $self->{_cache};
1000
c39e3507 1001 return \@tables;
996be9ee 1002}
1003
af31090c 1004sub _reload_classes {
106a976a 1005 my ($self, $tables, $unload) = @_;
1006
1007 my @tables = @$tables;
1008 $unload = 1 unless defined $unload;
181cc907 1009
4daef04f 1010 # so that we don't repeat custom sections
1011 @INC = grep $_ ne $self->dump_directory, @INC;
1012
181cc907 1013 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 1014
1015 unshift @INC, $self->dump_directory;
af31090c 1016
706ef173 1017 my @to_register;
1018 my %have_source = map { $_ => $self->schema->source($_) }
1019 $self->schema->sources;
1020
181cc907 1021 for my $table (@tables) {
1022 my $moniker = $self->monikers->{$table};
1023 my $class = $self->classes->{$table};
0ae6b65d 1024
1025 {
1026 no warnings 'redefine';
1027 local *Class::C3::reinitialize = sub {};
1028 use warnings;
1029
106a976a 1030 Class::Unload->unload($class) if $unload;
706ef173 1031 my ($source, $resultset_class);
1032 if (
1033 ($source = $have_source{$moniker})
1034 && ($resultset_class = $source->resultset_class)
1035 && ($resultset_class ne 'DBIx::Class::ResultSet')
1036 ) {
1037 my $has_file = Class::Inspector->loaded_filename($resultset_class);
106a976a 1038 Class::Unload->unload($resultset_class) if $unload;
1039 $self->_reload_class($resultset_class) if $has_file;
0ae6b65d 1040 }
106a976a 1041 $self->_reload_class($class);
af31090c 1042 }
706ef173 1043 push @to_register, [$moniker, $class];
1044 }
af31090c 1045
706ef173 1046 Class::C3->reinitialize;
1047 for (@to_register) {
1048 $self->schema->register_class(@$_);
af31090c 1049 }
1050}
1051
106a976a 1052# We use this instead of ensure_class_loaded when there are package symbols we
1053# want to preserve.
1054sub _reload_class {
1055 my ($self, $class) = @_;
1056
1057 my $class_path = $self->_class_path($class);
1058 delete $INC{ $class_path };
f53dcdf0 1059
1060# kill redefined warnings
502b65d4 1061 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
f53dcdf0 1062 local $SIG{__WARN__} = sub {
502b65d4 1063 $warn_handler->(@_)
1064 unless $_[0] =~ /^Subroutine \S+ redefined/;
f53dcdf0 1065 };
106a976a 1066 eval "require $class;";
1067}
1068
996be9ee 1069sub _get_dump_filename {
1070 my ($self, $class) = (@_);
1071
1072 $class =~ s{::}{/}g;
1073 return $self->dump_directory . q{/} . $class . q{.pm};
1074}
1075
1076sub _ensure_dump_subdirs {
1077 my ($self, $class) = (@_);
1078
1079 my @name_parts = split(/::/, $class);
dd03ee1a 1080 pop @name_parts; # we don't care about the very last element,
1081 # which is a filename
1082
996be9ee 1083 my $dir = $self->dump_directory;
7cab3ab7 1084 while (1) {
1085 if(!-d $dir) {
25328cc4 1086 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 1087 }
7cab3ab7 1088 last if !@name_parts;
1089 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 1090 }
1091}
1092
1093sub _dump_to_dir {
af31090c 1094 my ($self, @classes) = @_;
996be9ee 1095
fc2b71fd 1096 my $schema_class = $self->schema_class;
9c9c2f2b 1097 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 1098
e9b8719e 1099 my $target_dir = $self->dump_directory;
af31090c 1100 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1101 unless $self->{dynamic} or $self->{quiet};
996be9ee 1102
7cab3ab7 1103 my $schema_text =
1104 qq|package $schema_class;\n\n|
b4dcbcc5 1105 . qq|# Created by DBIx::Class::Schema::Loader\n|
1106 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 1107 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 1108 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 1109
f44ecc2f 1110 if ($self->use_namespaces) {
1111 $schema_text .= qq|__PACKAGE__->load_namespaces|;
1112 my $namespace_options;
2a8e93e9 1113
1114 my @attr = qw/resultset_namespace default_resultset_class/;
1115
1116 unshift @attr, 'result_namespace' unless (not $self->result_namespace) || $self->result_namespace eq 'Result';
1117
1118 for my $attr (@attr) {
f44ecc2f 1119 if ($self->$attr) {
1120 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
1121 }
1122 }
1123 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1124 $schema_text .= qq|;\n|;
1125 }
1126 else {
1127 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 1128 }
996be9ee 1129
1c95b304 1130 {
1131 local $self->{version_to_dump} = $self->schema_version_to_dump;
68d49e50 1132 $self->_write_classfile($schema_class, $schema_text, 1);
1c95b304 1133 }
996be9ee 1134
2229729e 1135 my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
9c9c2f2b 1136
af31090c 1137 foreach my $src_class (@classes) {
7cab3ab7 1138 my $src_text =
1139 qq|package $src_class;\n\n|
b4dcbcc5 1140 . qq|# Created by DBIx::Class::Schema::Loader\n|
1141 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 1142 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 1143 . qq|use base '$result_base_class';\n\n|;
996be9ee 1144
7cab3ab7 1145 $self->_write_classfile($src_class, $src_text);
02356864 1146 }
996be9ee 1147
a4b94090 1148 # remove Result dir if downgrading from use_namespaces, and there are no
1149 # files left.
b5f1b43c 1150 if (my $result_ns = $self->_downgrading_to_load_classes
1151 || $self->_rewriting_result_namespace) {
540a8149 1152 my $result_namespace = $self->_result_namespace(
1153 $schema_class,
1154 $result_ns,
1155 );
a4b94090 1156
540a8149 1157 (my $result_dir = $result_namespace) =~ s{::}{/}g;
a4b94090 1158 $result_dir = $self->dump_directory . '/' . $result_dir;
1159
1160 unless (my @files = glob "$result_dir/*") {
1161 rmdir $result_dir;
1162 }
1163 }
1164
af31090c 1165 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1166
7cab3ab7 1167}
1168
79193756 1169sub _sig_comment {
1170 my ($self, $version, $ts) = @_;
1171 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1172 . qq| v| . $version
1173 . q| @ | . $ts
1174 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1175}
1176
7cab3ab7 1177sub _write_classfile {
68d49e50 1178 my ($self, $class, $text, $is_schema) = @_;
7cab3ab7 1179
1180 my $filename = $self->_get_dump_filename($class);
1181 $self->_ensure_dump_subdirs($class);
1182
28b4691d 1183 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 1184 warn "Deleting existing file '$filename' due to "
af31090c 1185 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 1186 unlink($filename);
1187 }
1188
79193756 1189 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 1190
e1373c52 1191 if (my $old_class = $self->_upgrading_classes->{$class}) {
1192 my $old_filename = $self->_get_dump_filename($old_class);
f53dcdf0 1193
e1373c52 1194 my ($old_custom_content) = $self->_get_custom_content(
1195 $old_class, $old_filename, 0 # do not add default comment
1196 );
ffc705f3 1197
e1373c52 1198 $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
f53dcdf0 1199
e1373c52 1200 if ($old_custom_content) {
1201 $custom_content =
1202 "\n" . $old_custom_content . "\n" . $custom_content;
f53dcdf0 1203 }
e1373c52 1204
1205 unlink $old_filename;
f53dcdf0 1206 }
1207
b24cb177 1208 $custom_content = $self->_rewrite_old_classnames($custom_content);
1209
7cab3ab7 1210 $text .= qq|$_\n|
1211 for @{$self->{_dump_storage}->{$class} || []};
1212
79193756 1213 # Check and see if the dump is infact differnt
1214
1215 my $compare_to;
1216 if ($old_md5) {
1217 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1218
1219
1220 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
68d49e50 1221 return unless $self->_upgrading_from && $is_schema;
79193756 1222 }
1223 }
1224
1225 $text .= $self->_sig_comment(
01012543 1226 $self->version_to_dump,
79193756 1227 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1228 );
7cab3ab7 1229
1230 open(my $fh, '>', $filename)
1231 or croak "Cannot open '$filename' for writing: $!";
1232
1233 # Write the top half and its MD5 sum
a4476f41 1234 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 1235
1236 # Write out anything loaded via external partial class file in @INC
1237 print $fh qq|$_\n|
1238 for @{$self->{_ext_storage}->{$class} || []};
1239
1eea4fb1 1240 # Write out any custom content the user has added
7cab3ab7 1241 print $fh $custom_content;
1242
1243 close($fh)
e9b8719e 1244 or croak "Error closing '$filename': $!";
7cab3ab7 1245}
1246
79193756 1247sub _default_custom_content {
1248 return qq|\n\n# You can replace this text with custom|
1249 . qq| content, and it will be preserved on regeneration|
1250 . qq|\n1;\n|;
1251}
1252
7cab3ab7 1253sub _get_custom_content {
ffc705f3 1254 my ($self, $class, $filename, $add_default) = @_;
1255
1256 $add_default = 1 unless defined $add_default;
7cab3ab7 1257
79193756 1258 return ($self->_default_custom_content) if ! -f $filename;
1259
7cab3ab7 1260 open(my $fh, '<', $filename)
1261 or croak "Cannot open '$filename' for reading: $!";
1262
1263 my $mark_re =
419a2eeb 1264 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 1265
7cab3ab7 1266 my $buffer = '';
79193756 1267 my ($md5, $ts, $ver);
7cab3ab7 1268 while(<$fh>) {
79193756 1269 if(!$md5 && /$mark_re/) {
1270 $md5 = $2;
1271 my $line = $1;
1272
1273 # Pull out the previous version and timestamp
1274 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1275
1276 $buffer .= $line;
b4cc5793 1277 croak "Checksum mismatch in '$filename', the auto-generated part of the file has been modified outside of this loader. Aborting.\nIf you want to overwrite these modifications, set the 'overwrite_modifications' loader option.\n"
639a1367 1278 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 1279
1280 $buffer = '';
1281 }
1282 else {
1283 $buffer .= $_;
1284 }
996be9ee 1285 }
1286
28b4691d 1287 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 1288 . " it does not appear to have been generated by Loader"
79193756 1289 if !$md5;
5ef3c771 1290
79193756 1291 # Default custom content:
ffc705f3 1292 $buffer ||= $self->_default_custom_content if $add_default;
5ef3c771 1293
79193756 1294 return ($buffer, $md5, $ver, $ts);
996be9ee 1295}
1296
1297sub _use {
1298 my $self = shift;
1299 my $target = shift;
1300
1301 foreach (@_) {
cb54990b 1302 warn "$target: use $_;" if $self->debug;
996be9ee 1303 $self->_raw_stmt($target, "use $_;");
996be9ee 1304 }
1305}
1306
1307sub _inject {
1308 my $self = shift;
1309 my $target = shift;
1310 my $schema_class = $self->schema_class;
1311
af31090c 1312 my $blist = join(q{ }, @_);
1313 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1314 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 1315}
1316
540a8149 1317sub _result_namespace {
1318 my ($self, $schema_class, $ns) = @_;
1319 my @result_namespace;
1320
1321 if ($ns =~ /^\+(.*)/) {
1322 # Fully qualified namespace
1323 @result_namespace = ($1)
1324 }
1325 else {
1326 # Relative namespace
1327 @result_namespace = ($schema_class, $ns);
1328 }
1329
1330 return wantarray ? @result_namespace : join '::', @result_namespace;
1331}
1332
f96ef30f 1333# Create class with applicable bases, setup monikers, etc
1334sub _make_src_class {
1335 my ($self, $table) = @_;
996be9ee 1336
a13b2803 1337 my $schema = $self->schema;
1338 my $schema_class = $self->schema_class;
996be9ee 1339
f96ef30f 1340 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 1341 my @result_namespace = ($schema_class);
1342 if ($self->use_namespaces) {
1343 my $result_namespace = $self->result_namespace || 'Result';
540a8149 1344 @result_namespace = $self->_result_namespace(
1345 $schema_class,
1346 $result_namespace,
1347 );
f44ecc2f 1348 }
1349 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 1350
805dbe0a 1351 if ((my $upgrading_v = $self->_upgrading_from)
a4b94090 1352 || $self->_rewriting) {
805dbe0a 1353 local $self->naming->{monikers} = $upgrading_v
1354 if $upgrading_v;
1355
1356 my @result_namespace = @result_namespace;
a4b94090 1357 if ($self->_upgrading_from_load_classes) {
1358 @result_namespace = ($schema_class);
1359 }
1360 elsif (my $ns = $self->_downgrading_to_load_classes) {
540a8149 1361 @result_namespace = $self->_result_namespace(
1362 $schema_class,
1363 $ns,
1364 );
1365 }
1366 elsif ($ns = $self->_rewriting_result_namespace) {
1367 @result_namespace = $self->_result_namespace(
1368 $schema_class,
1369 $ns,
1370 );
a4b94090 1371 }
f53dcdf0 1372
1373 my $old_class = join(q{::}, @result_namespace,
1374 $self->_table2moniker($table));
1375
68d49e50 1376 $self->_upgrading_classes->{$table_class} = $old_class
1377 unless $table_class eq $old_class;
f53dcdf0 1378 }
1379
bfb43060 1380# this was a bad idea, should be ok now without it
1381# my $table_normalized = lc $table;
1382# $self->classes->{$table_normalized} = $table_class;
1383# $self->monikers->{$table_normalized} = $table_moniker;
1384
f96ef30f 1385 $self->classes->{$table} = $table_class;
f96ef30f 1386 $self->monikers->{$table} = $table_moniker;
996be9ee 1387
f96ef30f 1388 $self->_use ($table_class, @{$self->additional_classes});
af31090c 1389 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 1390
2229729e 1391 if (my @components = @{ $self->components }) {
1392 $self->_dbic_stmt($table_class, 'load_components', @components);
1393 }
996be9ee 1394
f96ef30f 1395 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1396 if @{$self->resultset_components};
af31090c 1397 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 1398}
996be9ee 1399
9fdf3d5b 1400sub _resolve_col_accessor_collisions {
1401 my ($self, $col_info) = @_;
1402
1403 my $base = $self->result_base_class || 'DBIx::Class::Core';
1404 my @components = map "DBIx::Class::$_", @{ $self->components };
1405
1406 my @methods;
1407
1408 for my $class ($base, @components) {
1409 eval "require ${class};";
1410 die $@ if $@;
1411
1412 push @methods, @{ Class::Inspector->methods($class) || [] };
1413 }
1414
1415 my %methods;
1416 @methods{@methods} = ();
1417
1418 while (my ($col, $info) = each %$col_info) {
1419 my $accessor = $info->{accessor} || $col;
1420
1421 next if $accessor eq 'id'; # XXX fix this in DBIC
1422
1423 if (exists $methods{$accessor}) {
1424 $info->{accessor} = ucfirst $accessor;
1425 }
1426 }
1427}
1428
af31090c 1429# Set up metadata (cols, pks, etc)
f96ef30f 1430sub _setup_src_meta {
1431 my ($self, $table) = @_;
996be9ee 1432
f96ef30f 1433 my $schema = $self->schema;
1434 my $schema_class = $self->schema_class;
a13b2803 1435
f96ef30f 1436 my $table_class = $self->classes->{$table};
1437 my $table_moniker = $self->monikers->{$table};
996be9ee 1438
ff30991a 1439 my $table_name = $table;
1440 my $name_sep = $self->schema->storage->sql_maker->name_sep;
1441
c177d483 1442 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 1443 $table_name = \ $self->_quote_table_name($table_name);
1444 }
1445
1446 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 1447
f96ef30f 1448 my $cols = $self->_table_columns($table);
45be2ce7 1449 my $col_info = $self->__columns_info_for($table);
1450 if ($self->_is_case_sensitive) {
1451 for my $col (keys %$col_info) {
1452 $col_info->{$col}{accessor} = lc $col
1453 if $col ne lc($col);
c9373b79 1454 }
45be2ce7 1455 } else {
1456 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1457 }
c9373b79 1458
9fdf3d5b 1459 $self->_resolve_col_accessor_collisions($col_info);
1460
45be2ce7 1461 my $fks = $self->_table_fk_info($table);
565335e6 1462
45be2ce7 1463 for my $fkdef (@$fks) {
1464 for my $col (@{ $fkdef->{local_columns} }) {
1465 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 1466 }
996be9ee 1467 }
45be2ce7 1468 $self->_dbic_stmt(
1469 $table_class,
1470 'add_columns',
1471 map { $_, ($col_info->{$_}||{}) } @$cols
1472 );
996be9ee 1473
d70c335f 1474 my %uniq_tag; # used to eliminate duplicate uniqs
1475
f96ef30f 1476 my $pks = $self->_table_pk_info($table) || [];
1477 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1478 : carp("$table has no primary key");
d70c335f 1479 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 1480
f96ef30f 1481 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 1482 for (@$uniqs) {
1483 my ($name, $cols) = @$_;
1484 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1485 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1486 }
1487
996be9ee 1488}
1489
d67d058e 1490sub __columns_info_for {
1491 my ($self, $table) = @_;
1492
1493 my $result = $self->_columns_info_for($table);
1494
1495 while (my ($col, $info) = each %$result) {
1496 $info = { %$info, %{ $self->_custom_column_info ($table, $col, $info) } };
1497 $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1498
1499 $result->{$col} = $info;
1500 }
1501
1502 return $result;
1503}
1504
996be9ee 1505=head2 tables
1506
1507Returns a sorted list of loaded tables, using the original database table
1508names.
1509
1510=cut
1511
1512sub tables {
1513 my $self = shift;
1514
b97c2c1e 1515 return keys %{$self->_tables};
996be9ee 1516}
1517
1518# Make a moniker from a table
c39e403e 1519sub _default_table2moniker {
66afce69 1520 no warnings 'uninitialized';
c39e403e 1521 my ($self, $table) = @_;
1522
a8d229ff 1523 if ($self->naming->{monikers} eq 'v4') {
1524 return join '', map ucfirst, split /[\W_]+/, lc $table;
1525 }
ecf930e6 1526 elsif ($self->naming->{monikers} eq 'v5') {
1527 return join '', map ucfirst, split /[\W_]+/,
1528 Lingua::EN::Inflect::Number::to_S(lc $table);
1529 }
1530
1531 (my $as_phrase = lc $table) =~ s/_+/ /g;
1532 my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
a8d229ff 1533
ecf930e6 1534 return join '', map ucfirst, split /\W+/, $inflected;
c39e403e 1535}
1536
996be9ee 1537sub _table2moniker {
1538 my ( $self, $table ) = @_;
1539
1540 my $moniker;
1541
1542 if( ref $self->moniker_map eq 'HASH' ) {
1543 $moniker = $self->moniker_map->{$table};
1544 }
1545 elsif( ref $self->moniker_map eq 'CODE' ) {
1546 $moniker = $self->moniker_map->($table);
1547 }
1548
c39e403e 1549 $moniker ||= $self->_default_table2moniker($table);
996be9ee 1550
1551 return $moniker;
1552}
1553
1554sub _load_relationships {
e8ad6491 1555 my ($self, $table) = @_;
996be9ee 1556
e8ad6491 1557 my $tbl_fk_info = $self->_table_fk_info($table);
1558 foreach my $fkdef (@$tbl_fk_info) {
1559 $fkdef->{remote_source} =
1560 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 1561 }
26f1c8c9 1562 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 1563
e8ad6491 1564 my $local_moniker = $self->monikers->{$table};
7824616e 1565 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 1566
996be9ee 1567 foreach my $src_class (sort keys %$rel_stmts) {
1568 my $src_stmts = $rel_stmts->{$src_class};
1569 foreach my $stmt (@$src_stmts) {
1570 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1571 }
1572 }
1573}
1574
1575# Overload these in driver class:
1576
1577# Returns an arrayref of column names
1578sub _table_columns { croak "ABSTRACT METHOD" }
1579
1580# Returns arrayref of pk col names
1581sub _table_pk_info { croak "ABSTRACT METHOD" }
1582
1583# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1584sub _table_uniq_info { croak "ABSTRACT METHOD" }
1585
1586# Returns an arrayref of foreign key constraints, each
1587# being a hashref with 3 keys:
1588# local_columns (arrayref), remote_columns (arrayref), remote_table
1589sub _table_fk_info { croak "ABSTRACT METHOD" }
1590
1591# Returns an array of lower case table names
1592sub _tables_list { croak "ABSTRACT METHOD" }
1593
1594# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1595sub _dbic_stmt {
bf654ab9 1596 my $self = shift;
1597 my $class = shift;
996be9ee 1598 my $method = shift;
bf654ab9 1599
1600 # generate the pod for this statement, storing it with $self->_pod
43b982ea 1601 $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
bf654ab9 1602
1603 my $args = dump(@_);
1604 $args = '(' . $args . ')' if @_ < 2;
1605 my $stmt = $method . $args . q{;};
1606
1607 warn qq|$class\->$stmt\n| if $self->debug;
1608 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1609 return;
1610}
1611
1612# generates the accompanying pod for a DBIC class method statement,
1613# storing it with $self->_pod
1614sub _make_pod {
1615 my $self = shift;
1616 my $class = shift;
1617 my $method = shift;
1618
fbcfebdd 1619 if ( $method eq 'table' ) {
1620 my ($table) = @_;
43b982ea 1621 my $pcm = $self->pod_comment_mode;
1622 my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
fbcfebdd 1623 if ( $self->can('_table_comment') ) {
43b982ea 1624 $comment = $self->_table_comment($table);
1625 $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1626 $comment_in_name = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1627 $comment_in_desc = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
fbcfebdd 1628 }
43b982ea 1629 $self->_pod( $class, "=head1 NAME" );
1630 my $table_descr = $class;
1631 $table_descr .= " - " . $comment if $comment and $comment_in_name;
fbcfebdd 1632 $self->{_class2table}{ $class } = $table;
1633 $self->_pod( $class, $table_descr );
43b982ea 1634 if ($comment and $comment_in_desc) {
1635 $self->_pod( $class, "=head1 DESCRIPTION" );
1636 $self->_pod( $class, $comment );
1637 }
fbcfebdd 1638 $self->_pod_cut( $class );
1639 } elsif ( $method eq 'add_columns' ) {
1640 $self->_pod( $class, "=head1 ACCESSORS" );
79a00530 1641 my $col_counter = 0;
1642 my @cols = @_;
1643 while( my ($name,$attrs) = splice @cols,0,2 ) {
1644 $col_counter++;
1645 $self->_pod( $class, '=head2 ' . $name );
1646 $self->_pod( $class,
1647 join "\n", map {
1648 my $s = $attrs->{$_};
fca5431b 1649 $s = !defined $s ? 'undef' :
1650 length($s) == 0 ? '(empty string)' :
f170d55b 1651 ref($s) eq 'SCALAR' ? $$s :
1652 ref($s) ? do {
1653 my $dd = Dumper;
1654 $dd->Indent(0);
1655 $dd->Values([$s]);
1656 $dd->Dump;
1657 } :
1658 looks_like_number($s) ? $s :
1659 qq{'$s'}
fca5431b 1660 ;
79a00530 1661
1662 " $_: $s"
1663 } sort keys %$attrs,
1664 );
1665
1666 if( $self->can('_column_comment')
1667 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1668 ) {
1669 $self->_pod( $class, $comment );
1670 }
fbcfebdd 1671 }
1672 $self->_pod_cut( $class );
1673 } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1674 $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1675 my ( $accessor, $rel_class ) = @_;
1676 $self->_pod( $class, "=head2 $accessor" );
1677 $self->_pod( $class, 'Type: ' . $method );
1678 $self->_pod( $class, "Related object: L<$rel_class>" );
1679 $self->_pod_cut( $class );
1680 $self->{_relations_started} { $class } = 1;
1681 }
996be9ee 1682}
1683
fbcfebdd 1684# Stores a POD documentation
1685sub _pod {
1686 my ($self, $class, $stmt) = @_;
1687 $self->_raw_stmt( $class, "\n" . $stmt );
1688}
1689
1690sub _pod_cut {
1691 my ($self, $class ) = @_;
1692 $self->_raw_stmt( $class, "\n=cut\n" );
1693}
1694
996be9ee 1695# Store a raw source line for a class (for dumping purposes)
1696sub _raw_stmt {
1697 my ($self, $class, $stmt) = @_;
af31090c 1698 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 1699}
1700
7cab3ab7 1701# Like above, but separately for the externally loaded stuff
1702sub _ext_stmt {
1703 my ($self, $class, $stmt) = @_;
af31090c 1704 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 1705}
1706
565335e6 1707sub _quote_table_name {
1708 my ($self, $table) = @_;
1709
1710 my $qt = $self->schema->storage->sql_maker->quote_char;
1711
c177d483 1712 return $table unless $qt;
1713
565335e6 1714 if (ref $qt) {
1715 return $qt->[0] . $table . $qt->[1];
1716 }
1717
1718 return $qt . $table . $qt;
1719}
1720
1721sub _is_case_sensitive { 0 }
1722
b639d969 1723sub _custom_column_info {
23d1f36b 1724 my ( $self, $table_name, $column_name, $column_info ) = @_;
b639d969 1725
d67d058e 1726 if (my $code = $self->custom_column_info) {
1727 return $code->($table_name, $column_name, $column_info) || {};
b639d969 1728 }
3a368709 1729 return {};
b639d969 1730}
1731
42e785fa 1732sub _datetime_column_info {
23d1f36b 1733 my ( $self, $table_name, $column_name, $column_info ) = @_;
d67d058e 1734 my $result = {};
1735 my $type = $column_info->{data_type} || '';
1736 if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1737 or ($type =~ /date|timestamp/i)) {
1738 $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1739 $result->{locale} = $self->datetime_locale if $self->datetime_locale;
42e785fa 1740 }
d67d058e 1741 return $result;
42e785fa 1742}
1743
ffc705f3 1744# remove the dump dir from @INC on destruction
1745sub DESTROY {
1746 my $self = shift;
1747
1748 @INC = grep $_ ne $self->dump_directory, @INC;
1749}
1750
996be9ee 1751=head2 monikers
1752
8f9d7ce5 1753Returns a hashref of loaded table to moniker mappings. There will
996be9ee 1754be two entries for each table, the original name and the "normalized"
1755name, in the case that the two are different (such as databases
1756that like uppercase table names, or preserve your original mixed-case
1757definitions, or what-have-you).
1758
1759=head2 classes
1760
8f9d7ce5 1761Returns a hashref of table to class mappings. In some cases it will
996be9ee 1762contain multiple entries per table for the original and normalized table
1763names, as above in L</monikers>.
1764
1765=head1 SEE ALSO
1766
1767L<DBIx::Class::Schema::Loader>
1768
be80bba7 1769=head1 AUTHOR
1770
9cc8e7e1 1771See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
be80bba7 1772
1773=head1 LICENSE
1774
1775This library is free software; you can redistribute it and/or modify it under
1776the same terms as Perl itself.
1777
996be9ee 1778=cut
1779
17801;
bfb43060 1781# vim:et sts=4 sw=4 tw=0: