diff options
author | Christopher Jones <christopher.jones@oracle.com> | 2015-07-29 20:15:43 +1000 |
---|---|---|
committer | Christopher Jones <christopher.jones@oracle.com> | 2015-07-29 20:15:43 +1000 |
commit | 0615aa82d74f91efa4a5a7bcee1b9718fa03f15c (patch) | |
tree | 604b1c1101878b888fc402ed71b0f94a2860ab61 /ext/pdo_oci | |
parent | 0787cd60ed3d0c8c8c8ff7e49b9bb3587bf33b64 (diff) | |
download | php-git-0615aa82d74f91efa4a5a7bcee1b9718fa03f15c.tar.gz |
Fix temporary-LOB leak and add tests (Senthil)
Diffstat (limited to 'ext/pdo_oci')
-rw-r--r-- | ext/pdo_oci/oci_statement.c | 21 | ||||
-rw-r--r-- | ext/pdo_oci/tests/bug46274.phpt | 2 | ||||
-rw-r--r-- | ext/pdo_oci/tests/bug46274_2.phpt | 2 | ||||
-rw-r--r-- | ext/pdo_oci/tests/pdo_oci_stream_2a.phpt | 74 | ||||
-rw-r--r-- | ext/pdo_oci/tests/pdo_oci_stream_2b.phpt | 70 | ||||
-rw-r--r-- | ext/pdo_oci/tests/pdo_oci_templob_1.phpt | 85 |
6 files changed, 248 insertions, 6 deletions
diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c index 9c2e149935..d6844d3f16 100644 --- a/ext/pdo_oci/oci_statement.c +++ b/ext/pdo_oci/oci_statement.c @@ -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; } diff --git a/ext/pdo_oci/tests/bug46274.phpt b/ext/pdo_oci/tests/bug46274.phpt index 77f2a01151..23ee8ee20f 100644 --- a/ext/pdo_oci/tests/bug46274.phpt +++ b/ext/pdo_oci/tests/bug46274.phpt @@ -56,8 +56,6 @@ var_dump($res->fetch()); $db->exec("DROP TABLE test_one_blob"); ?> ---XFAIL-- -Corrupts memory --EXPECTF-- array(2) { ["blob1"]=> diff --git a/ext/pdo_oci/tests/bug46274_2.phpt b/ext/pdo_oci/tests/bug46274_2.phpt index 9e9225415f..cbadcef4f8 100644 --- a/ext/pdo_oci/tests/bug46274_2.phpt +++ b/ext/pdo_oci/tests/bug46274_2.phpt @@ -60,8 +60,6 @@ fclose($row[0]); $db->exec("DROP TABLE test_one_blob"); ?> ---XFAIL-- -Corrupts memory --EXPECTF-- array(2) { ["blob1"]=> diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt new file mode 100644 index 0000000000..a7f22d5c41 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt @@ -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 diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt new file mode 100644 index 0000000000..22ae7c6724 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt @@ -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! diff --git a/ext/pdo_oci/tests/pdo_oci_templob_1.phpt b/ext/pdo_oci/tests/pdo_oci_templob_1.phpt new file mode 100644 index 0000000000..41391058dd --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_templob_1.phpt @@ -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. + |