diff Lib/IMPL/SQL/Schema/Traits.pm @ 271:56364d0c4b4f

+IMPL::SQL::Schema::MySQL: added basic support for MySQL
author cin
date Mon, 28 Jan 2013 02:43:14 +0400
parents dacfe7c0311a
children 47db27ed5b43
line wrap: on
line diff
--- a/Lib/IMPL/SQL/Schema/Traits.pm	Fri Jan 25 00:25:02 2013 +0400
+++ b/Lib/IMPL/SQL/Schema/Traits.pm	Mon Jan 28 02:43:14 2013 +0400
@@ -114,6 +114,8 @@
 use fields qw(
     foreignTable
     foreignColumns
+    onUpdate
+    onDelete
 );
 
 use constant { constraintClass => typeof IMPL::SQL::Schema::Constraint::ForeignKey };
@@ -123,10 +125,13 @@
 );
 
 sub CTOR {
-    my ($this,$foreignTable,$foreignColumns) = @_[0,3,4];
+    my ($this,$foreignTable,$foreignColumns,%args) = @_[0,3..$#_];
     
     $this->{foreignTable} = $foreignTable;
-    $this->{foreignColunms} = $foreignColumns;
+    $this->{foreignColumns} = $foreignColumns;
+    
+    $this->{onDelete} = $args{onDelete} if $args{onDelete};
+    $this->{onUpdate} = $args{onUpdate} if $args{onUpdate};
 }
  
 
@@ -134,238 +139,398 @@
 
 package IMPL::SQL::Schema::Traits::CreateTable;
 
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        Table => '-IMPL::SQL::Schema::Traits::Table',
+        ArgException => '-IMPL::InvalidArgumentException',
+        OpException => '-IMPL::InvalidOperationException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        table => PROP_RO,
+    ]
+};
 use IMPL::lang;
 
-BEGIN {
-    public property table => prop_get | owner_set;
-}
-
 sub CTOR {
     my ($this,$table) = @_;
     
-    die new IMPL::InvalidArgumentException("table", "An object of IMPL::SQL::Schema::Traits::Table type is required")
-        unless is $table, typeof IMPL::SQL::Schema::Traits::Table;
+    die ArgException->new("table", "An object of IMPL::SQL::Schema::Traits::Table type is required")
+        unless is($table, Table);
     
     $this->table($table);
 }
 
-sub apply {
+sub CanApply {
     my ($this,$schema) = @_;
     
-    return 0 if ( $schema->GetTable( $this->table->{name} ) );
+    return( $schema->GetTable( $this->table->{name} ) ? 0 : 1 );
+}
+
+sub Apply {
+    my ($this,$schema) = @_;
     
-    $schema->AddTable($this->table);
-    return 1;
+    my $args = {%{$this->table}};
+    
+    my $constraints = delete $args->{constraints} || [];
+    
+    my $table = $schema->AddTable($args);
+    
+    $table->AddConstraint($_->constraintClass, $_) foreach @{$constraints};
 }
 
 ##################################################
 
 package IMPL::SQL::Schema::Traits::DropTable;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
-
-BEGIN {
-    public property tableName => prop_get | owner_set;
-}
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        ArgException => '-IMPL::InvalidArgumentException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+    ]
+};
 
 sub CTOR {
     my ($this,$tableName) = @_;
     
-    $this->tableName($tableName) or die new IMPL::InvalidArgumentException("tableName is required");
+    $this->tableName($tableName) or die ArgException->new("tableName is required");
 }
 
-sub apply {
+sub CanApply {
     my ($this,$schema) = @_;
     
-    return 0 if $schema->GetTable( $this->tableName );
+    return $schema->GetTable( $this->tableName ) ? 1 : 0;
+}
+
+sub Apply {
+    my ($this,$schema) = @_;
     
     $schema->RemoveTable($this->tableName);
-    
-    return 1;
 }
 
 ##################################################
 
 package IMPL::SQL::Schema::Traits::RenameTable;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
-
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property tableNewName => prop_get | owner_set;
-}
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        ArgException => '-IMPL::InvalidArgumentException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        tableNewName => PROP_RO,
+    ]
+};
 
 sub CTOR {
     my ($this, $oldName, $newName) = @_;
     
-    $this->tableName($oldName) or die new IMPL::InvalidArgumentException("A table name is required");
-    $this->tableNewName($newName) or die new  IMPL::InvalidArgumentException("A new table name is required");
+    $this->tableName($oldName) or die ArgException->new("A table name is required");
+    $this->tableNewName($newName) or die ArgException->new("A new table name is required");
 }
 
