mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Fix temporary-LOB leak and add tests (Senthil)
This commit is contained in:
parent
0787cd60ed
commit
0615aa82d7
6 changed files with 248 additions and 6 deletions
|
@ -53,6 +53,15 @@
|
|||
|
||||
static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob);
|
||||
|
||||
#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \
|
||||
do \
|
||||
{ \
|
||||
boolean isTempLOB; \
|
||||
OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \
|
||||
if (isTempLOB) \
|
||||
OCILobFreeTemporary(svchp, errhp, lob); \
|
||||
} while(0)
|
||||
|
||||
static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
|
||||
{
|
||||
pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
|
||||
|
@ -99,6 +108,8 @@ static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
|
|||
switch (S->cols[i].dtype) {
|
||||
case SQLT_BLOB:
|
||||
case SQLT_CLOB:
|
||||
OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err,
|
||||
(OCILobLocator *) S->cols[i].data);
|
||||
OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
|
||||
break;
|
||||
default:
|
||||
|
@ -293,7 +304,13 @@ static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *pa
|
|||
|
||||
case PDO_PARAM_EVT_FREE:
|
||||
P = param->driver_data;
|
||||
if (P) {
|
||||
if (P && P->thing) {
|
||||
OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
|
||||
OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
|
||||
P->thing = NULL;
|
||||
efree(P);
|
||||
}
|
||||
else if (P) {
|
||||
efree(P);
|
||||
}
|
||||
break;
|
||||
|
@ -381,7 +398,6 @@ static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *pa
|
|||
if (stm) {
|
||||
OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
|
||||
php_stream_to_zval(stm, parameter);
|
||||
P->thing = NULL;
|
||||
}
|
||||
} else {
|
||||
/* we're a LOB being used for insert; transfer the data now */
|
||||
|
@ -430,6 +446,7 @@ static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *pa
|
|||
OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
|
||||
}
|
||||
}
|
||||
OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
|
||||
OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
|
||||
P->thing = NULL;
|
||||
}
|
||||
|
|
|
@ -56,8 +56,6 @@ var_dump($res->fetch());
|
|||
$db->exec("DROP TABLE test_one_blob");
|
||||
|
||||
?>
|
||||
--XFAIL--
|
||||
Corrupts memory
|
||||
--EXPECTF--
|
||||
array(2) {
|
||||
["blob1"]=>
|
||||
|
|
|
@ -60,8 +60,6 @@ fclose($row[0]);
|
|||
$db->exec("DROP TABLE test_one_blob");
|
||||
|
||||
?>
|
||||
--XFAIL--
|
||||
Corrupts memory
|
||||
--EXPECTF--
|
||||
array(2) {
|
||||
["blob1"]=>
|
||||
|
|
74
ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
Normal file
74
ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
Normal file
|
@ -0,0 +1,74 @@
|
|||
--TEST--
|
||||
PDO OCI: Inserts 10K with 1 number and 2 LOB columns (stress test)
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded');
|
||||
if (getenv('SKIP_SLOW_TESTS')) die('skip slow tests excluded by request');
|
||||
require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc');
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc');
|
||||
|
||||
$db = PDOTest::factory();
|
||||
|
||||
$query = "begin execute immediate 'drop table pdo_oci_stream_2'; exception when others then if sqlcode <> -942 then raise; end if; end;";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
|
||||
$query = "create table pdo_oci_stream_2 (id number, data1 blob, data2 blob)";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
|
||||
function do_insert($db, $id, $data1, $data2)
|
||||
{
|
||||
$db->beginTransaction();
|
||||
$stmt = $db->prepare("insert into pdo_oci_stream_2 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2");
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB);
|
||||
$stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB);
|
||||
$blob1 = null;
|
||||
$blob2 = null;
|
||||
$stmt->execute();
|
||||
|
||||
fwrite($blob1, $data1);
|
||||
fclose($blob1);
|
||||
fwrite($blob2, $data2);
|
||||
fclose($blob2);
|
||||
$db->commit();
|
||||
}
|
||||
|
||||
$a1 = str_repeat('a', 4086);
|
||||
$a2 = str_repeat('b', 4087);
|
||||
$a3 = str_repeat('c', 4088);
|
||||
$a4 = str_repeat('d', 4089);
|
||||
$a5 = str_repeat('e', 4090);
|
||||
$a6 = str_repeat('f', 4091);
|
||||
$a7 = str_repeat('g', 4092);
|
||||
$a8 = str_repeat('h', 4093);
|
||||
$a9 = str_repeat('i', 4094);
|
||||
$a10 = str_repeat('j', 4095);
|
||||
|
||||
printf("Inserting 10000 Records ... ");
|
||||
for($i=0; $i<1000; $i++) {
|
||||
do_insert($db, 1, $a1, $a10);
|
||||
do_insert($db, 1, $a2, $a9);
|
||||
do_insert($db, 1, $a3, $a8);
|
||||
do_insert($db, 1, $a4, $a7);
|
||||
do_insert($db, 1, $a5, $a6);
|
||||
do_insert($db, 1, $a6, $a5);
|
||||
do_insert($db, 1, $a7, $a4);
|
||||
do_insert($db, 1, $a8, $a3);
|
||||
do_insert($db, 1, $a9, $a2);
|
||||
do_insert($db, 1, $a10, $a1);
|
||||
}
|
||||
printf("Done\n");
|
||||
|
||||
/* Cleanup is done in pdo_oci_stream_2b.phpt */
|
||||
//$db->exec("drop table pdo_oci_stream_2");
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Inserting 10000 Records ... Done
|
70
ext/pdo_oci/tests/pdo_oci_stream_2b.phpt
Normal file
70
ext/pdo_oci/tests/pdo_oci_stream_2b.phpt
Normal file
|
@ -0,0 +1,70 @@
|
|||
--TEST--
|
||||
PDO OCI: Fetches 10K records from a table that contains 1 number and 2 LOB columns (stress test)
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded');
|
||||
if (getenv('SKIP_SLOW_TESTS')) die('skip slow tests excluded by request');
|
||||
require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc');
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// !! Note: uses data inserted in pdo_oci_stream_2a.phpt !!
|
||||
|
||||
require('ext/pdo/tests/pdo_test.inc');
|
||||
$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt');
|
||||
|
||||
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams
|
||||
|
||||
// Since each column only has one lob descriptor, the last row is
|
||||
// shown twice because the lob descriptor for each column is reused in
|
||||
// the stream
|
||||
|
||||
$i = 0;
|
||||
$j = 9;
|
||||
$a_val = ord('a');
|
||||
foreach($db->query("select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id") as $row) {
|
||||
$a = $row['d4_1'];
|
||||
$a1 = $row['d4_2'];
|
||||
|
||||
$str1 = stream_get_contents($a);
|
||||
$str2 = stream_get_contents($a1);
|
||||
|
||||
$str1len = strlen($str1);
|
||||
$str2len = strlen($str2);
|
||||
|
||||
$b = ord($str1[0]);
|
||||
$b1 = ord($str2[0]);
|
||||
|
||||
if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) &&
|
||||
($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) {
|
||||
printf("There is a bug!\n");
|
||||
printf("Col1:\n");
|
||||
printf("a_val = %d\n", $a_val);
|
||||
printf("b = %d\n", $b);
|
||||
printf("i = %d\n", $i);
|
||||
printf("str1len = %d\n", $str1len);
|
||||
|
||||
printf("Col2:\n");
|
||||
printf("a_val = %d\n", $a_val);
|
||||
printf("b1 = %d\n", $b1);
|
||||
printf("j = %d\n", $j);
|
||||
printf("str2len = %d\n", $str1len);
|
||||
|
||||
}
|
||||
$i++;
|
||||
if ($i>9)
|
||||
$i = 0;
|
||||
$j--;
|
||||
if ($j<0)
|
||||
$j = 9;
|
||||
}
|
||||
echo "Fetch operation done!\n";
|
||||
|
||||
/* Cleanup */
|
||||
$db->exec("drop table pdo_oci_stream_2");
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fetch operation done!
|
85
ext/pdo_oci/tests/pdo_oci_templob_1.phpt
Normal file
85
ext/pdo_oci/tests/pdo_oci_templob_1.phpt
Normal file
|
@ -0,0 +1,85 @@
|
|||
--TEST--
|
||||
PDO OCI: Test to verify all implicitly created temporary LOB are cleaned up
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded');
|
||||
require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc');
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?PHP
|
||||
|
||||
require('ext/pdo/tests/pdo_test.inc');
|
||||
$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt');
|
||||
|
||||
$clobquery1 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery2 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery3 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery4 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery5 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery6 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery7 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery8 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery9 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
$clobquery10 = "select TO_CLOB('Hello World') CLOB_DATA from dual";
|
||||
|
||||
$stmt= $db->prepare($clobquery1);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery2);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery3);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery4);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery5);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery6);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery7);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery8);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery9);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
$stmt= $db->prepare($clobquery10);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch();
|
||||
|
||||
$query1 = "SELECT SYS_CONTEXT('USERENV', 'SID') SID FROM DUAL";
|
||||
|
||||
$stmt1 = $db->prepare($query1);
|
||||
$stmt1->execute();
|
||||
|
||||
$row1 = $stmt1->fetch();
|
||||
$sid_value = $row1[0];
|
||||
|
||||
$query2 = "SELECT (CACHE_LOBS+NOCACHE_LOBS+ABSTRACT_LOBS) FROM V\$TEMPORARY_LOBS WHERE SID = :SID_VALUE";
|
||||
|
||||
$stmt2 = $db->prepare($query2);
|
||||
$stmt2->bindParam(':SID_VALUE', $sid_value);
|
||||
$stmt2->execute();
|
||||
|
||||
$row2 = $stmt2->fetch();
|
||||
/* 1 temporary LOB still exists in V$TEMPORARY_LOBS since the destructor of $stmt is not yet called by PHP */
|
||||
if ($row2[0] > 1)
|
||||
{
|
||||
echo "TEMP_LOB is not yet cleared!" . $row2[0] . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Success! All the temporary LOB in previously closed statements are properly cleaned.\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Success! All the temporary LOB in previously closed statements are properly cleaned.
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue