-#!/usr/bin/perl
-
use strict;
use warnings;
-use List::Util qw(sum);
use Test::More;
+use SQL::Abstract::Test import => [qw(
+ eq_sql_bind eq_sql eq_bind is_same_sql_bind dumper $sql_differ
+)];
my @sql_tests = (
# WHERE condition - equal
equal => 1,
statements => [
q/SELECT foo FROM bar WHERE a = 1 AND b = 1 AND c = 1/,
+ q/SELECT foo FROM bar WHERE (a = 1 AND b = 1 AND c = 1)/,
q/SELECT foo FROM bar WHERE (a = 1 AND b = 1) AND c = 1/,
q/SELECT foo FROM bar WHERE a = 1 AND (b = 1 AND c = 1)/,
q/SELECT foo FROM bar WHERE ((((a = 1))) AND (b = 1 AND c = 1))/,
q/SELECT foo FROM bar WHERE ( ((a = 1) AND ( b = 1 OR (c = 1 OR d = 1) )) AND ((e = 1)) AND f = 1) /,
]
},
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar WHERE (a) AND (b = 2)/,
+ q/SELECT foo FROM bar WHERE (a AND b = 2)/,
+ q/SELECT foo FROM bar WHERE (a AND (b = 2))/,
+ q/SELECT foo FROM bar WHERE a AND (b = 2)/,
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar WHERE ((NOT a) AND b = 2)/,
+ q/SELECT foo FROM bar WHERE (NOT a) AND (b = 2)/,
+ q/SELECT foo FROM bar WHERE (NOT (a)) AND b = 2/,
+ ],
+ },
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar WHERE NOT a AND (b = 2)/,
+ q/SELECT foo FROM bar WHERE (NOT a) AND (b = 2)/,
+ ]
+ },
+ {
+ equal => 0,
+ opts => { parenthesis_significant => 1 },
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 AND c = 1/,
+ q/SELECT foo FROM bar WHERE (a = 1 AND b = 1 AND c = 1)/,
+ q/SELECT foo FROM bar WHERE (a = 1 AND b = 1) AND c = 1/,
+ q/SELECT foo FROM bar WHERE a = 1 AND (b = 1 AND c = 1)/,
+ q/SELECT foo FROM bar WHERE ((((a = 1))) AND (b = 1 AND c = 1))/,
+ ]
+ },
+ {
+ equal => 0,
+ opts => { parenthesis_significant => 1 },
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1 OR b = 1 OR c = 1/,
+ q/SELECT foo FROM bar WHERE (a = 1 OR b = 1) OR c = 1/,
+ q/SELECT foo FROM bar WHERE a = 1 OR (b = 1 OR c = 1)/,
+ q/SELECT foo FROM bar WHERE a = 1 OR ((b = 1 OR (c = 1)))/,
+ ]
+ },
+ {
+ equal => 0,
+ opts => { parenthesis_significant => 1 },
+ statements => [
+ q/SELECT foo FROM bar WHERE (a = 1) AND (b = 1 OR c = 1 OR d = 1) AND (e = 1 AND f = 1)/,
+ q/SELECT foo FROM bar WHERE a = 1 AND (b = 1 OR c = 1 OR d = 1) AND e = 1 AND (f = 1)/,
+ q/SELECT foo FROM bar WHERE ( ((a = 1) AND ( b = 1 OR (c = 1 OR d = 1) )) AND ((e = 1)) AND f = 1) /,
+ ]
+ },
# WHERE condition - different
{
q/SELECT foo FROM bar WHERE a = 1 OR (b = 1 AND c = 1)/,
]
},
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar WHERE a IN (1,3,2)/,
+ q/SELECT foo FROM bar WHERE a IN 1,2,3/,
+ q/SELECT foo FROM bar WHERE a IN (1,2,3)/,
+ q/SELECT foo FROM bar WHERE a IN ((1,2,3))/,
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ # BETWEEN with/without parenthesis around itself/RHS is a sticky business
+ # if I made a mistake here, simply rewrite the special BETWEEN handling in
+ # _recurse_parse()
+ #
+ # by RIBASUSHI
+ q/SELECT foo FROM bar WHERE ( completion_date BETWEEN ? AND ? AND status = ? )/,
+ q/SELECT foo FROM bar WHERE completion_date BETWEEN (? AND ?) AND status = ?/,
+ q/SELECT foo FROM bar WHERE ( (completion_date BETWEEN (? AND ?) ) AND status = ? )/,
+ q/SELECT foo FROM bar WHERE ( (completion_date BETWEEN (? AND ? AND status = ?) ) )/,
+ ]
+ },
+
+ # IS NULL (special LHS-only op)
+ {
+ equal => 1,
+ statements => [
+ q/WHERE a IS NOT NULL AND b IS NULL/,
+ q/WHERE (a IS NOT NULL) AND b IS NULL/,
+ q/WHERE a IS NOT NULL AND (b IS NULL)/,
+ q/WHERE (a IS NOT NULL) AND ((b IS NULL))/,
+ ],
+ },
# JOIN condition - equal
{
{
equal => 0,
statements => [
+ q/DELETE FROM cd WHERE ( cdid IN ( SELECT me.cdid FROM (SELECT * FROM cd me WHERE ( year != ? ) GROUP BY me.cdid) me WHERE ( year != ? ) ) )/,
+ q/DELETE FROM cd WHERE ( cdid IN ( SELECT me.cdid FROM cd me WHERE ( year != ? ) GROUP BY me.cdid ) )/,
+ ],
+ },
+ {
+ equal => 0,
+ statements => [
q/SELECT * FROM (SELECT * FROM bar WHERE b = 1) AS foo WHERE a = 1/,
q/SELECT * FROM (SELECT * FROM bar WHERE b = 1) AS foo WHERE a = 2/,
q/SELECT * FROM (SELECT * FROM bar WHERE b = 1) AS foo WHERE (a = 3)/,
q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 10))) AS foo WHERE (a = 2)/,
]
},
+
+ # order by
+ {
+ equal => 1,
+ statements => [
+ q/SELECT * FROM foo ORDER BY bar/,
+ q/SELECT * FROM foo ORDER BY bar ASC/,
+ q/SELECT * FROM foo ORDER BY bar asc/,
+ ],
+ },
+ {
+ equal => 1,
+ statements => [
+ q/SELECT * FROM foo ORDER BY bar, baz ASC/,
+ q/SELECT * FROM foo ORDER BY bar ASC, baz/,
+ q/SELECT * FROM foo ORDER BY bar asc, baz ASC/,
+ q/SELECT * FROM foo ORDER BY bar, baz/,
+ ],
+ },
+ {
+ equal => 1,
+ statements => [
+ q/ORDER BY colA, colB LIKE ? DESC, colC LIKE ?/,
+ q/ORDER BY colA ASC, colB LIKE ? DESC, colC LIKE ? ASC/,
+ ],
+ },
+ {
+ equal => 1,
+ statements => [
+ q/ORDER BY name + ?, [me].[id]/,
+ q/ORDER BY name + ? ASC, [me].[id]/,
+ ],
+ },
+ {
+ equal => 0,
+ opts => { order_by_asc_significant => 1 },
+ statements => [
+ q/SELECT * FROM foo ORDER BY bar/,
+ q/SELECT * FROM foo ORDER BY bar ASC/,
+ q/SELECT * FROM foo ORDER BY bar desc/,
+ ],
+ },
+
+ # list permutations
+ {
+ equal => 0,
+ statements => [
+ 'SELECT a,b,c FROM foo',
+ 'SELECT a,c,b FROM foo',
+ 'SELECT b,a,c FROM foo',
+ 'SELECT b,c,a FROM foo',
+ 'SELECT c,a,b FROM foo',
+ 'SELECT c,b,a FROM foo',
+ ],
+ },
+ {
+ equal => 0,
+ statements => [
+ 'SELECT * FROM foo WHERE a IN (1,2,3)',
+ 'SELECT * FROM foo WHERE a IN (1,3,2)',
+ 'SELECT * FROM foo WHERE a IN (2,1,3)',
+ 'SELECT * FROM foo WHERE a IN (2,3,1)',
+ 'SELECT * FROM foo WHERE a IN (3,1,2)',
+ 'SELECT * FROM foo WHERE a IN (3,2,1)',
+ ]
+ },
+
+ # list consistency
+ {
+ equal => 0,
+ statements => [
+ 'SELECT a,b FROM foo',
+ 'SELECT a,,b FROM foo',
+ 'SELECT a,b, FROM foo',
+ 'SELECT ,a,b, FROM foo',
+ 'SELECT ,a,,b, FROM foo',
+ ],
+ },
+
+ # misc func
+ {
+ equal => 0,
+ statements => [
+ 'SELECT count(*) FROM foo',
+ 'SELECT count(*) AS bar FROM foo',
+ 'SELECT count(*) AS "bar" FROM foo',
+ 'SELECT count(a) FROM foo',
+ 'SELECT count(1) FROM foo',
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ 'SELECT foo() bar FROM baz',
+ 'SELECT foo ( )bar FROM baz',
+ 'SELECT foo (())bar FROM baz',
+ 'SELECT foo(( ) ) bar FROM baz',
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ 'SELECT foo() FROM bar',
+ 'SELECT foo FROM bar',
+ 'SELECT foo FROM bar ()',
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ 'SELECT COUNT * FROM foo',
+ 'SELECT COUNT( * ) FROM foo',
+ ]
+ },
+ # single ? of unknown funcs do not unroll unless
+ # explicitly allowed (e.g. Like)
+ {
+ equal => 0,
+ statements => [
+ 'SELECT foo FROM bar WHERE bar > foo ?',
+ 'SELECT foo FROM bar WHERE bar > foo( ? )',
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ 'SELECT foo FROM bar WHERE bar LIKE ?',
+ 'SELECT foo FROM bar WHERE bar LiKe (?)',
+ 'SELECT foo FROM bar WHERE bar lIkE( (?))',
+ ]
+ },
+ # test multival
+ {
+ equal => 0,
+ statements => [
+ 'SELECT foo FROM bar WHERE foo IN (?, ?)',
+ 'SELECT foo FROM bar WHERE foo IN ?, ?',
+ ]
+ },
+ # math
+ {
+ equal => 0,
+ statements => [
+ 'SELECT * FROM foo WHERE 1 = ( a > b)',
+ 'SELECT * FROM foo WHERE 1 = a > b',
+ 'SELECT * FROM foo WHERE (1 = a) > b',
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ 'SELECT * FROM foo WHERE bar = baz(buzz)',
+ 'SELECT * FROM foo WHERE bar = (baz( buzz ))',
+ ]
+ },
+ # oddballs
+ {
+ equal => 1,
+ statements => [
+ 'WHERE ( foo GLOB ? )',
+ 'WHERE foo GLOB ?',
+ ],
+ },
+ {
+ equal => 1,
+ statements => [
+ 'SELECT FIRST ? SKIP ? [me].[id], [me].[owner]
+ FROM [books] [me]
+ WHERE ( ( (EXISTS (
+ SELECT FIRST ? SKIP ? [owner].[id]
+ FROM [owners] [owner]
+ WHERE ( [books].[owner] = [owner].[id] )
+ )) AND [source] = ? ) )',
+ 'SELECT FIRST ? SKIP ? [me].[id], [me].[owner]
+ FROM [books] [me]
+ WHERE ( ( EXISTS (
+ SELECT FIRST ? SKIP ? [owner].[id]
+ FROM [owners] [owner]
+ WHERE ( [books].[owner] = [owner].[id] )
+ ) AND [source] = ? ) )',
+ ],
+ },
+ {
+ equal => 1,
+ statements => [
+ 'WHERE foo = ? FETCH FIRST 1 ROWS ONLY',
+ 'WHERE ( foo = ? ) FETCH FIRST 1 ROWS ONLY',
+ 'WHERE (( foo = ? )) FETCH FIRST 1 ROWS ONLY',
+ ],
+ },
);
my @bind_tests = (
},
);
-plan tests => 1 +
- sum(
- map { $_ * ($_ - 1) / 2 }
- map { scalar @{$_->{statements}} }
- @sql_tests
- ) +
- sum(
- map { $_ * ($_ - 1) / 2 }
- map { scalar @{$_->{bindvals}} }
- @bind_tests
- ) +
- 3;
-
-use_ok('SQL::Abstract::Test', import => [qw(
- eq_sql_bind eq_sql eq_bind is_same_sql_bind
-)]);
-
-for my $test (@sql_tests) {
+for my $test ( @sql_tests ) {
+
+ # this does not work on 5.8.8 and earlier :(
+ #local @{*SQL::Abstract::Test::}{keys %{$test->{opts}}} = map { \$_ } values %{$test->{opts}}
+ # if $test->{opts};
+
+ my %restore_globals;
+
+ for (keys %{$test->{opts} || {} }) {
+ $restore_globals{$_} = ${${*SQL::Abstract::Test::}{$_}};
+ ${*SQL::Abstract::Test::}{$_} = \ do { my $cp = $test->{opts}{$_} };
+ }
+
my $statements = $test->{statements};
while (@$statements) {
my $sql1 = shift @$statements;
foreach my $sql2 (@$statements) {
+
my $equal = eq_sql($sql1, $sql2);
+
TODO: {
local $TODO = $test->{todo} if $test->{todo};
}
if ($equal ^ $test->{equal}) {
- diag("sql1: $sql1");
- diag("sql2: $sql2");
+ my ($ast1, $ast2) = map { SQL::Abstract::Test::parse ($_) } ($sql1, $sql2);
+ $_ = dumper($_) for ($ast1, $ast2);
+
+ diag "sql1: $sql1";
+ diag "sql2: $sql2";
+ note $sql_differ || 'No differences found';
+ note "ast1: $ast1";
+ note "ast2: $ast2";
}
}
}
}
+
+ ${*SQL::Abstract::Test::}{$_} = \$restore_globals{$_}
+ for keys %restore_globals;
}
for my $test (@bind_tests) {
}
if ($equal ^ $test->{equal}) {
- diag("bind1: " . Dumper($bind1));
- diag("bind2: " . Dumper($bind2));
+ diag("bind1: " . dumper($bind1));
+ diag("bind2: " . dumper($bind2));
}
}
}
),
"eq_sql_bind considers different SQL expressions and equal bind values different"
);
+
+# test diag string
+ok (! eq_sql (
+ 'SELECT owner_name FROM books me WHERE ( source = ? )',
+ 'SELECT owner_name FROM books me WHERE ( sUOrce = ? )',
+));
+like(
+ $sql_differ,
+ qr/\Q[ source ] != [ sUOrce ]/,
+ 'expected debug of literal diff',
+);
+
+ok (! eq_sql (
+ 'SELECT owner_name FROM books me ORDER BY owner_name',
+ 'SELECT owner_name FROM books me GROUP BY owner_name',
+));
+like(
+ $sql_differ,
+ qr/\QOP [ORDER BY] != [GROUP BY]/,
+ 'expected debug of op diff',
+);
+
+ok (! eq_sql (
+ 'SELECT owner_name FROM books WHERE ( source = ? )',
+ 'SELECT owner_name FROM books'
+));
+
+like(
+ $sql_differ,
+ qr|\Q[WHERE source = ?] != [N/A]|,
+ 'expected debug of missing branch',
+);
+
+
+ok (eq_sql_bind (
+ \[ 'SELECT foo FROM bar WHERE baz = ? or buzz = ?', [ {} => 1 ], 2 ],
+ 'SELECT foo FROM bar WHERE (baz = ?) OR buzz = ?',
+ [ [ {} => 1 ], 2 ],
+), 'arrayrefref unpacks correctly' );
+
+is_same_sql_bind(
+ \[ 'SELECT foo FROM bar WHERE baz = ? or buzz = ?', [ {} => 1 ], 2 ],
+ \[ 'SELECT foo FROM bar WHERE (( baz = ? OR (buzz = ?) ))', [ {} => 1 ], 2 ],
+ 'double arrayrefref unpacks correctly'
+);
+
+done_testing;