Alzabo
view release on metacpan or search on metacpan
lib/Alzabo/RDBMSRules/MySQL.pm view on Meta::CPAN
package Alzabo::RDBMSRules::MySQL;
use strict;
use vars qw($VERSION);
use Alzabo::RDBMSRules;
use base qw(Alzabo::RDBMSRules);
$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
TINYBLOB
TINYTEXT
BLOB
TEXT
MEDIUMBLOB
MEDIUMTEXT
LONGBLOB
LONGTEXT
INTEGER
TINYINT
SMALLINT
MEDIUMINT
BIGINT
FLOAT
DOUBLE
REAL
DECIMAL
NUMERIC
TIMESTAMP
CHAR
VARCHAR
YEAR
),
);
return uc $type if $simple_types{uc $type};
return 'DOUBLE' if $type =~ /DOUBLE\s+PRECISION/i;
return 'CHAR' if $type =~ /\A(?:NATIONAL\s+)?CHAR(?:ACTER)?/i;
return 'VARCHAR' if $type =~ /\A(?:NATIONAL\s+)?(?:VARCHAR|CHARACTER VARYING)/i;
my $t = $self->_capitalize_type($type);
return $t if $t;
Alzabo::Exception::RDBMSRules->throw( error => "Unrecognized type: $type" );
}
sub _capitalize_type
{
my $self = shift;
my $type = shift;
if ( uc substr($type, 0, 4) eq 'ENUM' )
{
return 'ENUM' . substr($type, 4);
}
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;
Alzabo::Exception::RDBMSRules->throw( error => $column->type . " columns cannot have a precision." )
if defined $column->precision;
return;
}
if ( $column->type =~ /\A(?:FLOAT|DOUBLE(?:\s+PRECISION)?|REAL)/i )
{
if (defined $column->length)
{
Alzabo::Exception::RDBMSRules->throw( error => "Max display value is too long. Maximum allowed value is 255." )
if $column->length > 255;
Alzabo::Exception::RDBMSRules->throw( error => "Max display value specified without floating point precision." )
unless defined $column->precision;
Alzabo::Exception::RDBMSRules->throw( error =>
"Floating point precision is too high. The maximum value is " .
"30 or the maximum display size - 2, whichever is smaller." )
if $column->precision > 30 || $column->precision > ($column->length - $column->precision);
}
return;
}
if ( $column->type =~ /\A(?:DECIMAL|NUMERIC)\z/i )
{
Alzabo::Exception::RDBMSRules->throw( error => "Max display value is too long. Maximum allowed value is 255." )
if defined $column->length && $column->length > 255;
Alzabo::Exception::RDBMSRules->throw( error =>
"Floating point precision is too high. The maximum value is " .
"30 or the maximum display size - 2, whichever is smaller." )
if defined $column->precision && ($column->precision > 30 || $column->precision > ($column->length - 2) );
return;
}
if ( uc $column->type eq 'TIMESTAMP' )
{
Alzabo::Exception::RDBMSRules->throw( error => "Max display value is too long. Maximum allowed value is 14." )
if defined $column->length && $column->length > 14;
Alzabo::Exception::RDBMSRules->throw( error => $column->type . " columns cannot have a precision." )
if defined $column->precision;
return;
}
if ( $column->type =~ /\A(?:(?:NATIONAL\s+)?VAR)?(?:CHAR|BINARY)/i )
{
Alzabo::Exception::RDBMSRules->throw( error => "(VAR)CHAR and (VAR)BINARY columns must have a length provided." )
unless defined $column->length && $column->length > 0;
Alzabo::Exception::RDBMSRules->throw( error => "Max display value is too long. Maximum allowed value is 255." )
if $column->length > 255;
Alzabo::Exception::RDBMSRules->throw( error => $column->type . " columns cannot have a precision." )
if defined $column->precision;
return;
}
if ( uc $column->type eq 'YEAR' )
{
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' )
{
Alzabo::Exception::RDBMSRules->throw( error => "$a attribute can only be applied to numeric columns" )
unless $column->is_numeric;
return;
}
if ( $a eq 'AUTO_INCREMENT' )
{
Alzabo::Exception::RDBMSRules->throw( error => "$a attribute can only be applied to integer columns" )
unless $column->is_integer;
return;
}
if ($a eq 'BINARY')
{
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')" )
unless $prefix =~ /\d+/ && $prefix > 0;
Alzabo::Exception::RDBMSRules->throw( error => 'Non-character/blob columns cannot have an index prefix' )
unless $c->is_blob || $c->is_character || $c->type =~ /^(?:VAR)BINARY$/i;
}
if ( $c->is_blob )
{
Alzabo::Exception::RDBMSRules->throw( error => 'Blob columns must have an index prefix' )
unless $prefix || $index->fulltext;
}
if ( $index->fulltext )
{
Alzabo::Exception::RDBMSRules->throw( error => 'A fulltext index can only include text or char columns' )
unless $c->is_character;
}
}
Alzabo::Exception::RDBMSRules->throw( error => 'An fulltext index cannot be unique' )
if $index->unique && $index->fulltext;
Alzabo::Exception::RDBMSRules->throw( error => 'MySQL does not support function indexes' )
if defined $index->function;
}
sub type_is_integer
{
my $self = shift;
my $col = shift;
my $type = uc $col->type;
return 1 if $type =~ /\A(?:(?:TINY|SMALL|MEDIUM|BIG)?INT|INTEGER)\z/;
}
sub type_is_floating_point
{
my $self = shift;
my $col = shift;
my $type = uc $col->type;
return 1 if $type =~ /\A(?:DECIMAL|NUMERIC|FLOAT|DOUBLE|REAL)\z/;
}
sub type_is_char
{
my $self = shift;
my $col = shift;
my $type = uc $col->type;
( run in 0.818 second using v1.01-cache-2.11-cpan-39bf76dae61 )