Remove pointless DESTROY methods
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Field.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Field;
2
3c5de62a 3=pod
4
5=head1 NAME
6
7SQL::Translator::Schema::Field - SQL::Translator field object
8
9=head1 SYNOPSIS
10
11 use SQL::Translator::Schema::Field;
12 my $field = SQL::Translator::Schema::Field->new(
b9dc0b40 13 name => 'foo',
14 table => $table,
3c5de62a 15 );
16
17=head1 DESCRIPTION
18
19C<SQL::Translator::Schema::Field> is the field object.
20
21=head1 METHODS
22
23=cut
24
a14ab50e 25use Moo;
43b9dc7a 26use SQL::Translator::Schema::Constants;
a14ab50e 27use SQL::Translator::Types qw(schema_obj);
28use SQL::Translator::Utils qw(parse_list_arg ex2err throw);
3c5de62a 29
a14ab50e 30with qw(
46ad748f 31 SQL::Translator::Schema::Role::BuildArgs
a14ab50e 32 SQL::Translator::Schema::Role::Extra
33 SQL::Translator::Schema::Role::Error
34 SQL::Translator::Schema::Role::Compare
35);
b6a880d1 36
0c04c5a2 37our $VERSION = '1.59';
65dd38c0 38
39# Stringify to our name, being careful not to pass any args through so we don't
40# accidentally set it to undef. We also have to tweak bool so the object is
41# still true when it doesn't have a name (which shouldn't happen!).
42use overload
43 '""' => sub { shift->name },
44 'bool' => sub { $_[0]->name || $_[0] },
45 fallback => 1,
46;
3c5de62a 47
9ab59f87 48use DBI qw(:sql_types);
49
50# Mapping from string to sql contstant
51our %type_mapping = (
52 integer => SQL_INTEGER,
53 int => SQL_INTEGER,
54
55 smallint => SQL_SMALLINT,
56 bigint => 9999, # DBI doesn't export a constatn for this. Le suck
57
58 double => SQL_DOUBLE,
59
60 decimal => SQL_DECIMAL,
61 numeric => SQL_NUMERIC,
62 dec => SQL_DECIMAL,
63
64 bit => SQL_BIT,
65
66 date => SQL_DATE,
67 datetime => SQL_DATETIME,
68 timestamp => SQL_TIMESTAMP,
69 time => SQL_TIME,
70
71 char => SQL_CHAR,
72 varchar => SQL_VARCHAR,
73 binary => SQL_BINARY,
74 varbinary => SQL_VARBINARY,
75 tinyblob => SQL_BLOB,
76 blob => SQL_BLOB,
77 text => SQL_LONGVARCHAR
78
79);
9371be50 80
3c5de62a 81=head2 new
82
83Object constructor.
84
0bf88ce5 85 my $field = SQL::Translator::Schema::Field->new(
86 name => 'foo',
87 table => $table,
b9dc0b40 88 );
3c5de62a 89
5ac417ad 90=head2 comments
91
ea93df61 92Get or set the comments on a field. May be called several times to
5ac417ad 93set and it will accumulate the comments. Called in an array context,
94returns each comment individually; called in a scalar context, returns
95all the comments joined on newlines.
96
97 $field->comments('foo');
98 $field->comments('bar');
99 print join( ', ', $field->comments ); # prints "foo, bar"
100
101=cut
102
a14ab50e 103has comments => (
104 is => 'rw',
105 coerce => sub { ref($_[0]) eq 'ARRAY' ? $_[0] : [$_[0]] },
106 default => sub { [] },
107);
108
109around comments => sub {
110 my $orig = shift;
5ac417ad 111 my $self = shift;
aadf4042 112
113 for my $arg ( @_ ) {
114 $arg = $arg->[0] if ref $arg;
a14ab50e 115 push @{ $self->$orig }, $arg if $arg;
c33df5c4 116 }
5ac417ad 117
a14ab50e 118 return wantarray
119 ? @{ $self->$orig }
120 : join( "\n", @{ $self->$orig } );
121};
3c5de62a 122
3c5de62a 123
124=head2 data_type
125
43b9dc7a 126Get or set the field's data type.
3c5de62a 127
128 my $data_type = $field->data_type('integer');
129
130=cut
131
a14ab50e 132has data_type => ( is => 'rw', default => sub { '' } );
6b2dbb1a 133
134=head2 sql_data_type
135
136Constant from DBI package representing this data type. See L<DBI/DBI Constants>
137for more details.
138
139=cut
140
a14ab50e 141has sql_data_type => ( is => 'rw', lazy => 1, builder => 1 );
6b2dbb1a 142
a14ab50e 143sub _build_sql_data_type {
144 $type_mapping{lc $_[0]->data_type} || SQL_UNKNOWN_TYPE;
6b2dbb1a 145}
146
43b9dc7a 147=head2 default_value
148
149Get or set the field's default value. Will return undef if not defined
ea93df61 150and could return the empty string (it's a valid default value), so don't
43b9dc7a 151assume an error like other methods.
152
153 my $default = $field->default_value('foo');
154
155=cut
156
a14ab50e 157has default_value => ( is => 'rw' );
9966eebc 158
159=head2 extra
160
161Get or set the field's "extra" attibutes (e.g., "ZEROFILL" for MySQL).
162Accepts a hash(ref) of name/value pairs to store; returns a hash.
163
164 $field->extra( qualifier => 'ZEROFILL' );
165 my %extra = $field->extra;
166
167=cut
168
9966eebc 169=head2 foreign_key_reference
170
171Get or set the field's foreign key reference;
172
173 my $constraint = $field->foreign_key_reference( $constraint );
174
175=cut
176
a14ab50e 177has foreign_key_reference => (
178 is => 'rw',
179 predicate => '_has_foreign_key_reference',
180 isa => schema_obj('Constraint'),
181);
182
183around foreign_key_reference => sub {
184 my $orig = shift;
9966eebc 185 my $self = shift;
186
187 if ( my $arg = shift ) {
a14ab50e 188 return $self->error(
189 'Foreign key reference for ', $self->name, 'already defined'
190 ) if $self->_has_foreign_key_reference;
9966eebc 191
a14ab50e 192 return ex2err($orig, $self, $arg);
9966eebc 193 }
a14ab50e 194 $self->$orig;
195};
43b9dc7a 196
197=head2 is_auto_increment
198
199Get or set the field's C<is_auto_increment> attribute.
200
b9dc0b40 201 my $is_auto = $field->is_auto_increment(1);
43b9dc7a 202
203=cut
204
a14ab50e 205has is_auto_increment => (
206 is => 'rw',
207 coerce => sub { $_[0] ? 1 : 0 },
208 builder => 1,
209 lazy => 1,
210);
43b9dc7a 211
a14ab50e 212sub _build_is_auto_increment {
213 my ( $self ) = @_;
43b9dc7a 214
a14ab50e 215 if ( my $table = $self->table ) {
216 if ( my $schema = $table->schema ) {
217 if (
218 $schema->database eq 'PostgreSQL' &&
219 $self->data_type eq 'serial'
220 ) {
221 return 1;
43b9dc7a 222 }
223 }
224 }
a14ab50e 225 return 0;
43b9dc7a 226}
227
9966eebc 228=head2 is_foreign_key
229
230Returns whether or not the field is a foreign key.
231
232 my $is_fk = $field->is_foreign_key;
233
234=cut
235
a14ab50e 236has is_foreign_key => (
237 is => 'rw',
238 coerce => sub { $_[0] ? 1 : 0 },
239 builder => 1,
240 lazy => 1,
241);
242
243sub _build_is_foreign_key {
244 my ( $self ) = @_;
245
246 if ( my $table = $self->table ) {
247 for my $c ( $table->get_constraints ) {
248 if ( $c->type eq FOREIGN_KEY ) {
249 my %fields = map { $_, 1 } $c->fields;
250 if ( $fields{ $self->name } ) {
251 $self->foreign_key_reference( $c );
252 return 1;
9966eebc 253 }
254 }
255 }
256 }
a14ab50e 257 return 0;
9966eebc 258}
259
ec2ab48d 260=head2 is_nullable
261
ea93df61 262Get or set whether the field can be null. If not defined, then
ec2ab48d 263returns "1" (assumes the field can be null). The argument is evaluated
264by Perl for True or False, so the following are eqivalent:
265
266 $is_nullable = $field->is_nullable(0);
267 $is_nullable = $field->is_nullable('');
268 $is_nullable = $field->is_nullable('0');
269
270While this is technically a field constraint, it's probably easier to
271represent this as an attribute of the field. In order keep things
272consistent, any other constraint on the field (unique, primary, and
273foreign keys; checks) are represented as table constraints.
274
275=cut
276
a14ab50e 277has is_nullable => (
278 is => 'rw',
279 coerce => sub { $_[0] ? 1 : 0 },
280 default => sub { 1 },
281 );
ec2ab48d 282
a14ab50e 283around is_nullable => sub {
284 my ($orig, $self, $arg) = @_;
ec2ab48d 285
a14ab50e 286 $self->$orig($self->is_primary_key ? 0 : defined $arg ? $arg : ());
287};
3c5de62a 288
289=head2 is_primary_key
290
ec2ab48d 291Get or set the field's C<is_primary_key> attribute. Does not create
292a table constraint (should it?).
3c5de62a 293
294 my $is_pk = $field->is_primary_key(1);
295
296=cut
297
a14ab50e 298has is_primary_key => (
299 is => 'rw',
300 coerce => sub { $_[0] ? 1 : 0 },
301 lazy => 1,
302 builder => 1,
303);
3c5de62a 304
a14ab50e 305sub _build_is_primary_key {
306 my ( $self ) = @_;
3c5de62a 307
a14ab50e 308 if ( my $table = $self->table ) {
309 if ( my $pk = $table->primary_key ) {
310 my %fields = map { $_, 1 } $pk->fields;
311 return $fields{ $self->name } || 0;
43b9dc7a 312 }
313 }
a14ab50e 314 return 0;
3c5de62a 315}
316
ee2766f4 317=head2 is_unique
318
319Determine whether the field has a UNIQUE constraint or not.
320
321 my $is_unique = $field->is_unique;
322
323=cut
324
a14ab50e 325has is_unique => ( is => 'lazy', init_arg => undef );
ea93df61 326
a14ab50e 327sub _build_is_unique {
328 my ( $self ) = @_;
329
330 if ( my $table = $self->table ) {
331 for my $c ( $table->get_constraints ) {
332 if ( $c->type eq UNIQUE ) {
333 my %fields = map { $_, 1 } $c->fields;
334 if ( $fields{ $self->name } ) {
335 return 1;
ee2766f4 336 }
337 }
338 }
339 }
a14ab50e 340 return 0;
ee2766f4 341}
342
ec2ab48d 343sub is_valid {
344
345=pod
346
347=head2 is_valid
348
349Determine whether the field is valid or not.
350
351 my $ok = $field->is_valid;
352
353=cut
354
355 my $self = shift;
356 return $self->error('No name') unless $self->name;
357 return $self->error('No data type') unless $self->data_type;
358 return $self->error('No table object') unless $self->table;
359 return 1;
360}
361
3c5de62a 362=head2 name
363
364Get or set the field's name.
365
65dd38c0 366 my $name = $field->name('foo');
367
368The field object will also stringify to its name.
369
370 my $setter_name = "set_$field";
371
372Errors ("No field name") if you try to set a blank name.
3c5de62a 373
374=cut
375
a14ab50e 376has name => ( is => 'rw', isa => sub { throw( "No field name" ) unless $_[0] } );
377
378around name => sub {
379 my $orig = shift;
3c5de62a 380 my $self = shift;
43b9dc7a 381
a14ab50e 382 if ( my ($arg) = @_ ) {
383 if ( my $schema = $self->table ) {
65dd38c0 384 return $self->error( qq[Can't use field name "$arg": field exists] )
a14ab50e 385 if $schema->get_field( $arg );
43b9dc7a 386 }
43b9dc7a 387 }
388
a14ab50e 389 return ex2err($orig, $self, @_);
390};
3c5de62a 391
4809213f 392sub full_name {
393
394=head2 full_name
395
396Read only method to return the fields name with its table name pre-pended.
397e.g. "person.foo".
398
399=cut
400
401 my $self = shift;
402 return $self->table.".".$self->name;
403}
404
ec2ab48d 405=head2 order
3c5de62a 406
ec2ab48d 407Get or set the field's order.
3c5de62a 408
ec2ab48d 409 my $order = $field->order(3);
3c5de62a 410
411=cut
412
a14ab50e 413has order => ( is => 'rw', default => sub { 0 } );
414
415around order => sub {
416 my ( $orig, $self, $arg ) = @_;
3c5de62a 417
ec2ab48d 418 if ( defined $arg && $arg =~ /^\d+$/ ) {
a14ab50e 419 return $self->$orig($arg);
3c5de62a 420 }
421
a14ab50e 422 return $self->$orig;
423};
43b9dc7a 424
c1e3c768 425sub schema {
426
ea93df61 427=head2 schema
c1e3c768 428
429Shortcut to get the fields schema ($field->table->schema) or undef if it
430doesn't have one.
431
432 my $schema = $field->schema;
433
434=cut
435
436 my $self = shift;
437 if ( my $table = $self->table ) { return $table->schema || undef; }
438 return undef;
439}
440
43b9dc7a 441=head2 size
442
443Get or set the field's size. Accepts a string, array or arrayref of
444numbers and returns a string.
445
446 $field->size( 30 );
447 $field->size( [ 255 ] );
448 $size = $field->size( 10, 2 );
449 print $size; # prints "10,2"
450
451 $size = $field->size( '10, 2' );
452 print $size; # prints "10,2"
453
454=cut
455
a14ab50e 456has size => (
457 is => 'rw',
458 default => sub { [0] },
459 coerce => sub {
460 my @sizes = grep { defined && m/^\d+(?:\.\d+)?$/ } @{parse_list_arg($_[0])};
461 @sizes ? \@sizes : [0];
462 },
463);
464
465around size => sub {
466 my $orig = shift;
43b9dc7a 467 my $self = shift;
ec2ab48d 468 my $numbers = parse_list_arg( @_ );
43b9dc7a 469
470 if ( @$numbers ) {
471 my @new;
472 for my $num ( @$numbers ) {
473 if ( defined $num && $num =~ m/^\d+(?:\.\d+)?$/ ) {
474 push @new, $num;
475 }
476 }
a14ab50e 477 $self->$orig(\@new) if @new; # only set if all OK
43b9dc7a 478 }
479
ea93df61 480 return wantarray
a14ab50e 481 ? @{ $self->$orig || [0] }
482 : join( ',', @{ $self->$orig || [0] } )
ec2ab48d 483 ;
a14ab50e 484};
43b9dc7a 485
486=head2 table
487
b9dc0b40 488Get or set the field's table object. As the table object stringifies this can
489also be used to get the table name.
43b9dc7a 490
491 my $table = $field->table;
b9dc0b40 492 print "Table name: $table";
43b9dc7a 493
494=cut
495
a14ab50e 496has table => ( is => 'rw', isa => schema_obj('Table') );
43b9dc7a 497
a14ab50e 498around table => \&ex2err;
07d6e5f7 499
ea93df61 500=head2
07d6e5f7 501
502Returns the field exactly as the parser found it
503
504=cut
505
a14ab50e 506has parsed_field => ( is => 'rw' );
07d6e5f7 507
a14ab50e 508around parsed_field => sub {
509 my $orig = shift;
510 my $self = shift;
abf315bb 511
a14ab50e 512 return $self->$orig(@_) || $self;
513};
abf315bb 514
515=head2 equals
516
517Determines if this field is the same as another
518
519 my $isIdentical = $field1->equals( $field2 );
520
521=cut
522
a14ab50e 523around equals => sub {
524 my $orig = shift;
abf315bb 525 my $self = shift;
526 my $other = shift;
527 my $case_insensitive = shift;
ea93df61 528
a14ab50e 529 return 0 unless $self->$orig($other);
abf315bb 530 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
6b2dbb1a 531
532 # Comparing types: use sql_data_type if both are not 0. Else use string data_type
533 if ($self->sql_data_type && $other->sql_data_type) {
534 return 0 unless $self->sql_data_type == $other->sql_data_type
535 } else {
536 return 0 unless lc($self->data_type) eq lc($other->data_type)
537 }
538
abf315bb 539 return 0 unless $self->size eq $other->size;
f5fd433f 540
541 {
542 my $lhs = $self->default_value;
543 $lhs = \'NULL' unless defined $lhs;
544 my $lhs_is_ref = ! ! ref $lhs;
545
546 my $rhs = $other->default_value;
547 $rhs = \'NULL' unless defined $rhs;
548 my $rhs_is_ref = ! ! ref $rhs;
549
550 # If only one is a ref, fail. -- rjbs, 2008-12-02
551 return 0 if $lhs_is_ref xor $rhs_is_ref;
552
553 my $effective_lhs = $lhs_is_ref ? $$lhs : $lhs;
554 my $effective_rhs = $rhs_is_ref ? $$rhs : $rhs;
555
556 return 0 if $effective_lhs ne $effective_rhs;
557 }
558
abf315bb 559 return 0 unless $self->is_nullable eq $other->is_nullable;
afb07483 560# return 0 unless $self->is_unique eq $other->is_unique;
abf315bb 561 return 0 unless $self->is_primary_key eq $other->is_primary_key;
65e7a069 562# return 0 unless $self->is_foreign_key eq $other->is_foreign_key;
abf315bb 563 return 0 unless $self->is_auto_increment eq $other->is_auto_increment;
564# return 0 unless $self->comments eq $other->comments;
4598b71c 565 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 566 return 1;
a14ab50e 567};
abf315bb 568
a14ab50e 569# Must come after all 'has' declarations
570around new => \&ex2err;
571
3c5de62a 5721;
573
3c5de62a 574=pod
575
576=head1 AUTHOR
577
c3b0b535 578Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 579
580=cut