Alzabo
view release on metacpan or search on metacpan
lib/Alzabo/Runtime/Schema.pm view on Meta::CPAN
$self->{maintain_integrity} = $val if defined $val;
}
sub set_quote_identifiers
{
my $self = shift;
my $val = shift;
$self->{quote_identifiers} = $val if defined $val;
}
sub connect
{
my $self = shift;
my %p;
$p{user} = $self->user if defined $self->user;
$p{password} = $self->password if defined $self->password;
$p{host} = $self->host if defined $self->host;
$p{port} = $self->port if defined $self->port;
$self->driver->connect( %p, @_ );
# $self->set_referential_integrity( ! $self->driver->supports_referential_integrity );
}
sub disconnect
{
my $self = shift;
$self->driver->disconnect;
}
sub one_row
{
# could be replaced with something potentially more efficient
return shift->join(@_)->next;
}
use constant JOIN_SPEC => { join => { type => ARRAYREF | OBJECT,
optional => 1 },
tables => { type => ARRAYREF | OBJECT,
optional => 1 },
select => { type => ARRAYREF | OBJECT,
optional => 1 },
where => { type => ARRAYREF,
optional => 1 },
order_by => { type => ARRAYREF | HASHREF | OBJECT,
optional => 1 },
limit => { type => SCALAR | ARRAYREF,
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
@tables = values %{ { map { $_ => $_ }
grep { Alzabo::Utils::safe_isa( $_, 'Alzabo::Table' ) }
map { @$_ } @{ $p{join} } } };
}
else
{
@tables = grep { Alzabo::Utils::safe_isa($_, 'Alzabo::Table') } @{ $p{join} };
}
if ( $p{distinct} )
{
$p{distinct} =
Alzabo::Utils::is_arrayref( $p{distinct} ) ? $p{distinct} : [ $p{distinct} ];
}
if ( $p{order_by} )
{
$p{order_by} =
Alzabo::Utils::is_arrayref( $p{order_by} )
? $p{order_by}
: $p{order_by}
? [ $p{order_by} ]
: undef;
}
# We go in this order: $p{select}, $p{distinct}, @tables
my @select_tables = ( $p{select} ?
( Alzabo::Utils::is_arrayref( $p{select} ) ?
@{ $p{select} } : $p{select} ) :
$p{distinct} ?
@{ $p{distinct} } :
@tables );
my $sql = Alzabo::Runtime::sqlmaker( $self, \%p );
my @select_cols;
if ( $p{distinct} )
{
my %distinct = map { $_ => 1 } @{ $p{distinct} };
# hack so distinct is not treated as a function, just a
# bareword in the SQL
@select_cols = ( 'DISTINCT',
map { ( $_->primary_key,
$_->prefetch ?
$_->columns( $_->prefetch ) :
() ) }
@{ $p{distinct} }
);
lib/Alzabo/Runtime/Schema.pm view on Meta::CPAN
my %p = @_;
return $self->function( select => Alzabo::Runtime::sqlmaker( $self, \%p )->COUNT('*'),
%p,
);
}
sub function
{
my $self = shift;
my %p = @_;
my $sql = $self->_select_sql(%p);
my $method =
Alzabo::Utils::is_arrayref( $p{select} ) && @{ $p{select} } > 1 ? 'rows' : 'column';
$sql->debug(\*STDERR) if Alzabo::Debug::SQL;
print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;
return $self->driver->$method( sql => $sql->sql,
bind => $sql->bind );
}
sub select
{
my $self = shift;
my $sql = $self->_select_sql(@_);
$sql->debug(\*STDERR) if Alzabo::Debug::SQL;
print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;
return $self->driver->statement( sql => $sql->sql,
bind => $sql->bind );
}
use constant _SELECT_SQL_SPEC => { join => { type => ARRAYREF | OBJECT,
optional => 1 },
tables => { type => ARRAYREF | OBJECT,
optional => 1 },
select => { type => SCALAR | ARRAYREF | OBJECT,
optional => 1 },
where => { type => ARRAYREF,
optional => 1 },
group_by => { type => ARRAYREF | HASHREF | OBJECT,
optional => 1 },
order_by => { type => ARRAYREF | HASHREF | OBJECT,
optional => 1 },
having => { type => ARRAYREF,
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
@tables = values %{ { map { $_ => $_ }
grep { Alzabo::Utils::safe_isa( 'Alzabo::Table', $_ ) }
map { @$_ } @{ $p{join} } } };
}
else
{
@tables = grep { Alzabo::Utils::safe_isa( 'Alzabo::Table', $_ ) } @{ $p{join} };
}
my @funcs = Alzabo::Utils::is_arrayref( $p{select} ) ? @{ $p{select} } : $p{select};
my $sql = ( Alzabo::Runtime::sqlmaker( $self, \%p )->
select(@funcs) );
$self->_join_all_tables( sql => $sql,
join => $p{join} );
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};
Alzabo::Runtime::process_having_clause( $sql, $p{having} )
if exists $p{having};
Alzabo::Runtime::process_order_by_clause( $sql, $p{order_by} )
if exists $p{order_by};
$sql->limit( ref $p{limit} ? @{ $p{limit} } : $p{limit} ) if $p{limit};
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 ],
# [ $t_1 => $t_3, $fk ],
# [ left_outer_join => $t_3 => $t_4 ],
# [ left_outer_join => $t_3 => $t_5, undef, [ $where_clause ] ]
#
if ( Alzabo::Utils::is_arrayref( $p{join}->[0] ) )
{
my %map;
my %tables;
foreach my $set ( @{ $p{join} } )
{
# we take some care not to change the contents of $set,
# because the caller may reuse the variable being
# referenced, and changes here could break that.
# XXX - improve
params_exception
'The table map must contain only two tables per array reference'
if @$set > 5;
my @tables;
if ( ! ref $set->[0] )
{
$set->[0] =~ /^(right|left|full)_outer_join$/i
or params_exception "Invalid join type: $set->[0]";
@tables = @$set[1,2];
push @from, [ $1, @tables, @$set[3, 4] ];
}
else
{
@tables = @$set[0,1];
push @from, grep { ! exists $tables{ $_->alias_name } } @tables;
push @joins, [ @tables, $set->[2] ];
}
# Track the tables we've seen
@tables{ $tables[0]->alias_name, $tables[1]->alias_name } = (1, 1);
# Track their relationships
push @{ $map{ $tables[0]->alias_name } }, $tables[1]->alias_name;
push @{ $map{ $tables[1]->alias_name } }, $tables[0]->alias_name;
}
# just get one key to start with
my ($key) = (each %tables)[0];
delete $tables{$key};
my @t = @{ delete $map{$key} };
( run in 0.564 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )