DBIO-Firebird
view release on metacpan or search on metacpan
t/94-blob-compare.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
use DBIO::Firebird::Storage;
use DBIO::Firebird::Storage::InterBase;
use DBIO::Firebird::Storage::ODBC;
# OFFLINE (karr k60, cases bind-empty-string ... bind-lob-row-operations).
#
# WHY: text and blob columns are BLOBs on Firebird. DBD::Firebird dies on a
# BLOB parameter in a SELECT ("BLOB as an input param for SELECT is not
# allowed") -- so search({ v => 'x' }), find on such a column, the label of a
# document lookup, all die in the DBD -- while the server compares a BLOB with
# a VARCHAR happily. Measured on Firebird 5.0.4 (a UTF8 and a NONE database):
# v = CAST(? AS VARCHAR(8191) CHARACTER SET UTF8) finds the row, also for
# '' and for non-ASCII text up to 8191 characters, and the binary form with
# CHARACTER SET OCTETS for every byte value up to 32765 bytes; UPDATE and
# DELETE take a BLOB parameter and need none. Past the VARCHAR limit nothing
# can be compared, and the storage refuses before the DBD does.
#
# THE STAND-IN: the SQL maker answers a canned statement and bind, the source
# answers its column infos; the storage resolves the binds as it always does.
{
package T::Source;
sub new { bless {}, shift }
sub name { 'val' }
sub from { 'val' }
sub columns_info {
return {
id => { data_type => 'integer' },
v => { data_type => 'text' },
b => { data_type => 'blob' },
c => { data_type => 'varchar', size => 20 },
s => { data_type => 'string' },
};
}
}
{
package T::Maker;
sub new { my ($c, $sql, @bind) = @_; bless { sql => $sql, bind => \@bind }, $c }
sub select { my $s = shift; return ($s->{sql}, @{ $s->{bind} }) }
sub update { my $s = shift; return ($s->{sql}, @{ $s->{bind} }) }
}
my %SUB;
sub gen {
my ($class, $op, $sql, @bind) = @_;
my $sub = $SUB{$class} ||= do {
(my $name = "T::Storage::$class") =~ s/::/_/g;
$name = "T::$name";
eval qq{package $name; use mro 'c3'; our \@ISA = ('$class'); sub sql_maker { \$_[0]{_sql_maker} } 1 }
or die $@;
$name;
};
my $storage = bless { _sql_maker => T::Maker->new($sql, @bind) }, $sub;
return $storage->_gen_sql_bind($op, T::Source->new, [ undef, undef, {} ]);
}
my $TEXT = 'CAST(? AS VARCHAR(8191) CHARACTER SET UTF8)';
my $BIN = 'CAST(? AS VARCHAR(32765) CHARACTER SET OCTETS)';
for my $class (qw(DBIO::Firebird::Storage DBIO::Firebird::Storage::InterBase)) {
my ($sql, $bind) = gen($class, 'select', 'SELECT id FROM val WHERE v = ?', [ v => 'abc' ]);
is $sql, "SELECT id FROM val WHERE v = $TEXT", "$class: a text BLOB is compared with a cast VARCHAR";
is $bind->[0][1], 'abc', '... the bound value is the one written';
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE b = ?', [ b => "\x00\xff" ]);
is $sql, "SELECT id FROM val WHERE b = $BIN", "$class: a binary BLOB with an OCTETS cast";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE s = ?', [ s => 'x' ]);
like $sql, qr/\Q$TEXT\E/, "$class: a string without a size is a text BLOB too";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE v = ?', [ v => '' ]);
like $sql, qr/\Q$TEXT\E/, "$class: the empty string is cast as well";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE v LIKE ?', [ v => 'a%' ]);
like $sql, qr/LIKE \Q$TEXT\E/, "$class: LIKE too";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE c = ? AND id = ? AND v = ?',
[ c => 'x' ], [ id => 3 ], [ v => 'y' ]);
is $sql, "SELECT id FROM val WHERE c = ? AND id = ? AND v = $TEXT",
"$class: only the BLOB comparison is rewritten, among others";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE v = ? AND v = ?', [ v => 'a' ], [ v => 'b' ]);
is $sql, "SELECT id FROM val WHERE v = $TEXT AND v = $TEXT", "$class: every such placeholder";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE v IS NULL AND id = ?', [ id => 1 ]);
is $sql, 'SELECT id FROM val WHERE v IS NULL AND id = ?', "$class: no BLOB bind, no change";
($sql) = gen($class, 'select', 'SELECT id FROM val WHERE v = ?', [ v => undef ]);
is $sql, 'SELECT id FROM val WHERE v = ?', "$class: an undef is no value to cast";
# a ? in a literal, an identifier or a comment is not a placeholder
($sql) = gen($class, 'select',
q{SELECT 'a?b', "q?" FROM val /* ? */ WHERE v = ? -- ?
AND 'it''s ?' = 'x'}, [ v => 'x' ]);
is $sql, qq{SELECT 'a?b', "q?" FROM val /* ? */ WHERE v = $TEXT -- ?\n AND 'it''s ?' = 'x'},
"$class: a ? in a string, an identifier or a comment is left alone";
# core k388: the shared scanner -- a ? in a comment or literal ahead of the
# BLOB comparison does not move the cast
($sql) = gen($class, 'select',
( run in 0.634 second using v1.01-cache-2.11-cpan-145b269f2f3 )