view release on metacpan or search on metacpan
lib/Alzabo/BackCompat.pm view on Meta::CPAN
[ 0.71, 0.73,
\&convert_pk_to_array,
],
[ 0.79, $Alzabo::VERSION,
\&add_table_attributes,
],
);
sub update_schema
{
my %p = validate( @_, { name => { type => SCALAR },
version => { type => SCALAR },
} );
my @cb;
foreach my $c (@compat)
{
return
if ( ( $p{version} >= $c->[0] &&
$p{version} <= $c->[1] ) &&
lib/Alzabo/ChangeTracker.pm view on Meta::CPAN
++$STACK;
my $self = $STACK;
bless \$self, $class;
}
sub add
{
my $self = shift;
validate_pos( @_, { type => CODEREF } );
push @CHANGES, shift;
}
sub backout
{
my $self = shift;
$_->() foreach @CHANGES;
lib/Alzabo/Column.pm view on Meta::CPAN
}
sub attributes
{
return keys %{ $_[0]->{attributes} };
}
sub has_attribute
{
my $self = shift;
my %p = validate( @_, { attribute => { type => SCALAR },
case_sensitive => { type => SCALAR,
default => 0 } } );
if ( $p{case_sensitive} )
{
return exists $self->{attributes}{ $p{attribute} };
}
else
{
return 1 if grep { lc $p{attribute} eq lc $_ } keys %{ $self->{attributes} };
lib/Alzabo/Create/Column.pm view on Meta::CPAN
$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,
lib/Alzabo/Create/Column.pm view on Meta::CPAN
$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
{
lib/Alzabo/Create/Column.pm view on Meta::CPAN
{
$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($_);
lib/Alzabo/Create/Column.pm view on Meta::CPAN
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;
lib/Alzabo/Create/ColumnDefinition.pm view on Meta::CPAN
$self->_init(@_);
return $self;
}
sub _init
{
my $self = shift;
validate( @_, { owner => { isa => 'Alzabo::Create::Column' },
type => { type => SCALAR },
length => { type => UNDEF | SCALAR,
optional => 1 },
precision => { type => UNDEF | SCALAR,
optional => 1 },
} );
my %p = @_;
$p{type} =
$p{owner}->table->schema->rules->validate_column_type( $p{type}, $p{owner}->table );
foreach ( qw( owner type ) )
{
$self->{$_} = $p{$_} if exists $p{$_};
}
}
sub alter
{
my $self = shift;
validate( @_, { type => { type => SCALAR },
length => { type => UNDEF | SCALAR,
optional => 1 },
precision => { type => UNDEF | SCALAR,
optional => 1 },
} );
my %p = @_;
my $old_type = $self->{type};
my $old_length = $self->{length};
my $old_precision = $self->{precision};
$self->{length} = $p{length} if exists $p{length};
$self->{precision} = $p{precision} if exists $p{precision};
eval
{
$self->{type} =
$self->owner->table->schema->rules->validate_column_type($p{type}, $self->owner->table);
$self->owner->table->schema->rules->validate_primary_key($self->owner)
if $self->owner->is_primary_key;
$self->owner->table->schema->rules->validate_column_length($self->owner);
};
if ($@)
{
$self->{type} = $old_type;
$self->{length} = $old_length;
$self->{precision} = $old_precision;
rethrow_exception($@);
}
}
sub set_type
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $type = shift;
my $old_type = $self->{type};
eval
{
$self->{type} =
$self->owner->table->schema->rules->validate_column_type($type, $self->owner->table);
$self->owner->table->schema->rules->validate_primary_key($self->owner)
if eval { $self->owner->is_primary_key };
# eval ^^ cause if we're creating the column its not in the table yet
};
if ($@)
{
$self->{type} = $old_type;
rethrow_exception($@);
}
}
sub set_length
{
my $self = shift;
validate( @_, { length => { type => UNDEF | SCALAR },
precision => { type => UNDEF | SCALAR,
optional => 1 } } );
my %p = @_;
my $old_length = $self->{length};
my $old_precision = $self->{precision};
$self->{length} = $p{length};
$self->{precision} = $p{precision} if exists $p{precision};
eval
{
$self->owner->table->schema->rules->validate_column_length($self->owner);
};
if ($@)
{
$self->{length} = $old_length;
$self->{precision} = $old_precision;
rethrow_exception($@);
}
}
lib/Alzabo/Create/ForeignKey.pm view on Meta::CPAN
$VERSION = 2.0;
1;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
validate( @_, { columns_from => { type => ARRAYREF | OBJECT },
columns_to => { type => ARRAYREF | OBJECT },
cardinality => { type => ARRAYREF },
from_is_dependent => { type => SCALAR },
to_is_dependent => { type => SCALAR },
comment => { type => UNDEF | SCALAR,
default => '' },
} );
my %p = @_;
my $self = bless {}, $class;
lib/Alzabo/Create/ForeignKey.pm view on Meta::CPAN
$self->set_comment( $p{comment} );
return $self;
}
sub set_columns_from
{
my $self = shift;
my $c = Alzabo::Utils::is_arrayref( $_[0] ) ? shift : [ shift ];
validate_pos( @$c, ( { isa => 'Alzabo::Create::Column' } ) x @$c );
if ( exists $self->{columns_to} )
{
params_exception
"The number of columns in each part of the relationship must be the same"
unless @{ $self->{columns_to} } == @$c;
}
$self->{columns_from} = $c;
}
sub set_columns_to
{
my $self = shift;
my $c = Alzabo::Utils::is_arrayref( $_[0] ) ? shift : [ shift ];
validate_pos( @$c, ( { isa => 'Alzabo::Create::Column' } ) x @$c );
if ( exists $self->{columns_from} )
{
params_exception
"The number of columns in each part of the relationship must be the same"
unless @{ $self->{columns_from} } == @$c;
}
$self->{columns_to} = $c;
}
lib/Alzabo/Create/Index.pm view on Meta::CPAN
$VERSION = 2.0;
1;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
columns => { type => ARRAYREF },
unique => { type => BOOLEAN, default => 0 },
fulltext => { type => BOOLEAN, default => 0 },
function => { type => UNDEF | SCALAR, default => undef },
} );
my %p = @_;
my $self = bless {}, $class;
$self->{table} = $p{table};
lib/Alzabo/Create/Index.pm view on Meta::CPAN
$self->{function} = $p{function};
$self->{columns} = Tie::IxHash->new;
foreach my $c (@{ $p{columns} })
{
my %p = Alzabo::Utils::safe_isa( $c, 'Alzabo::Column' ) ? ( column => $c ) : %$c;
$self->add_column(%p);
}
$self->table->schema->rules->validate_index($self);
return $self;
}
sub add_column
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
prefix => { type => SCALAR,
optional => 1 } } );
my %p = @_;
my $new_name = $p{column}->name;
params_exception "Column $new_name already exists in index."
if $self->{columns}->EXISTS($new_name);
$self->{columns}->STORE( $new_name, \%p );
eval { $self->table->schema->rules->validate_index($self); };
if ($@)
{
$self->{columns}->DELETE($new_name);
rethrow_exception($@);
}
}
sub delete_column
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Column' } );
my $c = shift;
params_exception "Column " . $c->name . " is not part of index."
unless $self->{columns}->EXISTS( $c->name );
$self->{columns}->DELETE( $c->name );
}
sub set_prefix
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
prefix => { type => SCALAR } } );
my %p = @_;
params_exception "Column " . $p{column}->name . " is not part of index."
unless $self->{columns}->EXISTS( $p{column}->name );
my $col = $self->{columns}->FETCH( $p{column}->name );
my $old_val = delete $col->{prefix};
$col->{prefix} = $p{prefix};
eval { $self->table->schema->rules->validate_index($self); };
if ($@)
{
if ($old_val)
{
$col->{prefix} = $old_val;
}
else
{
delete $col->{prefix};
}
rethrow_exception($@);
}
}
sub set_unique
{
my $self = shift;
validate_pos( @_, 1 );
$self->{unique} = shift;
}
sub set_fulltext
{
my $self = shift;
validate_pos( @_, 1 );
my $old_val = $self->{fulltext};
$self->{fulltext} = shift;
eval { $self->table->schema->rules->validate_index($self); };
if ($@)
{
$self->{fulltext} = $old_val;
rethrow_exception($@);
}
}
sub register_column_name_change
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
old_name => { type => SCALAR } } );
my %p = @_;
return unless $self->{columns}->EXISTS( $p{old_name} );
my $new_name = $p{column}->name;
my $index = $self->{columns}->Indices( $p{old_name} );
my $val = $self->{columns}->Values($index);
$val->{column} = $p{column};
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
$VERSION = 2.0;
1;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
validate( @_, { rdbms => { type => SCALAR },
name => { type => SCALAR },
no_cache => { type => SCALAR, default => 0 },
} );
my %p = @_;
my $self = bless {}, $class;
params_exception "Alzabo does not support the '$p{rdbms}' RDBMS"
unless ( ( grep { $p{rdbms} eq $_ } Alzabo::Driver->available ) &&
( grep { $p{rdbms} eq $_ } Alzabo::RDBMSRules->available ) );
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
$self->{original} = Storable::dclone($self);
$self->{driver} = $driver;
delete $self->{original}{original};
return $self;
}
sub set_name
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $name = shift;
return if defined $self->{name} && $name eq $self->{name};
my $old_name = $self->{name};
$self->{name} = $name;
eval { $self->rules->validate_schema_name($self); };
if ($@)
{
$self->{name} = $old_name;
rethrow_exception($@);
}
# Gotta clean up old files or we have a mess!
$self->delete( name => $old_name ) if $old_name;
$self->set_instantiated(0);
undef $self->{original};
}
sub set_instantiated
{
my $self = shift;
validate_pos( @_, 1 );
$self->{instantiated} = shift;
}
sub make_table
{
my $self = shift;
my %p = @_;
my %p2;
foreach ( qw( before after ) )
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
%p ),
%p2 );
return $self->table( $p{name} );
}
sub add_table
{
my $self = shift;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
before => { optional => 1 },
after => { optional => 1 } } );
my %p = @_;
my $table = $p{table};
params_exception "Table " . $table->name . " already exists in schema"
if $self->{tables}->EXISTS( $table->name );
$self->{tables}->STORE( $table->name, $table );
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
table => $table );
last;
}
}
}
sub delete_table
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Table' } );
my $table = shift;
params_exception "Table " . $table->name ." doesn't exist in schema"
unless $self->{tables}->EXISTS( $table->name );
foreach my $fk ($table->all_foreign_keys)
{
foreach my $other_fk ( $fk->table_to->foreign_keys_by_table($table) )
{
$fk->table_to->delete_foreign_key($other_fk);
}
}
$self->{tables}->DELETE( $table->name );
}
sub move_table
{
my $self = shift;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
before => { isa => 'Alzabo::Create::Table',
optional => 1 },
after => { isa => 'Alzabo::Create::Table',
optional => 1 } } );
my %p = @_;
if ( exists $p{before} && exists $p{after} )
{
params_exception
"move_table method cannot be called with both 'before' and 'after' parameters";
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
$index = $self->{tables}->Indices( $p{after}->name ) + 1;
}
$self->{tables}->Splice( $index, 0, $p{table}->name => $p{table} );
}
sub register_table_name_change
{
my $self = shift;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
old_name => { type => SCALAR } } );
my %p = @_;
params_exception "Table $p{old_name} doesn't exist in schema"
unless $self->{tables}->EXISTS( $p{old_name} );
my $index = $self->{tables}->Indices( $p{old_name} );
$self->{tables}->Replace( $index, $p{table}, $p{table}->name );
}
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
# ColumnDefinition object.
# This is called when a relationship is created and the columns aren't
# specified. This means that changes to the column in one table are
# automatically reflected in the other table, which is generally a
# good thing.
sub _add_foreign_key_column
{
my $self = shift;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
column => { isa => 'Alzabo::Create::Column' } } );
my %p = @_;
my $tracker = Alzabo::ChangeTracker->new;
# Note: This code _does_ explicitly want to compare the string
# representation of the $p{column}->definition reference.
my $new_col;
if ( eval { $p{table}->column( $p{column}->name ) } &&
( $p{column}->definition ne $p{table}->column( $p{column}->name )->definition ) )
lib/Alzabo/Create/Schema.pm view on Meta::CPAN
close $fh
or system_exception "Unable to close $runtime_save_name: $!";
$self->_save_to_cache;
}
sub clone
{
my $self = shift;
validate( @_, { name => { type => SCALAR } } );
my %p = @_;
my $driver = delete $self->{driver};
my $clone = Storable::dclone($self);
$self->{driver} = $driver;
$clone->{name} = $p{name};
$clone->{driver} = Alzabo::Driver->new( rdbms => $self->{driver}->driver_id,
schema => $clone );
$clone->rules->validate_schema_name($clone);
$clone->{original}{name} = $p{name} if $p{name};
$clone->set_instantiated(0);
return $clone;
}
sub runtime_clone
{
my $self = shift;
lib/Alzabo/Create/Table.pm view on Meta::CPAN
$VERSION = 2.0;
1;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
validate( @_, { schema => { isa => 'Alzabo::Create::Schema' },
name => { type => SCALAR },
attributes => { type => ARRAYREF,
optional => 1 },
comment => { type => UNDEF | SCALAR,
default => '' },
} );
my %p = @_;
my $self = bless {}, $class;
lib/Alzabo/Create/Table.pm view on Meta::CPAN
# Setting this prevents run time type errors.
$self->{fk} = {};
return $self;
}
sub set_name
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $name = shift;
params_exception "Table $name already exists in schema"
if $self->schema->has_table($name);
my @i;
if ($self->{indexes})
{
@i = $self->indexes;
$self->delete_index($_) foreach @i;
}
my $old_name = $self->{name};
$self->{name} = $name;
eval
{
$self->schema->rules->validate_table_name($self);
};
$self->add_index($_) foreach @i;
if ($@)
{
$self->{name} = $old_name;
rethrow_exception($@);
}
lib/Alzabo/Create/Table.pm view on Meta::CPAN
my $col = $self->column( $p{name} );
$self->add_primary_key($col) if $is_pk;
return $col;
}
sub add_column
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
before => { optional => 1 },
after => { optional => 1 } } );
my %p = @_;
my $col = $p{column};
params_exception "Column " . $col->name . " already exists in " . $self->name
if $self->{columns}->EXISTS( $col->name );
$col->set_table($self) unless $col->table eq $self;
lib/Alzabo/Create/Table.pm view on Meta::CPAN
column => $col );
last;
}
}
}
sub delete_column
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Column' } );
my $col = shift;
params_exception"Column $col doesn't exist in $self->{name}"
unless $self->{columns}->EXISTS( $col->name );
$self->delete_primary_key($col) if $col->is_primary_key;
foreach my $fk ($self->foreign_keys_by_column($col))
{
$self->delete_foreign_key($fk);
lib/Alzabo/Create/Table.pm view on Meta::CPAN
$self->delete_index($i) if grep { $_ eq $col } $i->columns;
}
$self->{columns}->DELETE( $col->name );
}
sub move_column
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
before => { isa => 'Alzabo::Create::Column',
optional => 1 },
after => { isa => 'Alzabo::Create::Column',
optional => 1 } } );
my %p = @_;
if ( exists $p{before} && exists $p{after} )
{
params_exception
"move_column method cannot be called with both 'before' and 'after' parameters";
lib/Alzabo/Create/Table.pm view on Meta::CPAN
$self->{columns}->Splice( $index, 0, $p{column}->name => $p{column} );
$self->{pk} = [ $self->{columns}->Indices( map { $_->name } @pk ) ];
}
sub add_primary_key
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Column' } );
my $col = shift;
my $name = $col->name;
params_exception "Column $name doesn't exist in $self->{name}"
unless $self->{columns}->EXISTS($name);
params_exception "Column $name is already a primary key"
if $col->is_primary_key;
$self->schema->rules->validate_primary_key($col);
$col->set_nullable(0);
my $idx = $self->{columns}->Indices($name);
push @{ $self->{pk} }, $idx;
}
sub delete_primary_key
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Column' } );
my $col = shift;
my $name = $col->name;
params_exception "Column $name doesn't exist in $self->{name}"
unless $self->{columns}->EXISTS($name);
params_exception "Column $name is not a primary key"
unless $col->is_primary_key;
my $idx = $self->{columns}->Indices($name);
lib/Alzabo/Create/Table.pm view on Meta::CPAN
{
my $self = shift;
$self->add_foreign_key( Alzabo::Create::ForeignKey->new( @_ ) );
}
sub add_foreign_key
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::ForeignKey' } );
my $fk = shift;
foreach my $c ( $fk->columns_from )
{
push @{ $self->{fk}{ $fk->table_to->name }{ $c->name } }, $fk;
}
if ( ( $fk->is_one_to_one || $fk->is_one_to_many )
&& !
( $self->primary_key_size == grep { $_->is_primary_key } $fk->columns_from )
lib/Alzabo/Create/Table.pm view on Meta::CPAN
# method is somewhat broken)
$self->delete_index($i) if $self->has_index( $i->id );
$self->add_index($i);
}
}
sub delete_foreign_key
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::ForeignKey' } );
my $fk = shift;
foreach my $c ( $fk->columns_from )
{
params_exception "Column " . $c->name . " doesn't exist in $self->{name}"
unless $self->{columns}->EXISTS( $c->name );
}
params_exception
"No foreign keys to " . $fk->table_to->name . " exist in $self->{name}"
lib/Alzabo/Create/Table.pm view on Meta::CPAN
my Alzabo::Table $self = shift;
$self->add_index( Alzabo::Create::Index->new( table => $self,
@_ ) );
}
sub add_index
{
my Alzabo::Table $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Index' } );
my $i = shift;
my $id = $i->id;
params_exception "Index already exists (id $id)."
if $self->{indexes}->EXISTS($id);
$self->{indexes}->STORE( $id, $i );
return $i;
}
sub delete_index
{
my Alzabo::Table $self = shift;
validate_pos( @_, { isa => 'Alzabo::Create::Index' } );
my $i = shift;
params_exception "Index does not exist."
unless $self->{indexes}->EXISTS( $i->id );
$self->{indexes}->DELETE( $i->id );
}
sub register_table_name_change
{
my $self = shift;
validate( @_, { table => { isa => 'Alzabo::Create::Table' },
old_name => { type => SCALAR } } );
my %p = @_;
$self->{fk}{ $p{table}->name } = delete $self->{fk}{ $p{old_name} }
if exists $self->{fk}{ $p{old_name} };
}
sub register_column_name_change
{
my $self = shift;
validate( @_, { column => { isa => 'Alzabo::Create::Column' },
old_name => { type => SCALAR } } );
my %p = @_;
my $new_name = $p{column}->name;
my $index = $self->{columns}->Indices( $p{old_name} );
$self->{columns}->Replace( $index, $p{column}, $new_name );
foreach my $t ( keys %{ $self->{fk} } )
{
$self->{fk}{$t}{$new_name} = delete $self->{fk}{$t}{ $p{old_name} }
lib/Alzabo/Create/Table.pm view on Meta::CPAN
{
$i->register_column_name_change(%p);
$self->add_index($i);
}
}
sub set_attributes
{
my $self = shift;
validate_pos( @_, ( { type => SCALAR } ) x @_ );
%{ $self->{attributes} } = ();
foreach ( grep { defined && length } @_ )
{
$self->add_attribute($_);
}
}
sub add_attribute
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $attr = shift;
$attr =~ s/^\s+//;
$attr =~ s/\s+$//;
$self->schema->rules->validate_table_attribute( table => $self,
attribute => $attr );
$self->{attributes}{$attr} = 1;
}
sub delete_attribute
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $attr = shift;
params_exception "Table " . $self->name . " doesn't have attribute $attr"
unless exists $self->{attributes}{$attr};
delete $self->{attributes}{$attr};
}
sub set_comment { $_[0]->{comment} = defined $_[1] ? $_[1] : '' }
lib/Alzabo/Design.pod view on Meta::CPAN
creating new values for sequenced columns.
=item * C<Alzabo::SQLMaker>
These objects handle the generation of all SQL for runtime operations.
The subclasses are used to implement functionality that varies between
RDBMS's, such as outer joins.
=item * C<Alzabo::RDBMSRules>
These objects perform several funtions. First, they validate things
such as schema or table names, column type and length, etc.
Second they are used to generate SQL for creating and updating the
database and its tables.
And finally, they also handle the reverse engineering of an existing
database.
=item * C<Alzabo::Runtime::Row> and C<Alzabo::Runtime::RowState::*>
lib/Alzabo/Driver.pm view on Meta::CPAN
package Alzabo::Driver;
use strict;
use vars qw($VERSION);
use Alzabo::Exceptions;
use Class::Factory::Util;
use DBI;
use Params::Validate qw( validate validate_pos UNDEF SCALAR ARRAYREF );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = 2.0;
1;
sub new
{
shift;
my %p = @_;
lib/Alzabo/Driver.pm view on Meta::CPAN
use constant _PREPARE_AND_EXECUTE_SPEC => { sql => { type => SCALAR },
bind => { type => UNDEF | SCALAR | ARRAYREF,
optional => 1 },
};
sub _prepare_and_execute
{
my $self = shift;
validate( @_, _PREPARE_AND_EXECUTE_SPEC );
my %p = @_;
Alzabo::Exception::Driver->throw( error => "Attempt to access the database without database handle. Was ->connect called?" )
unless $self->{dbh};
my @bind = exists $p{bind} ? ( ref $p{bind} ? @{ $p{bind} } : $p{bind} ) : ();
my $sth;
eval
{
lib/Alzabo/Driver.pm view on Meta::CPAN
$self->{dbh}->disconnect if $self->{dbh};
delete $self->{dbh};
}
sub handle
{
my $self = shift;
if (@_)
{
validate_pos( @_, { isa => 'DBI::db' } );
$self->{dbh} = shift;
}
return $self->{dbh};
}
sub rdbms_version
{
shift()->_virtual;
}
lib/Alzabo/Driver.pm view on Meta::CPAN
package Alzabo::DriverStatement;
use strict;
use vars qw($VERSION);
use Alzabo::Exceptions;
use DBI;
use Params::Validate qw( validate UNDEF SCALAR ARRAYREF );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = '0.1';
sub new
{
my $self = shift->new_no_execute(@_);
$self->execute;
lib/Alzabo/Driver.pm view on Meta::CPAN
optional => 1 },
limit => { type => UNDEF | ARRAYREF,
optional => 1 },
};
sub new_no_execute
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p = validate( @_, NEW_NO_EXECUTE_SPEC );
my $self = bless {}, $class;
$self->{limit} = $p{limit} ? $p{limit}[0] : 0;
$self->{offset} = $p{limit} && $p{limit}[1] ? $p{limit}[1] : 0;
$self->{rows_fetched} = 0;
eval
{
$self->{sth} = $p{dbh}->prepare( $p{sql} );
lib/Alzabo/Driver/MySQL.pm view on Meta::CPAN
$dbh->disconnect;
}
sub _connect_params
{
my $self = shift;
my %p = @_;
%p = validate( @_, { name => { type => SCALAR },
user => { type => SCALAR | UNDEF,
optional => 1 },
password => { type => SCALAR | UNDEF,
optional => 1 },
host => { type => SCALAR | UNDEF,
optional => 1 },
port => { type => SCALAR | UNDEF,
optional => 1 },
map { $_ => 0 } grep { /^mysql_/ } keys %p,
} );
lib/Alzabo/Driver/PostgreSQL.pm view on Meta::CPAN
name => $self->{schema}->db_schema_name
);
}
sub supports_referential_integrity { 1 }
sub schemas
{
my $self = shift;
my %p = validate( @_, { user => { type => SCALAR | UNDEF,
optional => 1 },
password => { type => SCALAR | UNDEF,
optional => 1 },
host => { type => SCALAR | UNDEF,
optional => 1 },
port => { type => SCALAR | UNDEF,
optional => 1 },
options => { type => SCALAR | UNDEF,
optional => 1 },
tty => { type => SCALAR | UNDEF,
lib/Alzabo/Driver/PostgreSQL.pm view on Meta::CPAN
$e ||= $@;
Alzabo::Exception::Driver->throw( error => $e ) if $e;
}
sub _connect_params
{
my $self = shift;
my %p = @_;
%p = validate( @_, { name => { type => SCALAR },
user => { type => SCALAR | UNDEF,
optional => 1 },
password => { type => SCALAR | UNDEF,
optional => 1 },
host => { type => SCALAR | UNDEF,
optional => 1 },
port => { type => SCALAR | UNDEF,
optional => 1 },
options => { type => SCALAR | UNDEF,
optional => 1 },
lib/Alzabo/Intro.pod view on Meta::CPAN
generic wrappers around it, inasmuch as is possible.
The first, the C<Alzabo::Driver::*> hierarchy, is used to handle
communication with the database. It uses C<DBI> and the appropriate
C<DBD::*> module to handle communications. It provides a higher level
of abstraction than C<DBI>, requiring that the RDBMS specific modules
implement methods to do such things as create databases or return the
next value in a sequence.
The second, the C<Alzabo::RDBMSRules::*> hierarchy, is used during
schema creation in order to validate user input such as schema and
table names. It also generates DDL SQL to create the database or turn one
schema into another (sort of a SQL diff). Finally, it also handles
reverse engineering of an existing database.
The C<Alzabo::SQLMaker::*> hierarchy is used to generate DML SQL and
handle bound parameters.
The RDBMS to be used is specified when creating the schema.
Currently, there is no easy way to convert a schema from one RDBMS to
another, though this is a future goal.
lib/Alzabo/MethodMaker.pm view on Meta::CPAN
insert_hooks
update_hooks
select_hooks
delete_hooks
);
sub import
{
my $class = shift;
validate( @_, { schema => { type => SCALAR },
class_root => { type => SCALAR,
optional => 1 },
name_maker => { type => CODEREF,
optional => 1 },
( map { $_ => { optional => 1 } } 'all', @options ) } );
my %p = @_;
return unless exists $p{schema};
return unless grep { exists $p{$_} && $p{$_} } 'all', @options;
lib/Alzabo/MethodMaker.pm view on Meta::CPAN
foreach my $fk (@fk)
{
$self->_make_fk_method($fk);
}
}
}
sub _make_method
{
my $self = shift;
my %p = validate @_, { type => { type => SCALAR },
class => { type => SCALAR },
returns => { type => SCALAR, optional => 1 },
code => { type => CODEREF },
# Stuff we can pass through to name_maker
foreign_key => { optional => 1 },
foreign_key_2 => { optional => 1 },
column => { optional => 1 },
table => { optional => 1 },
parent => { optional => 1 },
lib/Alzabo/MethodMaker.pm view on Meta::CPAN
Params::Validate::OBJECT )
{
push @types, $type_to_string{$_} if $mask & $_;
}
return @types ? @types : ('unknown');
}
}
package Alzabo::MethodDocs;
use Params::Validate qw( validate SCALAR ARRAYREF HASHREF );
use base qw(Alzabo::Docs);
sub new
{
my $class = shift;
my %p = validate( @_, { name => { type => SCALAR },
group => { type => SCALAR },
description => { type => SCALAR },
spec => { type => SCALAR | ARRAYREF | HASHREF,
default => undef },
} );
return bless \%p, $class;
}
sub name { shift->{name} }
lib/Alzabo/MethodMaker.pm view on Meta::CPAN
EOF
$pod .= $params if $params;
return $pod;
}
package Alzabo::ClassDocs;
use Params::Validate qw( validate SCALAR );
use base qw(Alzabo::Docs);
sub new
{
my $class = shift;
my %p = validate( @_, { group => { type => SCALAR },
description => { type => SCALAR },
} );
return bless \%p, $class;
}
sub as_pod
{
my $self = shift;
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
package Alzabo::RDBMSRules;
use strict;
use vars qw($VERSION);
use Alzabo::Exceptions ( abbr => [ 'recreate_table_exception' ] );
use Class::Factory::Util;
use Params::Validate qw( validate validate_pos );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = 2.0;
1;
sub new
{
shift;
my %p = @_;
eval "use Alzabo::RDBMSRules::$p{rdbms};";
Alzabo::Exception::Eval->throw( error => $@ ) if $@;
return "Alzabo::RDBMSRules::$p{rdbms}"->new(@_);
}
sub available { __PACKAGE__->subclasses }
# validation
sub validate_schema_name
{
shift()->_virtual;
}
sub validate_table_name
{
shift()->_virtual;
}
sub validate_column_name
{
shift()->_virtual;
}
sub validate_column_type
{
shift()->_virtual;
}
sub validate_column_length
{
shift()->_virtual;
}
sub validate_table_attribute
{
shift()->_virtual;
}
sub validate_column_attribute
{
shift()->_virtual;
}
sub validate_primary_key
{
shift()->_virtual;
}
sub validate_sequenced_attribute
{
shift()->_virtual;
}
sub validate_index
{
shift()->_virtual;
}
sub type_is_numeric
{
my $self = shift;
my $col = shift;
return $self->type_is_integer($col) || $self->type_is_floating_point($col);
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
sub column_attributes
{
shift()->_virtual;
}
sub schema_sql
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Schema' } );
my $schema = shift;
my @sql;
local $self->{state};
foreach my $t ( $schema->tables )
{
push @sql, $self->table_sql($t);
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
sub column_sql_diff
{
shift()->_virtual;
}
sub index_sql_diff
{
my $self = shift;
validate( @_, { new => { isa => 'Alzabo::Index' },
old => { isa => 'Alzabo::Index' } } );
my %p = @_;
my $new_sql = $self->index_sql($p{new});
my @sql;
if ( $new_sql ne $self->index_sql($p{old}) )
{
push @sql, $self->drop_index_sql( $p{old}, $p{new}->table->name );
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
sub rules_id
{
shift()->_virtual;
}
sub schema_sql_diff
{
my $self = shift;
validate( @_, { new => { isa => 'Alzabo::Schema' },
old => { isa => 'Alzabo::Schema' } } );
my %p = @_;
local $self->{state};
my @sql;
my %changed_name;
foreach my $new_t ( $p{new}->tables )
{
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
}
}
return @sql, @{ $self->{state}{deferred_sql} || [] };
}
sub table_sql_diff
{
my $self = shift;
validate( @_, { new => { isa => 'Alzabo::Table' },
old => { isa => 'Alzabo::Table' } } );
my %p = @_;
my @sql;
foreach my $old_i ( $p{old}->indexes )
{
unless ( eval { $p{new}->index( $old_i->id ) } )
{
push @sql, $self->drop_index_sql($old_i, $p{new}->name)
if eval { $p{new}->columns( map { $_->name } $old_i->columns ) } && ! $@;
lib/Alzabo/RDBMSRules.pm view on Meta::CPAN
=item * fulltext_indexes
This should be self-explanatory.
=item * functional_indexes
Indexes on functions, as supported by PostgreSQL.
=back
=head2 validate_schema_name (C<Alzabo::Schema> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the schema's name is not valid.
=head2 validate_table_name (C<Alzabo::Create::Table> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the table's name is not valid.
=head2 validate_column_name (C<Alzabo::Create::Column> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the column's name is not valid.
=head2 validate_column_type ($type_as_string)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the type is not valid.
This method returns a canonized version of the type.
=head2 validate_column_length (C<Alzabo::Create::Column> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the length or precision is not valid for the given column.
=head2 validate_column_attribute
This method takes two parameters:
=over 4
=item * column => C<Alzabo::Create::Column> object
=item * attribute => $attribute
=back
This method is a bit different from the others in that it takes an
existing column object and a B<potential> attribute.
It throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the attribute is is not valid for the column.
=head2 validate_primary_key (C<Alzabo::Create::Column> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the column is not a valid primary key for its table.
=head2 validate_sequenced_attribute (C<Alzabo::Create::Column> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the column cannot be sequenced.
=head2 validate_index (C<Alzabo::Create::Index> object)
Throws an L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions> if
the index is not valid.
=head2 table_sql (C<Alzabo::Create::Table> object)
Returns an array of SQL statements to create the specified table.
=head2 column_sql (C<Alzabo::Create::Column> object)
lib/Alzabo/RDBMSRules/MySQL.pm view on Meta::CPAN
$VERSION = 2.0;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
return bless {}, $class;
}
sub validate_schema_name
{
my $self = shift;
my $name = shift->name;
Alzabo::Exception::RDBMSRules->throw( error => "Schema name must be at least one character long" )
unless length $name;
# These are characters that are illegal in a dir name. I'm trying
# to accomodate both Win32 and UNIX here.
foreach my $c ( qw( : \ / ) )
{
Alzabo::Exception::RDBMSRules->throw( error => "Schema name contains an illegal character ($c)" )
if index($name, $c) != -1;
}
}
# Note: These rules are valid for MySQL 3.22.x. MySQL 3.23.x is
# actually less restrictive but this should be enough freedom.
sub validate_table_name
{
my $self = shift;
my $name = shift->name;
Alzabo::Exception::RDBMSRules->throw( error => "Table name must be at least one character long" )
unless length $name;
Alzabo::Exception::RDBMSRules->throw( error => "Table name is too long. Names must be 64 characters or less." )
if length $name >= 64;
Alzabo::Exception::RDBMSRules->throw( error => "Table name must only contain alphanumerics or underscore(_)." )
if $name =~ /\W/;
}
sub validate_column_name
{
my $self = shift;
my $name = shift->name;
Alzabo::Exception::RDBMSRules->throw( error => "Column name must be at least one character long" )
unless length $name;
Alzabo::Exception::RDBMSRules->throw( error => 'Name is too long. Names must be 64 characters or less.' )
if length $name >= 64;
Alzabo::Exception::RDBMSRules->throw( error =>
'Name contains characters that are not alphanumeric or the dollar sign ($).' )
if $name =~ /[^\w\$]/;
Alzabo::Exception::RDBMSRules->throw( error =>
'Name contains only digits. Names must contain at least one alpha character.' )
unless $name =~ /[^\W\d]/;
}
sub validate_column_type
{
my $self = shift;
my $type = shift;
$type = 'INTEGER' if uc $type eq 'INT';
# Columns which take no modifiers.
my %simple_types = map {$_ => 1} ( qw( DATE
DATETIME
TIME
lib/Alzabo/RDBMSRules/MySQL.pm view on Meta::CPAN
elsif ( uc substr($type, 0, 3) eq 'SET' )
{
return 'SET' . substr($type, 3);
}
else
{
return uc $type;
}
}
sub validate_column_length
{
my $self = shift;
my $column = shift;
# integer column
if ( $column->type =~ /\A(?:(?:(?:TINY|SMALL|MEDIUM|BIG)?INT)|INTEGER)/i )
{
Alzabo::Exception::RDBMSRules->throw( error => "Max display value is too long. Maximum allowed value is 255." )
if defined $column->length && $column->length > 255;
lib/Alzabo/RDBMSRules/MySQL.pm view on Meta::CPAN
Alzabo::Exception::RDBMSRules->throw( error => "Valid values for the length specification are 2 or 4." )
if defined $column->length && ($column->length != 2 && $column->length != 4);
return;
}
Alzabo::Exception::RDBMSRules->throw( error => $column->type . " columns cannot have a length or precision." )
if defined $column->length || defined $column->precision;
}
# placeholder in case we decide to try to do something better later
sub validate_table_attribute { 1 }
sub validate_column_attribute
{
my $self = shift;
my %p = @_;
my $column = $p{column};
my $a = uc $p{attribute};
$a =~ s/\A\s//;
$a =~ s/\s\z//;
if ( $a eq 'UNSIGNED' || $a eq 'ZEROFILL' )
lib/Alzabo/RDBMSRules/MySQL.pm view on Meta::CPAN
Alzabo::Exception::RDBMSRules->throw( error => "$a attribute can only be applied to character columns" )
unless $column->is_character;
return;
}
return if $a =~ /\A(?:REFERENCES|UNIQUE\z)/i;
Alzabo::Exception::RDBMSRules->throw( error => "Unrecognized attribute: $a" );
}
sub validate_primary_key
{
my $self = shift;
my $col = shift;
Alzabo::Exception::RDBMSRules->throw( error => 'Blob columns cannot be part of a primary key' )
if $col->type =~ /\A(?:TINY|MEDIUM|LONG)?(?:BLOB|TEXT)\z/i;
}
sub validate_sequenced_attribute
{
my $self = shift;
my $col = shift;
Alzabo::Exception::RDBMSRules->throw( error => 'Non-integer columns cannot be sequenced' )
unless $col->is_integer;
Alzabo::Exception::RDBMSRules->throw( error => 'Only one sequenced column per table is allowed.' )
if grep { $_ ne $col && $_->sequenced } $col->table->columns;
}
sub validate_index
{
my $self = shift;
my $index = shift;
foreach my $c ( $index->columns )
{
my $prefix = $index->prefix($c);
if (defined $prefix)
{
Alzabo::Exception::RDBMSRules->throw( error => "Invalid prefix specification ('$prefix')" )
lib/Alzabo/RDBMSRules/PostgreSQL.pm view on Meta::CPAN
use Alzabo::Exceptions ( abbr => [ 'recreate_table_exception' ] );
use Alzabo::RDBMSRules;
use Digest::MD5;
use Text::Balanced ();
use base qw(Alzabo::RDBMSRules);
use Params::Validate qw( validate_pos );
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
$VERSION = 2.0;
1;
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
return bless {}, $class;
}
sub validate_schema_name
{
my $self = shift;
my $name = shift->name;
$self->_check_name($name, 'schema');
Alzabo::Exception::RDBMSRules->throw( error => "Schema name ($name) contains a single quote char (')" )
if index($name, "'") != -1;
}
sub validate_table_name
{
my $self = shift;
$self->_check_name( shift->name, 'table' );
}
sub validate_column_name
{
my $self = shift;
$self->_check_name( shift->name, 'column' );
}
sub _check_name
{
my $self = shift;
my $name = shift;
Alzabo::Exception::RDBMSRules->throw( error => "Name ($name) must be at least one character long" )
unless length $name;
Alzabo::Exception::RDBMSRules->throw( error => "Name ($name) is too long. Names must be 31 characters or less." )
if length $name > 31;
Alzabo::Exception::RDBMSRules->throw( error => "Name ($name) must start with an alpha or underscore(_) and must contain only alphanumerics and underscores." )
unless $name =~ /\A[a-zA-Z]\w*\z/;
}
sub validate_column_type
{
my $self = shift;
my $type = uc shift;
my $table = shift;
if ( $table->primary_key_size > 1 )
{
return 'INT4' if $type =~ /^SERIAL4?$/;
return 'INT8' if $type eq 'BIGSERIAL' or $type eq 'SERIAL8';
}
lib/Alzabo/RDBMSRules/PostgreSQL.pm view on Meta::CPAN
return $type if $type =~ /BIT\s+VARYING/;
return $type if $type =~ /CHARACTER\s+VARYING/;
return $type if $type =~ /\ABOX|CIRCLE|LINE|LSEG|PATH|POINT|POLYGON/;
Alzabo::Exception::RDBMSRules->throw( error => "Invalid column type: $type" );
}
sub validate_column_length
{
my $self = shift;
my $column = shift;
if ( defined $column->length )
{
Alzabo::Exception::RDBMSRules->throw( error => "Length is not supported except for char, varchar, decimal, float, and numeric columns (" . $column->name . " column)" )
unless $column->type =~ /\A(?:(?:VAR)?CHAR|CHARACTER|DECIMAL|FLOAT|NUMERIC|(?:VAR)?BIT|BIT VARYING)\z/i;
}
if ( defined $column->precision )
{
Alzabo::Exception::RDBMSRules->throw( error => "Precision is not supported except for decimal, float, and numeric columns" )
unless $column->type =~ /\A(?:DECIMAL|FLOAT|NUMERIC)\z/i;
}
}
# placeholder in case we decide to try to do something better later
sub validate_table_attribute { 1 }
sub validate_column_attribute
{
my $self = shift;
my %p = @_;
my $column = $p{column};
my $type = $column->type;
my $a = uc $p{attribute};
$a =~ s/\A\s//;
$a =~ s/\s\z//;
return if $a =~ /\A(?:UNIQUE\z|CHECK|CONSTRAINT|REFERENCES)/i;
Alzabo::Exception::RDBMSRules->throw( error => "Only column constraints are supported as column attributes" )
}
sub validate_primary_key
{
my $self = shift;
my $col = shift;
my $serial_col = (grep { $_->type =~ /^(?:SERIAL(?:4|8)?|BIGSERIAL)$/ } $col->table->primary_key)[0];
if ( defined $serial_col &&
$serial_col->name ne $col->name )
{
$serial_col->set_type( $serial_col->type =~ /^SERIAL4?$/
? 'INT4'
: 'INT8' );
}
}
sub validate_sequenced_attribute
{
my $self = shift;
my $col = shift;
Alzabo::Exception::RDBMSRules->throw( error => 'Non-number columns cannot be sequenced' )
unless $col->is_integer || $col->is_floating_point;
}
sub validate_index
{
my $self = shift;
my $index = shift;
foreach my $c ( $index->columns )
{
Alzabo::Exception::RDBMSRules->throw( error => "PostgreSQL does not support index prefixes" )
if defined $index->prefix($c)
}
lib/Alzabo/RDBMSRules/PostgreSQL.pm view on Meta::CPAN
}
sub quote_identifiers { 1 }
sub quote_identifiers_character { '"' }
sub schema_sql
{
my $self = shift;
validate_pos( @_, { isa => 'Alzabo::Schema' } );
my $schema = shift;
my @sql = $self->SUPER::schema_sql($schema);
# This has to come at the end because we don't know which tables
# reference other tables.
foreach my $t ( $schema->tables )
{
foreach my $con ( grep { /\s*(?:check|constraint)/i } $t->attributes )
lib/Alzabo/Runtime/Column.pm view on Meta::CPAN
Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
use base qw(Alzabo::Column);
$VERSION = 2.0;
sub alias_clone
{
my $self = shift;
my %p = validate( @_, { table => { isa => 'Alzabo::Runtime::Table' },
} );
my $clone;
%$clone = %$self;
$clone->{table} = $p{table};
bless $clone, ref $self;
return $clone;
}
sub alias
{
my $self = shift;
my %p = validate( @_, { as => { type => SCALAR } } );
my $clone;
%$clone = %$self;
bless $clone, ref $self;
$clone->{alias_name} = $p{as};
$clone->{real_column} = $self;
return $clone;
lib/Alzabo/Runtime/ForeignKey.pm view on Meta::CPAN
package Alzabo::Runtime::ForeignKey;
use strict;
use vars qw( $VERSION %DELETED );
use Alzabo::Runtime;
use Alzabo::Exceptions ( abbr => 'params_exception' );
use Params::Validate qw( validate ARRAYREF OBJECT );
Params::Validate::validation_options
( on_fail => sub { params_exception join '', @_ } );
use base qw(Alzabo::ForeignKey);
$VERSION = 2.0;
1;
# FIXME - needs docs
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
validate( @_, { columns_from => { type => ARRAYREF | OBJECT },
columns_to => { type => ARRAYREF | OBJECT },
} );
my %p = @_;
my $self = bless {}, $class;
# XXX - needs a little more validation, like that both "sides"
# have the same number of columns
$self->{columns_from} = $p{columns_from};
$self->{columns_to} = $p{columns_to};
lib/Alzabo/Runtime/InsertHandle.pm view on Meta::CPAN
use constant NEW_SPEC => { table => { isa => 'Alzabo::Runtime::Table' },
sql => { isa => 'Alzabo::SQLMaker' },
columns => { type => ARRAYREF },
values => { type => HASHREF, default => {} },
};
sub new
{
my $class = shift;
my %p = validate( @_, NEW_SPEC );
my $self = bless \%p, $class;
$self->{handle} =
$self->{table}->schema->driver->statement_no_execute( sql => $p{sql}->sql );
return $self;
}
sub insert
{
my $self = shift;
my %p = @_;
%p = validate( @_,
{ ( map { $_ => { optional => 1 } } keys %p ),
values => { type => HASHREF, default => {} },
},
);
my $vals = { %{ $self->{values} },
%{ $p{values} },
};
my $schema = $self->{table}->schema;
lib/Alzabo/Runtime/JoinCursor.pm view on Meta::CPAN
use constant NEW_SPEC => { statement => { isa => 'Alzabo::DriverStatement' },
tables => { type => ARRAYREF },
};
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p = validate( @_, NEW_SPEC );
my $self = bless { %p,
count => 0,
}, $class;
return $self;
}
sub next
{
lib/Alzabo/Runtime/Row.pm view on Meta::CPAN
use Alzabo::Exceptions ( abbr => [ qw( logic_exception no_such_row_exception
params_exception storable_exception ) ] );
use Alzabo::Runtime;
use Alzabo::Runtime::RowState::Deleted;
use Alzabo::Runtime::RowState::Live;
use Alzabo::Runtime::RowState::Potential;
use Alzabo::Utils;
use Params::Validate qw( validate validate_with UNDEF SCALAR HASHREF BOOLEAN );
Params::Validate::validation_options
( on_fail => sub { params_exception join '', @_ } );
use Storable ();
$VERSION = 2.0;
BEGIN
{
no strict 'refs';
lib/Alzabo/Runtime/Row.pm view on Meta::CPAN
},
no_cache => { type => BOOLEAN, default => 0 },
};
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p =
validate( @_, NEW_SPEC );
my $self = $p{potential_row} ? $p{potential_row} : {};
bless $self, $class;
$self->{table} = $p{table};
$self->{state} = $p{state};
$self->{state}->_init($self, @_) or return;
lib/Alzabo/Runtime/Row.pm view on Meta::CPAN
return $self->table->schema;
}
sub set_state { $_[0]->{state} = $_[1] };
use constant ROWS_BY_FOREIGN_KEY_SPEC => { foreign_key => { isa => 'Alzabo::ForeignKey' } };
sub rows_by_foreign_key
{
my $self = shift;
my %p = validate_with( params => \@_,
spec => ROWS_BY_FOREIGN_KEY_SPEC,
allow_extra => 1,
);
my $fk = delete $p{foreign_key};
if ($p{where})
{
$p{where} = [ $p{where} ] unless Alzabo::Utils::is_arrayref( $p{where}[0] );
}
lib/Alzabo/Runtime/RowCursor.pm view on Meta::CPAN
use constant NEW_SPEC => { statement => { isa => 'Alzabo::DriverStatement' },
table => { isa => 'Alzabo::Runtime::Table' },
};
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p = validate( @_, NEW_SPEC );
my $self = bless { %p,
count => 0,
}, $class;
return $self;
}
sub next
{
lib/Alzabo/Runtime/Schema.pm view on Meta::CPAN
optional => 1 },
distinct => { type => ARRAYREF | OBJECT,
optional => 1 },
quote_identifiers => { type => BOOLEAN,
optional => 1 },
};
sub join
{
my $self = shift;
my %p = validate( @_, JOIN_SPEC );
$p{join} ||= delete $p{tables};
$p{join} = [ $p{join} ] unless Alzabo::Utils::is_arrayref( $p{join} );
my @tables;
if ( Alzabo::Utils::is_arrayref( $p{join}->[0] ) )
{
# flattens the nested structure and produces a unique set of
# tables
lib/Alzabo/Runtime/Schema.pm view on Meta::CPAN
optional => 1 },
limit => { type => SCALAR | ARRAYREF,
optional => 1 },
quote_identifiers => { type => BOOLEAN,
optional => 1 },
};
sub _select_sql
{
my $self = shift;
my %p = validate( @_, _SELECT_SQL_SPEC );
$p{join} ||= delete $p{tables};
$p{join} = [ $p{join} ] unless Alzabo::Utils::is_arrayref( $p{join} );
my @tables;
if ( Alzabo::Utils::is_arrayref( $p{join}->[0] ) )
{
# flattens the nested structure and produces a unique set of
# tables
lib/Alzabo/Runtime/Schema.pm view on Meta::CPAN
return $sql;
}
use constant _JOIN_ALL_TABLES_SPEC => { join => { type => ARRAYREF },
sql => { isa => 'Alzabo::SQLMaker' } };
sub _join_all_tables
{
my $self = shift;
my %p = validate( @_, _JOIN_ALL_TABLES_SPEC );
my @from;
my @joins;
# outer join given as only join
$p{join} = [ $p{join} ] unless ref $p{join}->[0];
# A structure like:
#
# [ [ $t_1 => $t_2 ],
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
$VERSION = 2.0;
sub insert
{
my $self = shift;
logic_exception "Can't make rows for tables without a primary key"
unless $self->primary_key;
my %p = @_;
%p = validate( @_,
{ ( map { $_ => { optional => 1 } } keys %p ),
values => { type => HASHREF, optional => 1 },
quote_identifiers => { type => BOOLEAN,
optional => 1 },
},
);
my $vals = delete $p{values} || {};
my $schema = $self->schema;
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
}
sub insert_handle
{
my $self = shift;
logic_exception "Can't make rows for tables without a primary key"
unless $self->primary_key;
my %p = @_;
%p = validate( @_,
{ ( map { $_ => { optional => 1 } } keys %p ),
columns => { type => ARRAYREF, default => [] },
values => { type => HASHREF, default => {} },
quote_identifiers => { type => BOOLEAN,
optional => 1 },
},
);
my %func_vals;
my %static_vals;
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
return $class->new(%p);
}
sub _row_class { 'Alzabo::Runtime::Row' }
sub row_by_id
{
my $self = shift;
my %p = @_;
validate( @_, { row_id => { type => SCALAR },
( map { $_ => { optional => 1 } } keys %p ) } );
my (undef, undef, %pk) = split ';:;_;:;', delete $p{row_id};
return $self->row_by_pk( %p, pk => \%pk );
}
sub rows_where
{
my $self = shift;
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
from( $self ) );
return $sql;
}
sub _cursor_by_sql
{
my $self = shift;
my %p = @_;
validate( @_, { sql => { isa => 'Alzabo::SQLMaker' },
order_by => { type => ARRAYREF | HASHREF | OBJECT,
optional => 1 },
limit => { type => SCALAR | ARRAYREF,
optional => 1 },
( map { $_ => { optional => 1 } } keys %p ) } );
Alzabo::Runtime::process_order_by_clause( $p{sql}, $p{order_by} ) if exists $p{order_by};
if ( exists $p{limit} )
{
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
limit => { type => SCALAR | ARRAYREF,
optional => 1 },
quote_identifiers => { type => BOOLEAN,
optional => 1 },
};
sub _select_sql
{
my $self = shift;
my %p = validate( @_, _SELECT_SQL_SPEC );
my @funcs = Alzabo::Utils::is_arrayref( $p{select} ) ? @{ $p{select} } : $p{select};
my $sql = Alzabo::Runtime::sqlmaker( $self->schema, \%p )->select(@funcs)->from($self);
Alzabo::Runtime::process_where_clause( $sql, $p{where} )
if exists $p{where};
Alzabo::Runtime::process_group_by_clause( $sql, $p{group_by} )
if exists $p{group_by};
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
{
my $self = shift;
$self->{prefetch} = $self->_canonize_prefetch(@_);
}
sub _canonize_prefetch
{
my $self = shift;
validate_pos( @_, ( { isa => 'Alzabo::Column' } ) x @_ );
foreach my $c (@_)
{
params_exception "Column " . $c->name . " doesn't exist in $self->{name}"
unless $self->has_column( $c->name );
}
return [ map { $_->name } grep { ! $_->is_primary_key } @_ ];
}
lib/Alzabo/Runtime/Table.pm view on Meta::CPAN
{
my $self = shift;
return ref $self->{prefetch} ? @{ $self->{prefetch} } : ();
}
sub add_group
{
my $self = shift;
validate_pos( @_, ( { isa => 'Alzabo::Column' } ) x @_ );
my @names = map { $_->name } @_;
foreach my $col (@_)
{
params_exception "Column " . $col->name . " doesn't exist in $self->{name}"
unless $self->has_column( $col->name );
next if $col->is_primary_key;
$self->{groups}{ $col->name } = \@names;
}
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
$VERSION = 2.0;
1;
sub make_function
{
my $class = caller;
my %p =
validate( @_,
{ function => { type => SCALAR },
min => { type => SCALAR, optional => 1 },
max => { type => UNDEF | SCALAR, optional => 1 },
groups => { type => ARRAYREF },
quote => { type => ARRAYREF, optional => 1 },
format => { type => SCALAR, optional => 1 },
is_modifier => { type => SCALAR, default => 0 },
has_spaces => { type => SCALAR, default => 0 },
allows_alias => { type => SCALAR, default => 1 },
no_parens => { type => SCALAR, default => 0 },
} );
my $valid = '';
if ( $p{min} || $p{max} )
{
$valid .= 'validate_pos( @_, ';
$valid .= join ', ', ('1') x $p{min};
}
if ( defined $p{min} && defined $p{max} && $p{max} > $p{min} )
{
$valid .= ', ';
$valid .= join ', ', ('0') x ( $p{max} - $p{min} );
}
elsif ( exists $p{min} && ! defined $p{max} )
{
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
}
use constant NEW_SPEC => { driver => { isa => 'Alzabo::Driver' },
quote_identifiers => { type => BOOLEAN,
default => 0 },
};
sub new
{
my $class = shift;
my %p = validate( @_, NEW_SPEC );
return bless { last_op => undef,
expect => undef,
type => undef,
sql => '',
bind => [],
placeholders => [],
as_id => 'aaaaa10000',
alias_in_having => 1,
%p,
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
sub from
{
my $self = shift;
$self->_assert_last_op( qw( select delete function ) );
my $spec =
$self->{last_op} eq 'select' ? { type => OBJECT | ARRAYREF } : { can => 'alias_name' };
validate_pos( @_, ( $spec ) x @_ );
$self->{sql} .= ' FROM ';
if ( $self->{last_op} eq 'delete' )
{
$self->{sql} .=
join ', ', map { ( $self->{quote_identifiers} ?
$self->{driver}->quote_identifier( $_->name ) :
$_->name ) } @_;
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
( { can => 'alias_name' } ) x 2,
{ type => UNDEF | ARRAYREF | OBJECT, optional => 1 },
{ type => UNDEF | ARRAYREF, optional => 1 },
);
sub _outer_join
{
my $self = shift;
my $tables = @_ - 1;
validate_pos( @_, _OUTER_JOIN_SPEC );
my $type = uc shift;
my $join_from = shift;
my $join_on = shift;
my $fk;
$fk = shift if $_[0] && Alzabo::Utils::safe_isa( $_[0], 'Alzabo::ForeignKey' );
my $where = shift;
unless ($fk)
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
$self->{last_op} = $self->{subgroup} ? 'subgroup_end' : 'condition';
return $self;
}
sub condition
{
my $self = shift;
validate_pos( @_,
{ type => OBJECT },
{ type => SCALAR },
{ type => UNDEF | SCALAR | OBJECT },
( { type => UNDEF | SCALAR | OBJECT, optional => 1 } ) x (@_ - 3) );
my $lhs = shift;
my $comp = uc shift;
my $rhs = shift;
my $in_having = $self->{last_op} eq 'having' ? 1 : 0;
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
sub order_by
{
my $self = shift;
$self->_assert_last_op( qw( select from condition group_by ) );
Alzabo::Exception::SQL->throw
( error => "Cannot use order by in a '$self->{type}' statement" )
unless $self->{type} eq 'select';
validate_pos( @_, ( { type => SCALAR | OBJECT,
callbacks =>
{ 'column_or_function_or_sort' =>
sub { Alzabo::Utils::safe_can( $_[0], 'table' ) ||
Alzabo::Utils::safe_isa( $_[0], 'Alzabo::SQLMaker::Function' ) ||
$_[0] =~ /^(?:ASC|DESC)$/i } } }
) x @_ );
$self->{sql} .= ' ORDER BY ';
my $x = 0;
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
sub group_by
{
my $self = shift;
$self->_assert_last_op( qw( select from condition ) );
Alzabo::Exception::SQL->throw
( error => "Cannot use group by in a '$self->{type}' statement" )
unless $self->{type} eq 'select';
validate_pos( @_, ( { can => 'table' } ) x @_ );
foreach my $c (@_)
{
unless ( $self->{tables}{ $c->table } )
{
my $err = 'Cannot use column (';
$err .= join '.', $c->table->name, $c->name;
$err .= ") in $self->{type} unless its table is included in the FROM clause";
Alzabo::Exception::SQL->throw( error => $err );
}
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
return $self;
}
sub into
{
my $self = shift;
$self->_assert_last_op( qw( insert ) );
validate_pos( @_, { can => 'alias_name' }, ( { can => 'table' } ) x (@_ - 1) );
my $table = shift;
$self->{tables} = { $table => 1 };
foreach my $c (@_)
{
unless ( $c->table eq $table )
{
my $err = 'Cannot into column (';
$err .= join '.', $c->table->name, $c->name;
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
return $self;
}
sub values
{
my $self = shift;
$self->_assert_last_op( qw( into ) );
validate_pos( @_, ( { type => UNDEF | SCALAR | OBJECT } ) x @_ );
if ( ref $_[0] && $_[0]->isa('Alzabo::SQLMaker') )
{
$self->{sql} = $_[0]->sql;
push @{ $self->{bind} }, $_[0]->bind;
}
else
{
my @vals = @_;
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
return $self;
}
use constant UPDATE_SPEC => { can => 'alias_name' };
sub update
{
my $self = shift;
validate_pos( @_, UPDATE_SPEC );
my $table = shift;
$self->{sql} = 'UPDATE ';
$self->{sql} .= ( $self->{quote_identifiers} ?
$self->{driver}->quote_identifier( $table->name ) :
$table->name );
$self->{tables} = { $table => 1 };
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
{
my $self = shift;
my @vals = @_;
$self->_assert_last_op('update');
Alzabo::Exception::Params->throw
( error => "'set' method expects key/value pairs of column objects and values'" )
if !@vals || @vals % 2;
validate_pos( @_, ( { can => 'table' },
{ type => UNDEF | SCALAR | OBJECT } ) x (@vals / 2) );
$self->{sql} .= ' SET ';
my @set;
my $table = ( keys %{ $self->{tables} } )[0];
while ( my ($col, $val) = splice @vals, 0, 2 )
{
unless ( $table eq $col->table )
{
lib/Alzabo/SQLMaker.pm view on Meta::CPAN
use constant _BIND_VAL_FOR_INSERT_SPEC => ( { isa => 'Alzabo::Runtime::Column' },
{ type => UNDEF | SCALAR | OBJECT }
);
sub _bind_val_for_insert
{
my $self = shift;
my ( $col, $val ) =
validate_pos( @_, _BIND_VAL_FOR_INSERT_SPEC );
if ( defined $val && $val eq $placeholder )
{
push @{ $self->{placeholders} }, $col->name;
return '?';
}
else
{
return $self->_bind_val($val);
}
}
use constant _BIND_VAL_SPEC => { type => UNDEF | SCALAR | OBJECT };
sub _bind_val
{
my $self = shift;
validate_pos( @_, _BIND_VAL_SPEC );
return $_[0]->as_string( $self->{driver}, $self->{quote_identifiers} )
if Alzabo::Utils::safe_isa( $_[0], 'Alzabo::SQLMaker::Function' );
push @{ $self->{bind} }, $_[0];
return '?';
}
sub sql
{
lib/Alzabo/Schema.pm view on Meta::CPAN
use Tie::IxHash ();
$VERSION = 2.0;
1;
sub _load_from_file
{
my $class = shift;
my %p = validate( @_, { name => { type => SCALAR },
} );
# Making these (particularly from files) is expensive.
return $class->_cached_schema($p{name}) if $class->_cached_schema($p{name});
my $schema_dir = Alzabo::Config::schema_dir;
my $file = $class->_schema_filename( $p{name} );
-e $file or Alzabo::Exception::Params->throw( error => "No saved schema named $p{name} ($file)" );
lib/Alzabo/Schema.pm view on Meta::CPAN
$schema->_save_to_cache;
return $schema;
}
sub _cached_schema
{
my $class = shift->isa('Alzabo::Runtime::Schema') ? 'Alzabo::Runtime::Schema' : 'Alzabo::Create::Schema';
validate_pos( @_, { type => SCALAR } );
my $name = shift;
my $schema_dir = Alzabo::Config::schema_dir();
my $file = $class->_schema_filename($name);
if (exists $CACHE{$name}{$class}{object})
{
my $mtime = (stat($file))[9]
or Alzabo::Exception::System->throw( error => "can't stat $file: $!" );
lib/Alzabo/Schema.pm view on Meta::CPAN
( exists $self->{db_schema_name}
? $self->{db_schema_name}
: $self->name
);
}
sub has_table
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
return $self->{tables}->FETCH(shift);
}
use constant TABLE_SPEC => { type => SCALAR };
sub table
{
my $self = shift;
my ($name) = validate_pos( @_, TABLE_SPEC );
return
$self->{tables}->FETCH($name) ||
params_exception "Table $name doesn't exist in $self->{name}";
}
sub tables
{
my $self = shift;
lib/Alzabo/Schema.pm view on Meta::CPAN
my $self = shift;
return $self->{rules};
}
sub quote_identifiers { $_[0]->{quote_identifiers} }
sub sqlmaker
{
my $self = shift;
my %p = validate( @_, { quote_identifiers =>
{ type => BOOLEAN,
default => $self->{quote_identifiers},
},
},
);
return $self->{sql}->new( driver => $self->driver,
quote_identifiers => $p{quote_identifiers},
);
}
lib/Alzabo/Table.pm view on Meta::CPAN
return $self->{name};
}
use constant HAS_COLUMN_SPEC => { type => SCALAR };
sub has_column
{
my $self = shift;
validate_pos( @_, HAS_COLUMN_SPEC );
return $self->{columns}->FETCH(shift);
}
sub column
{
my $self = shift;
my $name = shift;
if ( my $col = $self->{columns}->FETCH($name) )
lib/Alzabo/Table.pm view on Meta::CPAN
return scalar @{ $self->{pk} };
}
use constant COLUMN_IS_PRIMARY_KEY_SPEC => { isa => 'Alzabo::Column' };
sub column_is_primary_key
{
my $self = shift;
validate_pos( @_, COLUMN_IS_PRIMARY_KEY_SPEC );
my $name = shift->name;
Alzabo::Exception::Params->throw( error => "Column $name doesn't exist in $self->{name}" )
unless $self->{columns}->EXISTS($name);
my $idx = $self->{columns}->Indices($name);
return 1 if grep { $idx == $_ } @{ $self->{pk} };
return 0;
lib/Alzabo/Table.pm view on Meta::CPAN
}
use constant HAS_ATTRIBUTE_SPEC => { attribute => { type => SCALAR },
case_sensitive => { type => SCALAR,
default => 0 },
};
sub has_attribute
{
my $self = shift;
my %p = validate( @_, HAS_ATTRIBUTE_SPEC );
if ( $p{case_sensitive} )
{
return exists $self->{attributes}{ $p{attribute} };
}
else
{
return 1 if grep { lc $p{attribute} eq lc $_ } keys %{ $self->{attributes} };
}
}
use constant FOREIGN_KEYS_SPEC => { column => { isa => 'Alzabo::Column' },
table => { isa => 'Alzabo::Table' },
};
sub foreign_keys
{
my $self = shift;
validate( @_, FOREIGN_KEYS_SPEC );
my %p = @_;
my $c_name = $p{column}->name;
my $t_name = $p{table}->name;
Alzabo::Exception::Params->throw( error => "Column $c_name doesn't exist in $self->{name}" )
unless $self->{columns}->EXISTS($c_name);
Alzabo::Exception::Params->throw( error => "No foreign keys to $t_name exist in $self->{name}" )
unless exists $self->{fk}{$t_name};
lib/Alzabo/Table.pm view on Meta::CPAN
return wantarray ? @{ $self->{fk}{$t_name}{$c_name} } : $self->{fk}{$t_name}{$c_name}[0];
}
use constant FOREIGN_KEYS_BY_TABLE_SPEC => { isa => 'Alzabo::Table' };
sub foreign_keys_by_table
{
my $self = shift;
validate_pos( @_, FOREIGN_KEYS_BY_TABLE_SPEC );
my $name = shift->name;
my $fk = $self->{fk};
my %fk;
if ( exists $fk->{$name} )
{
foreach my $c ( keys %{ $fk->{$name} } )
{
return $fk->{$name}{$c}[0] unless wantarray;
lib/Alzabo/Table.pm view on Meta::CPAN
return values %fk;
}
use constant FOREIGN_KEYS_BY_COLUMN_SPEC => { isa => 'Alzabo::Column' };
sub foreign_keys_by_column
{
my $self = shift;
my ($col) = validate_pos( @_, FOREIGN_KEYS_BY_COLUMN_SPEC );
Alzabo::Exception::Params->throw( error => "Column " . $col->name . " doesn't exist in $self->{name}" )
unless $self->{columns}->EXISTS( $col->name );
my $fk = $self->{fk};
my %fk;
foreach my $t (keys %$fk)
{
if ( exists $fk->{$t}{ $col->name } )
lib/Alzabo/Table.pm view on Meta::CPAN
}
}
return wantarray ? @fk : $fk[0];
}
sub index
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $id = shift;
Alzabo::Exception::Params->throw( error => "Index $id doesn't exist in $self->{name}" )
unless $self->{indexes}->EXISTS($id);
return $self->{indexes}->FETCH($id);
}
sub has_index
{
my $self = shift;
validate_pos( @_, { type => SCALAR } );
my $id = shift;
return $self->{indexes}->EXISTS($id);
}
sub indexes
{
my $self = shift;
return $self->{indexes}->Values;