Skip to content

Commit b5268a7

Browse files
committed
Fix PDO OCI Bug #60994 (Reading a multibyte CLOB caps at 8192 characters)
based on GH-5233 (authored by by gschc1)
1 parent 54c952f commit b5268a7

File tree

5 files changed

+76
-11
lines changed

5 files changed

+76
-11
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ PHP NEWS
2020
- MySQLnd:
2121
. Fixed bug GH-7972 (MariaDB version prefix 5.5.5- is not stripped). (Kamil Tekiela)
2222

23+
- PDO_OCI:
24+
. Fixed bug #60994 (Reading a multibyte CLOB caps at 8192 characters). (Randy Stark)
25+
2326
- Sockets:
2427
. Fixed ext/sockets build on Haiku. (David Carlier)
2528
. Fixed bug GH-7978 (sockets extension compilation errors). (David Carlier)

ext/pdo_oci/config.m4

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,30 @@ if test "$PHP_PDO_OCI" != "no"; then
182182
-L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD
183183
])
184184

185-
PHP_CHECK_PDO_INCLUDES
185+
dnl Can handle bytes vs. characters?
186+
PHP_CHECK_LIBRARY(clntsh, OCILobRead2,
187+
[
188+
AC_DEFINE(HAVE_OCILOBREAD2,1,[ ])
189+
], [], [
190+
-L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD
191+
])
192+
193+
ifdef([PHP_CHECK_PDO_INCLUDES],
194+
[
195+
PHP_CHECK_PDO_INCLUDES
196+
],[
197+
AC_MSG_CHECKING([for PDO includes])
198+
if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then
199+
pdo_cv_inc_path=$abs_srcdir/ext
200+
elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then
201+
pdo_cv_inc_path=$abs_srcdir/ext
202+
elif test -f $phpincludedir/ext/pdo/php_pdo_driver.h; then
203+
pdo_cv_inc_path=$phpincludedir/ext
204+
else
205+
AC_MSG_ERROR([Cannot find php_pdo_driver.h.])
206+
fi
207+
AC_MSG_RESULT($pdo_cv_inc_path)
208+
])
186209

187210
PHP_NEW_EXTENSION(pdo_oci, pdo_oci.c oci_driver.c oci_statement.c, $ext_shared,,-I$pdo_cv_inc_path)
188211

ext/pdo_oci/oci_statement.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ struct oci_lob_self {
620620
OCILobLocator *lob;
621621
oci_lob_env *E;
622622
ub4 offset;
623+
ub1 csfrm;
623624
};
624625

625626
static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
@@ -642,7 +643,31 @@ static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
642643
return amt;
643644
}
644645

646+
#if HAVE_OCILOBREAD2
647+
static size_t oci_blob_read(php_stream *stream, char *buf, size_t count)
645648
static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count)
649+
{
650+
struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
651+
oraub8 byte_amt = (oraub8) count;
652+
oraub8 char_amt = 0;
653+
654+
sword r = OCILobRead2(self->E->svc, self->E->err, self->lob,
655+
&byte_amt, &char_amt, (oraub8) self->offset, buf,
656+
(oraub8) count, OCI_ONE_PIECE,
657+
NULL, NULL, 0, self->csfrm);
658+
659+
if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
660+
return (size_t)-1;
661+
}
662+
663+
self->offset += self->csfrm == 0 ? byte_amt : char_amt;
664+
if (byte_amt < count) {
665+
stream->eof = 1;
666+
}
667+
return byte_amt;
668+
}
669+
#else
670+
static size_t oci_blob_read(php_stream *stream, char *buf, size_t count)
646671
{
647672
struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
648673
ub4 amt;
@@ -663,6 +688,7 @@ static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count)
663688
}
664689
return amt;
665690
}
691+
#endif
666692

667693
static int oci_blob_close(php_stream *stream, int close_handle)
668694
{
@@ -728,6 +754,8 @@ static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLoca
728754
self->E->svc = self->S->H->svc;
729755
self->E->err = self->S->err;
730756

757+
OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm);
758+
731759
stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
732760

733761
if (stm) {

ext/pdo_oci/tests/bug60994.phpt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@ $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
1818
$dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
1919

2020
@$dbh->exec('DROP TABLE pdo_oci_bug60994');
21-
$dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB)');
21+
$dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB, data2 NCLOB)');
2222

2323
$id = null;
24-
$insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data) VALUES (:id, :data)');
24+
$insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data, data2) VALUES (:id, :data, :data2)');
2525
$insert->bindParam(':id', $id, \PDO::PARAM_STR);
26-
$select = $dbh->prepare("SELECT data FROM pdo_oci_bug60994 WHERE id = :id");
26+
$select = $dbh->prepare("SELECT data, data2 FROM pdo_oci_bug60994 WHERE id = :id");
2727

2828

