From: Peter Rabbitson Date: Mon, 4 Nov 2013 09:03:54 +0000 (+0100) Subject: Convert many live-only SQL test to standalone is_same_sql_bind cases X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ac0c082542;p=dbsrgits%2FDBIx-Class-Historic.git Convert many live-only SQL test to standalone is_same_sql_bind cases --- diff --git a/t/71mysql.t b/t/71mysql.t index 58a5669..1e31e8c 100644 --- a/t/71mysql.t +++ b/t/71mysql.t @@ -214,29 +214,6 @@ lives_ok { $cd->set_producers ([ $producer ]) } 'set_relationship doesnt die'; ); } -{ - # Test support for straight joins - my $cdsrc = $schema->source('CD'); - my $artrel_info = $cdsrc->relationship_info ('artist'); - $cdsrc->add_relationship( - 'straight_artist', - $artrel_info->{class}, - $artrel_info->{cond}, - { %{$artrel_info->{attrs}}, join_type => 'straight' }, - ); - is_same_sql_bind ( - $cdsrc->resultset->search({}, { prefetch => 'straight_artist' })->as_query, - '( - SELECT `me`.`cdid`, `me`.`artist`, `me`.`title`, `me`.`year`, `me`.`genreid`, `me`.`single_track`, - `straight_artist`.`artistid`, `straight_artist`.`name`, `straight_artist`.`rank`, `straight_artist`.`charfield` - FROM cd `me` - STRAIGHT_JOIN `artist` `straight_artist` ON `straight_artist`.`artistid` = `me`.`artist` - )', - [], - 'straight joins correctly supported for mysql' - ); -} - ## Can we properly deal with the null search problem? ## ## Only way is to do a SET SQL_AUTO_IS_NULL = 0; on connect diff --git a/t/73oracle_hq.t b/t/73oracle_hq.t index c09cbfd..0f887fa 100644 --- a/t/73oracle_hq.t +++ b/t/73oracle_hq.t @@ -36,11 +36,6 @@ BEGIN { use DBICTest; use DBICTest::Schema; -use DBIC::SqlMakerTest; - -use DBIx::Class::SQLMaker::LimitDialects; -my $ROWS = DBIx::Class::SQLMaker::LimitDialects->__rows_bindtype, -my $TOTAL = DBIx::Class::SQLMaker::LimitDialects->__total_bindtype, my $schema = DBICTest::Schema->connect($dsn, $user, $pass); @@ -114,35 +109,12 @@ do_creates($dbh); connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - )', - [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'] ], - ); is_deeply ( [ $rs->get_column ('name')->all ], [ qw/root child1 grandchild greatgrandchild child2/ ], 'got artist tree', ); - is_same_sql_bind ( - $rs->count_rs->as_query, - '( - SELECT COUNT( * ) - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - )', - [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'] ], - ); - is( $rs->count, 5, 'Connect By count ok' ); } @@ -158,19 +130,6 @@ do_creates($dbh); order_siblings_by => { -desc => 'name' }, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - ORDER SIBLINGS BY name DESC - )', - [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'] ], - ); - is_deeply ( [ $rs->get_column ('name')->all ], [ qw/root child2 child1 grandchild greatgrandchild/ ], @@ -185,19 +144,6 @@ do_creates($dbh); connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - WHERE ( parentid IS NULL ) - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - )', - [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'] ], - ); - is_deeply( [ $rs->get_column('name')->all ], [ 'root' ], @@ -220,48 +166,12 @@ do_creates($dbh); } ); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - LEFT JOIN cd cds ON cds.artist = me.artistid - WHERE ( cds.title LIKE ? ) - START WITH me.name = ? - CONNECT BY parentid = PRIOR artistid - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'cds.title', 'sqlt_size' => 100 } - => '%cd'], - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'me.name', 'sqlt_size' => 100 } - => 'root'], - ], - ); - is_deeply( [ $rs->get_column('name')->all ], [ 'grandchild' ], 'Connect By with a join result name ok' ); - is_same_sql_bind ( - $rs->count_rs->as_query, - '( - SELECT COUNT( * ) - FROM artist me - LEFT JOIN cd cds ON cds.artist = me.artistid - WHERE ( cds.title LIKE ? ) - START WITH me.name = ? - CONNECT BY parentid = PRIOR artistid - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'cds.title', 'sqlt_size' => 100 } - => '%cd'], - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'me.name', 'sqlt_size' => 100 } - => 'root'], - ], - ); - is( $rs->count, 1, 'Connect By with a join; count ok' ); } @@ -273,22 +183,6 @@ do_creates($dbh); order_by => { -asc => [ 'LEVEL', 'name' ] }, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - ORDER BY LEVEL ASC, name ASC - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'], - ], - ); - - # Don't use "$rs->get_column ('name')->all" they build a query arround the $rs. # If $rs has a order by, the order by is in the subquery and this doesn't work with Oracle 8i. # TODO: write extra test and fix order by handling on Oracle 8i @@ -322,53 +216,12 @@ do_creates($dbh); rows => 2, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM ( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - ORDER BY name ASC, artistid DESC - ) me - WHERE ROWNUM <= ? - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'], [ $ROWS => 2 ], - ], - ); - is_deeply ( [ $rs->get_column ('name')->all ], [qw/child1 child2/], 'LIMIT a Connect By query - correct names' ); - is_same_sql_bind ( - $rs->count_rs->as_query, - '( - SELECT COUNT( * ) - FROM ( - SELECT me.artistid - FROM ( - SELECT me.artistid - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - ) me - WHERE ROWNUM <= ? - ) me - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'], - [ $ROWS => 2 ], - ], - ); - is( $rs->count, 2, 'Connect By; LIMIT count ok' ); } @@ -384,27 +237,6 @@ do_creates($dbh); having => \[ 'count(rank) < ?', [ cnt => 2 ] ], }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT COUNT(rank) + ? - FROM artist me - START WITH name = ? - CONNECT BY parentid = PRIOR artistid - GROUP BY( rank + ? ) HAVING count(rank) < ? - )', - [ - [ { dbic_colname => '__cbind' } - => 3 ], - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'root'], - [ { dbic_colname => '__gbind' } - => 1 ], - [ { dbic_colname => 'cnt' } - => 2 ], - ], - ); - is_deeply ( [ $rs->get_column ('cnt')->all ], [4, 4], @@ -437,19 +269,6 @@ do_creates($dbh); connect_by_nocycle => { parentid => { -prior => { -ident => 'artistid' } } }, }); - is_same_sql_bind ( - $rs->as_query, - '( - SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid, CONNECT_BY_ISCYCLE - FROM artist me - START WITH name = ? - CONNECT BY NOCYCLE parentid = PRIOR artistid - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'cycle-root'], - ], - ); is_deeply ( [ $rs->get_column ('name')->all ], [ qw/cycle-root cycle-child1 cycle-grandchild cycle-child2/ ], @@ -461,20 +280,6 @@ do_creates($dbh); 'got artist tree with nocycle (CONNECT_BY_ISCYCLE)', ); - is_same_sql_bind ( - $rs->count_rs->as_query, - '( - SELECT COUNT( * ) - FROM artist me - START WITH name = ? - CONNECT BY NOCYCLE parentid = PRIOR artistid - )', - [ - [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } - => 'cycle-root'], - ], - ); - is( $rs->count, 4, 'Connect By Nocycle count ok' ); } } diff --git a/t/746mssql.t b/t/746mssql.t index 57c44fb..2cc0281 100644 --- a/t/746mssql.t +++ b/t/746mssql.t @@ -11,11 +11,6 @@ plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missin use lib qw(t/lib); use DBICTest; -use DBIC::SqlMakerTest; -use DBIx::Class::SQLMaker::LimitDialects; - -my $OFFSET = DBIx::Class::SQLMaker::LimitDialects->__offset_bindtype; -my $TOTAL = DBIx::Class::SQLMaker::LimitDialects->__total_bindtype; my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MSSQL_ODBC_${_}" } qw/DSN USER PASS/}; @@ -373,77 +368,6 @@ SQL }, ); - is_same_sql_bind ( - $owners->page(3)->as_query, - $dialect eq 'Top' - ? '( - SELECT TOP 2147483647 [me].[id], [me].[name], - [books].[id], [books].[source], [books].[owner], [books].[title], [books].[price] - FROM ( - SELECT TOP 2147483647 [me].[id], [me].[name] - FROM ( - SELECT TOP 3 [me].[id], [me].[name], [ORDER__BY__001] - FROM ( - SELECT TOP 9 [me].[id], [me].[name], name + ? AS [ORDER__BY__001] - FROM [owners] [me] - LEFT JOIN [books] [books] - ON [books].[owner] = [me].[id] - WHERE [books].[id] IS NOT NULL AND [me].[name] != ? - GROUP BY [me].[id], [me].[name] - ORDER BY name + ? ASC, [me].[id] - ) [me] - ORDER BY [ORDER__BY__001] DESC, [me].[id] DESC - ) [me] - ORDER BY [ORDER__BY__001] ASC, [me].[id] - ) [me] - LEFT JOIN [books] [books] - ON [books].[owner] = [me].[id] - WHERE [books].[id] IS NOT NULL AND [me].[name] != ? - ORDER BY name + ? ASC, [me].[id] - )' - : '( - SELECT TOP 2147483647 [me].[id], [me].[name], - [books].[id], [books].[source], [books].[owner], [books].[title], [books].[price] - FROM ( - SELECT TOP 2147483647 [me].[id], [me].[name] - FROM ( - SELECT [me].[id], [me].[name], - ROW_NUMBER() OVER( ORDER BY [ORDER__BY__001] ASC, [me].[id] ) AS [rno__row__index] - FROM ( - SELECT [me].[id], [me].[name], name + ? AS [ORDER__BY__001] - FROM [owners] [me] - LEFT JOIN [books] [books] - ON [books].[owner] = [me].[id] - WHERE [books].[id] IS NOT NULL AND [me].[name] != ? - GROUP BY [me].[id], [me].[name] - ) [me] - ) [me] - WHERE [rno__row__index] >= ? AND [rno__row__index] <= ? - ) [me] - LEFT JOIN [books] [books] - ON [books].[owner] = [me].[id] - WHERE [books].[id] IS NOT NULL AND [me].[name] != ? - ORDER BY name + ? ASC, [me].[id] - )' - , - [ - [ { dbic_colname => 'test' } - => 'xxx' ], - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } - => 'somebogusstring' ], - - ($dialect eq 'Top' - ? [ { dbic_colname => 'test' } => 'xxx' ] # the extra re-order bind - : ([ $OFFSET => 7 ], [ $TOTAL => 9 ]) # parameterised RNO - ), - - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } - => 'somebogusstring' ], - [ { dbic_colname => 'test' } - => 'xxx' ], - ], - ) if $quoted; - is ($owners->page(1)->all, 3, "$test_type: has_many prefetch returns correct number of rows"); is ($owners->page(1)->count, 3, "$test_type: has-many prefetch returns correct count"); @@ -471,88 +395,6 @@ SQL }, ); - is_same_sql_bind ( - $books->page(3)->as_query, - $dialect eq 'Top' - ? '( - SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], - [owner].[id], [owner].[name] - FROM ( - SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - FROM ( - SELECT TOP 2 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - FROM ( - SELECT TOP 6 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - FROM [books] [me] - JOIN [owners] [owner] - ON [owner].[id] = [me].[owner] - WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? - GROUP BY [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - HAVING 1 = ? - ORDER BY [me].[owner] DESC, [me].[id] - ) [me] - ORDER BY [me].[owner] ASC, [me].[id] DESC - ) [me] - ORDER BY [me].[owner] DESC, [me].[id] - ) [me] - JOIN [owners] [owner] - ON [owner].[id] = [me].[owner] - WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? - ORDER BY [me].[owner] DESC, [me].[id] - )' - : '( - SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], - [owner].[id], [owner].[name] - FROM ( - SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - FROM ( - SELECT [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], - ROW_NUMBER() OVER( ORDER BY [me].[owner] DESC, [me].[id] ) AS [rno__row__index] - FROM ( - SELECT [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - FROM [books] [me] - JOIN [owners] [owner] - ON [owner].[id] = [me].[owner] - WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? - GROUP BY [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] - HAVING 1 = ? - ) [me] - ) [me] - WHERE [rno__row__index] >= ? AND [rno__row__index] <= ? - ) [me] - JOIN [owners] [owner] - ON [owner].[id] = [me].[owner] - WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? - ORDER BY [me].[owner] DESC, [me].[id] - )' - , - [ - # inner - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } - => 'wiggle' ], - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } - => 'woggle' ], - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } - => 'Library' ], - [ { dbic_colname => 'test' } - => '1' ], - - # top(?) - $dialect eq 'Top' - ? () - : ( [ $OFFSET => 5 ], [ $TOTAL => 6 ] ) - , - - # outer - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } - => 'wiggle' ], - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } - => 'woggle' ], - [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } - => 'Library' ], - ], - ) if $quoted; - is ($books->page(1)->all, 2, "$test_type: Prefetched grouped search returns correct number of rows"); is ($books->page(1)->count, 2, "$test_type: Prefetched grouped search returns correct count"); diff --git a/t/751msaccess.t b/t/751msaccess.t index 48ff756..8d8aa7e 100644 --- a/t/751msaccess.t +++ b/t/751msaccess.t @@ -164,6 +164,8 @@ EOF s/^'//, s/'\z// for @bind; + # test is duplicated in t/sqlmaker/msaccess.t, keep a duplicate here anyway, just to be safe + # -- ribasushi is_same_sql_bind( $sql, \@bind, @@ -195,6 +197,8 @@ EOF s/^'//, s/'\z// for @bind; + # test is duplicated in t/sqlmaker/msaccess.t, keep a duplicate here anyway, just to be safe + # -- ribasushi is_same_sql_bind( $sql, \@bind, diff --git a/t/sqlmaker/hierarchical/oracle.t b/t/sqlmaker/hierarchical/oracle.t new file mode 100644 index 0000000..1283140 --- /dev/null +++ b/t/sqlmaker/hierarchical/oracle.t @@ -0,0 +1,314 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); + +use DBIx::Class::Optional::Dependencies; +plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('id_shortener') + unless DBIx::Class::Optional::Dependencies->req_ok_for ('id_shortener'); + +use DBICTest::Schema::Artist; +BEGIN { + DBICTest::Schema::Artist->add_column('parentid'); + + DBICTest::Schema::Artist->has_many( + children => 'DBICTest::Schema::Artist', + { 'foreign.parentid' => 'self.artistid' } + ); + + DBICTest::Schema::Artist->belongs_to( + parent => 'DBICTest::Schema::Artist', + { 'foreign.artistid' => 'self.parentid' } + ); +} + +use DBICTest; +use DBICTest::Schema; +use DBIC::SqlMakerTest; + +use DBIx::Class::SQLMaker::LimitDialects; +my $ROWS = DBIx::Class::SQLMaker::LimitDialects->__rows_bindtype; +my $TOTAL = DBIx::Class::SQLMaker::LimitDialects->__total_bindtype; + +for my $q ( '', '"' ) { + + my $schema = DBICTest->init_schema( + storage_type => 'DBIx::Class::Storage::DBI::Oracle::Generic', + no_deploy => 1, + quote_char => $q, + ); + + # select the whole tree + { + my $rs = $schema->resultset('Artist')->search({}, { + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'] ], + ); + + is_same_sql_bind ( + $rs->count_rs->as_query, + "( + SELECT COUNT( * ) + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'] ], + ); + } + + # use order siblings by statement + { + my $rs = $schema->resultset('Artist')->search({}, { + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + order_siblings_by => { -desc => 'name' }, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + ORDER SIBLINGS BY ${q}name${q} DESC + )", + [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'] ], + ); + } + + # get the root node + { + my $rs = $schema->resultset('Artist')->search({ parentid => undef }, { + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + WHERE ( ${q}parentid${q} IS NULL ) + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'] ], + ); + } + + # combine a connect by with a join + { + my $rs = $schema->resultset('Artist')->search( + {'cds.title' => { -like => '%cd'} }, + { + join => 'cds', + start_with => { 'me.name' => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + } + ); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + LEFT JOIN cd ${q}cds${q} ON ${q}cds${q}.${q}artist${q} = ${q}me${q}.${q}artistid${q} + WHERE ( ${q}cds${q}.${q}title${q} LIKE ? ) + START WITH ${q}me${q}.${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'cds.title', 'sqlt_size' => 100 } + => '%cd'], + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'me.name', 'sqlt_size' => 100 } + => 'root'], + ], + ); + + is_same_sql_bind ( + $rs->count_rs->as_query, + "( + SELECT COUNT( * ) + FROM ${q}artist${q} ${q}me${q} + LEFT JOIN cd ${q}cds${q} ON ${q}cds${q}.${q}artist${q} = ${q}me${q}.${q}artistid${q} + WHERE ( ${q}cds${q}.${q}title${q} LIKE ? ) + START WITH ${q}me${q}.${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'cds.title', 'sqlt_size' => 100 } + => '%cd'], + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'me.name', 'sqlt_size' => 100 } + => 'root'], + ], + ); + } + + # combine a connect by with order_by + { + my $rs = $schema->resultset('Artist')->search({}, { + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + order_by => { -asc => [ 'LEVEL', 'name' ] }, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + ORDER BY ${q}LEVEL${q} ASC, ${q}name${q} ASC + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'], + ], + ); + } + + # limit a connect by + { + my $rs = $schema->resultset('Artist')->search({}, { + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + order_by => [ { -asc => 'name' }, { -desc => 'artistid' } ], + rows => 2, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q} + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + ORDER BY ${q}name${q} ASC, ${q}artistid${q} DESC + ) ${q}me${q} + WHERE ROWNUM <= ? + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'], [ $ROWS => 2 ], + ], + ); + + is_same_sql_bind ( + $rs->count_rs->as_query, + "( + SELECT COUNT( * ) + FROM ( + SELECT ${q}me${q}.${q}artistid${q} + FROM ( + SELECT ${q}me${q}.${q}artistid${q} + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + ) ${q}me${q} + WHERE ROWNUM <= ? + ) ${q}me${q} + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'], + [ $ROWS => 2 ], + ], + ); + } + + # combine a connect_by with group_by and having + # add some bindvals to make sure things still work + { + my $rs = $schema->resultset('Artist')->search({}, { + select => \[ 'COUNT(rank) + ?', [ __cbind => 3 ] ], + as => 'cnt', + start_with => { name => 'root' }, + connect_by => { parentid => { -prior => { -ident => 'artistid' } } }, + group_by => \[ 'rank + ? ', [ __gbind => 1] ], + having => \[ 'count(rank) < ?', [ cnt => 2 ] ], + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT COUNT(rank) + ? + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY ${q}parentid${q} = PRIOR ${q}artistid${q} + GROUP BY( rank + ? ) + HAVING count(rank) < ? + )", + [ + [ { dbic_colname => '__cbind' } + => 3 ], + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'root'], + [ { dbic_colname => '__gbind' } + => 1 ], + [ { dbic_colname => 'cnt' } + => 2 ], + ], + ); + } + + # select the whole cycle tree with nocylce + { + my $rs = $schema->resultset('Artist')->search({}, { + start_with => { name => 'cycle-root' }, + '+select' => \ 'CONNECT_BY_ISCYCLE', + '+as' => [ 'connector' ], + connect_by_nocycle => { parentid => { -prior => { -ident => 'artistid' } } }, + }); + + is_same_sql_bind ( + $rs->as_query, + "( + SELECT ${q}me${q}.${q}artistid${q}, ${q}me${q}.${q}name${q}, ${q}me${q}.${q}rank${q}, ${q}me${q}.${q}charfield${q}, ${q}me${q}.${q}parentid${q}, CONNECT_BY_ISCYCLE + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY NOCYCLE ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'cycle-root'], + ], + ); + + is_same_sql_bind ( + $rs->count_rs->as_query, + "( + SELECT COUNT( * ) + FROM ${q}artist${q} ${q}me${q} + START WITH ${q}name${q} = ? + CONNECT BY NOCYCLE ${q}parentid${q} = PRIOR ${q}artistid${q} + )", + [ + [ { 'sqlt_datatype' => 'varchar', 'dbic_colname' => 'name', 'sqlt_size' => 100 } + => 'cycle-root'], + ], + ); + } +} + +done_testing; diff --git a/t/sqlmaker/limit_dialects/mssql_torture.t b/t/sqlmaker/limit_dialects/mssql_torture.t new file mode 100644 index 0000000..7806dfb --- /dev/null +++ b/t/sqlmaker/limit_dialects/mssql_torture.t @@ -0,0 +1,259 @@ +use strict; +use warnings; +use Test::More; +use lib qw(t/lib); +use DBICTest; +use DBIC::SqlMakerTest; +use DBIx::Class::SQLMaker::LimitDialects; +my $OFFSET = DBIx::Class::SQLMaker::LimitDialects->__offset_bindtype; +my $TOTAL = DBIx::Class::SQLMaker::LimitDialects->__total_bindtype; + +my $schema = DBICTest->init_schema ( + storage_type => 'DBIx::Class::Storage::DBI::MSSQL', + no_deploy => 1, + quote_names => 1 +); +# prime caches +$schema->storage->sql_maker; + +# more involved limit dialect torture testcase migrated from the +# live mssql tests +my $tests = { + pref_hm_and_page_and_group_rs => { + + rs => scalar $schema->resultset ('Owners')->search ( + { + 'books.id' => { '!=', undef }, + 'me.name' => { '!=', 'somebogusstring' }, + }, + { + prefetch => 'books', + order_by => [ { -asc => \['name + ?', [ test => 'xxx' ]] }, 'me.id' ], # test bindvar propagation + group_by => [ map { "me.$_" } $schema->source('Owners')->columns ], # the literal order_by requires an explicit group_by + rows => 3, + unsafe_subselect_ok => 1, + }, + )->page(3), + + result => { + Top => [ + '( + SELECT TOP 2147483647 [me].[id], [me].[name], + [books].[id], [books].[source], [books].[owner], [books].[title], [books].[price] + FROM ( + SELECT TOP 2147483647 [me].[id], [me].[name] + FROM ( + SELECT TOP 3 [me].[id], [me].[name], [ORDER__BY__001] + FROM ( + SELECT TOP 9 [me].[id], [me].[name], name + ? AS [ORDER__BY__001] + FROM [owners] [me] + LEFT JOIN [books] [books] + ON [books].[owner] = [me].[id] + WHERE [books].[id] IS NOT NULL AND [me].[name] != ? + GROUP BY [me].[id], [me].[name] + ORDER BY name + ? ASC, [me].[id] + ) [me] + ORDER BY [ORDER__BY__001] DESC, [me].[id] DESC + ) [me] + ORDER BY [ORDER__BY__001] ASC, [me].[id] + ) [me] + LEFT JOIN [books] [books] + ON [books].[owner] = [me].[id] + WHERE [books].[id] IS NOT NULL AND [me].[name] != ? + ORDER BY name + ? ASC, [me].[id] + )', + [ + [ { dbic_colname => 'test' } + => 'xxx' ], + + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } + => 'somebogusstring' ], + + [ { dbic_colname => 'test' } => 'xxx' ], # the extra re-order bind + + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } + => 'somebogusstring' ], + + [ { dbic_colname => 'test' } + => 'xxx' ], + ], + ], + + RowNumberOver => [ + '( + SELECT TOP 2147483647 [me].[id], [me].[name], + [books].[id], [books].[source], [books].[owner], [books].[title], [books].[price] + FROM ( + SELECT TOP 2147483647 [me].[id], [me].[name] + FROM ( + SELECT [me].[id], [me].[name], + ROW_NUMBER() OVER( ORDER BY [ORDER__BY__001] ASC, [me].[id] ) AS [rno__row__index] + FROM ( + SELECT [me].[id], [me].[name], name + ? AS [ORDER__BY__001] + FROM [owners] [me] + LEFT JOIN [books] [books] + ON [books].[owner] = [me].[id] + WHERE [books].[id] IS NOT NULL AND [me].[name] != ? + GROUP BY [me].[id], [me].[name] + ) [me] + ) [me] + WHERE [rno__row__index] >= ? AND [rno__row__index] <= ? + ) [me] + LEFT JOIN [books] [books] + ON [books].[owner] = [me].[id] + WHERE [books].[id] IS NOT NULL AND [me].[name] != ? + ORDER BY name + ? ASC, [me].[id] + )', + [ + [ { dbic_colname => 'test' } + => 'xxx' ], + + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } + => 'somebogusstring' ], + + [ $OFFSET => 7 ], # parameterised RNO + + [ $TOTAL => 9 ], + + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.name' } + => 'somebogusstring' ], + + [ { dbic_colname => 'test' } + => 'xxx' ], + ], + ], + } + }, + + pref_bt_and_page_and_group_rs => { + + rs => scalar $schema->resultset ('BooksInLibrary')->search ( + { + 'owner.name' => [qw/wiggle woggle/], + }, + { + distinct => 1, + having => \['1 = ?', [ test => 1 ] ], #test having propagation + prefetch => 'owner', + rows => 2, # 3 results total + order_by => [{ -desc => 'me.owner' }, 'me.id'], + unsafe_subselect_ok => 1, + }, + )->page(3), + + result => { + Top => [ + '( + SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], + [owner].[id], [owner].[name] + FROM ( + SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + FROM ( + SELECT TOP 2 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + FROM ( + SELECT TOP 6 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + FROM [books] [me] + JOIN [owners] [owner] + ON [owner].[id] = [me].[owner] + WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? + GROUP BY [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + HAVING 1 = ? + ORDER BY [me].[owner] DESC, [me].[id] + ) [me] + ORDER BY [me].[owner] ASC, [me].[id] DESC + ) [me] + ORDER BY [me].[owner] DESC, [me].[id] + ) [me] + JOIN [owners] [owner] + ON [owner].[id] = [me].[owner] + WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? + ORDER BY [me].[owner] DESC, [me].[id] + )', + [ + # inner + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'wiggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'woggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } + => 'Library' ], + [ { dbic_colname => 'test' } + => '1' ], + + # outer + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'wiggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'woggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } + => 'Library' ], + ], + ], + RowNumberOver => [ + '( + SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], + [owner].[id], [owner].[name] + FROM ( + SELECT TOP 2147483647 [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + FROM ( + SELECT [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price], + ROW_NUMBER() OVER( ORDER BY [me].[owner] DESC, [me].[id] ) AS [rno__row__index] + FROM ( + SELECT [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + FROM [books] [me] + JOIN [owners] [owner] + ON [owner].[id] = [me].[owner] + WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? + GROUP BY [me].[id], [me].[source], [me].[owner], [me].[title], [me].[price] + HAVING 1 = ? + ) [me] + ) [me] + WHERE [rno__row__index] >= ? AND [rno__row__index] <= ? + ) [me] + JOIN [owners] [owner] + ON [owner].[id] = [me].[owner] + WHERE ( [owner].[name] = ? OR [owner].[name] = ? ) AND [source] = ? + ORDER BY [me].[owner] DESC, [me].[id] + )', + [ + # inner + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'wiggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'woggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } + => 'Library' ], + [ { dbic_colname => 'test' } + => '1' ], + + [ $OFFSET => 5 ], + [ $TOTAL => 6 ], + + # outer + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'wiggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'owner.name' } + => 'woggle' ], + [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'source' } + => 'Library' ], + ], + ], + }, + }, +}; + +for my $tname (keys %$tests) { + for my $limtype (keys %{$tests->{$tname}{result}} ) { + + delete $schema->storage->_sql_maker->{_cached_syntax}; + $schema->storage->_sql_maker->limit_dialect ($limtype); + + is_same_sql_bind( + $tests->{$tname}{rs}->as_query, + @{ $tests->{$tname}{result}{$limtype} }, + "Correct SQL for $limtype on $tname", + ); + } +} + +done_testing; diff --git a/t/sqlmaker/msaccess.t b/t/sqlmaker/msaccess.t index 6d76f82..2805d03 100644 --- a/t/sqlmaker/msaccess.t +++ b/t/sqlmaker/msaccess.t @@ -5,9 +5,81 @@ use lib qw(t/lib); use DBICTest; use DBIC::SqlMakerTest; -use DBIx::Class::SQLMaker::ACCESS (); +# the entire point of the subclass is that parenthesis have to be +# just right for ACCESS to be happy +# globalize for entirety of the test +$SQL::Abstract::Test::parenthesis_significant = 1; -my $sa = DBIx::Class::SQLMaker::ACCESS->new; +my $schema = DBICTest->init_schema (storage_type => 'DBIx::Class::Storage::DBI::ACCESS', no_deploy => 1, quote_names => 1); + +is_same_sql_bind( + $schema->resultset('Artist')->search( + { + artistid => 1, + }, + { + join => [{ cds => 'tracks' }], + '+select' => [ 'tracks.title' ], + '+as' => [ 'track_title' ], + } + )->as_query, + '( + SELECT [me].[artistid], [me].[name], [me].[rank], [me].[charfield], + [tracks].[title] + FROM ( + ( + [artist] [me] + LEFT JOIN cd [cds] + ON [cds].[artist] = [me].[artistid] + ) + LEFT JOIN [track] [tracks] + ON [tracks].[cd] = [cds].[cdid] + ) + WHERE ( [artistid] = ? ) + )', + [ + [{ sqlt_datatype => 'integer', dbic_colname => 'artistid' } + => 1 ], + ], + 'correct SQL for two-step left join' +); + +is_same_sql_bind( + $schema->resultset('Track')->search( + { + trackid => 1, + }, + { + join => [{ cd => 'artist' }], + '+select' => [ 'artist.name' ], + '+as' => [ 'artist_name' ], + } + )->as_query, + '( + SELECT [me].[trackid], [me].[cd], [me].[position], [me].[title], [me].[last_updated_on], [me].[last_updated_at], + [artist].[name] + FROM ( + ( + [track] [me] + INNER JOIN cd [cd] + ON [cd].[cdid] = [me].[cd] + ) + INNER JOIN [artist] [artist] + ON [artist].[artistid] = [cd].[artist] + ) + WHERE ( [trackid] = ? ) + )', + [ + [{ sqlt_datatype => 'integer', dbic_colname => 'trackid' } + => 1 ], + ], + 'correct SQL for two-step inner join', +); + + +my $sa = $schema->storage->sql_maker; +# the legacy tests assume no quoting - leave things as-is +local $sa->{quote_char}; # my ($self, $table, $fields, $where, $order, @rest) = @_; my ($sql, @bind) = $sa->select( diff --git a/t/sqlmaker/mysql.t b/t/sqlmaker/mysql.t index b00691f..2755a3d 100644 --- a/t/sqlmaker/mysql.t +++ b/t/sqlmaker/mysql.t @@ -115,4 +115,27 @@ bless ( $schema->storage, 'DBIx::Class::Storage::DBI::mysql' ); $schema->storage->debug ($orig_debug); } +# Test support for straight joins +{ + my $cdsrc = $schema->source('CD'); + my $artrel_info = $cdsrc->relationship_info ('artist'); + $cdsrc->add_relationship( + 'straight_artist', + $artrel_info->{class}, + $artrel_info->{cond}, + { %{$artrel_info->{attrs}}, join_type => 'straight' }, + ); + is_same_sql_bind ( + $cdsrc->resultset->search({}, { prefetch => 'straight_artist' })->as_query, + '( + SELECT `me`.`cdid`, `me`.`artist`, `me`.`title`, `me`.`year`, `me`.`genreid`, `me`.`single_track`, + `straight_artist`.`artistid`, `straight_artist`.`name`, `straight_artist`.`rank`, `straight_artist`.`charfield` + FROM cd `me` + STRAIGHT_JOIN `artist` `straight_artist` ON `straight_artist`.`artistid` = `me`.`artist` + )', + [], + 'straight joins correctly supported for mysql' + ); +} + done_testing;