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