Alzabo

 view release on metacpan or  search on metacpan

lib/Alzabo/Create/Schema.pm  view on Meta::CPAN

    $self->{sql} = Alzabo::SQLMaker->load( rdbms => $p{rdbms} );

    params_exception "Alzabo::Create::Schema->new requires a name parameter\n"
        unless exists $p{name};

    $self->set_name($p{name});

    $self->{tables} = Tie::IxHash->new;

    $self->_save_to_cache unless $p{no_cache};

    return $self;
}

sub load_from_file
{
    return shift->_load_from_file(@_);
}

sub reverse_engineer
{
    my $proto = shift;
    my $class = ref $proto || $proto;
    my %p = @_;

    my $self = $class->new( name     => $p{name},
                            rdbms    => $p{rdbms},
                            no_cache => 1,
                          );

    delete $p{rdbms};
    $self->{driver}->connect(%p);

    $self->{rules}->reverse_engineer($self);

    $self->set_instantiated(1);
    my $driver = delete $self->{driver};
    $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 ) )
    {
        $p2{$_} = delete $p{$_} if exists $p{$_};
    }
    $self->add_table( table => Alzabo::Create::Table->new( schema => $self,
                                                           %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 );

    foreach ( qw( before after ) )
    {
        if ( exists $p{$_} )
        {
            $self->move_table( $_ => $p{$_},
                               table => $table );
            last;
        }
    }
}

lib/Alzabo/Create/Schema.pm  view on Meta::CPAN

            $p{columns_to} :
            [ $p{columns_to} ] ) :
          undef );

    my $f_table = $p{table_from} || $p{columns_from}->[0]->table;
    my $t_table = $p{table_to} || $p{columns_to}->[0]->table;

    if ( $p{columns_from} && $p{columns_to} )
    {
        params_exception
            "Cannot create a relationship with differing numbers of columns " .
            "on either side of the relation"
                unless @{ $p{columns_from} } == @{ $p{columns_to} };
    }

    foreach ( [ columns_from => $f_table ], [ columns_to => $t_table ] )
    {
        my ($key, $table) = @$_;
        if ( defined $p{$key} )
        {
            params_exception
                "All the columns in a given side of the relationship ".
                "must be from the same table"
                    if grep { $_->table ne $table } @{ $p{$key} };
        }
    }

    # Determined later.  This is the column that the relationship is
    # to.  As in table A/column B maps _to_ table X/column Y
    my ($col_from, $col_to);

    # cardinality from -> to
    my $cardinality =
        ( $p{cardinality}->[0] eq '1' && $p{cardinality}->[1] eq '1' ?
          '1_to_1' :
          $p{cardinality}->[0] eq '1' && $p{cardinality}->[1] eq 'n' ?
          '1_to_n' :
          'n_to_1'
        );
    my $method = "_create_${cardinality}_relationship";

    ($col_from, $col_to) = $self->$method( %p,
                                           table_from => $f_table,
                                           table_to   => $t_table,
                                         );

    eval
    {
        $f_table->make_foreign_key( columns_from => $col_from,
                                    columns_to   => $col_to,
                                    cardinality  => $p{cardinality},
                                    from_is_dependent => $p{from_is_dependent},
                                    to_is_dependent   => $p{to_is_dependent},
                                    comment => $p{comment},
                                  );
    };
    if ($@)
    {
        $tracker->backout;

        rethrow_exception($@);
    }

    my @fk;
    eval
    {
        foreach my $c ( @$col_from )
        {
            push @fk, $f_table->foreign_keys( table => $t_table,
                                              column => $c );
        }
    };
    if ($@)
    {
        $tracker->backout;

        rethrow_exception($@);
    }

    $tracker->add( sub { $f_table->delete_foreign_key($_) foreach @fk } );

    # cardinality to -> to
    my $inverse_cardinality =
        ( $p{cardinality}->[1] eq '1' && $p{cardinality}->[0] eq '1' ?
          '1_to_1' :
          $p{cardinality}->[1] eq '1' && $p{cardinality}->[0] eq 'n' ?
          '1_to_n' :
          'n_to_1'
        );
    my $inverse_method = "_create_${inverse_cardinality}_relationship";

    ($col_from, $col_to) = $self->$method( table_from => $t_table,
                                           table_to   => $f_table,
                                           columns_from => $col_to,
                                           columns_to   => $col_from,
                                           cardinality  => [ @{ $p{cardinality} }[1,0] ],
                                           from_is_dependent => $p{to_is_dependent},
                                           to_is_dependent   => $p{from_is_dependent},
                                         );

    if ($p{from_is_dependent})
    {
        $_->nullable(0) foreach @{ $p{columns_from} };
    }

    if ($p{to_is_dependent})
    {
        $_->nullable(0) foreach @{ $p{columns_to} };
    }

    eval
    {
        $t_table->make_foreign_key( columns_from => $col_from,
                                    columns_to   => $col_to,
                                    cardinality  => [ @{ $p{cardinality} }[1,0] ],
                                    from_is_dependent => $p{to_is_dependent},
                                    to_is_dependent   => $p{from_is_dependent},
                                    comment => $p{comment},
                                  );
    };
    if ($@)
    {
        $tracker->backout;

        rethrow_exception($@);
    }
}
# old name - deprecated
*add_relation = \&add_relationship;

