[Jifty-commit] r6669 - Jifty-DBI/branches/tisql/lib/Jifty/DBI

Jifty commits jifty-commit at lists.jifty.org
Wed Mar 18 21:31:58 EDT 2009


Author: ruz
Date: Wed Mar 18 21:31:58 2009
New Revision: 6669

Modified:
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Tisql.pm

Log:
* factor out Tisql::Column and Tisql::Condition classes,
  to implement not strings based API

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Tisql.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Tisql.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Tisql.pm	Wed Mar 18 21:31:58 2009
@@ -111,6 +111,8 @@
         },
     );
 
+    Test::More::diag( Dumper $tree->{'conditions'} );
+
     $self->{'tisql'}{'conditions'} = $tree->{'conditions'};
     $self->apply_query_tree( $tree->{'conditions'} );
     return $self;
@@ -132,10 +134,14 @@
             $self->apply_query_tree( $element, $ea );
             next;
         }
+        elsif ( blessed $element && $element->isa( 'Jifty::DBI::Tisql::Condition' ) ) {
+            $self->apply_query_condition( $collection, $ea, $element );
+        }
         elsif ( ref $element eq 'HASH' ) {
+            Carp::confess( "booo" );
             $self->apply_query_condition( $collection, $ea, $element );
         } else {
-            die "wrong query tree";
+            die "wrong query tree ". Dumper( $element, $tree );
         }
     }
     $collection->close_paren('tisql');
@@ -145,7 +151,7 @@
     my ($self, $collection, $ea, $condition, $join) = @_;
 
     die "left hand side must be always column specififcation"
-        unless ref $condition->{'lhs'} eq 'HASH';
+        unless ref $condition->{'lhs'} eq 'Jifty::DBI::Tisql::Column';
 
     my $modifier = $condition->{'modifier'};
     my $op     = $condition->{'op'};
@@ -208,7 +214,7 @@
             column           => $condition->{'lhs'}{'chain'}[-1]{'name'},
             operator         => $op,
         );
