From: Peter Rabbitson Date: Mon, 25 May 2009 14:08:40 +0000 (+0000) Subject: Merge 'top_limit_tweaks' into 'top_limit_altfix' X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=8f6dbee92d73f1d3676515a544bffc96002402d2;p=dbsrgits%2FDBIx-Class-Historic.git Merge 'top_limit_tweaks' into 'top_limit_altfix' Shoot another Top problem, move test from top_limit_tweaks branch and delete --- 8f6dbee92d73f1d3676515a544bffc96002402d2 diff --cc lib/DBIx/Class/SQLAHacks.pm index f9a5675,4cbbcc6..7a77169 --- a/lib/DBIx/Class/SQLAHacks.pm +++ b/lib/DBIx/Class/SQLAHacks.pm @@@ -91,39 -92,6 +91,44 @@@ SQ return $sql; } +# Crappy Top based Limit/Offset support. MSSQL uses this currently, +# but may have to switch to RowNumberOver one day +sub _Top { + my ( $self, $sql, $order, $rows, $offset ) = @_; + + croak '$order supplied to SQLAHacks limit emulators must be a hash' + if (ref $order ne 'HASH'); + ++ $order = { %$order }; #copy ++ + my $last = $rows + $offset; + + my $req_order = $self->_order_by ($order->{order_by}); + my $limit_order = $req_order ? $order->{order_by} : $order->{_virtual_order_by}; + ++ delete $order->{$_} for qw/order_by _virtual_order_by/; ++ my $grpby_having = $self->_order_by ($order); ++ + my ( $order_by_inner, $order_by_outer ) = $self->_order_directions($limit_order); + + $sql =~ s/^\s*(SELECT|select)//; + + $sql = <<"SQL"; + SELECT * FROM + ( + SELECT TOP $rows * FROM + ( - SELECT TOP $last $sql $order_by_inner ++ SELECT TOP $last $sql $grpby_having $order_by_inner + ) AS foo + $order_by_outer + ) AS bar + $req_order + +SQL + return $sql; +} + + # While we're at it, this should make LIMIT queries more efficient, # without digging into things too deeply diff --cc t/42toplimit.t index 9e2bec7,6f05660..f63b74c --- a/t/42toplimit.t +++ b/t/42toplimit.t @@@ -1,119 -1,52 +1,138 @@@ -use strict; -use warnings; - -use Test::More; -use DBIx::Class::Storage::DBI; -use lib qw(t/lib); -use DBICTest; # do not remove even though it is not used -use DBIC::SqlMakerTest; - -plan tests => 8; - -my $sa = new DBIx::Class::SQLAHacks; -$sa->limit_dialect( 'Top' ); - -sub test_order { - my $args = shift; - my $order_by = $args->{order_by}; - my $expected_sql_order = $args->{expected_sql_order}; - - my $query = $sa->select( 'foo', [qw{bar baz}], undef, { - order_by => $order_by, - }, 1, 3 - ); - is_same_sql( - $query, - "SELECT * FROM ( SELECT TOP 1 * FROM ( SELECT TOP 4 bar,baz FROM foo ORDER BY $expected_sql_order->[0] ) AS foo ORDER BY $expected_sql_order->[1] ) AS bar ORDER BY $expected_sql_order->[0]", - ); -} - - test_order({ order_by => \'foo DESC' , expected_sql_order => [ 'foo DESC', 'foo ASC' ] }); - test_order({ order_by => 'foo' , expected_sql_order => [ 'foo ASC', 'foo DESC'] }); - test_order({ order_by => [ qw{ foo bar} ], expected_sql_order => [ 'foo ASC,bar ASC', 'foo DESC, bar DESC']}); - test_order({ order_by => { -asc => 'foo' }, expected_sql_order => [ 'foo ASC', 'foo DESC' ] }); - test_order({ order_by => { -desc => 'foo' }, expected_sql_order => [ 'foo DESC', 'foo ASC' ] }); - - test_order({ order_by => ['foo', { -desc => 'bar' } ], expected_sql_order => [ 'foo ASC, bar DESC', 'foo DESC, bar ASC'] }); - test_order({ order_by => {-asc => [qw{ foo bar }] }, expected_sql_order => ['foo ASC, bar ASC', 'foo DESC, bar DESC' ] }); - test_order({ order_by => - [ - { -asc => 'foo' }, - { -desc => [qw{bar}] }, - { -asc => [qw{baz frew}]}, - ], - expected_sql_order => ['foo ASC, bar DESC, baz ASC, frew ASC', 'foo DESC, bar ASC, baz DESC, frew DESC'] - }); - - is_same_sql( - $sa->select( 'foo', [qw{ bar baz}], undef, { - group_by => 'bar', - order_by => 'bar', - }, 1, 3), - "SELECT * FROM ( SELECT TOP 1 * FROM ( SELECT TOP 4 bar,baz FROM foo ORDER BY bar ASC GROUP BY bar ) AS foo ORDER BY bar DESC ) AS bar ORDER BY bar ASC"); +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; +use DBIC::SqlMakerTest; + +my $schema = DBICTest->init_schema; + +# Trick the sqlite DB to use Top limit emulation ++# We could test all of this via $sq->$op directly, ++# but some conditions needs a $rsrc +delete $schema->storage->_sql_maker->{_cached_syntax}; +$schema->storage->_sql_maker->limit_dialect ('Top'); + +my $rs = $schema->resultset ('FourKeys')->search ({}, { rows => 1, offset => 3 }); + +sub test_order { + my $args = shift; + + my $req_order = $args->{order_req} + ? "ORDER BY $args->{order_req}" + : '' + ; + + is_same_sql_bind( + $rs->search ({}, {order_by => $args->{order_by}})->as_query, + "( + SELECT * FROM ( + SELECT TOP 1 * FROM ( + SELECT TOP 4 me.foo, me.bar, me.hello, me.goodbye, me.sensors, me.read_count FROM fourkeys me ORDER BY $args->{order_inner} + ) foo ORDER BY $args->{order_outer} + ) bar + $req_order + )", + [], + ); +} + +my @tests = ( + { + order_by => \ 'foo DESC', + order_req => 'foo DESC', + order_inner => 'foo DESC', + order_outer => 'foo ASC' + }, + { + order_by => { -asc => 'foo' }, + order_req => 'foo ASC', + order_inner => 'foo ASC', + order_outer => 'foo DESC', + }, + { + order_by => 'foo', + order_req => 'foo', + order_inner => 'foo ASC', + order_outer => 'foo DESC', + }, + { + order_by => [ qw{ foo bar} ], + order_req => 'foo, bar', + order_inner => 'foo ASC,bar ASC', + order_outer => 'foo DESC, bar DESC', + }, + { + order_by => { -desc => 'foo' }, + order_req => 'foo DESC', + order_inner => 'foo DESC', + order_outer => 'foo ASC', + }, + { + order_by => ['foo', { -desc => 'bar' } ], + order_req => 'foo, bar DESC', + order_inner => 'foo ASC, bar DESC', + order_outer => 'foo DESC, bar ASC', + }, + { + order_by => { -asc => [qw{ foo bar }] }, + order_req => 'foo ASC, bar ASC', + order_inner => 'foo ASC, bar ASC', + order_outer => 'foo DESC, bar DESC', + }, + { + order_by => [ + { -asc => 'foo' }, + { -desc => [qw{bar}] }, + { -asc => [qw{hello sensors}]}, + ], + order_req => 'foo ASC, bar DESC, hello ASC, sensors ASC', + order_inner => 'foo ASC, bar DESC, hello ASC, sensors ASC', + order_outer => 'foo DESC, bar ASC, hello DESC, sensors DESC', + }, + { + order_by => undef, + order_req => undef, + order_inner => 'foo ASC, bar ASC, hello ASC, goodbye ASC', + order_outer => 'foo DESC, bar DESC, hello DESC, goodbye DESC', + }, + { + order_by => '', + order_req => undef, + order_inner => 'foo ASC, bar ASC, hello ASC, goodbye ASC', + order_outer => 'foo DESC, bar DESC, hello DESC, goodbye DESC', + }, + { + order_by => {}, + order_req => undef, + order_inner => 'foo ASC, bar ASC, hello ASC, goodbye ASC', + order_outer => 'foo DESC, bar DESC, hello DESC, goodbye DESC', + }, + { + order_by => [], + order_req => undef, + order_inner => 'foo ASC, bar ASC, hello ASC, goodbye ASC', + order_outer => 'foo DESC, bar DESC, hello DESC, goodbye DESC', + }, +); + - plan (tests => scalar @tests); ++plan (tests => scalar @tests + 1); ++ +test_order ($_) for @tests; ++ ++is_same_sql_bind ( ++ $rs->search ({}, { group_by => 'bar', order_by => 'bar' })->as_query, ++ '( ++ SELECT * FROM ++ ( ++ SELECT TOP 1 * FROM ++ ( ++ SELECT TOP 4 me.foo, me.bar, me.hello, me.goodbye, me.sensors, me.read_count FROM fourkeys me GROUP BY bar ORDER BY bar ASC ++ ) AS foo ++ ORDER BY bar DESC ++ ) AS bar ++ ORDER BY bar ++ )', ++ [], ++);