package Alzabo::Create::Column; use strict; use vars qw($VERSION); use Alzabo::Create; use Alzabo::Exceptions ( abbr => 'params_exception' ); use Params::Validate qw( :all ); Params::Validate::validation_options ( on_fail => sub { params_exception join '', @_ } ); use base qw(Alzabo::Column); $VERSION = 2.0; 1; sub new { my $proto = shift; my $class = ref $proto || $proto; my $self = bless {}, $class; $self->_init(@_); return $self; } sub _init { my $self = shift; my %p = validate( @_, { table => { isa => 'Alzabo::Table' }, name => { type => SCALAR }, null => { optional => 1 }, nullable => { optional => 1 }, type => { type => SCALAR, optional => 1 }, attributes => { type => ARRAYREF, default => [] }, default => { type => BOOLEAN, optional => 1 }, default_is_raw => { type => BOOLEAN, default => 0 }, sequenced => { optional => 1 }, length => { type => BOOLEAN, optional => 1 }, precision => { type => BOOLEAN, optional => 1 }, definition => { isa => 'Alzabo::Create::ColumnDefinition', optional => 1 }, comment => { type => BOOLEAN, default => '' }, } ); $self->set_table( $p{table} ); $self->set_name( $p{name} ); $self->{nullable} = $p{nullable} || $p{null} || 0; if ($p{definition}) { $self->set_definition( $p{definition} ); } else { $self->set_definition ( Alzabo::Create::ColumnDefinition->new ( owner => $self, type => $p{type}, ) ); } my %attr; tie %{ $self->{attributes} }, 'Tie::IxHash'; $self->set_attributes( @{ $p{attributes} } ); $self->set_sequenced( $p{sequenced} || 0 ); $self->set_default( $p{default} ) if exists $p{default}; $self->set_default_is_raw( $p{default_is_raw} ); # We always set length, since not giving a length at all may be an # error for some column types, unless we got a definition object, # in which case it should contain the length & precision. $self->set_length( length => $p{length}, precision => $p{precision} ) unless $p{definition}; $self->set_comment( $p{comment} ); } sub set_table { my $self = shift; validate_pos( @_, { isa => 'Alzabo::Create::Table' } ); $self->{table} = shift; } sub set_name { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $name = shift; params_exception "Column $name already exists in table" if $self->table->has_column($name); my $old_name = $self->{name}; $self->{name} = $name; eval { $self->table->schema->rules->validate_column_name($self); }; if ($@) { $self->{name} = $old_name; rethrow_exception($@); } $self->table->register_column_name_change( column => $self, old_name => $old_name ) if $old_name; } sub set_nullable { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $n = shift; params_exception "Invalid value for nullable attribute: $n" unless $n eq '1' || $n eq '0'; params_exception "Primary key column cannot be nullable" if $n eq '1' && $self->is_primary_key; $self->{nullable} = $n; } sub set_default { my $self = shift; validate_pos( @_, { type => BOOLEAN } ); $self->{default} = shift; } sub set_default_is_raw { my $self = shift; validate_pos( @_, { type => BOOLEAN } ); $self->{default_is_raw} = shift; } sub set_length { my $self = shift; $self->{definition}->set_length(@_); } sub set_attributes { my $self = shift; validate_pos( @_, ( { type => SCALAR } ) x @_ ); %{ $self->{attributes} } = (); foreach (@_) { $self->add_attribute($_); } } sub add_attribute { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $attr = shift; $attr =~ s/^\s+//; $attr =~ s/\s+$//; $self->table->schema->rules->validate_column_attribute( column => $self, attribute => $attr ); $self->{attributes}{$attr} = 1; } sub delete_attribute { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $attr = shift; params_exception "Column " . $self->name . " doesn't have attribute $attr" unless exists $self->{attributes}{$attr}; delete $self->{attributes}{$attr}; } sub alter { my $self = shift; $self->{definition}->alter(@_); # this will force them to go through the rules code again. # Attributes that don't work with the new type are silently # discarded. foreach ( $self->attributes ) { $self->delete_attribute($_); eval { $self->add_attribute($_) }; } } sub set_type { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $t = shift; $self->{definition}->set_type($t); # this will force them to go through the rules code again. # Attributes that don't work with the new type are silently # discarded. foreach ( $self->attributes ) { $self->delete_attribute($_); eval { $self->add_attribute($_) }; } if ( $self->length ) { eval { $self->set_length( length => $self->length, precision => $self->precision ) }; if ($@) { eval { $self->set_length( length => $self->length, precision => undef ) }; if ($@) { $self->set_length( length => undef, precision => undef ); } } } } sub set_sequenced { my $self = shift; validate_pos( @_, { type => SCALAR } ); my $s = shift; params_exception "Invalid value for sequenced attribute: $s" unless $s eq '1' || $s eq '0'; $self->table->schema->rules->validate_sequenced_attribute($self) if $s eq '1'; $self->{sequenced} = $s; } sub set_definition { my $self = shift; validate_pos( @_, { isa => 'Alzabo::Create::ColumnDefinition' } ); my $d = shift; $self->{definition} = $d; } sub set_comment { $_[0]->{comment} = defined $_[1] ? $_[1] : '' } sub save_current_name { my $self = shift; $self->{last_instantiated_name} = $self->name; } sub former_name { $_[0]->{last_instantiated_name} } __END__ =head1 NAME Alzabo::Create::Column - Column objects for use in schema creation =head1 SYNOPSIS use Alzabo::Create::Column; =head1 DESCRIPTION This object represents a column. It holds data specific to a column. Additional data is held in a L|Alzabo::Create::ColumnDefinition> object, which is used to allow two columns to share a type (which is good when two columns in different tables are related as it means that if the type of one is changed, the other is also.) =head1 INHERITS FROM C =for pod_merge merged =head1 METHODS =head2 new The constructor accepts the following parameters: =over 4 =item * table => C object =item * name => $name =item * nullable => 0 or 1 (optional) Defaults to false. =item * sequenced => 0 or 1 (optional) Defaults to false. =item * default => $default (optional) =item * default_is_raw => $boolean (optional) If "default_is_raw" is true, then it will not be quoted when passed to the DBMS in SQL statements. This should be used to allow a default which is a function, like C. =item * attributes => \@attributes (optional) =item * length => $length (optional) =item * precision => $precision (optional) One of either ... =item * type => $type ... or ... =item * definition => C object =item * comment => $comment An optional comment. =back It returns a new C object. Throws: L|Alzabo::Exceptions> =for pod_merge type =head2 alter This method allows you to change a column's type, length, and precision as a single operation. It should be instead of calling C followed by C. It takes the following parameters: =over 4 =item * type => $type =item * length => $length (optional) =item * precision => $precision (optional) =back Throws: L|Alzabo::Exceptions>, L|Alzabo::Exceptions> =head2 set_type ($type) Sets the column's type. Throws: L|Alzabo::Exceptions>, L|Alzabo::Exceptions> =head2 set_table (C object) Sets the L|Alzabo::Create::Table> object in which this column is located. Throws: L|Alzabo::Exceptions> =for pod_merge name =head2 set_name ($name) Sets the column's name (a string). Throws: L|Alzabo::Exceptions>, L|Alzabo::Exceptions> =for pod_merge nullable =head2 set_nullable (0 or 1) Sets the nullability of the column (this determines whether nulls are allowed in the column or not). Must be 0 or 1. Throws: L|Alzabo::Exceptions> =for pod_merge attributes =for pod_merge has_attribute =head2 set_attributes (@attributes) Sets the column's attributes. These are strings describing the column (for example, valid attributes in MySQL are "PRIMARY KEY" or "AUTO_INCREMENT"). Throws: L|Alzabo::Exceptions> =head2 add_attribute ($attribute) Add an attribute to the column's list of attributes. Throws: L|Alzabo::Exceptions> =head2 delete_attribute ($attribute) Delete the given attribute from the column's list of attributes. Throws: Throws: L|Alzabo::Exceptions>, L|Alzabo::Exceptions> =for pod_merge default =head2 set_default ($default) Sets the column's default value. =for pod_merge length =for pod_merge precision =head2 set_length This method takes the following parameters: =over 4 =item * length => $length =item * precision => $precision (optional) =back This method sets the column's length and precision. The precision parameter is optional (though some column types may require it if the length is set). Throws: L|Alzabo::Exceptions> =for pod_merge sequenced =head2 set_sequenced (0 or 1) Sets the value of the column's sequenced attribute. Throws: L|Alzabo::Exceptions>, L|Alzabo::Exceptions> =for pod_merge is_primary_key =for pod_merge is_numeric =for pod_merge is_character =for pod_merge is_blob =for pod_merge definition =head2 set_definition (C object) Sets the L|Alzabo::Create::ColumnDefinition> object which holds this column's type information. =head2 former_name If the column's name has been changed since the last time the schema was instantiated, this method returns the column's previous name. =for pod_merge comment =head2 set_comment ($comment) Set the comment for the column object. =cut