Remove documentation for field moved to role
[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
3a69e561 46# Mapping from string to sql constant
9ab59f87 47our %type_mapping = (
48 integer => SQL_INTEGER,
49 int => SQL_INTEGER,
50
51 smallint => SQL_SMALLINT,
3a69e561 52 bigint => 9999, # DBI doesn't export a constant for this. Le suck
9ab59f87 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
9966eebc 155=head2 foreign_key_reference
156
157Get or set the field's foreign key reference;
158
159 my $constraint = $field->foreign_key_reference( $constraint );
160
161=cut
162
a14ab50e 163has foreign_key_reference => (
164 is => 'rw',
165 predicate => '_has_foreign_key_reference',
166 isa => schema_obj('Constraint'),
a5bfeba8 167 weak_ref => 1,
a14ab50e 168);
169
170around foreign_key_reference => sub {
171 my $orig = shift;
9966eebc 172 my $self = shift;
173
174 if ( my $arg = shift ) {
a14ab50e 175 return $self->error(
176 'Foreign key reference for ', $self->name, 'already defined'
177 ) if $self->_has_foreign_key_reference;
9966eebc 178
a14ab50e 179 return ex2err($orig, $self, $arg);
9966eebc 180 }
a14ab50e 181 $self->$orig;
182};
43b9dc7a 183
184=head2 is_auto_increment
185
186Get or set the field's C<is_auto_increment> attribute.
187
b9dc0b40 188 my $is_auto = $field->is_auto_increment(1);
43b9dc7a 189
190=cut
191
a14ab50e 192has is_auto_increment => (
193 is => 'rw',
c804300c 194 coerce => quote_sub(q{ $_[0] ? 1 : 0 }),
a14ab50e 195 builder => 1,
196 lazy => 1,
197);
43b9dc7a 198
a14ab50e 199sub _build_is_auto_increment {
200 my ( $self ) = @_;
43b9dc7a 201
a14ab50e 202 if ( my $table = $self->table ) {
203 if ( my $schema = $table->schema ) {
204 if (
205 $schema->database eq 'PostgreSQL' &&
206 $self->data_type eq 'serial'
207 ) {
208 return 1;
43b9dc7a 209 }
210 }
211 }
a14ab50e 212 return 0;
43b9dc7a 213}
214
9966eebc 215=head2 is_foreign_key
216
217Returns whether or not the field is a foreign key.
218
219 my $is_fk = $field->is_foreign_key;
220
221=cut
222
a14ab50e 223has is_foreign_key => (
224 is => 'rw',
c804300c 225 coerce => quote_sub(q{ $_[0] ? 1 : 0 }),
a14ab50e 226 builder => 1,
227 lazy => 1,
228);
229
230sub _build_is_foreign_key {
231 my ( $self ) = @_;
232
233 if ( my $table = $self->table ) {
234 for my $c ( $table->get_constraints ) {
235 if ( $c->type eq FOREIGN_KEY ) {
236 my %fields = map { $_, 1 } $c->fields;
237 if ( $fields{ $self->name } ) {
238 $self->foreign_key_reference( $c );
239 return 1;
9966eebc 240 }
241 }
242 }
243 }
a14ab50e 244 return 0;
9966eebc 245}
246
ec2ab48d 247=head2 is_nullable
248
ea93df61 249Get or set whether the field can be null. If not defined, then
ec2ab48d 250returns "1" (assumes the field can be null). The argument is evaluated
3a69e561 251by Perl for True or False, so the following are equivalent:
ec2ab48d 252
253 $is_nullable = $field->is_nullable(0);
254 $is_nullable = $field->is_nullable('');
255 $is_nullable = $field->is_nullable('0');
256
257While this is technically a field constraint, it's probably easier to
258represent this as an attribute of the field. In order keep things
259consistent, any other constraint on the field (unique, primary, and
260foreign keys; checks) are represented as table constraints.
261
262=cut
263
a14ab50e 264has is_nullable => (
265 is => 'rw',
c804300c 266 coerce => quote_sub(q{ $_[0] ? 1 : 0 }),
68d75205 267 default => quote_sub(q{ 1 }),
a14ab50e 268 );
ec2ab48d 269
a14ab50e 270around is_nullable => sub {
271 my ($orig, $self, $arg) = @_;
ec2ab48d 272
a14ab50e 273 $self->$orig($self->is_primary_key ? 0 : defined $arg ? $arg : ());
274};
3c5de62a 275
276=head2 is_primary_key
277
ec2ab48d 278Get or set the field's C<is_primary_key> attribute. Does not create
279a table constraint (should it?).
3c5de62a 280
281 my $is_pk = $field->is_primary_key(1);
282
283=cut
284
a14ab50e 285has is_primary_key => (
286 is => 'rw',
c804300c 287 coerce => quote_sub(q{ $_[0] ? 1 : 0 }),
a14ab50e 288 lazy => 1,
289 builder => 1,
290);
3c5de62a 291
a14ab50e 292sub _build_is_primary_key {
293 my ( $self ) = @_;
3c5de62a 294
a14ab50e 295 if ( my $table = $self->table ) {
296 if ( my $pk = $table->primary_key ) {
297 my %fields = map { $_, 1 } $pk->fields;
298 return $fields{ $self->name } || 0;
43b9dc7a 299 }
300 }
a14ab50e 301 return 0;
3c5de62a 302}
303
ee2766f4 304=head2 is_unique
305
306Determine whether the field has a UNIQUE constraint or not.
307
308 my $is_unique = $field->is_unique;
309
310=cut
311
a14ab50e 312has is_unique => ( is => 'lazy', init_arg => undef );
ea93df61 313
f8783818 314around is_unique => carp_ro('is_unique');
315
a14ab50e 316sub _build_is_unique {
317 my ( $self ) = @_;
318
319 if ( my $table = $self->table ) {
320 for my $c ( $table->get_constraints ) {
321 if ( $c->type eq UNIQUE ) {
322 my %fields = map { $_, 1 } $c->fields;
323 if ( $fields{ $self->name } ) {
324 return 1;
ee2766f4 325 }
326 }
327 }
328 }
a14ab50e 329 return 0;
ee2766f4 330}
331
ec2ab48d 332sub is_valid {
333
334=pod
335
336=head2 is_valid
337
338Determine whether the field is valid or not.
339
340 my $ok = $field->is_valid;
341
342=cut
343
344 my $self = shift;
345 return $self->error('No name') unless $self->name;
346 return $self->error('No data type') unless $self->data_type;
347 return $self->error('No table object') unless $self->table;
348 return 1;
349}
350
3c5de62a 351=head2 name
352
353Get or set the field's name.
354
65dd38c0 355 my $name = $field->name('foo');
356
357The field object will also stringify to its name.
358
359 my $setter_name = "set_$field";
360
361Errors ("No field name") if you try to set a blank name.
3c5de62a 362
363=cut
364
a14ab50e 365has name => ( is => 'rw', isa => sub { throw( "No field name" ) unless $_[0] } );
366
367around name => sub {
368 my $orig = shift;
3c5de62a 369 my $self = shift;
43b9dc7a 370
a14ab50e 371 if ( my ($arg) = @_ ) {
372 if ( my $schema = $self->table ) {
65dd38c0 373 return $self->error( qq[Can't use field name "$arg": field exists] )
a14ab50e 374 if $schema->get_field( $arg );
43b9dc7a 375 }
43b9dc7a 376 }
377
a14ab50e 378 return ex2err($orig, $self, @_);
379};
3c5de62a 380
4809213f 381sub full_name {
382
383=head2 full_name
384
385Read only method to return the fields name with its table name pre-pended.
386e.g. "person.foo".
387
388=cut
389
390 my $self = shift;
391 return $self->table.".".$self->name;
392}
393
ec2ab48d 394=head2 order
3c5de62a 395
ec2ab48d 396Get or set the field's order.
3c5de62a 397
ec2ab48d 398 my $order = $field->order(3);
3c5de62a 399
400=cut
401
68d75205 402has order => ( is => 'rw', default => quote_sub(q{ 0 }) );
a14ab50e 403
404around order => sub {
405 my ( $orig, $self, $arg ) = @_;
3c5de62a 406
ec2ab48d 407 if ( defined $arg && $arg =~ /^\d+$/ ) {
a14ab50e 408 return $self->$orig($arg);
3c5de62a 409 }
410
a14ab50e 411 return $self->$orig;
412};
43b9dc7a 413
c1e3c768 414sub schema {
415
ea93df61 416=head2 schema
c1e3c768 417
418Shortcut to get the fields schema ($field->table->schema) or undef if it
419doesn't have one.
420
421 my $schema = $field->schema;
422
423=cut
424
425 my $self = shift;
426 if ( my $table = $self->table ) { return $table->schema || undef; }
427 return undef;
428}
429
43b9dc7a 430=head2 size
431
432Get or set the field's size. Accepts a string, array or arrayref of
433numbers and returns a string.
434
435 $field->size( 30 );
436 $field->size( [ 255 ] );
437 $size = $field->size( 10, 2 );
438 print $size; # prints "10,2"
439
440 $size = $field->size( '10, 2' );
441 print $size; # prints "10,2"
442
443=cut
444
a14ab50e 445has size => (
446 is => 'rw',
68d75205 447 default => quote_sub(q{ [0] }),
a14ab50e 448 coerce => sub {
449 my @sizes = grep { defined && m/^\d+(?:\.\d+)?$/ } @{parse_list_arg($_[0])};
450 @sizes ? \@sizes : [0];
451 },
452);
453
454around size => sub {
455 my $orig = shift;
43b9dc7a 456 my $self = shift;
ec2ab48d 457 my $numbers = parse_list_arg( @_ );
43b9dc7a 458
459 if ( @$numbers ) {
460 my @new;
461 for my $num ( @$numbers ) {
462 if ( defined $num && $num =~ m/^\d+(?:\.\d+)?$/ ) {
463 push @new, $num;
464 }
465 }
a14ab50e 466 $self->$orig(\@new) if @new; # only set if all OK
43b9dc7a 467 }
468
ea93df61 469 return wantarray
a14ab50e 470 ? @{ $self->$orig || [0] }
471 : join( ',', @{ $self->$orig || [0] } )
ec2ab48d 472 ;
a14ab50e 473};
43b9dc7a 474
475=head2 table
476
b9dc0b40 477Get or set the field's table object. As the table object stringifies this can
478also be used to get the table name.
43b9dc7a 479
480 my $table = $field->table;
b9dc0b40 481 print "Table name: $table";
43b9dc7a 482
483=cut
484
a5bfeba8 485has table => ( is => 'rw', isa => schema_obj('Table'), weak_ref => 1 );
43b9dc7a 486
a14ab50e 487around table => \&ex2err;
07d6e5f7 488
ea93df61 489=head2
07d6e5f7 490
491Returns the field exactly as the parser found it
492
493=cut
494
a14ab50e 495has parsed_field => ( is => 'rw' );
07d6e5f7 496
a14ab50e 497around parsed_field => sub {
498 my $orig = shift;
499 my $self = shift;
abf315bb 500
a14ab50e 501 return $self->$orig(@_) || $self;
502};
abf315bb 503
504=head2 equals
505
506Determines if this field is the same as another
507
508 my $isIdentical = $field1->equals( $field2 );
509
510=cut
511
a14ab50e 512around equals => sub {
513 my $orig = shift;
abf315bb 514 my $self = shift;
515 my $other = shift;
516 my $case_insensitive = shift;
ea93df61 517
a14ab50e 518 return 0 unless $self->$orig($other);
abf315bb 519 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
6b2dbb1a 520
521 # Comparing types: use sql_data_type if both are not 0. Else use string data_type
522 if ($self->sql_data_type && $other->sql_data_type) {
523 return 0 unless $self->sql_data_type == $other->sql_data_type
524 } else {
525 return 0 unless lc($self->data_type) eq lc($other->data_type)
526 }
527
abf315bb 528 return 0 unless $self->size eq $other->size;
f5fd433f 529
530 {
531 my $lhs = $self->default_value;
532 $lhs = \'NULL' unless defined $lhs;
533 my $lhs_is_ref = ! ! ref $lhs;
534
535 my $rhs = $other->default_value;
536 $rhs = \'NULL' unless defined $rhs;
537 my $rhs_is_ref = ! ! ref $rhs;
538
539 # If only one is a ref, fail. -- rjbs, 2008-12-02
540 return 0 if $lhs_is_ref xor $rhs_is_ref;
541
542 my $effective_lhs = $lhs_is_ref ? $$lhs : $lhs;
543 my $effective_rhs = $rhs_is_ref ? $$rhs : $rhs;
544
545 return 0 if $effective_lhs ne $effective_rhs;
546 }
547
abf315bb 548 return 0 unless $self->is_nullable eq $other->is_nullable;
afb07483 549# return 0 unless $self->is_unique eq $other->is_unique;
abf315bb 550 return 0 unless $self->is_primary_key eq $other->is_primary_key;
65e7a069 551# return 0 unless $self->is_foreign_key eq $other->is_foreign_key;
abf315bb 552 return 0 unless $self->is_auto_increment eq $other->is_auto_increment;
553# return 0 unless $self->comments eq $other->comments;
4598b71c 554 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 555 return 1;
a14ab50e 556};
abf315bb 557
a14ab50e 558# Must come after all 'has' declarations
559around new => \&ex2err;
560
3c5de62a 5611;
562
3c5de62a 563=pod
564
565=head1 AUTHOR
566
c3b0b535 567Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 568
569=cut