-        if ( ref $condition->{'rhs'} eq 'HASH' ) {
+        if ( ref $condition->{'rhs'} eq 'HASH' || ref $condition->{'rhs'} eq 'Jifty::DBI::Tisql::Column' ) {
             $limit{'quote_value'} = 0;
             $limit{'value'} =
                 $self->resolve_join( $condition->{'rhs'} )
@@ -236,7 +242,7 @@
             column           => $condition->{'lhs'}{'chain'}[-1]{'name'},
             operator         => $op,
         );
-        if ( ref $condition->{'rhs'} eq 'HASH' ) {
+        if ( ref $condition->{'rhs'} eq 'HASH' || ref $condition->{'rhs'} eq 'Jifty::DBI::Tisql::Column' ) {
             $limit{'quote_value'} = 0;
             $limit{'value'} =
                 $self->resolve_join( $condition->{'rhs'} )
@@ -292,7 +298,7 @@
     if ( my $alias = $meta->{'alias'} ) {
         die "Couldn't find alias $alias"
             unless $aliases->{ $alias };
-        my $target = $self->qualify_column( $aliases->{ $alias }, $aliases, $collection );
+        my $target = $aliases->{ $alias }->qualify( $self, $aliases, $collection );
         my $item = $target->{'chain'}[-1]{'refers_to'}
             or die "Last column of alias '$alias' is not a reference";
         %last = (
@@ -336,7 +342,7 @@
                         column      => $cond->{'lhs'}{'column'},
                         operator    => $cond->{'op'},
                     );
-                    if ( ref($cond->{'rhs'}) eq 'HASH' ) {
+                    if ( ref($cond->{'rhs'}) eq 'HASH' || ref($cond->{'rhs'}) eq 'Jifty::DBI::Tisql::Column' ) {
                         $limit{'quote_value'} = 0;
                         $limit{'value'} = $cond->{'rhs'}{'table'}{'sql_alias'} .'.'. $cond->{'rhs'}{'column'};
                     }
@@ -496,7 +502,9 @@
 
         foreach my $side (qw(lhs rhs)) {
             my ($left_border, $right_border) = (0, -1);
-            unless ( ref $cond->{ $side } eq 'HASH' ) {
+            unless ( ref $cond->{ $side } eq 'HASH' || ref $cond->{ $side } eq 'Jifty::DBI::Tisql::Column' ) {
+                Carp::confess( "boo") if ref $cond->{ $side } eq 'HASH';
+
                 if ( $placeholders && $cond->{ $side } =~ /^%($re_ph_name)$/o ) {
                     if ( defined ( my $phs = $placeholders->{ $1 } ) ) {
                         if ( ref $phs ) {
@@ -578,7 +586,104 @@
     return \@res;
 }
 
+sub check_query_condition {
+    my ($self, $cond) = @_;
+
+    die "Last column in '". $cond->{'lhs'}{'string'} ."' is virtual" 
+        if $cond->{'lhs'}{'column'}->virtual;
+
+    if ( $cond->{'op_type'} eq 'col_op_col' ) {
+        die "Last column in '". $cond->{'rhs'}{'string'} ."' is virtual" 
+            if $cond->{'rhs'}{'column'}->virtual;
+    }
+
+    return $cond;
+}
+
 sub parse_condition {
+    my $self = shift;
+    return Jifty::DBI::Tisql::Condition->parse(@_);
+}
+
+sub parse_column {
+    my $self = shift;
+    return Jifty::DBI::Tisql::Column->parse(@_);
+}
+
+sub external_reference {
+    my $self = shift;
+    my %args = @_;
+
+    my $record = $args{'record'};
+    my $column = $args{'column'};
+    my $name   = $column->name;
+
+    my $sql_alias = $self->{'collection'}->new_alias( $record );
+    push @{ $self->{'collection'}{'explicit_joins_order'} ||= [] }, $sql_alias;
+
+    my $aliases;
+    local $self->{'aliases'} = $aliases = { __record__ => Jifty::DBI::Tisql::Column->new(
+        string       => '.__record__',
+        alias        => '',
+        is_qualified => 1,
+        chain        => [ {
+            name      => '__record__',
+            refers_to => $record,
+            sql_alias => $sql_alias,
+        } ],
+    ) };
+
+    my $column_cb = sub {
+        my $str = shift;
+        $str = "__record__". $str if 0 == rindex $str, '.', 0;
+        substr($str, 0, length($name)) = '' if 0 == rindex $str, "$name.", 0;
+        return $self->parse_column($str)->qualify( $self, $aliases );
+    };
+    my $conditions = $parser->as_array(
+        $column->tisql,
+        operand_cb => sub {
+            return $self->parse_condition( 'join', $_[0], $column_cb )
+        },
+    );
+    $conditions = [
+        $conditions,
+        'AND',
+        Jifty::DBI::Tisql::Condition->from_struct(
+            $self->parse_column('__record__.id')->qualify( $self, $aliases),
+            $record->id || 0,
+        ),
+    ];
+    $self->apply_query_tree( $conditions );
+
+    return $self;
+}
+
+package Jifty::DBI::Tisql::Condition;
+
+use strict;
+use warnings;
+
+use overload
+    "&"  => "bit_and_op",
+    "|"  => "bit_or_op",
+    "&=" => "bit_assign_and_op",
+    "|=" => "bit_assign_or_op",
+;
+
+use Scalar::Util qw(blessed);
+
+sub new {
+    my $proto = shift;
+    return (bless { }, ref($proto)||$proto)->init(@_);
+}
+
+sub init {
+    my $self = shift;
+    %$self = @_;
+    return $self;
+}
+
+sub parse {
     my ($self, $type, $string, $cb) = @_;
 
     my %res = (
@@ -629,25 +734,98 @@
     else {
         die "$type is not valid type of a condition";
     }
-    return \%res;
+    return $self->new( %res );
 }
 
-sub check_query_condition {
-    my ($self, $cond) = @_;
+sub from_struct {
+    my $self = shift;
+    my @args = @_;
 
-    die "Last column in '". $cond->{'lhs'}{'string'} ."' is virtual" 
-        if $cond->{'lhs'}{'column'}->virtual;
+    my %res = (
+        type     => 'query',
+        op_type  => undef,    # 'col_op', 'col_op_val' or 'col_op_col'
+        modifier => '',       # '', 'has' or 'has no'
+        lhs      => undef,
+        op       => undef,
+        rhs      => undef,
+    );
 
-    if ( $cond->{'op_type'} eq 'col_op_col' ) {
-        die "Last column in '". $cond->{'rhs'}{'string'} ."' is virtual" 
-            if $cond->{'rhs'}{'column'}->virtual;
+    unless ( blessed $args[0] ) {
+        if ( lc($args[0]) eq 'has' || lc($args[0]) eq 'has no' ) {
+            $res{'modifier'} = lc shift @args;
+        } else {
+            die "First argument should be either operator modifier or column, but not '$args[0]'";
+        }
     }
 
-    return $cond;
+    if ( @args == 2 ) {
+        if ( $args[1] =~ /^$re_sql_op_un$/i ) {
+            @res{qw(op_type lhs op rhs)}
+                = ('col_op', $args[0], split /\s*(?=null)/i, $args[1]);
+        }
+        else {
+            # XXX: do we care about op_type?
+            @res{qw(op_type lhs op rhs)}
+                = ('col_op_val', $args[0], '=', $args[1]);
+        }
+    }
+    elsif ( @args == 3 ) {
+        die "$args[1] is not a valid operator"
+            unless $args[1] =~ /^$re_sql_op_bin$/i;
+        @res{qw(op_type lhs op rhs)}
+            = ('col_op_col', $args[0], $args[1], $args[2]);
+    }
+    else {
+        die "Invalid format";
+    }
+    return $self->new( %res );
+}
+
+sub bit_and_op {
+    return (shift)->bit_op( @_, 'AND');
+}
+
+sub bit_or_op {
+    return (shift)->bit_op( @_, 'OR');
+}
+
+sub bit_op {
+    my ($self, $other, $invert, $op) = @_;
+    die "'$other' is not a query condition"
+        unless blessed $other && $other->isa(ref($self));
+    my $res = $invert
+        ? [ $other, $op, $self->{'tree'} ]
+        : [ $self->{'tree'}, $op, $other ];
+    return $self->new( tree => $res );
+}
+
+sub bit_assign_and_op {
+    return (shift)->bit_assign_op( @_, 'AND');
+}
+
+sub bit_assign_or_op {
+    return (shift)->bit_assign_op( @_, 'OR');
+}
+
+sub bit_assign_op {
+    my ($self, $other, $invert, $op) = @_;
+    die "'$other' is not a query condition"
+        unless blessed $other && $other->isa(ref($self));
+
+    if ( $invert ) {
+        $other->{'tree'} = [ $other->{'tree'}, $op, $self->{'tree'} ];
+    } else {
+        $self->{'tree'} = [ $self->{'tree'}, $op, $other->{'tree'} ];
+    }
+    return $self;
 }
 
+package Jifty::DBI::Tisql::Column;
+
+use strict;
+use warnings;
 
-# returns something like:
+# represents something like:
 # {
 #   'string'  => 'nodes.attr{name => "category"}.value',
 #   'alias'   => 'nodes',                            # alias or ''
@@ -655,7 +833,7 @@
 #        {
 #          'name' => 'attr',
 #          'string' => 'nodes.attr{name => "category"}',
-#          'placeholders' => {name => '"category"'},
+#          'placeholders' => {name => 'category'},
 #        },
 #        {
 #          'name' => 'value',
@@ -665,9 +843,19 @@
 #   ],
 # }
 # no look ups, everything returned as is,
-# even placeholders' strings are not de-escaped
 
-sub parse_column {
+sub new {
+    my $proto = shift;
+    return (bless { }, ref($proto)||$proto)->init(@_);
+}
+
+sub init {
+    my $self = shift;
+    %$self = @_;
+    return $self;
+}
+
+sub parse {
     my $self = shift;
     my $string = shift;
 
@@ -703,18 +891,45 @@
         $prev = $col->{'string'};
     }
     $res{'chain'} = \@columns;
-    return \%res;
+
+    return bless \%res, ref($self)||$self;
 }
 
-sub qualify_column {
+sub from_struct {
     my $self = shift;
-    my $meta = shift;
+    my @args = @_;
+
+    my %res = (alias => '');
+
+    ($res{'alias'}) = splice @args, 0, 2
+        if @args > 2 && $args[1] eq '.';
+
+    my @columns;
+    foreach my $element ( @args ) {
+        unless ( ref $element ) {
+            push @columns, { name => $element };
+        }
+        elsif ( ref $element eq 'HASH' ) {
+            $columns[-1]{'placeholders'} = $element;
+        }
+        else {
+            die "bla-bla wrong arguments";
+        }
+    }
+    $res{'chain'} = \@columns;
+
+    return bless \%res, ref($self)||$self;
+}
+
+sub qualify {
+    my $self = shift;
+    my $tisql = shift;
     my $aliases = shift;
-    my $collection = shift || $self->{'collection'};
+    my $collection = shift || $tisql->{'collection'};
 
-    return $meta if $meta->{'is_qualified'}++;
+    return $self if $self->{'is_qualified'}++;
 
-    my $start_from = $meta->{'alias'};
+    my $start_from = $self->{'alias'};
     my ($item, $last);
     if ( !$start_from && !$aliases->{''} ) {
         $item = $collection->new_item;
@@ -722,7 +937,7 @@
         my $alias = $aliases->{ $start_from }
             || die "Couldn't find alias '$start_from'";
 
-        $self->qualify_column( $alias, $aliases, $collection );
+        $alias->qualify( $tisql, $aliases, $collection );
 
         $last = $alias;
         $item = $alias->{'chain'}[-1]{'refers_to'};
@@ -732,20 +947,21 @@
         $item = $item->new_item if $item->isa('Jifty::DBI::Collection');
     }
 
-    my @chain = @{ $meta->{'chain'} };
+    my @chain = @{ $self->{'chain'} };
     while ( my $joint = shift @chain ) {
         my $name = $joint->{'name'};
 
-        my $column = $self->get_reference( $item => $name );
+        my $column = $tisql->get_reference( $item => $name );
 
         $joint->{'column'} = $column;
         $joint->{'on'} = $item;
 
         my $classname = $column->refers_to;
         if ( !$classname && @chain ) {
-            die "column '$name' of ". ref($item) ." is not a reference, but used so in '". $meta->{'string'} ."'";
+            die "column '$name' of ". ref($item) ." is not a reference"
+                .", but used so in '". $self->{'string'} ."'";
         }
-        return $meta unless $classname;
+        return $self unless $classname;
 
         if ( UNIVERSAL::isa( $classname, 'Jifty::DBI::Collection' ) ) {
             $joint->{'refers_to'} = $classname->new( handle => $collection->_handle );
@@ -760,54 +976,6 @@
         $last = $joint;
     }
 
-    return $meta;
-}
-
-sub external_reference {
-    my $self = shift;
-    my %args = @_;
-
-    my $record = $args{'record'};
-    my $column = $args{'column'};
-    my $name   = $column->name;
-
-    my $sql_alias = $self->{'collection'}->new_alias( $record );
-    push @{ $self->{'collection'}{'explicit_joins_order'} ||= [] }, $sql_alias;
-
-    my $aliases;
-    local $self->{'aliases'} = $aliases = { __record__ => {
-        string       => '.__record__',
-        alias        => '',
-        is_qualified => 1,
-        chain        => [ {
-            name      => '__record__',
-            refers_to => $record,
-            sql_alias => $sql_alias,
-        } ],
-    } };
-
-    my $column_cb = sub {
-        my $str = shift;
-        $str = "__record__". $str if 0 == rindex $str, '.', 0;
-        substr($str, 0, length($name)) = '' if 0 == rindex $str, "$name.", 0;
-        return $self->qualify_column($self->parse_column($str), $aliases);
-    };
-    my $conditions = $parser->as_array(
-        $column->tisql,
-        operand_cb => sub {
-            return $self->parse_condition( 'join', $_[0], $column_cb )
-        },
-    );
-    $conditions = [
-        $conditions, 'AND',
-        {
-            lhs => $self->qualify_column($self->parse_column('__record__.id'), $aliases),
-            op => '=',
-            rhs => $record->id || 0,
-        },
-    ];
-    $self->apply_query_tree( $conditions );
-
     return $self;
 }
 


More information about the Jifty-commit mailing list