-sub apply {
+sub CanApply {
+    my ($this, $schema) = @_;
+    
+    return ($schema->GetTable($this->tableName) and not $schema->GetTable($this->tableNewName) ? 1 : 0 );
+}
+
+sub Apply {
     my ($this,$schema) = @_;
     
-    return 0 if not $schema->GetTable($this->tableName) or $schema->GetTable($this->tableNewName);
+    $schema->RenameTable($this->tableName, $this->tableNewName);
     
-    $this->RenameTable($this->tableName, $this->tableNewName);
-    
-    return 1;
 }
 
 #################################################
 
 package IMPL::SQL::Schema::Traits::AlterTableAddColumn;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
+
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        Column => '-IMPL::SQL::Schema::Traits::Column',
+        ArgException => '-IMPL::InvalidArgumentException',
+        OpException => '-IMPL::InvalidOperationException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        column => PROP_RO,
+        position => PROP_RO
+    ]
+};
 use IMPL::lang;
 
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property column => prop_get | owner_set;
-    public property position => prop_get | owner_set;
-}
 
 sub CTOR {
     my ($this,$tableName,$column) = @_;
     
-    $this->tableName($tableName) or die new IMPL::InvalidArgumentException("A table name is required");
+    $this->tableName($tableName) or die ArgException->new("A table name is required");
     
-    die new IMPL::InvalidArgumentException("A column should be a IMPL::SQL::Schema::Traits::Column object")
-        unless is $column, typeof IMPL::SQL::Schema::Traits::Column;
+    die ArgException->new("A column should be a IMPL::SQL::Schema::Traits::Column object")
+        unless is($column, Column);
         
     $this->column($column);
 }
 
-sub apply {
+sub CanApply {
     my ($this,$schema) = @_;
     
-    my $table = $schema->GetTable($this->tableName) or return 0;
+    my $table = $schema->GetTable($this->tableName)
+        or return 0;
     
-    return 0 if $table->GetColumn( $this->column->{name} );
+    return $table->GetColumn( $this->column->{name} ) ? 0 : 1;
+}
+
+sub Apply {
+    my ($this,$schema) = @_;
     
-    $table->AddColumn($this->column);
+    my $table = $schema->GetTable($this->tableName)
+        or die OpException->new("The specified table doesn't exists", $this->tableName);
     
-    return 1;
+    if ($this->position) {
+        $table->AddColumn($this->column);
+    } else {
+        $table->InsertColumn($this->column,$this->position);
+    }
 }
 
 #################################################
 
 package IMPL::SQL::Schema::Traits::AlterTableDropColumn;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
 
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property columnName => prop_get | owner_set;
-}
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        FK => '-IMPL::SQL::Schema::Constraint::ForeignKey',
+        ArgException => '-IMPL::InvalidArgumentException',
+        OpException => '-IMPL::InvalidOperationException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        columnName => PROP_RO,
+    ]
+};
+use IMPL::lang;
+
 
 sub CTOR { 
     my ($this,$table,$column) = @_;
     
-    $this->tableName($table) or die new IMPL::InvalidArgumentException(tableName => "A table name should be specified");
-    $this->columnName($column) or die new IMPL::InvalidArgumentException(columnName => "A column name should be specified");
+    $this->tableName($table) or die ArgException->new(tableName => "A table name should be specified");
+    $this->columnName($column) or die ArgException->new(columnName => "A column name should be specified");
 }
 
