]> granicus.if.org Git - php/commitdiff
Fix temporary-LOB leak and add tests (Senthil)
authorChristopher Jones <christopher.jones@oracle.com>
Wed, 29 Jul 2015 10:15:43 +0000 (20:15 +1000)
committerChristopher Jones <christopher.jones@oracle.com>
Wed, 29 Jul 2015 10:15:43 +0000 (20:15 +1000)
ext/pdo_oci/oci_statement.c
ext/pdo_oci/tests/bug46274.phpt
ext/pdo_oci/tests/bug46274_2.phpt
ext/pdo_oci/tests/pdo_oci_stream_2a.phpt [new file with mode: 0644]
ext/pdo_oci/tests/pdo_oci_stream_2b.phpt [new file with mode: 0644]
ext/pdo_oci/tests/pdo_oci_templob_1.phpt [new file with mode: 0644]

index 9c2e149935ec777ddf0dc683fc73a6d919b4deff..d6844d3f16c93e1232c5173991a1a297e3e8fd58 100644 (file)
 
 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;
                                        }
index 77f2a011512543d8f573e7340de2f739cba9119d..23ee8ee20f7b74ecca4b31a50d7bbf2d141dec15 100644 (file)
@@ -56,8 +56,6 @@ var_dump($res->fetch());
 $db->exec("DROP TABLE test_one_blob");
 
 ?>
---XFAIL--
-Corrupts memory
 --EXPECTF--
 array(2) {
   ["blob1"]=>
index 9e9225415f72052e5d5e9ab60cc71da60f5e0cbe..cbadcef4f8cb1f1d0612f2da46f608b3597ac2bb 100644 (file)
@@ -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 (file)
index 0000000..a7f22d5
--- /dev/null
@@ -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 (file)
index 0000000..22ae7c6
--- /dev/null
@@ -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 (file)
index 0000000..4139105
--- /dev/null
@@ -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.
+