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