2929
echo PHP_EOL, 'Test 1: j', PHP_EOL;
3030
$string1 = 'abc' . str_repeat('j', 8187) . 'xyz'; // 8193 chars total works fine here (even 1 million works fine, subject to memory_limit)
3131
$id = 1;
3232
$insert->bindParam(':data', $string1, \PDO::PARAM_STR, strlen($string1)); // length in bytes
33+
$insert->bindParam(':data2', $string1, \PDO::PARAM_STR, strlen($string1));
3334
$insert->execute();
3435
$select->bindParam(':id', $id, \PDO::PARAM_STR);
3536
$select->execute();
@@ -41,12 +42,15 @@ echo 'size of string1 is ', strlen($string1), ' bytes, ', mb_strlen($string1), '
4142
echo 'size of stream1 is ', strlen($stream1), ' bytes, ', mb_strlen($stream1), ' chars.', PHP_EOL;
4243
echo 'beg of stream1 is ', $start1, PHP_EOL;
4344
echo 'end of stream1 is ', $ending1, PHP_EOL;
44-
45+
if ($stream1 != stream_get_contents($row['DATA2'])) {
46+
echo 'Expected nclob value to match clob value for stream1', PHP_EOL;
47+
}
4548

4649
echo PHP_EOL, 'Test 2: £', PHP_EOL;
4750
$string2 = 'abc' . str_repeat('£', 8187) . 'xyz'; // 8193 chars total is when it breaks
4851
$id = 2;
4952
$insert->bindParam(':data', $string2, \PDO::PARAM_STR, strlen($string2)); // length in bytes
53+
$insert->bindParam(':data2', $string2, \PDO::PARAM_STR, strlen($string2));
5054
$insert->execute();
5155
$select->bindParam(':id', $id, \PDO::PARAM_STR);
5256
$select->execute();
@@ -58,12 +62,15 @@ echo 'size of string2 is ', strlen($string2), ' bytes, ', mb_strlen($string2), '
5862
echo 'size of stream2 is ', strlen($stream2), ' bytes, ', mb_strlen($stream2), ' chars.', PHP_EOL;
5963
echo 'beg of stream2 is ', $start2, PHP_EOL;
6064
echo 'end of stream2 is ', $ending2, PHP_EOL;
61-
65+
if ($stream2 != stream_get_contents($row['DATA2'])) {
66+
echo 'Expected nclob value to match clob value for stream2', PHP_EOL;
67+
}
6268

6369
echo PHP_EOL, 'Test 3: Җ', PHP_EOL;
6470
$string3 = 'abc' . str_repeat('Җ', 8187) . 'xyz'; // 8193 chars total is when it breaks
6571
$id = 3;
6672
$insert->bindParam(':data', $string3, \PDO::PARAM_STR, strlen($string3)); // length in bytes
73+
$insert->bindParam(':data2', $string3, \PDO::PARAM_STR, strlen($string3));
6774
$insert->execute();
6875
$select->bindParam(':id', $id, \PDO::PARAM_STR);
6976
$select->execute();
@@ -75,12 +82,15 @@ echo 'size of string3 is ', strlen($string3), ' bytes, ', mb_strlen($string3), '
7582
echo 'size of stream3 is ', strlen($stream3), ' bytes, ', mb_strlen($stream3), ' chars.', PHP_EOL;
7683
echo 'beg of stream3 is ', $start3, PHP_EOL;
7784
echo 'end of stream3 is ', $ending3, PHP_EOL;
78-
85+
if ($stream3 != stream_get_contents($row['DATA2'])) {
86+
echo 'Expected nclob value to match clob value for stream3', PHP_EOL;
87+
}
7988

8089
echo PHP_EOL, 'Test 4: の', PHP_EOL;
8190
$string4 = 'abc' . str_repeat('', 8187) . 'xyz'; // 8193 chars total is when it breaks
8291
$id = 4;
8392
$insert->bindParam(':data', $string4, \PDO::PARAM_STR, strlen($string4)); // length in bytes
93+
$insert->bindParam(':data2', $string4, \PDO::PARAM_STR, strlen($string4));
8494
$insert->execute();
8595
$select->bindParam(':id', $id, \PDO::PARAM_STR);
8696
$select->execute();
@@ -92,13 +102,14 @@ echo 'size of string4 is ', strlen($string4), ' bytes, ', mb_strlen($string4), '
92102
echo 'size of stream4 is ', strlen($stream4), ' bytes, ', mb_strlen($stream4), ' chars.', PHP_EOL;
93103
echo 'beg of stream4 is ', $start4, PHP_EOL;
94104
echo 'end of stream4 is ', $ending4, PHP_EOL;
105+
if ($stream4 != stream_get_contents($row['DATA2'])) {
106+
echo 'Expected nclob value to match clob value for stream4', PHP_EOL;
107+
}
95108
?>
96-
--XFAIL--
97-
Fails due to Bug 60994
98109
--EXPECT--
99110
Test 1: j
100-
size of string1 is 1000006 bytes, 1000006 chars.
101-
size of stream1 is 1000006 bytes, 1000006 chars.
111+
size of string1 is 8193 bytes, 8193 chars.
112+
size of stream1 is 8193 bytes, 8193 chars.
102113
beg of stream1 is abcjjjjjjj
103114
end of stream1 is jjjjjjjxyz
104115

tests/classes/autoload_derived.inc

100644100755
File mode changed.

0 commit comments

Comments
 (0)