-sub apply {
+sub CanApply {
     my ($this,$schema) = @_;
     
-    local $@;
+    my $table = $schema->GetTable($this->tableName)
+        or return 0;
+    
+    $table->GetColumn($this->columnName) or
+        return 0;
     
-    return eval {
-        $schema->GetTable($this->tableName)->RemoveColumn($this->columnName);
-        return 1;
-    } || 0;
+    # столбец 
+    return $table->GetColumnConstraints($this->columnName)
+        ? 0
+        : 1
+    ;
+}
+
+sub Apply {
+    my ($this,$schema) = @_;
+    
+    my $table = $schema->GetTable($this->tableName)
+        or die OpException->new("The specified table doesn't exists", $this->tableName);
+    
+    $table->RemoveColumn($this->columnName);        
 }
 
 #################################################
 
 package IMPL::SQL::Schema::Traits::AlterTableChangeColumn;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
 
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property columnName => prop_get | owner_set;
-    public property columnType => prop_all;
-    public property defaultValue => prop_all;
-    public property isNullable => prop_all;
-    public property position => prop_all;
-    public property options => prop_all; # hash diff format, (keys have a prefix '+' - add or update value, '-' remove value)
-}
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        Constraint => '-IMPL::SQL::Schema::Traits::Constraint',
+        ArgException => '-IMPL::InvalidArgumentException',
+        OpException => '-IMPL::InvalidOperationException'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        columnName => PROP_RO,
+        columnType => PROP_RW,
+        defaultValue => PROP_RW,
+        isNullable => PROP_RW,
+        position => PROP_RW,
+        options => PROP_RW # hash diff format, (keys have a prefix '+' - add or update value, '-' remove value)
+    ]
+};
+use IMPL::lang;
 
 sub CTOR {
     my ($this, $table,$column,%args) = @_;
     
-    $this->tableName($table) or die new IMPL::InvalidArgumentException(tableName => "A table name is required");
-    $this->columnName($column) or die new IMPL::InvalidArgumentException(columnName => "A column name is required");
+    $this->tableName($table) or die ArgException->new(tableName => "A table name is required");
+    $this->columnName($column) or die ArgException->new(columnName => "A column name is required");
     
     $this->$_($args{$_})
         for (grep exists $args{$_}, qw(columnType defaultValue isNullable options));
 }
 
-sub apply {
+sub CanApply {
+    my ($this,$schema) = @_;
+    
+    my $table = $schema->GetTable($this->tableName)
+        or return 0;
+    
+    return $table->GetColumn($this->columnName) ? 1 : 0;
+}
+
+sub Apply {
     my ($this,$schema) = @_;
     
-    local $@;
+    my $table = $schema->GetTable($this->tableName)
+        or die OpException->new("The specified table doesn't exists", $this->tableName);
+    
+    my $column = $table->GetColumn($this->columnName)
+        or die OpException->new("The specified column doesn't exists", $this->tableName, $this->columnName);
     
-    return eval {
-        my $column = $schema->GetTable($this->tableName)->GetColumn($this->columnName);
-        $column->SetType($this->columnType) if defined $this->columnType;
-        $column->SetNullable($this->isNullable) if defined $this->isNullable;
-        $column->SetDefaultValue($this->defaultValue) if defined $this->defaultValue;
-        $column->SetOptions($this->options) if defined $this->options;
-        
-        return 1;
-    } || 0;
+    $column->SetType($this->columnType) if defined $this->columnType;
+    $column->SetNullable($this->isNullable) if defined $this->isNullable;
+    $column->SetDefaultValue($this->defaultValue) if defined $this->defaultValue;
+    $column->SetOptions($this->options) if defined $this->options;
+    
+    $table->SetColumnPosition($this->position)
+        if ($this->position);
+
 }
 
 #################################################
 
 package IMPL::SQL::Schema::Traits::AlterTableAddConstraint;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
+
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        Constraint => '-IMPL::SQL::Schema::Traits::Constraint',
+        ArgException => '-IMPL::InvalidArgumentException',
+        FK => '-IMPL::SQL::Schema::Traits::ForeignKey'
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        constraint => PROP_RO
+    ]
+};
 use IMPL::lang;
 
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property constraint => prop_get | owner_set;
-}
-
 sub CTOR {
     my ($this,$table,$constraint) = @_;
     
-    $this->tableName($table) or die new IMPL::InvalidArgumentException( tableName => "A table name is required");
+    $this->tableName($table) or die ArgException->new( tableName => "A table name is required");
     
-    die new IMPL::InvalidArgumentException(constaraint => "A valid IMPL::SQL::Schema::Traits::Constarint is required")
-        unless is $constraint, typeof IMPL::SQL::Schema::Traits::Constraint;
+    die ArgException->new(constaraint => "A valid " . Constraint . " is required")
+        unless is($constraint, Constraint);
         
     $this->constraint($constraint);
 }
 
