#!perl -w
use
DBD::Oracle
qw(:ora_types SQLCS_NCHAR SQLCS_IMPLICIT ORA_OCI)
;
*BAILOUT
=
sub
{
die
"@_\n"
}
unless
defined
&BAILOUT
;
unshift
@INC
,
't'
;
require
'nchar_test_lib.pl'
;
my
@test_sets
;
push
@test_sets
, [
"LONG"
, 0, 0 ];
push
@test_sets
, [
"LONG RAW"
, ORA_LONGRAW, 0 ];
push
@test_sets
, [
"NCLOB"
, ORA_CLOB, 0 ]
unless
ORA_OCI() < 9.0 or
$ENV
{DBD_ALL_TESTS};
push
@test_sets
, [
"CLOB"
, ORA_CLOB, 0 ] ;
push
@test_sets
, [
"BLOB"
, ORA_BLOB, 0 ] ;
my
$tests_per_set
= 96;
my
$tests
=
@test_sets
*
$tests_per_set
-1;
plan
tests
=>
$tests
;
$| = 1;
my
$dbuser
=
$ENV
{ORACLE_USERID} ||
'scott/tiger'
;
my
$table
= table();
my
$use_utf8_data
;
my
%warnings
;
my
@skip_unicode
;
push
@skip_unicode
,
"Perl < 5.6 "
if
$] < 5.006;
push
@skip_unicode
,
"Oracle client < 9.0 "
if
ORA_OCI() < 9.0 and !
$ENV
{DBD_ALL_TESTS};
my
$sz
= 8;
my
(
$p1
,
$p2
,
$tmp
,
@tmp
);
my
$dbh
= db_handle() or BAILOUT(
"Can't connect to database: $DBI::errstr"
);
my
$ora_server_version
=
$dbh
->func(
"ora_server_version"
);
diag(
"ora_server_version: @$ora_server_version\n"
);
show_db_charsets(
$dbh
)
if
$dbh
;
foreach
(
@test_sets
) {
my
(
$type_name
,
$type_num
,
$test_no_type
) =
@$_
;
$use_utf8_data
= use_utf8_data(
$dbh
,
$type_name
);
diag(
qq(
=========================================================================
Running long test for $type_name ($type_num)
use_utf8_data=
$use_utf8_data
));
run_long_tests(
$dbh
,
$type_name
,
$type_num
);
run_long_tests(
$dbh
,
$type_name
, 0)
if
$test_no_type
;
}
exit
0;
END {
drop_table(
$dbh
)
if
not
$ENV
{DBD_SKIP_TABLE_DROP};
$dbh
->disconnect
if
$dbh
;
}
sub
use_utf8_data
{
my
(
$dbh
,
$type_name
) =
@_
;
if
( (
$type_name
=~ m/^CLOB/i and db_ochar_is_utf(
$dbh
) && client_ochar_is_utf8())
or (
$type_name
=~ m/^NCLOB/i and db_nchar_is_utf(
$dbh
) && client_nchar_is_utf8()) ) {
return
1
unless
@skip_unicode
;
warn
"Skipping Unicode data tests: @skip_unicode\n"
if
!
$warnings
{use_utf8_data}++;
}
return
0;
}
sub
run_long_tests
{
my
(
$dbh
,
$type_name
,
$type_num
) =
@_
;
my
(
$sth
);
my
$append_len
;
SKIP:
{
my
%long_data
;
my
@long_data
;
$long_data
[2] = (
"2bcdefabcd"
x 1024) x (
$sz
-1);
$long_data
[1] = (
"1234567890"
x 1024) x (
$sz
);
$long_data
[0] = (
"0\177x\0X"
x 2048) x (1 );
if
(
$use_utf8_data
) {
my
$utf_x
=
"0\x{263A}xyX"
;
$long_data
[0] = (
$utf_x
x 2048) x (1 );
if
(
length
(
$long_data
[0]) > 10240) {
diag
"known bug in perl5.6.0 utf8 support, applying workaround\n"
;
my
$utf_z
=
"0\x{263A}xyZ"
;
$long_data
[0] =
$utf_z
;
$long_data
[0] .=
$utf_z
foreach
(1..2047);
}
if
(
$type_name
eq
'BLOB'
) {
$long_data
[0] =
pack
"C*"
, (
unpack
"C*"
,
$long_data
[0]);
}
}
my
$be_utf8
= (
$type_name
eq
'BLOB'
) ? 0
: (
$type_name
eq
'CLOB'
) ? client_ochar_is_utf8()
: (
$type_name
eq
'NCLOB'
) ? client_nchar_is_utf8()
: 0;
$long_data
[0] =
"00FF"
x (
length
(
$long_data
[0]) / 2)
if
$type_name
=~ /RAW/i;
my
$len_data0
=
length
(
$long_data
[0]);
my
$len_data1
=
length
(
$long_data
[1]);
my
$len_data2
=
length
(
$long_data
[2]);
warn
"long_data[0] is > 64KB: $len_data0\n"
if
$len_data0
> 65535;
warn
"long_data[1] is < 64KB: $len_data1\n"
if
$len_data1
< 65535;
warn
"long_data[2] is not smaller than $long_data[1] ($len_data2 > $len_data1)\n"
if
$len_data2
>=
$len_data1
;
my
$tdata
= {
cols
=> long_test_cols(
$type_name
),
rows
=> []
};
skip
"Unable to create test table for '$type_name' data ($DBI::err)."
,
$tests_per_set
if
(!create_table(
$dbh
,
$tdata
, 1));
diag(
"long_data[0] length $len_data0\n"
);
diag(
"long_data[1] length $len_data1\n"
);
diag(
"long_data[2] length $len_data2\n"
);
diag(
" --- insert some $type_name data (ora_type $type_num)\n"
);
my
$sqlstr
=
"insert into $table values (?, ?, SYSDATE)"
;
ok(
$sth
=
$dbh
->prepare(
$sqlstr
),
"prepare: $sqlstr"
);
my
$bind_attr
= {
ora_type
=>
$type_num
};
$bind_attr
->{ora_csform} = (
$type_name
=~ /^NCLOB/) ? SQLCS_NCHAR : SQLCS_IMPLICIT;
$sth
->bind_param(2,
undef
,
$bind_attr
)
or
die
"$type_name: $DBI::errstr"
if
$type_num
;
ok(
$sth
->execute(40,
$long_data
{40} =
$long_data
[0] ),
"insert long data 40"
);
ok(
$sth
->execute(41,
$long_data
{41} =
$long_data
[1] ),
"insert long data 41"
);
ok(
$sth
->execute(42,
$long_data
{42} =
$long_data
[2] ),
"insert long data 42"
);
ok(
$sth
->execute(43,
$long_data
{43} =
undef
),
"insert long data undef 43"
);
array_test(
$dbh
);
diag(
" --- fetch $type_name data back again -- truncated - LongTruncOk == 1\n"
);
$dbh
->{LongReadLen} = 20;
$dbh
->{LongTruncOk} = 1;
diag(
"LongReadLen $dbh->{LongReadLen}, LongTruncOk $dbh->{LongTruncOk}\n"
);
my
$out_len
=
$dbh
->{LongReadLen};
$out_len
*= 2
if
(
$type_name
=~ /RAW/i);
$sqlstr
=
"select * from $table order by idx"
;
ok(
$sth
=
$dbh
->prepare(
$sqlstr
),
"prepare: $sqlstr"
);
$sth
->trace(0);
ok(
$sth
->execute,
"execute: $sqlstr"
);
ok(
$tmp
=
$sth
->fetchall_arrayref,
"fetch_arrayref for $sqlstr"
);
$sth
->trace(0);
SKIP: {
if
(
$DBI::err
&&
$DBI::errstr
=~ /ORA-01801:/) {
skip
" If you're using Oracle <= 8.1.7 then this error is probably\n"
.
" due to an Oracle bug and not a DBD::Oracle problem.\n"
, 5 ;
}
cmp_ok(
@$tmp
,
'=='
,4 ,
'four rows'
);
for
my
$i
(0..2) {
my
$v
=
$tmp
->[
$i
][1];
cmp_ok_byte_nice(
$v
,
substr
(
$long_data
[
$i
],0,
$out_len
),
"truncated to LongReadLen $out_len"
);
if
(
$type_name
eq
'BLOB'
) {
ok( !utf8::is_utf8(
$v
),
"BLOB non-UTF8"
);
}
else
{
ok( utf8::is_utf8(
$v
) >= utf8::is_utf8(
$long_data
[
$i
]),
"$type_name UTF8 setting"
);
}
}
ok(!
defined
$tmp
->[3][1],
"last row undefined"
);
}
diag(
" --- fetch $type_name data back again -- truncated - LongTruncOk == 0\n"
);
$dbh
->{LongReadLen} =
$len_data1
- 10;
$dbh
->{LongReadLen} =
$dbh
->{LongReadLen} / 2
if
$type_name
=~ /RAW/i;
my
$LongReadLen
=
$dbh
->{LongReadLen};
$dbh
->{LongTruncOk} = 0;
diag(
"LongReadLen $dbh->{LongReadLen}, LongTruncOk $dbh->{LongTruncOk}\n"
);
$sqlstr
=
"select * from $table order by idx"
;
ok(
$sth
=
$dbh
->prepare(
$sqlstr
),
"prepare $sqlstr"
);
ok(
$sth
->execute,
"execute $sqlstr"
);
ok(
$tmp
=
$sth
->fetchrow_arrayref,
"fetchrow_arrayref $sqlstr"
);
ok(
$tmp
->[1] eq
$long_data
[0],
"length tmp->[1] "
.
length
(
$tmp
->[1]) );
{
local
$sth
->{PrintError} = 0;
ok(!
defined
$sth
->fetchrow_arrayref,
"truncation error not triggered "
.
"(LongReadLen $LongReadLen, data "
.
length
(
$tmp
->[1]||0).
")"
);
$tmp
=
$sth
->err || 0;
ok( (
$tmp
== 1406 ||
$tmp
== 24345) ,
"tmp==1406 || tmp==24345 tmp actually=$tmp"
);
}
$sth
->finish;
diag(
" --- fetch $type_name data back again -- complete - LongTruncOk == 0\n"
);
$dbh
->{LongReadLen} =
$len_data1
+1000;
$dbh
->{LongTruncOk} = 0;
diag(
"LongReadLen $dbh->{LongReadLen}, LongTruncOk $dbh->{LongTruncOk}\n"
);
$sqlstr
=
"select * from $table order by idx"
;
ok(
$sth
=
$dbh
->prepare(
$sqlstr
),
"prepare: $sqlstr"
);
ok(
$sth
->execute,
"execute $sqlstr"
);
for
my
$i
(0..2) {
ok(
$tmp
=
$sth
->fetchrow_arrayref,
"fetchrow_arrayref $sqlstr"
);
ok(
$tmp
->[1] eq
$long_data
[
$i
],
cdif(
$tmp
->[1],
$long_data
[
$i
],
"Len "
.
length
(
$tmp
->[1])) );
}
$sth
->finish;
SKIP: {
skip(
"blob_read tests for LONGs - not currently supported"
, 15 )
if
(
$type_name
=~ /LONG/i) ;
diag(
" --- fetch $type_name data back again -- via blob_read\n\n"
);
$dbh
->{LongReadLen} = 1024 * 90;
$dbh
->{LongTruncOk} = 1;
$sqlstr
=
"select idx, lng, dt from $table order by idx"
;
ok(
$sth
=
$dbh
->prepare(
$sqlstr
) ,
"prepare $sqlstr"
);
ok(
$sth
->execute,
"execute $sqlstr"
);
diag(
"fetch via fetchrow_arrayref\n"
);
ok(
$tmp
=
$sth
->fetchrow_arrayref,
"fetchrow_arrayref 1: $sqlstr"
);
cmp_ok_byte_nice(
$tmp
->[1],
$long_data
[0],
"truncated to LongReadLen $out_len"
);
diag(
"read via blob_read_all\n"
);
cmp_ok(blob_read_all(
$sth
, 1, \
$p1
, 4096) ,
'=='
,
length
(
$long_data
[0]),
"blob_read_all = length(\$long_data[0])"
);
ok(
$p1
eq
$long_data
[0], cdif(
$p1
,
$long_data
[0]) );
$sth
->trace(0);
ok(
$tmp
=
$sth
->fetchrow_arrayref,
"fetchrow_arrayref 2: $sqlstr"
);
cmp_ok(blob_read_all(
$sth
, 1, \
$p1
, 12345) ,
'=='
,
length
(
$long_data
[1]),
"blob_read_all = length(long_data[1])"
);
ok(
$p1
eq
$long_data
[1], cdif(
$p1
,
$long_data
[1]) );
ok(
$tmp
=
$sth
->fetchrow_arrayref,
"fetchrow_arrayref 3: $sqlstr"
);
my
$len
= blob_read_all(
$sth
, 1, \
$p1
, 34567);
cmp_ok(
$len
,
'=='
,
length
(
$long_data
[2]),
"length of long_data[2] = $len"
);
cmp_ok_byte_nice(
$p1
,
$long_data
[2],
"3rd row via blob_read_all"
);
diag(
"result is "
.(utf8::is_utf8(
$p1
) ?
"UTF8"
:
"non-UTF8"
).
"\n"
);
if
(
$be_utf8
) {
ok( utf8::is_utf8(
$p1
),
"result should be utf8"
);
}
else
{
ok( !utf8::is_utf8(
$p1
),
"result should not be utf8"
);
}
}
SKIP: {
skip(
"ora_auto_lob tests for $type_name"
.
"s - not supported"
, 7+(13*3) )
if
not (
$type_name
=~ /LOB/i );
diag(
" --- testing ora_auto_lob to access $type_name LobLocator\n\n"
);
my
$data_fmt
=
"%03d foo!"
;
$sqlstr
=
qq{
SELECT lng, idx FROM $table ORDER BY idx
FOR UPDATE -- needed so lob locator is writable
}
;
my
$ll_sth
=
$dbh
->prepare(
$sqlstr
, {
ora_auto_lob
=> 0 } );
ok(
$ll_sth
,
"prepare $sqlstr"
);
ok(
$ll_sth
->execute ,
"execute $sqlstr"
);
while
(
my
(
$lob_locator
,
$idx
) =
$ll_sth
->fetchrow_array) {
diag(
"$idx: "
.DBI::neat(
$lob_locator
).
"\n"
);
last
if
!
defined
(
$lob_locator
) &&
$idx
== 43;
ok(
$lob_locator
,
'$lob_locator is true'
);
is(
ref
$lob_locator
,
'OCILobLocatorPtr'
,
'$lob_locator is a OCILobLocatorPtr'
);
ok( (
ref
$lob_locator
and
$$lob_locator
),
'$lob_locator deref ptr is true'
) ;
my
$chunk_size
=
$dbh
->func(
$lob_locator
,
'ora_lob_chunk_size'
);
ok(!
$DBI::err
,
"DBI::errstr"
);
my
$data
=
sprintf
$data_fmt
,
$idx
;
diag(
"length of data to be written at offset 1: "
.
length
(
$data
) .
"\n"
);
ok(
$dbh
->func(
$lob_locator
, 1,
$data
,
'ora_lob_write'
) ,
"ora_lob_write"
);
}
is(
$ll_sth
->rows, 4);
diag(
" --- round again to check contents after $type_name write updates...\n"
);
ok(
$ll_sth
->execute,
"execute (again 1) $sqlstr"
);
while
(
my
(
$lob_locator
,
$idx
) =
$ll_sth
->fetchrow_array) {
diag(
"$idx locator: "
.DBI::neat(
$lob_locator
).
"\n"
);
next
if
!
defined
(
$lob_locator
) &&
$idx
== 43;
diag(
"DBI::errstr=$DBI::errstr\n"
)
if
$DBI::err
;
my
$content
=
$dbh
->func(
$lob_locator
, 1, 20,
'ora_lob_read'
);
diag(
"DBI::errstr=$DBI::errstr\n"
)
if
$DBI::err
;
ok(
$content
,
"content is true"
);
diag(
"$idx content: "
.nice_string(
$content
).
"\n"
);
cmp_ok(
length
(
$content
) ,
'=='
, 20 ,
"lenth(content)"
);
my
$data
=
sprintf
$data_fmt
,
$idx
;
ok(
substr
(
$content
,0,
length
(
$data
)) eq
$data
,
"length(content)=length(data)"
);
my
$len
=
$dbh
->func(
$lob_locator
,
'ora_lob_length'
);
ok(!
$DBI::err
,
"DBI::errstr"
);
cmp_ok(
$len
,
'=='
,
length
(
$long_data
{
$idx
}) ,
"length(long_data{idx}) = length of locator data"
);
$dbh
->func(
$lob_locator
,
$idx
,
'ora_lob_trim'
);
ok(!
$DBI::err
,
"DBI::errstr"
);
SKIP: {
$append_len
= 0;
skip(
"ora_lob_append() not reliable in Oracle 8 (Oracle bug #886191)"
, 1 )
if
ORA_OCI() < 9 or
$ora_server_version
->[0] < 9;
my
$append_data
=
"12345"
;
$append_len
=
length
(
$append_data
);
$dbh
->func(
$lob_locator
,
$append_data
,
'ora_lob_append'
);
ok(!
$DBI::err
,
"ora_lob_append DBI::errstr"
);
}
}
is(
$ll_sth
->rows, 4);
diag(
" --- round again to check the $type_name length...\n"
);
ok(
$ll_sth
->execute ,
"execute (again 2) $sqlstr"
);
while
(
my
(
$lob_locator
,
$idx
) =
$ll_sth
->fetchrow_array) {
diag(
"$idx locator: "
.DBI::neat(
$lob_locator
).
"\n"
);
next
if
!
defined
(
$lob_locator
) &&
$idx
== 43;
my
$len
=
$dbh
->func(
$lob_locator
,
'ora_lob_length'
);
ok(!
$DBI::err
,
"DBI::errstr"
);
cmp_ok(
$len
,
'=='
,
$idx
+
$append_len
,
"len == idx+5"
);
}
is(
$ll_sth
->rows, 4);
}
}
$sth
->finish
if
$sth
;
drop_table(
$dbh
)
}
sub
array_test {
my
(
$dbh
) =
@_
;
return
0;
eval
{
$dbh
->{RaiseError}=1;
$dbh
->trace(0);
my
$sth
=
$dbh
->prepare(
qq{
UPDATE $table set idx=idx+1 RETURNING idx INTO ?
}
);
my
(
$a
,
$b
);
$a
= [];
$sth
->bind_param_inout(1,\
$a
, 2);
$sth
->execute;
diag(
"a=$a\n"
);
diag(
"a=@$a\n"
);
};
die
"RETURNING array: $@"
;
}
sub
print_substrs
{
my
(
$dbh
,
$len
) =
@_
;
my
$tsql
=
"select substr(lng,1,$len),idx from $table order by idx"
;
diag(
"-- prepare: $tsql\n"
) ;
my
$tsth
=
$dbh
->prepare(
$tsql
);
$tsth
->execute();
while
(
my
(
$d
,
$i
) =
$tsth
->fetchrow_array() )
{
last
if
not
defined
$d
;
diag(
"$i: $d\n"
);
}
}
sub
print_lengths
{
my
(
$dbh
) =
@_
;
my
$tsql
=
"select length(lng),idx from $table order by idx"
;
diag(
"-- prepare: $tsql\n"
);
my
$tsth
=
$dbh
->prepare(
$tsql
);
$tsth
->execute();
while
(
my
(
$l
,
$i
) =
$tsth
->fetchrow_array() )
{
last
if
not
defined
$l
;
diag(
"$i: $l\n"
);
}
}
sub
blob_read_all {
my
(
$sth
,
$field_idx
,
$blob_ref
,
$lump
) =
@_
;
$lump
||= 4096;
my
$offset
= 0;
my
@frags
;
while
(1) {
my
$frag
=
$sth
->blob_read(
$field_idx
,
$offset
,
$lump
);
last
unless
defined
$frag
;
my
$len
=
length
$frag
;
last
unless
$len
;
push
@frags
,
$frag
;
$offset
+=
$len
;
}
$$blob_ref
=
join
""
,
@frags
;
return
length
(
$$blob_ref
);
}
sub
unc {
my
@str
=
@_
;
foreach
(
@str
) { s/([\000-\037\177-\377])/
sprintf
"\\%03o"
,
ord
(
$_
) /eg; }
return
join
""
,
@str
unless
wantarray
;
return
@str
;
}
sub
cdif {
my
(
$s1
,
$s2
,
$msg
) =
@_
;
$msg
= (
$msg
) ?
", $msg"
:
""
;
my
(
$l1
,
$l2
) = (
length
(
$s1
),
length
(
$s2
));
return
"Strings are identical$msg"
if
$s1
eq
$s2
;
return
"Strings are of different lengths ($l1 vs $l2)$msg"
if
$l1
!=
$l2
;
my
$i
;
for
(
$i
=0;
$i
<
$l1
; ++
$i
) {
my
(
$c1
,
$c2
) = (
ord
(
substr
(
$s1
,
$i
,1)),
ord
(
substr
(
$s2
,
$i
,1)));
next
if
$c1
==
$c2
;
return
sprintf
"Strings differ at position %d (\\%03o vs \\%03o)$msg"
,
$i
,
$c1
,
$c2
;
}
return
"(cdif error $l1/$l2/$i)"
;
}