our $sql_differ; # keeps track of differing portion between SQLs
our $tb = __PACKAGE__->builder;
+# Parser states for _recurse_parse()
+use constant {
+ PARSE_TOP_LEVEL => 0,
+ PARSE_IN_EXPR => 1,
+ PARSE_IN_PARENS => 2,
+};
+
+# These SQL keywords always signal end of the current expression (except inside
+# of a parenthesized subexpression).
+# Format: A list of strings that will be compiled to extended syntax (ie.
+# /.../x) regexes, without capturing parentheses. They will be automatically
+# anchored to word boundaries to match the whole token).
+my @expression_terminator_sql_keywords = (
+ 'FROM',
+ '(?:
+ (?:
+ (?: \b (?: LEFT | RIGHT | FULL ) \s+ )?
+ (?: \b (?: CROSS | INNER | OUTER ) \s+ )?
+ )?
+ JOIN
+ )',
+ 'ON',
+ 'WHERE',
+ 'GROUP \s+ BY',
+ 'HAVING',
+ 'ORDER \s+ BY',
+ 'LIMIT',
+ 'OFFSET',
+ 'FOR',
+ 'UNION',
+ 'INTERSECT',
+ 'EXCEPT',
+);
+
+my $tokenizer_re_str = join('|',
+ map { '\b' . $_ . '\b' }
+ @expression_terminator_sql_keywords, 'AND', 'OR'
+);
+
+my $tokenizer_re = qr/
+ \s*
+ (
+ \(
+ |
+ \)
+ |
+ $tokenizer_re_str
+ )
+ \s*
+/xi;
+
+
sub is_same_sql_bind {
my ($sql1, $bind_ref1, $sql2, $bind_ref2, $msg) = @_;
# compare
- my $tree1 = parse($sql1);
- my $tree2 = parse($sql2);
- my $same_sql = eq_sql($tree1, $tree2);
+ my $same_sql = eq_sql($sql1, $sql2);
my $same_bind = eq_bind($bind_ref1, $bind_ref2);
# call Test::Builder::ok
}
sub eq_sql {
+ my ($sql1, $sql2) = @_;
+
+ # parse
+ my $tree1 = parse($sql1);
+ my $tree2 = parse($sql2);
+
+ return _eq_sql($tree1, $tree2);
+}
+
+sub _eq_sql {
my ($left, $right) = @_;
# ignore top-level parentheses
return $eq;
}
else { # binary operator
- return eq_sql($left->[1][0], $right->[1][0]) # left operand
- && eq_sql($left->[1][1], $right->[1][1]); # right operand
+ return _eq_sql($left->[1][0], $right->[1][0]) # left operand
+ && _eq_sql($left->[1][1], $right->[1][1]); # right operand
}
}
}
sub parse {
my $s = shift;
- # tokenize string
- my $tokens = [grep {!/^\s*$/} split /\s*(\(|\)|\bAND\b|\bOR\b)\s*/, $s];
+ # tokenize string, and remove all optional whitespace
+ my $tokens = [];
+ foreach my $token (split $tokenizer_re, $s) {
+ $token =~ s/\s+/ /g;
+ $token =~ s/\s+([^\w\s])/$1/g;
+ $token =~ s/([^\w\s])\s+/$1/g;
+ push @$tokens, $token if $token !~ /^$/;
+ }
- my $tree = _recurse_parse($tokens);
+ my $tree = _recurse_parse($tokens, PARSE_TOP_LEVEL);
return $tree;
}
sub _recurse_parse {
- my $tokens = shift;
+ my ($tokens, $state) = @_;
my $left;
while (1) { # left-associative parsing
my $lookahead = $tokens->[0];
- return $left if !defined($lookahead) || $lookahead eq ')';
+ return $left if !defined($lookahead)
+ || ($state == PARSE_IN_PARENS && $lookahead eq ')')
+ || ($state == PARSE_IN_EXPR && grep { $lookahead =~ /^$_$/xi }
+ '\)', @expression_terminator_sql_keywords
+ );
my $token = shift @$tokens;
# nested expression in ()
if ($token eq '(') {
- my $right = _recurse_parse($tokens);
+ my $right = _recurse_parse($tokens, PARSE_IN_PARENS);
$token = shift @$tokens or croak "missing ')'";
$token eq ')' or croak "unexpected token : $token";
$left = $left ? [CONCAT => [$left, [PAREN => $right]]]
}
# AND/OR
elsif ($token eq 'AND' || $token eq 'OR') {
- my $right = _recurse_parse($tokens);
+ my $right = _recurse_parse($tokens, PARSE_IN_EXPR);
$left = [$token => [$left, $right]];
}
+ # expression terminator keywords (as they start a new expression)
+ elsif (grep { $token =~ /^$_$/xi } @expression_terminator_sql_keywords) {
+ my $right = _recurse_parse($tokens, PARSE_IN_EXPR);
+ $left = $left ? [CONCAT => [$left, [CONCAT => [[EXPR => $token], [PAREN => $right]]]]]
+ : [CONCAT => [[EXPR => $token], [PAREN => $right]]];
+ }
# leaf expression
else {
$left = $left ? [CONCAT => [$left, [EXPR => $token]]]
L<SQL::Abstract>, L<Test::More>, L<Test::Builder>.
-=head1 AUTHOR
+=head1 AUTHORS
Laurent Dami, E<lt>laurent.dami AT etat geneve chE<gt>
+Norbert Buchmuller <norbi@nix.hu>
+
=head1 COPYRIGHT AND LICENSE
Copyright 2008 by Laurent Dami.
use strict;
use warnings;
use List::Util qw(sum);
-use Data::Dumper;
use Test::More;
+my @sql_tests = (
+ # WHERE condition - equal
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1/,
+ q/SELECT foo FROM bar WHERE a=1/,
+ q/SELECT foo FROM bar WHERE (a = 1)/,
+ q/SELECT foo FROM bar WHERE (a=1)/,
+ q/SELECT foo FROM bar WHERE ( a = 1 )/,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ a = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ (a = 1)
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ ( a = 1 )
+ /,
+ q/SELECT foo FROM bar WHERE ((a = 1))/,
+ q/SELECT foo FROM bar WHERE ( (a = 1) )/,
+ q/SELECT foo FROM bar WHERE ( ( a = 1 ) )/,
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1/,
+ q/SELECT foo FROM bar WHERE (a = 1) AND (b = 1)/,
+ q/SELECT foo FROM bar WHERE ((a = 1) AND (b = 1))/,
+ q/SELECT foo FROM bar WHERE (a = 1 AND b = 1)/,
+ q/SELECT foo FROM bar WHERE ((a = 1 AND b = 1))/,
+ q/SELECT foo FROM bar WHERE (((a = 1) AND (b = 1)))/,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ a = 1
+ AND
+ b = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ (a = 1
+ AND
+ b = 1)
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ (a = 1)
+ AND
+ (b = 1)
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ WHERE
+ ((a = 1)
+ AND
+ (b = 1))
+ /,
+ ]
+ },
+
+ # WHERE condition - different
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1/,
+ q/SELECT quux FROM bar WHERE a = 1/,
+ q/SELECT foo FROM quux WHERE a = 1/,
+ q/FOOBAR foo FROM bar WHERE a = 1/,
+
+ q/SELECT foo FROM bar WHERE a = 2/,
+ q/SELECT foo FROM bar WHERE a < 1/,
+ q/SELECT foo FROM bar WHERE b = 1/,
+ q/SELECT foo FROM bar WHERE (c = 1)/,
+ q/SELECT foo FROM bar WHERE (d = 1)/,
+
+ q/SELECT foo FROM bar WHERE a = 1 AND quux/,
+ q/SELECT foo FROM bar WHERE a = 1 GROUP BY foo/,
+ q/SELECT foo FROM bar WHERE a = 1 ORDER BY foo/,
+ q/SELECT foo FROM bar WHERE a = 1 LIMIT 1/,
+ q/SELECT foo FROM bar WHERE a = 1 OFFSET 1/,
+ q/SELECT foo FROM bar JOIN quux WHERE a = 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 WHERE a = 1/,
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1/,
+ q/SELECT quux FROM bar WHERE a = 1 AND b = 1/,
+ q/SELECT foo FROM quux WHERE a = 1 AND b = 1/,
+ q/FOOBAR foo FROM bar WHERE a = 1 AND b = 1/,
+
+ q/SELECT foo FROM bar WHERE a = 2 AND b = 1/,
+ q/SELECT foo FROM bar WHERE a = 3 AND (b = 1)/,
+ q/SELECT foo FROM bar WHERE (a = 4) AND b = 1/,
+ q/SELECT foo FROM bar WHERE (a = 5) AND (b = 1)/,
+ q/SELECT foo FROM bar WHERE ((a = 6) AND (b = 1))/,
+ q/SELECT foo FROM bar WHERE ((a = 7) AND (b = 1))/,
+
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 2/,
+ q/SELECT foo FROM bar WHERE a = 1 AND (b = 3)/,
+ q/SELECT foo FROM bar WHERE (a = 1) AND b = 4/,
+ q/SELECT foo FROM bar WHERE (a = 1) AND (b = 5)/,
+ q/SELECT foo FROM bar WHERE ((a = 1) AND (b = 6))/,
+ q/SELECT foo FROM bar WHERE ((a = 1) AND (b = 7))/,
+
+ q/SELECT foo FROM bar WHERE a < 1 AND b = 1/,
+ q/SELECT foo FROM bar WHERE b = 1 AND b = 1/,
+ q/SELECT foo FROM bar WHERE (c = 1) AND b = 1/,
+ q/SELECT foo FROM bar WHERE (d = 1) AND b = 1/,
+
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 AND quux/,
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 GROUP BY foo/,
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 ORDER BY foo/,
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 LIMIT 1/,
+ q/SELECT foo FROM bar WHERE a = 1 AND b = 1 OFFSET 1/,
+ q/SELECT foo FROM bar JOIN quux WHERE a = 1 AND b = 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 WHERE a = 1 AND b = 1/,
+ ]
+ },
+
+ # JOIN condition - equal
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar JOIN baz ON a = 1 WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON a=1 WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON (a = 1) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON (a=1) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON ( a = 1 ) WHERE x = 1/,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ a = 1
+ WHERE
+ x = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ (a = 1)
+ WHERE
+ x = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ ( a = 1 )
+ WHERE
+ x = 1
+ /,
+ q/SELECT foo FROM bar JOIN baz ON ((a = 1)) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON ( (a = 1) ) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON ( ( a = 1 ) ) WHERE x = 1/,
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ q/SELECT foo FROM bar JOIN baz ON a = 1 AND b = 1 WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON (a = 1) AND (b = 1) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON ((a = 1) AND (b = 1)) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON (a = 1 AND b = 1) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON ((a = 1 AND b = 1)) WHERE x = 1/,
+ q/SELECT foo FROM bar JOIN baz ON (((a = 1) AND (b = 1))) WHERE x = 1/,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ a = 1
+ AND
+ b = 1
+ WHERE
+ x = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ (a = 1
+ AND
+ b = 1)
+ WHERE
+ x = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ (a = 1)
+ AND
+ (b = 1)
+ WHERE
+ x = 1
+ /,
+ q/
+ SELECT
+ foo
+ FROM
+ bar
+ JOIN
+ baz
+ ON
+ ((a = 1)
+ AND
+ (b = 1))
+ WHERE
+ x = 1
+ /,
+ ]
+ },
+
+ # JOIN condition - different
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar JOIN quux ON a = 1 WHERE quuux/,
+ q/SELECT quux FROM bar JOIN quux ON a = 1 WHERE quuux/,
+ q/SELECT foo FROM quux JOIN quux ON a = 1 WHERE quuux/,
+ q/FOOBAR foo FROM bar JOIN quux ON a = 1 WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a = 2 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a < 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (c = 1) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (d = 1) WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 GROUP BY foo/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 ORDER BY foo/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 LIMIT 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 OFFSET 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 JOIN quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 JOIN quuux ON a = 1/,
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 WHERE quuux/,
+ q/SELECT quux FROM bar JOIN quux ON a = 1 AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM quux JOIN quux ON a = 1 AND b = 1 WHERE quuux/,
+ q/FOOBAR foo FROM bar JOIN quux ON a = 1 AND b = 1 WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a = 2 AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a = 3 AND (b = 1) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (a = 4) AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (a = 5) AND (b = 1) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON ((a = 6) AND (b = 1)) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON ((a = 7) AND (b = 1)) WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 2 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND (b = 3) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (a = 1) AND b = 4 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (a = 1) AND (b = 5) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON ((a = 1) AND (b = 6)) WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON ((a = 1) AND (b = 7)) WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a < 1 AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON b = 1 AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (c = 1) AND b = 1 WHERE quuux/,
+ q/SELECT foo FROM bar JOIN quux ON (d = 1) AND b = 1 WHERE quuux/,
+
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 AND quuux/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 GROUP BY foo/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 ORDER BY foo/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 LIMIT 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 AND b = 1 OFFSET 1/,
+ q/SELECT foo FROM bar JOIN quux JOIN quuux ON a = 1 AND b = 1/,
+ q/SELECT foo FROM bar JOIN quux ON a = 1 JOIN quuux ON a = 1 AND b = 1/,
+ ]
+ },
+
+ # DISTINCT ON (...) not confused with JOIN ON (...)
+ {
+ equal => 1,
+ statements => [
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE a = 1/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE a=1/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE (a = 1)/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE (a=1)/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE ( a = 1 )/,
+ q/
+ SELECT DISTINCT ON (foo, quux)
+ foo,
+ quux
+ FROM
+ bar
+ WHERE
+ a = 1
+ /,
+ q/
+ SELECT DISTINCT ON (foo, quux)
+ foo,
+ quux
+ FROM
+ bar
+ WHERE
+ (a = 1)
+ /,
+ q/
+ SELECT DISTINCT ON (foo, quux)
+ foo,
+ quux
+ FROM
+ bar
+ WHERE
+ ( a = 1 )
+ /,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE ((a = 1))/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE ( (a = 1) )/,
+ q/SELECT DISTINCT ON (foo, quux) foo, quux FROM bar WHERE ( ( a = 1 ) )/,
+ ]
+ },
+
+ # subselects - equal
+ {
+ equal => 1,
+ 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 = 1)/,
+ 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 = 1)/,
+ ]
+ },
+ {
+ equal => 1,
+ statements => [
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 1) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 1)) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 1) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 1)) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 1))) AS foo WHERE a = 1/,
+
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 1) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 1)) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 1) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 1)) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 1))) AS foo WHERE (a = 1)/,
+ ]
+ },
+
+ # subselects - different
+ {
+ 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)) AS foo WHERE a = 4/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1)) AS foo WHERE (a = 5)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 2) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 3) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 4)) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 5)) AS foo WHERE (a = 1)/,
+ ]
+ },
+ {
+ equal => 0,
+ statements => [
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 1) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 2) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 3)) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 4)) AS foo WHERE a = 1/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 5))) AS foo WHERE a = 1/,
+
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 6) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 7) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 8)) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 9)) AS foo WHERE (a = 1)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 10))) AS foo WHERE (a = 1)/,
+
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 1) AS foo WHERE a = 2/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 2) AS foo WHERE a = 2/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 3)) AS foo WHERE a = 2/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 4)) AS foo WHERE a = 2/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 5))) AS foo WHERE a = 2/,
+
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND c = 6) AS foo WHERE (a = 2)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND c = 7) AS foo WHERE (a = 2)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE b = 1 AND (c = 8)) AS foo WHERE (a = 2)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE (b = 1) AND (c = 9)) AS foo WHERE (a = 2)/,
+ q/SELECT * FROM (SELECT * FROM bar WHERE ((b = 1) AND (c = 10))) AS foo WHERE (a = 2)/,
+ ]
+ },
+);
+
my @bind_tests = (
# scalar - equal
{
},
);
-
-plan tests => 1 + sum
- map { $_ * ($_ - 1) / 2 }
- map { scalar @{$_->{bindvals}} }
- @bind_tests;
+plan tests => 1 +
+ sum(
+ map { $_ * ($_ - 1) / 2 }
+ map { scalar @{$_->{statements}} }
+ @sql_tests
+ ) +
+ sum(
+ map { $_ * ($_ - 1) / 2 }
+ map { scalar @{$_->{bindvals}} }
+ @bind_tests
+ );
use_ok('SQL::Abstract::Test', import => [qw(eq_sql eq_bind is_same_sql_bind)]);
+for my $test (@sql_tests) {
+ my $statements = $test->{statements};
+ while (@$statements) {
+ my $sql1 = shift @$statements;
+ foreach my $sql2 (@$statements) {
+ my $equal = eq_sql($sql1, $sql2);
+ if ($test->{equal}) {
+ ok($equal, "equal SQL expressions considered equal");
+ } else {
+ ok(!$equal, "different SQL expressions considered not equal");
+ }
+
+ if ($equal ^ $test->{equal}) {
+ diag("sql1: $sql1");
+ diag("sql2: $sql2");
+ }
+ }
+ }
+}
+
for my $test (@bind_tests) {
my $bindvals = $test->{bindvals};
while (@$bindvals) {