-sub apply {
+sub CanApply {
+    my ($this, $schema) = @_;
+    
+    my $table = $schema->GetTable($this->tableName)
+        or return 0;
+        
+    my $constraint = $this->constraint;
+    
+    my @columns = map $table->GetColumn($_), @{$constraint->{columns} || []};
+
+    # проверяем, что в таблице есть все столбцы для создания ограничения
+    return 0 if grep not($_), @columns;
+    
+    if (is($constraint,FK)) {
+        warn "FK";
+        my $foreignTable = $schema->GetTable($constraint->{foreignTable})
+            or return 0;
+
+        warn "Table OK";
+        my @foreignColumns = map $foreignTable->GetColumn($_), @{$constraint->{foreignColumns}||[]};
+        
+        # внешняя таблица имеет нужные столбцы
+        return 0
+            if grep not($_), @foreignColumns;
+            
+        warn "FK Columns OK";
+            
+        return 0
+            if grep not($columns[$_]->type->SameValue($foreignColumns[$_]->type)), (0 .. $#columns);
+            
+        warn "FK Columns types OK";
+    }
+    
+    return 1;
+}
+
+sub Apply {
     my ($this,$schema) = @_;
     
-    local $@;
+    my $table = $schema->GetTable($this->tableName)
+        or die IMPL::InvalidOperationException->new("The specified table doesn't exists", $this->tableName);
+        
+    my $constraint = $this->constraint;
     
-    return eval {
-        $schema->GetTable($this->tableName)->AddConstraint($this->constraint->constraintClass, $this->constraint);
-        return 1;
-    } || 0;
+    if (is($constraint,FK)) {
+        my $args = { %$constraint };
+        $args->{referencedTable} = $schema->GetTable(delete $args->{foreignTable});
+        $args->{referencedColumns} = delete $args->{foreignColumns};
+        $table->AddConstraint($constraint->constraintClass, $args);
+    } else {
+        $table->AddConstraint($constraint->constraintClass, $constraint);
+    }
     
 }
 
 #################################################
 
 package IMPL::SQL::Schema::Traits::AlterTableDropConstraint;
-use parent qw(-norequire IMPL::SQL::Schema::Traits);
-use IMPL::Class::Property;
-
-BEGIN {
-    public property tableName => prop_get | owner_set;
-    public property constraintName => prop_get | owner_set;
-}
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        PK => '-IMPL::SQL::Schema::Constraint::PrimaryKey'        
+    },
+    base => [
+        '-IMPL::SQL::Schema::Traits' => undef
+    ],
+    props => [
+        tableName => PROP_RO,
+        constraintName => PROP_RO
+    ]
+};
+use IMPL::lang qw(is);
 
 sub CTOR {
     my ($this,$table,$constraint) = @_;
@@ -377,15 +542,30 @@
     $this->constraintName($constraint);
 }
 
-sub apply {
+sub CanApply {
     my ($this,$schema) = @_;
     
-    my $table = $schema->GetTable($this->tableName) or return 0;
+    my $table = $schema->GetTable($this->tableName);
+    
+    my $constraint = $table->GetConstraint($this->constraintName)
+        or return 0;
     
-    return 0 unless $table->GetConstraint($this->constraintName);
+    # есть ли внешние ключи на данную таблицу    
+    return (
+        is($constraint,PK)
+        && values( %{$constraint->connectedFK || {}} )
+        ? 0
+        : 1
+    );
+}
+
+sub Apply {
+    my ($this,$schema) = @_;
+    
+    my $table = $schema->GetTable($this->tableName)
+        or die IMPL::InvalidOperationException->new("The specified table doesn't exists", $this->tableName);
     
     $table->RemoveConstraint($this->constraintName);
-    return 1;
 }
 
 
@@ -413,9 +593,9 @@
 
 Методы обще для всех примитивных операций.
 
-=head3 C<apply($schema)>
+=head3 C<CanApply($schema)>
 
-Пытается приминить операцию к указанной схеме.
+Определяет возможность применения операции к указанной схеме.
 
 Возвращаемое значение:
 
@@ -423,7 +603,7 @@
 
 =item C<true>
 
-Операция успешно применена к схеме.
+Операция приминима к схеме.
 
 =item C<false>
 
@@ -431,6 +611,10 @@
 
 =back
 
+=head3 C<Apply($schema)>
+
+Применяет операцию к указанной схеме.
+
 =head2 Primitive operations
 
 =head3 C<IMPL::SQL::Schema::Traits::CreateTable>