[Jifty-commit] r6294 - Jifty-DBI/branches/tisql-joins-refactoring/lib/Jifty/DBI
Jifty commits
jifty-commit at lists.jifty.org
Tue Feb 3 17:52:54 EST 2009
Author: ruz
Date: Tue Feb 3 17:52:54 2009
New Revision: 6294
Modified:
Jifty-DBI/branches/tisql-joins-refactoring/lib/Jifty/DBI/Tisql.pm
Log:
* another act of vandalizm on tisql
** placeholders and bindings are broken as well as aliases
** we have working crazy joins with conditions floating
around between chains
** and we're closer to prepared queries and cached joins descriptions
Modified: Jifty-DBI/branches/tisql-joins-refactoring/lib/Jifty/DBI/Tisql.pm
==============================================================================
--- Jifty-DBI/branches/tisql-joins-refactoring/lib/Jifty/DBI/Tisql.pm (original)
+++ Jifty-DBI/branches/tisql-joins-refactoring/lib/Jifty/DBI/Tisql.pm Tue Feb 3 17:52:54 2009
@@ -77,29 +77,24 @@
my $string = shift;
my @binds = @_;
- my $tree = {
- aliases => {},
- conditions => undef,
- };
-
- # parse "FROM..." prefix into $tree->{'aliases'}
+ # parse "FROM..." prefix into aliases
+ my %aliases;
if ( $string =~ s/^\s*FROM\s+($re_alias(?:\s*,\s*$re_alias)*)\s+WHERE\s+//oi ) {
- $tree->{'aliases'}->{ $_->[1] } = $self->find_column( $_->[0], $tree->{'aliases'} )
+ $aliases{ $_->[1] } = $self->parse_column( $_->[0] )
foreach map [split /\s+AS\s+/i, $_], split /\s*,\s*/, $1;
-
- while ( my ($name, $meta) = each %{ $tree->{aliases} } ) {
- $meta->{'name'} = $name;
- }
}
- my $operand_cb = sub {
- return $self->parse_condition(
- 'query', $_[0], sub { $self->find_column( $_[0], $tree->{'aliases'} ) }
- );
- };
+
+ my $tree = {};
+
$self->{'bindings'} = \@binds;
$tree->{'conditions'} = $parser->as_array(
- $string, operand_cb => $operand_cb,
+ $string, operand_cb => sub {
+ return $self->parse_condition(
+ 'query', $_[0], sub { $self->parse_column( $_[0] ) }
+ );
+ },
);
+
$self->{'bindings'} = undef;
$self->{'tisql'}{'conditions'} = $tree->{'conditions'};
$self->apply_query_tree( $tree->{'conditions'} );
@@ -107,28 +102,28 @@
}
sub apply_query_tree {
- my ($self, $tree, $join, $ea) = @_;
+ my ($self, $tree, $ea) = @_;
$ea ||= 'AND';
my $collection = $self->{'collection'};
- $collection->open_paren('tisql', $join);
+ $collection->open_paren('tisql');
foreach my $element ( @$tree ) {
unless ( ref $element ) {
$ea = $element;
next;
}
elsif ( ref $element eq 'ARRAY' ) {
- $self->apply_query_tree( $element, $join, $ea );
+ $self->apply_query_tree( $element, $ea );
next;
}
elsif ( ref $element eq 'HASH' ) {
- $self->apply_query_condition( $collection, $ea, $element, $join );
+ $self->apply_query_condition( $collection, $ea, $element );
} else {
die "wrong query tree";
}
}
- $collection->close_paren('tisql', $join);
+ $collection->close_paren('tisql');
}
sub apply_query_condition {
@@ -195,14 +190,14 @@
leftjoin => $join,
entry_aggregator => $ea,
alias => $self->resolve_join( $condition->{'lhs'} ),
- column => $condition->{'lhs'}{'column'}->name,
+ column => $condition->{'lhs'}{'chain'}[-1]{'name'},
operator => $op,
);
if ( ref $condition->{'rhs'} eq 'HASH' ) {
$limit{'quote_value'} = 0;
$limit{'value'} =
$self->resolve_join( $condition->{'rhs'} )
- .'.'. $condition->{'rhs'}{'column'}->name;
+ .'.'. $condition->{'rhs'}{'chain'}[-1]{'name'};
} else {
if ( ref $condition->{'rhs'} eq 'ARRAY' ) {
$parser->dq( $_ ) foreach @{ $condition->{'rhs'} };
@@ -220,14 +215,14 @@
my %limit = (
subclause => 'tisql',
alias => $self->resolve_join( $condition->{'lhs'} ),
- column => $condition->{'lhs'}{'column'}->name,
+ column => $condition->{'lhs'}{'chain'}[-1]{'name'},
operator => $op,
);
if ( ref $condition->{'rhs'} eq 'HASH' ) {
$limit{'quote_value'} = 0;
$limit{'value'} =
$self->resolve_join( $condition->{'rhs'} )
- .'.'. $condition->{'rhs'}{'column'}->name;
+ .'.'. $condition->{'rhs'}{'chain'}[-1]{'name'};
} else {
if ( ref $condition->{'rhs'} eq 'ARRAY' ) {
$parser->dq( $_ ) foreach @{ $condition->{'rhs'} };
@@ -258,184 +253,81 @@
sub resolve_join {
my $self = shift;
my $meta = shift;
- my $resolve_last = shift;
+ my $aliases = shift || {};
+ my $resolve_last = shift || 0;
- return $meta->{'sql_alias'}
- if $meta->{'sql_alias'} && $resolve_last;
+ return $meta->{'chain'}[-1]{'sql_alias'}
+ if $resolve_last && $meta->{'chain'}[-1]{'sql_alias'};
- my $collection = $self->{'collection'};
-
- my ($prev_alias) = ('main');
- if ( my $prev = $meta->{'previous'} ) {
- $prev_alias = $self->resolve_join( $prev, 'resolve_last' );
+ if ( my $prev = $meta->{'chain'}[-2] ) {
+ return $prev->{'sql_alias'} if !$resolve_last && $prev->{'sql_alias'};
}
- return $prev_alias unless $resolve_last;
-
- my $column = $meta->{'column'};
- my $refers = $meta->{'refers_to'};
- $refers = $refers->new_item
- if UNIVERSAL::isa( $refers, 'Jifty::DBI::Collection' );
+ #Test::More::diag( Dumper $meta );
- unless ( UNIVERSAL::isa( $refers, 'Jifty::DBI::Record' ) ) {
- die "Column '". $column->name ."' refers to '"
- . (ref($refers) || $refers)
- ."' that is not record or collection";
- }
-
- my $sql_alias = $meta->{'sql_alias'}
- = $collection->new_alias( $refers, 'LEFT' );
+ my $collection = $self->{'collection'};
- if ( $column->tisql ) {
- local $self->{'right_part_of_join'} = {
- %$meta
- };
- $self->resolve_tisql_join( $sql_alias, $meta );
+ my %last;
+ if ( my $alias = $meta->{'alias'} ) {
+ %last = (
+ sql_alias => 'main',
+ item => $collection,
+ );
} else {
- my %limit = (
- subclause => 'tisql-join',
- alias => $prev_alias,
- column => $column->virtual? 'id' : $column->name,
- operator => '=',
- quote_value => 0,
- value => $sql_alias .'.'. ($column->by || 'id')
+ %last = (
+ sql_alias => 'main',
+ item => $collection,
);
-
- if ( $self->{'inside_left_join'} ) {
- $limit{'leftjoin'} = $sql_alias;
- } else {
- $limit{'leftjoin'} = $sql_alias;
- }
- $collection->limit( %limit );
}
- return $sql_alias;
-}
-
-sub resolve_tisql_join {
- my $self = shift;
- my $alias = shift;
- my $meta = shift;
-
- my $tree = $parser->as_array(
- $meta->{'column'}->tisql,
- operand_cb => sub { return $self->parse_condition(
- 'join', $_[0], sub { return $self->find_column(
- $_[0],
- {
- '' => $meta->{'previous'},
- $meta->{'column'}->name => {
- %$meta,
- sql_alias => $alias,
- }
- },
- ) }
- ) },
- );
+ my @chain = @{ $meta->{'chain'} };
+ pop @chain unless $resolve_last;
+
+ while ( my $joint = shift @chain ) {
+ my $description = $self->describe_join($last{'item'}, $joint->{'name'});
+ my $linear = $self->linearize_join( $description );
- # fill in placeholders
- $tree = $parser->filter( $tree, sub {
- my $rhs = $_[0]->{'rhs'};
- if ( $rhs && !ref $rhs && $rhs =~ /^%([0-9]+)$/ ) {
- return 0 unless defined $meta->{'placeholders'}[ $1 - 1 ];
- $_[0]->{'rhs'} = $meta->{'placeholders'}[ $1 - 1 ];
- return 1;
- }
- foreach my $col ( grep ref $_ eq 'HASH', $rhs, $_[0]->{'lhs'} ) {
- my $tmp = $col;
- while ( $tmp ) {
- if ( my $phs = $tmp->{'placeholders'} ) {
- for ( my $i = 0; $i < @$phs; $i++ ) {
- my $ph = $phs->[$i];
- next unless defined $ph;
- next if ref $ph;
- $phs->[$i] = $meta->{'placeholders'}[ $ph - 1 ];
+ $linear->[0]{'sql_alias'} = $last{'sql_alias'};
+ $_->{'sql_alias'} = $collection->new_alias( $_->{'model'}, 'LEFT' )
+ foreach @{$linear}[1 .. @$linear - 1];
+
+ foreach my $table ( @$linear ) {
+ next unless $table->{'conditions'};
+ my $ea = 'AND';
+ $parser->walk( $table->{'conditions'}, {
+ open_paren => sub { $collection->open_paren('tisql-join', $_[1]) },
+ close_paren => sub { $collection->close_paren('tisql-join', $_[1]) },
+ operator => sub { ${$_[3]} = $_[0] },
+ operand => sub {
+ my ($cond, $collection, $alias, $ea) = @_;
+ my %limit = (
+ subclause => 'tisql-join',
+ leftjoin => $alias,
+ entry_aggregator => $$ea,
+ alias => $cond->{'lhs'}{'table'}{'sql_alias'},
+ column => $cond->{'lhs'}{'column'},
+ operator => $cond->{'op'},
+ );
+ if ( ref($cond->{'rhs'}) eq 'HASH' ) {
+ $limit{'quote_value'} = 0;
+ $limit{'value'} = $cond->{'rhs'}{'table'}{'sql_alias'} .'.'. $cond->{'rhs'}{'column'};
}
- }
- $tmp = $tmp->{previous};
- }
- }
- return 1;
- } );
-
-# Test::More::diag( Dumper $tree );
-
- $self->apply_join_tree( $tree, undef, $alias );
-}
-
-sub apply_join_tree {
- my ($self, $tree, $ea, $join, @rest) = @_;
- $ea ||= 'AND';
-
- my $collection = $self->{'collection'};
- $collection->open_paren('tisql', $join);
- foreach my $element ( @$tree ) {
- unless ( ref $element ) {
- $ea = $element;
- next;
- }
- elsif ( ref $element eq 'ARRAY' ) {
- $self->apply_join_tree( $element, $ea, $join, @rest );
- next;
- }
- elsif ( ref $element eq 'HASH' ) {
- Test::More::diag( Dumper($element) );
- if ( $element->{'lhs'}{'string'} =~ /^([^.]+)\.[^.]+\./ ) {
- # it's subjoin in join: a column described using tisql and has more
- # than just target.x = .source, but something like: target.x.y = ...
-
- # here we have
- my $alias = $1;
-
- $collection->open_paren('tisql', $join);
-
- die "here we are";
-
- $collection->close_paren('tisql', $join);
- }
- $self->apply_join_condition( $collection, $ea, $element, $join, @rest );
- } else {
- die "wrong query tree";
+ elsif ( $cond->{'rhs'} eq '?' ) {
+ die "not yet";
+ }
+ else {
+ $limit{'value'} = $cond->{'rhs'};
+ }
+ $collection->limit( %limit );
+ },
+ }, $collection, $table->{'sql_alias'}, \$ea );
}
- }
- $collection->close_paren('tisql', $join);
-}
-
-sub apply_join_condition {
- my ($self, $collection, $ea, $condition, $join) = @_;
-
- die "left hand side must be always column specififcation"
- unless ref $condition->{'lhs'} eq 'HASH';
-
-
- my $op = $condition->{'op'};
- if ( $condition->{'modifier'} && $condition->{'modifier'} eq 'has no' ) {
- die "'has' and 'has no' prefixes are only allowed in query, not in joins";
- }
- my %limit = (
- subclause => 'tisql',
- leftjoin => $join,
- entry_aggregator => $ea,
- alias => $self->resolve_join( $condition->{'lhs'} ),
- column => $condition->{'lhs'}{'column'}->name,
- operator => $op,
- );
- if ( ref $condition->{'rhs'} eq 'HASH' ) {
- $limit{'quote_value'} = 0;
- $limit{'value'} =
- $self->resolve_join( $condition->{'rhs'} )
- .'.'. $condition->{'rhs'}{'column'}->name;
- } else {
- if ( ref $condition->{'rhs'} eq 'ARRAY' ) {
- $parser->dq( $_ ) foreach @{ $condition->{'rhs'} };
- } else {
- $parser->dq( $condition->{'rhs'} );
- }
- $limit{'value'} = $condition->{'rhs'};
+ $last{'sql_alias'} = $joint->{'sql_alias'} = $linear->[-1]{'sql_alias'};
+ $last{'item'} = $linear->[-1]{'model'};
}
- $collection->limit( %limit );
+ return $last{'sql_alias'};
}
sub describe_join {
@@ -514,14 +406,6 @@
my ($orig_left, $orig_right) = @res;
@res = reverse @res if $inverse;
- my $transfer_short = sub {
- my %new = ();
- $new{'table'} = $_[0]->{'alias'}? $orig_right : $orig_left;
- weaken($new{'table'});
- $new{'column'} = $_[0]->{'chain'}[0]{'name'};
- return \%new;
- };
-
my ($tree, $node, @pnodes);
my %callback;
$callback{'open_paren'} = sub {
@@ -532,111 +416,62 @@
$callback{'operator'} = sub { push @$node, $_[0] };
$callback{'operand'} = sub {
my $cond = $_[0];
- #Test::More::diag( "dealing with condition ". Dumper($cond) );
my %new_cond = %$cond;
- foreach my $side (qw(lhs rhs)) {
- next unless ref $cond->{$side} eq 'HASH';
- next if $cond->{$side}{'is_long'};
- $new_cond{$side} = $transfer_short->( $cond->{$side} );
- }
- if ( $cond->{'op_type'} eq 'col_op_col' ) {
- if ( !$cond->{'lhs'}{'is_long'} && !$cond->{'rhs'}{'is_long'} ) {
- push @$node, \%new_cond;
- } else {
- my $deliver_to;
- foreach my $side (qw(lhs rhs)) {
- next unless $cond->{$side}{'is_long'};
- #Test::More::diag( "condition on $side side ". Dumper($cond->{$side}) );
- my @chain = @{ $cond->{$side}{'chain'} };
- my $last_column = pop @chain;
-
- my ($last_join, $conditions) = ( undef, [] );
- my $model = ($cond->{$side}{'alias'}? $orig_right : $orig_left)->{'model'};
- foreach my $ref ( @chain ) {
- my $description = $self->describe_join( $model => $ref->{'name'} );
- if ( $cond->{$side}{'alias'} eq $inverse_on ) {
- my $linear = $self->linearize_join(
- $description, 'inverse', $res[-1], $conditions
- );
- $last_join = $deliver_to = $linear->[0];
- splice @res, -1, 1, @$linear;
- } else {
-
- #Test::More::diag( "attaching to left of ". Dumper( \@res ) );
- my $linear = $self->linearize_join(
- $description, undef, $res[0]
- );
- #Test::More::diag( "linear ". Dumper( $linear ) );
- $last_join = $linear->[-1];
- splice @res, 0, 1, @$linear;
- }
+ my $set_condition_on;
- $model = $model->column( $ref->{'name'} )->refers_to->new;
- }
- push @$node, [ shift @$conditions, map +( 'AND' => $_ ), @$conditions ]
- if @$conditions;
+ foreach my $side (qw(lhs rhs)) {
+ next unless ref $cond->{ $side } eq 'HASH';
- $new_cond{$side} = {
- table => $last_join,
- column => $last_column->{'name'},
- };
+ my $col = $cond->{ $side };
- }
- if ( $deliver_to ) {
- $deliver_to->{'conditions'} = [ \%new_cond ];
- } else {
- push @$node, \%new_cond;
- }
+ unless ( $col->{'is_long'} ) {
+ $new_cond{ $side } = {
+ table => $col->{'alias'}? $orig_right : $orig_left,
+ column => $col->{'chain'}[0]{'name'},
+ };
+ weaken($new_cond{ $side }{'table'});
+ next;
}
- } else {
- unless ( $cond->{'lhs'}{'is_long'} ) {
- push @$node, \%new_cond;
- } else {
- my $side = 'lhs';
- my @chain = @{ $cond->{$side}{'chain'} };
- my $last_column = pop @chain;
-
- my $deliver_to;
-
- my ($last_join, $conditions) = ( undef, [] );
- my $model = ($cond->{$side}{'alias'}? $orig_right : $orig_left)->{'model'};
- foreach my $ref ( @chain ) {
- my $description = $self->describe_join( $model => $ref->{'name'} );
- if ( $cond->{$side}{'alias'} eq $inverse_on ) {
- my $linear = $self->linearize_join(
- $description, 'inverse', $res[-1], $conditions
- );
- $last_join = $deliver_to = $linear->[0];
- splice @res, -1, 1, @$linear;
- } else {
-
- #Test::More::diag( "attaching to left of ". Dumper( \@res ) );
- my $linear = $self->linearize_join(
- $description, undef, $res[0]
- );
- #Test::More::diag( "linear ". Dumper( $linear ) );
- $last_join = $linear->[-1];
- splice @res, 0, 1, @$linear;
- }
- $model = $model->column( $ref->{'name'} )->refers_to->new;
- }
- push @$node, [ shift @$conditions, map +( 'AND' => $_ ), @$conditions ]
- if @$conditions;
-
- $new_cond{$side} = {
- table => $last_join,
- column => $last_column->{'name'},
- };
+ my @chain = @{ $col->{'chain'} };
+ my $last_column = pop @chain;
- if ( $deliver_to ) {
- $deliver_to->{'conditions'} = [ \%new_cond ];
+ my ($last_join, $conditions) = ( undef, [] );
+ my $model = ($col->{'alias'}? $orig_right : $orig_left)->{'model'};
+ foreach my $ref ( @chain ) {
+ my $description = $self->describe_join( $model => $ref->{'name'} );
+ if ( $cond->{$side}{'alias'} eq $inverse_on ) {
+ my $linear = $self->linearize_join(
+ $description, 'inverse', $res[-1], $conditions
+ );
+ $last_join = $set_condition_on = $linear->[0];
+ splice @res, -1, 1, @$linear;
} else {
- push @$node, \%new_cond;
+ my $linear = $self->linearize_join(
+ $description, undef, $res[0]
+ );
+ $last_join = $linear->[-1];
+ splice @res, 0, 1, @$linear;
}
+
+ $model = $model->column( $ref->{'name'} )->refers_to->new;
}
+ push @$node, [ shift @$conditions, map +( 'AND' => $_ ), @$conditions ]
+ if @$conditions;
+
+ $new_cond{$side} = {
+ table => $last_join,
+ column => $last_column->{'name'},
+ };
+
}
+ if ( $set_condition_on ) {
+ $set_condition_on->{'conditions'} = [ \%new_cond ];
+ } else {
+ push @$node, \%new_cond;
+ }
+ return;
};
$tree = $node = [];
@@ -664,8 +499,7 @@
);
if ( $type eq 'query' ) {
- # TODO: query can not have placeholders %##
- if ( $string =~ /^(has(\s+no)?\s+)?($re_column)\s*($re_sql_op_bin)\s*($re_value_ph_b)$/io ) {
+ if ( $string =~ /^(has(\s+no)?\s+)?($re_column)\s*($re_sql_op_bin)\s*($re_value|$re_binding)$/io ) {
$res{'modifier'} = $2? 'has no': $1? 'has': '';
@res{qw(op_type lhs op rhs)} = ('col_op_val', $cb->($3), $4, $5);
}
@@ -685,8 +519,7 @@
}
}
elsif ( $type eq 'join' ) {
- # TODO: join can not have bindings (?)
- if ( $string =~ /^($re_column)\s*($re_sql_op_bin)\s*($re_value_ph_b)$/io ) {
+ if ( $string =~ /^($re_column)\s*($re_sql_op_bin)\s*($re_value|$re_ph)$/io ) {
@res{qw(op_type lhs op rhs)} = ('col_op_val', $cb->($1), $2, $3);
}
elsif ( $string =~ /^($re_column)\s*($re_sql_op_un)$/o ) {
@@ -745,7 +578,9 @@
my $self = shift;
my $string = shift;
- my (%res, @columns);
+ my %res = (@_);
+
+ my @columns;
$res{'string'} = $string;
($res{'alias'}, @columns) = split /\.($re_field$re_ph_access*)/o, $string;
@columns = grep defined && length, @columns;
@@ -756,7 +591,7 @@
my $string = $col;
$col =~ s/^($re_field)//;
my $field = $1;
- my @phs = split /{\s*($re_cs_values|$re_ph)?\s*}/, $col;
+ my @phs = split /{\s*($re_cs_values|$re_ph|$re_binding)?\s*}/, $col;
@phs = grep !defined || length, @phs;
$col = {
name => $field,
More information about the Jifty-commit
mailing list