From: Peter Rabbitson Date: Thu, 2 Dec 2010 10:37:02 +0000 (+0100) Subject: Support for -value op in search (for pg arrays and stuff) X-Git-Tag: v0.08125~27 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=415193793de389fdd4e5ce547f4d615fe9d3c033;p=dbsrgits%2FDBIx-Class.git Support for -value op in search (for pg arrays and stuff) --- diff --git a/Changes b/Changes index 49e5617..9e8df50 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,8 @@ Revision history for DBIx::Class - Deprecate legacy $rs->search( %condition ) syntax - NULL is now supplied unquoted to all debug-objects, in order to differentiate between a real NULL and the string 'NULL' + - New search() condition operator -value used to pass complex bind + values to DBI: search({ array_col => { -value => [1,2,3] }}) - +columns now behaves just like columns by not stripping a fully-qualified 'as' spec (i.e. foo.bar results in $obj->foo->bar) diff --git a/lib/DBIx/Class.pm b/lib/DBIx/Class.pm index 7de97c0..792e938 100644 --- a/lib/DBIx/Class.pm +++ b/lib/DBIx/Class.pm @@ -318,6 +318,8 @@ jon: Jon Schutz jshirley: J. Shirley +kaare: Kaare Rasmussen + konobi: Scott McWhirter lukes: Luke Saunders diff --git a/lib/DBIx/Class/SQLMaker.pm b/lib/DBIx/Class/SQLMaker.pm index 6ea68ba..6db2aba 100644 --- a/lib/DBIx/Class/SQLMaker.pm +++ b/lib/DBIx/Class/SQLMaker.pm @@ -26,6 +26,8 @@ Currently the enhancements to L are: =item * The -ident operator +=item * The -value operator + =back =cut @@ -84,13 +86,14 @@ sub __max_int { 0xFFFFFFFF }; sub new { my $self = shift->next::method(@_); - # use the same coderef, it is prepared to handle both cases - push @{$self->{special_ops}}, { - regex => qr/^ ident $/xi, handler => '_where_op_IDENT', - }; - push @{$self->{unary_ops}}, { - regex => qr/^ ident $/xi, handler => '_where_op_IDENT', - }; + # use the same coderefs, they are prepared to handle both cases + my @extra_dbic_syntax = ( + { regex => qr/^ ident $/xi, handler => '_where_op_IDENT' }, + { regex => qr/^ value $/xi, handler => '_where_op_VALUE' }, + ); + + push @{$self->{special_ops}}, @extra_dbic_syntax; + push @{$self->{unary_ops}}, @extra_dbic_syntax; $self; } @@ -102,7 +105,7 @@ sub _where_op_IDENT { croak "-$op takes a single scalar argument (a quotable identifier)"; } - # in case we are called as a top level special op + # in case we are called as a top level special op (no '=') my $lhs = shift; $_ = $self->_convert($self->_quote($_)) for ($lhs, $rhs); @@ -113,6 +116,30 @@ sub _where_op_IDENT { ; } +sub _where_op_VALUE { + my $self = shift; + my ($op, $rhs) = splice @_, -2; + + # in case we are called as a top level special op (no '=') + my $lhs = shift; + + my @bind = [ + ($lhs || $self->{_nested_func_lhs} || croak "Unable to find bindtype for -value $rhs"), + $rhs + ]; + + return $lhs + ? ( + $self->_convert($self->_quote($lhs)) . ' = ' . $self->_convert('?'), + @bind + ) + : ( + $self->_convert('?'), + @bind, + ) + ; +} + # Handle limit-dialect selection sub select { my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_; diff --git a/t/72pg.t b/t/72pg.t index f9f61c4..2394bed 100644 --- a/t/72pg.t +++ b/t/72pg.t @@ -198,7 +198,7 @@ for my $use_insert_returning ($test_server_supports_insert_returning use strict; use warnings; - use base 'DBIx::Class::Core'; + use base 'DBICTest::BaseResult'; __PACKAGE__->table('dbic_t_schema.array_test'); __PACKAGE__->add_columns(qw/id arrayfield/); @@ -209,43 +209,89 @@ for my $use_insert_returning ($test_server_supports_insert_returning SKIP: { skip "Need DBD::Pg 2.9.2 or newer for array tests", 4 if $DBD::Pg::VERSION < 2.009002; + my $arr_rs = $schema->resultset('ArrayTest'); + lives_ok { - $schema->resultset('ArrayTest')->create({ + $arr_rs->create({ arrayfield => [1, 2], }); } 'inserting arrayref as pg array data'; lives_ok { - $schema->resultset('ArrayTest')->update({ + $arr_rs->update({ arrayfield => [3, 4], }); } 'updating arrayref as pg array data'; - $schema->resultset('ArrayTest')->create({ + $arr_rs->create({ arrayfield => [5, 6], }); - my $afield_rs = $schema->resultset('ArrayTest')->search({ - arrayfield => \[ '= ?' => [arrayfield => [3, 4]] ], #Todo anything less ugly than this? - }); + # Search using arrays + lives_ok { + is_deeply ( + $arr_rs->search({ arrayfield => { -value => [3,4] } })->first->arrayfield, + [3,4], + 'Array value matches' + ); + } 'searching by arrayref'; - my $count; lives_ok { - $count = $afield_rs->count - } 'comparing arrayref to pg array data does not blow up'; - is($count, 1, 'comparing arrayref to pg array data gives correct result'); + is_deeply ( + $arr_rs->search({ arrayfield => { '=' => { -value => [3,4] }} })->first->arrayfield, + [3,4],, + 'Array value matches explicit equal' + ); + } 'searching by arrayref (explicit equal sign)'; - TODO: { - local $TODO = 'No introspection of scalarref conditions :('; - my $row = $afield_rs->create({}); + lives_ok { + is_deeply ( + $arr_rs->search({ arrayfield => { '>' => { -value => [3,1] }} })->first->arrayfield, + [3,4], + 'Array value matches greater than' + ); + } 'searching by arrayref (greater than)'; + + lives_ok { + is ( + $arr_rs->search({ arrayfield => { '>' => { -value => [3,7] }} })->count, + 1, + 'Greater than search found [5,6]', + ); + } 'searching by arrayref (greater than)'; + + # Find using arrays + lives_ok { + is_deeply ( + $arr_rs->find({ arrayfield => { -value => [3,4] } })->arrayfield, + [3,4], + 'Array value matches implicit equal' + ); + } 'find by arrayref'; + + lives_ok { + is_deeply ( + $arr_rs->find({ arrayfield => { '=' => { -value => [3,4] }} })->arrayfield, + [3,4], + 'Array value matches explicit equal' + ); + } 'find by arrayref (equal)'; + + # test inferred condition for creation + TODO: for my $cond ( + { -value => [3,4] }, + \[ '= ?' => [arrayfield => [3, 4]] ], + ) { + local $TODO = 'No introspection of complex conditions :('; + my $arr_rs_cond = $arr_rs->search({ arrayfield => $cond }); + + my $row = $arr_rs_cond->create({}); is_deeply ($row->arrayfield, [3,4], 'Array value taken from $rs condition'); $row->discard_changes; is_deeply ($row->arrayfield, [3,4], 'Array value made it to storage'); } } - - ########## Case check BEGIN { diff --git a/t/sqlmaker/op_value.t b/t/sqlmaker/op_value.t new file mode 100644 index 0000000..ceb441e --- /dev/null +++ b/t/sqlmaker/op_value.t @@ -0,0 +1,36 @@ +use strict; +use warnings; + +use Test::More; + +use lib qw(t/lib); +use DBIC::SqlMakerTest; + +use_ok('DBICTest'); + +my $schema = DBICTest->init_schema(); + +my $sql_maker = $schema->storage->sql_maker; + +for my $q ('', '"') { + + $sql_maker->quote_char($q); + + is_same_sql_bind ( + \[ $sql_maker->select ('artist', '*', { arr1 => { -value => [1,2] }, arr2 => { '>', { -value => [3,4] } }, field => [5,6] } ) ], + "SELECT * + FROM ${q}artist${q} + WHERE ${q}arr1${q} = ? AND + ${q}arr2${q} > ? AND + ( ${q}field${q} = ? OR ${q}field${q} = ? ) + ", + [ + [ arr1 => [1,2] ], + [ arr2 => [3,4] ], + [ field => 5 ], + [ field => 6 ], + ], + ); +} + +done_testing;