sub _check_add_relationship_args
{
    my $self = shift;
    my %p = @_;

    foreach my $t ( $p{table_from}, $p{table_to} )
    {
        next unless defined $t;
        params_exception "Table " . $t->name . " doesn't exist in schema"
            unless $self->{tables}->EXISTS( $t->name );
    }

    params_exception "Incorrect number of cardinality elements"
        unless scalar @{ $p{cardinality} } == 2;

    foreach my $c ( @{ $p{cardinality} } )
    {
        params_exception "Invalid cardinality: $c"
            unless $c =~ /^[01n]$/i;
    }

    # No such thing as 1..0 or n..0
    params_exception "Invalid cardinality: $p{cardinality}->[0]..$p{cardinality}->[1]"
        if  $p{cardinality}->[1] eq '0';
}

sub _create_1_to_1_relationship
{
    my $self = shift;
    my %p = @_;

    return @p{ 'columns_from', 'columns_to' }
        if $p{columns_from} && $p{columns_to};

    # Add these columns to the table which _must_ participate in the
    # relationship, if there is one.  This reduces NULL values.
    # Otherwise, just add to the first table specified in the
    # relation.
    my @order;

    # If the from table is dependent or neither one is or both are ...
    if ( $p{from_is_dependent} ||
         $p{from_is_dependent} == $p{to_is_dependent} )
    {
        @order = ( 'from', 'to' );
    }
    # The to table is dependent
    else
    {
        @order = ( 'to', 'from' );
    }

    # Determine which table we are linking from.  This gets a new
    # column or has its column adjusted) ...
    my $f_table = $p{"table_$order[0]"};

lib/Alzabo/Create/Schema.pm  view on Meta::CPAN

        $t2_col = \@c;
    }

    # First we create the table.
    my $linking;
    my $name;

    if ( exists $p{name} )
    {
        $name = $p{name};
    }
    elsif ( lc $t1->name eq $t1->name )
    {
        $name = join '_', $t1->name, $t2->name;
    }
    else
    {
        $name = join '', $t1->name, $t2->name;
    }

    $linking = $self->make_table( name => $name );
    $tracker->add( sub { $self->delete_table($linking) } );

    eval
    {
        foreach my $c ( @$t1_col, @$t2_col )
        {
            $linking->make_column( name => $c->name,
                                   definition => $c->definition,
                                   primary_key => 1,
                                 );
        }

        $self->add_relationship
            ( table_from => $t1,
              table_to   => $linking,
              columns_from => $t1_col,
              columns_to   => [ $linking->columns( map { $_->name } @$t1_col ) ],
              cardinality  => [ '1', 'n' ],
              from_is_dependent => $p{from_is_dependent},
              to_is_dependent => 1,
              comment => $p{comment},
            );

        $self->add_relationship
            ( table_from => $t2,
              table_to   => $linking,
              columns_from => $t2_col,
              columns_to   => [ $linking->columns( map { $_->name } @$t2_col ) ],
              cardinality  => [ '1', 'n' ],
              from_is_dependent => $p{to_is_dependent},
              to_is_dependent => 1,
              comment => $p{comment},
            );
    };

    if ($@)
    {
        $tracker->backout;

        rethrow_exception($@);
    }
}

sub instantiated
{
    my $self = shift;

    return $self->{instantiated};
}

sub create
{
    my $self = shift;
    my %p = @_;

    my @sql = $self->make_sql;

    local $self->{db_schema_name} = delete $p{schema_name}
        if exists $p{schema_name};

    $self->{driver}->create_database(%p)
        unless $self->_has_been_instantiated(%p);

    $self->{driver}->connect(%p);

    foreach my $statement (@sql)
    {
        $self->{driver}->do( sql => $statement );
    }

    $self->save_current_name;

    $self->set_instantiated(1);
    my $driver = delete $self->{driver};
    $self->{original} = Storable::dclone($self);
    $self->{driver} = $driver;
    delete $self->{original}{original};
}

sub _has_been_instantiated
{
    my $self = shift;

    my $db_schema_name = $self->db_schema_name;

    return 1 if grep { $db_schema_name eq $_ } $self->{driver}->schemas(@_);
}

sub make_sql
{
    my $self = shift;

    if ($self->{instantiated})
    {
        return $self->rules->schema_sql_diff( old => $self->{original},
                                              new => $self );
    }
    else
    {
        return $self->rules->schema_sql($self);



( run in 0.639 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )