[Bps-public-commit] dbix-searchbuilder branch, in-operator, created. 1.63-2-ge37c201

Ruslan Zakirov ruz at bestpractical.com
Sun Feb 3 06:35:49 EST 2013


The branch, in-operator has been created
        at  e37c201c2b4df2b73a21df77ee0f58af1a90fcc4 (commit)

- Log -----------------------------------------------------------------
commit e37c201c2b4df2b73a21df77ee0f58af1a90fcc4
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Feb 15 00:06:31 2012 +0400

    add support for [NOT] IN operator in ->Limit
    
    VALUE can be array reference or DBIx::SearchBuilder instance

diff --git a/Changes b/Changes
index 3c0b285..d7bfd27 100755
--- a/Changes
+++ b/Changes
@@ -1,5 +1,9 @@
 Revision history for Perl extension DBIx::SearchBuilder.
 
+1.64
+
+* IN and NOT IN operators in ->Limit method
+
 1.63 Fri Sep 14 2012 01:19:38 GMT+0400 (MSK)
 
 * joins_are_distinct hint to indicate that distinct is not
diff --git a/Makefile.PL b/Makefile.PL
index 09afb1b..af765ff 100755
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -10,6 +10,7 @@ requires('Encode' => '1.99');
 requires('Class::ReturnValue', 0.40);
 requires('Cache::Simple::TimedExpiry' => '0.21');
 requires('Clone');
+requires('Scalar::Util');
 build_requires('Test::More' => 0.52);
 build_requires('DBD::SQLite');
 build_requires('File::Temp');
diff --git a/lib/DBIx/SearchBuilder.pm b/lib/DBIx/SearchBuilder.pm
index d042d27..798b8da 100755
--- a/lib/DBIx/SearchBuilder.pm
+++ b/lib/DBIx/SearchBuilder.pm
@@ -8,6 +8,7 @@ our $VERSION = "1.63";
 
 use Clone qw();
 use Encode qw();
+use Scalar::Util qw(blessed);
 
 =head1 NAME
 
@@ -760,6 +761,17 @@ ENDSWITH is like LIKE, except it prepends a % to the beginning of the string
 MATCHES is equivalent to the database's LIKE -- that is, it's actually LIKE, but
 doesn't surround the string in % signs as LIKE does.
 
+=item "IN" and "NOT IN"
+
+VALUE can be an array reference or an object inherited from this class. If
+it's not then it's treated as any other operator and in most cases SQL would
+be wrong. Values in array are considered as constants and quoted according
+to QUOTEVALUE.
+
+If object is passed as VALUE then its select statement is used. If no L</Column>
+is selected then C<id> is used, if more than one selected then warning is issued
+and first column is used.
+
 =back
 
 =item ENTRYAGGREGATOR 
@@ -836,6 +848,33 @@ sub Limit {
         elsif ( $args{'OPERATOR'} =~ /ENDSWITH/i ) {
             $args{'VALUE'}    = "%" . $args{'VALUE'};
         }
+        elsif ( $args{'OPERATOR'} =~ /\bIN$/i ) {
+            if ( blessed $args{'VALUE'} && $args{'VALUE'}->isa(__PACKAGE__) ) {
+                # if no columns selected then select id
+                local $args{'VALUE'}{'columns'} = $args{'VALUE'}{'columns'};
+                unless ( $args{'VALUE'}{'columns'} ) {
+                    $args{'VALUE'}->Column( FIELD => 'id' );
+                } elsif ( @{ $args{'VALUE'}{'columns'} } > 1 ) {
+                    warn "Collection in '$args{OPERATOR}' with more than one column selected, using first";
+                    splice @{ $args{'VALUE'}{'columns'} }, 1;
+                }
+                $args{'VALUE'} = '('. $args{'VALUE'}->BuildSelectQuery .')';
+                $args{'QUOTEVALUE'} = 0;
+            }
+            elsif ( ref $args{'VALUE'} ) {
+                if ( $args{'QUOTEVALUE'} ) {
+                    my $dbh = $self->_Handle->dbh;
+                    $args{'VALUE'} = join ', ', map $dbh->quote( $_ ), @{ $args{'VALUE'} };
+                } else {
+                    $args{'VALUE'} = join ', ', @{ $args{'VALUE'} };
+                }
+                $args{'VALUE'} = "($args{VALUE})";
+                $args{'QUOTEVALUE'} = 0;
+            }
+            else {
+                # otherwise behave in backwards compatible way
+            }
+        }
         $args{'OPERATOR'} =~ s/(?:MATCHES|ENDSWITH|STARTSWITH)/LIKE/i;
 
         #if we're explicitly told not to to quote the value or
diff --git a/t/01searches.t b/t/01searches.t
index 9fdfc45..ce01165 100644
--- a/t/01searches.t
+++ b/t/01searches.t
@@ -7,7 +7,7 @@ use Test::More;
 BEGIN { require "t/utils.pl" }
 our (@AvailableDrivers);
 
-use constant TESTS_PER_DRIVER => 117;
+use constant TESTS_PER_DRIVER => 130;
 
 my $total = scalar(@AvailableDrivers) * TESTS_PER_DRIVER;
 plan tests => $total;
@@ -167,6 +167,67 @@ SKIP: {
 	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS NOT', VALUE => 'NULL', QOUTEVALUE => 0 );
 	is( $users_obj->Count, $count_all - 2, "found users who has phone number filled" );
 
+	# IN [...] operator
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
+	is( $users_obj->Count, 2, "found two users using IN operator" );
+	is_deeply(
+        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
+        [ 'cubic', 'obra' ],
+        'found correct records',
+    );
+	$users_obj->CleanSlate;
+	$users_obj->Limit( FIELD => 'Login', OPERATOR => 'NOT IN', VALUE => ['obra', 'cubic'] );
+	is( $users_obj->Count, 2, "found two users using NOT IN operator" );
+	is_deeply(
+        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
+        [ 'autrijus', 'glasser' ],
+        'found correct records',
+    );
+
+	# IN $collection operator
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+    {
+        my $tmp = $users_obj->Clone;
+        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
+        $users_obj->Limit( FIELD => 'id', OPERATOR => 'IN', VALUE => $tmp );
+    }
+	is( $users_obj->Count, 2, "found two users using IN operator" );
+	is_deeply(
+        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
+        [ 'cubic', 'obra' ],
+        'found correct records',
+    );
+	$users_obj->CleanSlate;
+    {
+        my $tmp = $users_obj->Clone;
+        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
+        $users_obj->Limit( FIELD => 'id', OPERATOR => 'NOT IN', VALUE => $tmp );
+    }
+	is( $users_obj->Count, 2, "found two users using IN operator" );
+	is_deeply(
+        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
+        [ 'autrijus', 'glasser' ],
+        'found correct records',
+    );
+	# IN with object and Column preselected
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+    {
+        my $tmp = $users_obj->Clone;
+        $tmp->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => ['obra', 'cubic'] );
+        $tmp->Column(FIELD => 'Login');
+        $users_obj->Limit( FIELD => 'Login', OPERATOR => 'IN', VALUE => $tmp );
+    }
+	is( $users_obj->Count, 2, "found two users using IN operator" );
+	is_deeply(
+        [ sort map $_->Login, @{ $users_obj->ItemsArrayRef } ],
+        [ 'cubic', 'obra' ],
+        'found correct records',
+    );
+
 	# ORDER BY / GROUP BY
 	$users_obj->CleanSlate;
 	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list