Fix PDO OCI Bug #60994 (Reading a multibyte CLOB caps at 8192 chars)

This commit is contained in:
Michael Voříšek 2022-03-24 15:56:20 +01:00 committed by Ben Ramsey
parent 131b862ac0
commit 4df4264ac9
No known key found for this signature in database
GPG Key ID: F9C39DC0B9698544
3 changed files with 52 additions and 19 deletions

View File

@ -182,6 +182,14 @@ if test "$PHP_PDO_OCI" != "no"; then
-L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD
])
dnl Can handle bytes vs. characters?
PHP_CHECK_LIBRARY(clntsh, OCILobRead2,
[
AC_DEFINE(HAVE_OCILOBREAD2,1,[ ])
], [], [
-L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD
])
PHP_CHECK_PDO_INCLUDES
PHP_NEW_EXTENSION(pdo_oci, pdo_oci.c oci_driver.c oci_statement.c, $ext_shared,,-I$pdo_cv_inc_path)

View File

@ -616,6 +616,7 @@ struct oci_lob_self {
OCILobLocator *lob;
oci_lob_env *E;
ub4 offset;
ub1 csfrm;
};
static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
@ -641,23 +642,34 @@ static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count)
{
struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
ub4 amt;
sword r;
#if HAVE_OCILOBREAD2
oraub8 byte_amt = (oraub8) count;
oraub8 char_amt = 0;
amt = (ub4) count;
r = OCILobRead(self->E->svc, self->E->err, self->lob,
&amt, self->offset, buf, (ub4) count,
sword r = OCILobRead2(self->E->svc, self->E->err, self->lob,
&byte_amt, &char_amt, (oraub8) self->offset, buf, (oraub8) count,
OCI_ONE_PIECE, NULL, NULL, 0, self->csfrm);
#else
ub4 byte_amt = (ub4) count;
sword r = OCILobRead(self->E->svc, self->E->err, self->lob,
&byte_amt, self->offset, buf, (ub4) count,
NULL, NULL, 0, SQLCS_IMPLICIT);
#endif
if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
return (size_t)-1;
return (ssize_t)-1;
}
self->offset += amt;
if (amt < count) {
#if HAVE_OCILOBREAD2
self->offset += self->csfrm == 0 ? byte_amt : char_amt;
#else
self->offset += byte_amt;
#endif
if (byte_amt < count) {
stream->eof = 1;
}
return amt;
return byte_amt;
}
static int oci_blob_close(php_stream *stream, int close_handle)
@ -724,6 +736,8 @@ static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLoca
self->E->svc = self->S->H->svc;
self->E->err = self->S->err;
OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm);
stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
if (stm) {

View File

@ -21,18 +21,19 @@ $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
$dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
@$dbh->exec('DROP TABLE pdo_oci_bug60994');
$dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB)');
$dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB, data2 NCLOB)');
$id = null;
$insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data) VALUES (:id, :data)');
$insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data, data2) VALUES (:id, :data, :data2)');
$insert->bindParam(':id', $id, \PDO::PARAM_STR);
$select = $dbh->prepare("SELECT data FROM pdo_oci_bug60994 WHERE id = :id");
$select = $dbh->prepare("SELECT data, data2 FROM pdo_oci_bug60994 WHERE id = :id");
echo PHP_EOL, 'Test 1: j', PHP_EOL;
$string1 = 'abc' . str_repeat('j', 8187) . 'xyz'; // 8193 chars total works fine here (even 1 million works fine, subject to memory_limit)
$id = 1;
$insert->bindParam(':data', $string1, \PDO::PARAM_STR, strlen($string1)); // length in bytes
$insert->bindParam(':data2', $string1, \PDO::PARAM_STR, strlen($string1));
$insert->execute();
$select->bindParam(':id', $id, \PDO::PARAM_STR);
$select->execute();
@ -44,12 +45,15 @@ echo 'size of string1 is ', strlen($string1), ' bytes, ', mb_strlen($string1), '
echo 'size of stream1 is ', strlen($stream1), ' bytes, ', mb_strlen($stream1), ' chars.', PHP_EOL;
echo 'beg of stream1 is ', $start1, PHP_EOL;
echo 'end of stream1 is ', $ending1, PHP_EOL;
if ($string1 != $stream1 || $stream1 != stream_get_contents($row['DATA2'])) {
echo 'Expected nclob value to match clob value for stream1', PHP_EOL;
}
echo PHP_EOL, 'Test 2: £', PHP_EOL;
$string2 = 'abc' . str_repeat('£', 8187) . 'xyz'; // 8193 chars total is when it breaks
$id = 2;
$insert->bindParam(':data', $string2, \PDO::PARAM_STR, strlen($string2)); // length in bytes
$insert->bindParam(':data2', $string2, \PDO::PARAM_STR, strlen($string2));
$insert->execute();
$select->bindParam(':id', $id, \PDO::PARAM_STR);
$select->execute();
@ -61,12 +65,15 @@ echo 'size of string2 is ', strlen($string2), ' bytes, ', mb_strlen($string2), '
echo 'size of stream2 is ', strlen($stream2), ' bytes, ', mb_strlen($stream2), ' chars.', PHP_EOL;
echo 'beg of stream2 is ', $start2, PHP_EOL;
echo 'end of stream2 is ', $ending2, PHP_EOL;
if ($string2 != $stream2 || $stream2 != stream_get_contents($row['DATA2'])) {
echo 'Expected nclob value to match clob value for stream2', PHP_EOL;
}
echo PHP_EOL, 'Test 3: Җ', PHP_EOL;
$string3 = 'abc' . str_repeat('Җ', 8187) . 'xyz'; // 8193 chars total is when it breaks
$id = 3;
$insert->bindParam(':data', $string3, \PDO::PARAM_STR, strlen($string3)); // length in bytes
$insert->bindParam(':data2', $string3, \PDO::PARAM_STR, strlen($string3));
$insert->execute();
$select->bindParam(':id', $id, \PDO::PARAM_STR);
$select->execute();
@ -78,12 +85,15 @@ echo 'size of string3 is ', strlen($string3), ' bytes, ', mb_strlen($string3), '
echo 'size of stream3 is ', strlen($stream3), ' bytes, ', mb_strlen($stream3), ' chars.', PHP_EOL;
echo 'beg of stream3 is ', $start3, PHP_EOL;
echo 'end of stream3 is ', $ending3, PHP_EOL;
if ($string3 != $stream3 || $stream3 != stream_get_contents($row['DATA2'])) {
echo 'Expected nclob value to match clob value for stream3', PHP_EOL;
}
echo PHP_EOL, 'Test 4: の', PHP_EOL;
$string4 = 'abc' . str_repeat('の', 8187) . 'xyz'; // 8193 chars total is when it breaks
$id = 4;
$insert->bindParam(':data', $string4, \PDO::PARAM_STR, strlen($string4)); // length in bytes
$insert->bindParam(':data2', $string4, \PDO::PARAM_STR, strlen($string4));
$insert->execute();
$select->bindParam(':id', $id, \PDO::PARAM_STR);
$select->execute();
@ -95,13 +105,14 @@ echo 'size of string4 is ', strlen($string4), ' bytes, ', mb_strlen($string4), '
echo 'size of stream4 is ', strlen($stream4), ' bytes, ', mb_strlen($stream4), ' chars.', PHP_EOL;
echo 'beg of stream4 is ', $start4, PHP_EOL;
echo 'end of stream4 is ', $ending4, PHP_EOL;
if ($string4 != $stream4 || $stream4 != stream_get_contents($row['DATA2'])) {
echo 'Expected nclob value to match clob value for stream4', PHP_EOL;
}
?>
--XFAIL--
Fails due to Bug 60994
--EXPECT--
Test 1: j
size of string1 is 1000006 bytes, 1000006 chars.
size of stream1 is 1000006 bytes, 1000006 chars.
size of string1 is 8193 bytes, 8193 chars.
size of stream1 is 8193 bytes, 8193 chars.
beg of stream1 is abcjjjjjjj
end of stream1 is jjjjjjjxyz