package SQL::Translator::Schema::Field;
-# ----------------------------------------------------------------------
-# $Id: Field.pm,v 1.12 2003-08-12 22:03:59 kycl4rk Exp $
-# ----------------------------------------------------------------------
-# Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-# 02111-1307 USA
-# -------------------------------------------------------------------
-
=pod
=head1 NAME
use SQL::Translator::Schema::Field;
my $field = SQL::Translator::Schema::Field->new(
- name => 'foo',
- sql => 'select * from foo',
+ name => 'foo',
+ table => $table,
);
=head1 DESCRIPTION
=cut
use strict;
-use Class::Base;
+use warnings;
use SQL::Translator::Schema::Constants;
use SQL::Translator::Utils 'parse_list_arg';
-use base 'Class::Base';
+use base 'SQL::Translator::Schema::Object';
+
use vars qw($VERSION $TABLE_COUNT $VIEW_COUNT);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/;
+$VERSION = '1.59';
+
+# Stringify to our name, being careful not to pass any args through so we don't
+# accidentally set it to undef. We also have to tweak bool so the object is
+# still true when it doesn't have a name (which shouldn't happen!).
+use overload
+ '""' => sub { shift->name },
+ 'bool' => sub { $_[0]->name || $_[0] },
+ fallback => 1,
+;
+
+use DBI qw(:sql_types);
+
+# Mapping from string to sql contstant
+our %type_mapping = (
+ integer => SQL_INTEGER,
+ int => SQL_INTEGER,
+
+ smallint => SQL_SMALLINT,
+ bigint => 9999, # DBI doesn't export a constatn for this. Le suck
+
+ double => SQL_DOUBLE,
+
+ decimal => SQL_DECIMAL,
+ numeric => SQL_NUMERIC,
+ dec => SQL_DECIMAL,
+
+ bit => SQL_BIT,
+
+ date => SQL_DATE,
+ datetime => SQL_DATETIME,
+ timestamp => SQL_TIMESTAMP,
+ time => SQL_TIME,
-# ----------------------------------------------------------------------
-sub init {
+ char => SQL_CHAR,
+ varchar => SQL_VARCHAR,
+ binary => SQL_BINARY,
+ varbinary => SQL_VARBINARY,
+ tinyblob => SQL_BLOB,
+ blob => SQL_BLOB,
+ text => SQL_LONGVARCHAR
+
+);
+
+__PACKAGE__->_attributes( qw/
+ table name data_type size is_primary_key is_nullable
+ is_auto_increment default_value comments is_foreign_key
+ is_unique order sql_data_type
+/);
=pod
Object constructor.
- my $schema = SQL::Translator::Schema::Field->new;
+ my $field = SQL::Translator::Schema::Field->new(
+ name => 'foo',
+ table => $table,
+ );
=cut
- my ( $self, $config ) = @_;
-
- for my $arg (
- qw[
- table name data_type size is_primary_key is_nullable
- is_auto_increment default_value comments
- ]
- ) {
- next unless defined $config->{ $arg };
- defined $self->$arg( $config->{ $arg } ) or return;
- }
-
- return $self;
-}
-
-# ----------------------------------------------------------------------
sub comments {
=pod
=head2 comments
-Get or set the comments on a field. May be called several times to
+Get or set the comments on a field. May be called several times to
set and it will accumulate the comments. Called in an array context,
returns each comment individually; called in a scalar context, returns
all the comments joined on newlines.
}
if ( @{ $self->{'comments'} || [] } ) {
- return wantarray
+ return wantarray
? @{ $self->{'comments'} || [] }
: join( "\n", @{ $self->{'comments'} || [] } );
}
}
-# ----------------------------------------------------------------------
sub data_type {
=pod
=cut
my $self = shift;
- $self->{'data_type'} = shift if @_;
+ if (@_) {
+ $self->{'data_type'} = $_[0];
+ $self->{'sql_data_type'} = $type_mapping{lc $_[0]} || SQL_UNKNOWN_TYPE unless exists $self->{sql_data_type};
+ }
return $self->{'data_type'} || '';
}
-# ----------------------------------------------------------------------
+sub sql_data_type {
+
+=head2 sql_data_type
+
+Constant from DBI package representing this data type. See L<DBI/DBI Constants>
+for more details.
+
+=cut
+
+ my $self = shift;
+ $self->{sql_data_type} = shift if @_;
+ return $self->{sql_data_type} || 0;
+
+}
+
sub default_value {
=pod
=head2 default_value
Get or set the field's default value. Will return undef if not defined
-and could return the empty string (it's a valid default value), so don't
+and could return the empty string (it's a valid default value), so don't
assume an error like other methods.
my $default = $field->default_value('foo');
=cut
- my ( $self, $arg ) = @_;
- $self->{'default_value'} = $arg if defined $arg;
+ my $self = shift;
+ $self->{'default_value'} = shift if @_;
return $self->{'default_value'};
}
-# ----------------------------------------------------------------------
-sub extra {
-
=pod
=head2 extra
=cut
- my $self = shift;
- my $args = ref $_[0] eq 'HASH' ? shift : { @_ };
-
- while ( my ( $key, $value ) = each %$args ) {
- $self->{'extra'}{ $key } = $value;
- }
-
- return %{ $self->{'extra'} || {} };
-}
-
-# ----------------------------------------------------------------------
sub foreign_key_reference {
=pod
return $self->{'foreign_key_reference'};
}
-# ----------------------------------------------------------------------
sub is_auto_increment {
=pod
Get or set the field's C<is_auto_increment> attribute.
- my $is_pk = $field->is_auto_increment(1);
+ my $is_auto = $field->is_auto_increment(1);
=cut
unless ( defined $self->{'is_auto_increment'} ) {
if ( my $table = $self->table ) {
if ( my $schema = $table->schema ) {
- if (
+ if (
$schema->database eq 'PostgreSQL' &&
$self->data_type eq 'serial'
) {
return $self->{'is_auto_increment'} || 0;
}
-# ----------------------------------------------------------------------
sub is_foreign_key {
=pod
return $self->{'is_foreign_key'} || 0;
}
-# ----------------------------------------------------------------------
sub is_nullable {
=pod
=head2 is_nullable
-Get or set the whether the field can be null. If not defined, then
+Get or set whether the field can be null. If not defined, then
returns "1" (assumes the field can be null). The argument is evaluated
by Perl for True or False, so the following are eqivalent:
$self->{'is_nullable'} = $arg ? 1 : 0;
}
- if (
- defined $self->{'is_nullable'} &&
+ if (
+ defined $self->{'is_nullable'} &&
$self->{'is_nullable'} == 1 &&
$self->is_primary_key
) {
return defined $self->{'is_nullable'} ? $self->{'is_nullable'} : 1;
}
-# ----------------------------------------------------------------------
sub is_primary_key {
=pod
return $self->{'is_primary_key'} || 0;
}
-# ----------------------------------------------------------------------
sub is_unique {
=pod
=cut
my $self = shift;
-
+
unless ( defined $self->{'is_unique'} ) {
if ( my $table = $self->table ) {
for my $c ( $table->get_constraints ) {
return $self->{'is_unique'} || 0;
}
-# ----------------------------------------------------------------------
sub is_valid {
=pod
return 1;
}
-# ----------------------------------------------------------------------
sub name {
=pod
Get or set the field's name.
- my $name = $field->name('foo');
+ my $name = $field->name('foo');
+
+The field object will also stringify to its name.
+
+ my $setter_name = "set_$field";
+
+Errors ("No field name") if you try to set a blank name.
=cut
my $self = shift;
- if ( my $arg = shift ) {
+ if ( @_ ) {
+ my $arg = shift || return $self->error( "No field name" );
if ( my $table = $self->table ) {
- return $self->error( qq[Can't use field name "$arg": table exists] )
+ return $self->error( qq[Can't use field name "$arg": field exists] )
if $table->get_field( $arg );
}
return $self->{'name'} || '';
}
-# ----------------------------------------------------------------------
+sub full_name {
+
+=head2 full_name
+
+Read only method to return the fields name with its table name pre-pended.
+e.g. "person.foo".
+
+=cut
+
+ my $self = shift;
+ return $self->table.".".$self->name;
+}
+
sub order {
=pod
return $self->{'order'} || 0;
}
-# ----------------------------------------------------------------------
+sub schema {
+
+=head2 schema
+
+Shortcut to get the fields schema ($field->table->schema) or undef if it
+doesn't have one.
+
+ my $schema = $field->schema;
+
+=cut
+
+ my $self = shift;
+ if ( my $table = $self->table ) { return $table->schema || undef; }
+ return undef;
+}
+
sub size {
=pod
$self->{'size'} = \@new if @new; # only set if all OK
}
- return wantarray
+ return wantarray
? @{ $self->{'size'} || [0] }
: join( ',', @{ $self->{'size'} || [0] } )
;
}
-# ----------------------------------------------------------------------
sub table {
=pod
=head2 table
-Get or set the field's table object.
+Get or set the field's table object. As the table object stringifies this can
+also be used to get the table name.
my $table = $field->table;
+ print "Table name: $table";
=cut
return $self->{'table'};
}
-# ----------------------------------------------------------------------
+sub parsed_field {
+
+=head2
+
+Returns the field exactly as the parser found it
+
+=cut
+
+ my $self = shift;
+
+ if (@_) {
+ my $value = shift;
+ $self->{parsed_field} = $value;
+ return $value || $self;
+ }
+ return $self->{parsed_field} || $self;
+}
+
+sub equals {
+
+=pod
+
+=head2 equals
+
+Determines if this field is the same as another
+
+ my $isIdentical = $field1->equals( $field2 );
+
+=cut
+
+ my $self = shift;
+ my $other = shift;
+ my $case_insensitive = shift;
+
+ return 0 unless $self->SUPER::equals($other);
+ return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
+
+ # Comparing types: use sql_data_type if both are not 0. Else use string data_type
+ if ($self->sql_data_type && $other->sql_data_type) {
+ return 0 unless $self->sql_data_type == $other->sql_data_type
+ } else {
+ return 0 unless lc($self->data_type) eq lc($other->data_type)
+ }
+
+ return 0 unless $self->size eq $other->size;
+
+ {
+ my $lhs = $self->default_value;
+ $lhs = \'NULL' unless defined $lhs;
+ my $lhs_is_ref = ! ! ref $lhs;
+
+ my $rhs = $other->default_value;
+ $rhs = \'NULL' unless defined $rhs;
+ my $rhs_is_ref = ! ! ref $rhs;
+
+ # If only one is a ref, fail. -- rjbs, 2008-12-02
+ return 0 if $lhs_is_ref xor $rhs_is_ref;
+
+ my $effective_lhs = $lhs_is_ref ? $$lhs : $lhs;
+ my $effective_rhs = $rhs_is_ref ? $$rhs : $rhs;
+
+ return 0 if $effective_lhs ne $effective_rhs;
+ }
+
+ return 0 unless $self->is_nullable eq $other->is_nullable;
+# return 0 unless $self->is_unique eq $other->is_unique;
+ return 0 unless $self->is_primary_key eq $other->is_primary_key;
+# return 0 unless $self->is_foreign_key eq $other->is_foreign_key;
+ return 0 unless $self->is_auto_increment eq $other->is_auto_increment;
+# return 0 unless $self->comments eq $other->comments;
+ return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
+ return 1;
+}
+
sub DESTROY {
#
# Destroy cyclical references.
1;
-# ----------------------------------------------------------------------
-
=pod
=head1 AUTHOR
-Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
+Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
=cut