Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Support OCI8 statements crossing transactions [DBAL-202] #137

Merged
merged 1 commit into from almost 2 years ago

2 participants

Danny Berger Benjamin Eberlei
Danny Berger

Bug Fix: yes
Feature addition: no
Backwards compatibility break: no
Build Status

Scenario is documented in JIRA DBAL-202. Basically in oci8 if you prepare a statement outside of a transaction, start a transaction, execute the statement, rollback the transaction - the statement will still have been executed. Whether it's the correct behavior or not, it seems like it should match PDO's behavior.

This implementation affects the API, so it should probably be carefully reviewed.

A separate test script is available at https://gist.github.com/2515100.

$ php -v ; php --re oci8 | head -1
PHP 5.3.3-7+squeeze3 with Suhosin-Patch (cli) (built: Jun 28 2011 08:24:40) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.1.1, Copyright (c) 2002-2011, by Derick Rethans
    with Suhosin v0.9.32.1, Copyright (c) 2007-2010, by SektionEins GmbH
Extension [ <persistent> extension #52 oci8 version 1.4.2 ] {
$ phpunit -c oci8.phpunit.xml.dist tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php
PHPUnit 3.5.13 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 7.50Mb

OK (2 tests, 2 assertions)
$ phpunit -c oci8.phpunit.xml.dist tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL202Test.php
PHPUnit 3.5.13 by Sebastian Bergmann.

..

Time: 7 seconds, Memory: 9.50Mb
OK (2 tests, 6 assertions)

I had to drop the the following tests to run through the oracle test suite (seemed like my test user didn't have enough permissions for the temp db tests), but all other tests pass.

$ rm tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php
$ rm tests/Doctrine/Tests/DBAL/Functional/TableGeneratorTest.php # see pull 136
$ rm tests/Doctrine/Tests/DBAL/Functional/Schema/OracleSchemaManagerTest.php
$ phpunit -c oci8.phpunit.xml.dist
PHPUnit 3.5.13 by Sebastian Bergmann.

...............................................................  63 / 747 (  8%)
.......SSSS.....S........S......................S...........S.. 126 / 747 ( 16%)
.............................SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS 189 / 747 ( 25%)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS 252 / 747 ( 33%)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.............S........ 315 / 747 ( 42%)
...S...S...SSS................................................. 378 / 747 ( 50%)
............................................................... 441 / 747 ( 59%)
.....SSS....................................................... 504 / 747 ( 67%)
............................................................... 567 / 747 ( 75%)
..............S................................................ 630 / 747 ( 84%)
............................................................... 693 / 747 ( 92%)
......................................................

Time: 33 seconds, Memory: 46.00Mb

OK, but incomplete or skipped tests!
Tests: 747, Assertions: 1145, Skipped: 156.
Benjamin Eberlei beberlei merged commit 04fb8f9 into from May 05, 2012
Benjamin Eberlei beberlei closed this May 05, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Apr 27, 2012
Danny Berger Support OCI8 statements crossing transactions [DBAL-202] aa14fa5
This page is out of date. Refresh to see the latest.
10  lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php
@@ -60,7 +60,7 @@ public function __construct($username, $password, $db, $charset = null, $session
60 60
      */
61 61
     public function prepare($prepareString)
62 62
     {
63  
-        return new OCI8Statement($this->_dbh, $prepareString, $this->_executeMode);
  63
+        return new OCI8Statement($this->_dbh, $prepareString, $this);
64 64
     }
65 65
 
66 66
     /**
@@ -111,6 +111,14 @@ public function lastInsertId($name = null)
111 111
     }
112 112
 
113 113
     /**
  114
+     * Return the current execution mode.
  115
+     */
  116
+    public function getExecuteMode()
  117
+    {
  118
+        return $this->_executeMode;
  119
+    }
  120
+
  121
+    /**
114 122
      * Start a transactiom
115 123
      *
116 124
      * Oracle has to explicitly set the autocommit mode off. That means
8  lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php
@@ -34,7 +34,7 @@ class OCI8Statement implements \IteratorAggregate, Statement
34 34
     /** Statement handle. */
35 35
     protected $_dbh;
36 36
     protected $_sth;
37  
-    protected $_executeMode;
  37
+    protected $_conn;
38 38
     protected static $_PARAM = ':param';
39 39
     protected static $fetchStyleMap = array(
40 40
         PDO::FETCH_BOTH => OCI_BOTH,
@@ -52,13 +52,13 @@ class OCI8Statement implements \IteratorAggregate, Statement
52 52
      * @param resource $dbh The connection handle.
53 53
      * @param string $statement The SQL statement.
54 54
      */
55  
-    public function __construct($dbh, $statement, $executeMode)
  55
+    public function __construct($dbh, $statement, OCI8Connection $conn)
56 56
     {
57 57
         list($statement, $paramMap) = self::convertPositionalToNamedPlaceholders($statement);
58 58
         $this->_sth = oci_parse($dbh, $statement);
59 59
         $this->_dbh = $dbh;
60 60
         $this->_paramMap = $paramMap;
61  
-        $this->_executeMode = $executeMode;
  61
+        $this->_conn = $conn;
62 62
     }
63 63
 
64 64
     /**
@@ -180,7 +180,7 @@ public function execute($params = null)
180 180
             }
181 181
         }
182 182
 
183  
-        $ret = @oci_execute($this->_sth, $this->_executeMode);
  183
+        $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode());
184 184
         if ( ! $ret) {
185 185
             throw OCI8Exception::fromErrorInfo($this->errorInfo());
186 186
         }
28  tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php
@@ -15,21 +15,13 @@ public function setUp()
15 15
         parent::setUp();
16 16
     }
17 17
 
18  
-    protected function getMockOCI8Statement()
19  
-    {
20  
-        $dbh = null;
21  
-        $statement = "update table set field1 = ?, field2 = ? where field3 = ?";
22  
-        $executeMode = OCI_COMMIT_ON_SUCCESS;
23  
-
24  
-        return $this->getMock('\Doctrine\DBAL\Driver\OCI8\OCI8Statement',
25  
-		    array('bindValue', 'errorInfo'),
26  
-            array(null, $statement, $executeMode), '', false);
27  
-    }
28  
-
29 18
     /**
30 19
      * This scenario shows that when the first parameter is not null
31 20
      * it properly sets $hasZeroIndex to 1 and calls bindValue starting at 1.
32 21
      *
  22
+     * This also verifies that the statement will check with the connection to
  23
+     * see what the current execution mode is.
  24
+     *
33 25
      * The expected exception is due to oci_execute failing due to no valid connection.
34 26
      *
35 27
      * @dataProvider executeDataProvider
@@ -37,7 +29,9 @@ protected function getMockOCI8Statement()
37 29
      */
38 30
     public function testExecute(array $params)
39 31
     {
40  
-        $statement = $this->getMockOCI8Statement();
  32
+        $statement = $this->getMock('\Doctrine\DBAL\Driver\OCI8\OCI8Statement',
  33
+            array('bindValue', 'errorInfo'),
  34
+            array(), '', false);
41 35
 
42 36
         $statement->expects($this->at(0))
43 37
             ->method('bindValue')
@@ -58,6 +52,16 @@ public function testExecute(array $params)
58 52
                 $this->equalTo($params[2])
59 53
           );
60 54
 
  55
+        // can't pass to constructor since we don't have a real database handle,
  56
+        // but execute must check the connection for the executeMode
  57
+        $conn = $this->getMock('\Doctrine\DBAL\Driver\OCI8\OCI8Connection', array('getExecuteMode'), array(), '', false);
  58
+        $conn->expects($this->once())
  59
+            ->method('getExecuteMode');
  60
+
  61
+        $reflProperty = new \ReflectionProperty($statement, '_conn');
  62
+        $reflProperty->setAccessible(true);
  63
+        $reflProperty->setValue($statement, $conn);
  64
+
61 65
         $statement->execute($params);
62 66
     }
63 67
 
48  tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL202Test.php
... ...
@@ -0,0 +1,48 @@
  1
+<?php
  2
+
  3
+namespace Doctrine\Tests\DBAL\Functional\Ticket;
  4
+
  5
+/**
  6
+ * @group DBAL-202
  7
+ */
  8
+class DBAL202Test extends \Doctrine\Tests\DbalFunctionalTestCase
  9
+{
  10
+    protected function setUp()
  11
+    {
  12
+        parent::setUp();
  13
+
  14
+        if ($this->_conn->getDatabasePlatform()->getName() != 'oracle') {
  15
+            $this->markTestSkipped('OCI8 only test');
  16
+        }
  17
+
  18
+        if ($this->_conn->getSchemaManager()->tablesExist('DBAL202')) {
  19
+            $this->_conn->executeQuery('DELETE FROM DBAL202');
  20
+        } else {
  21
+            $table = new \Doctrine\DBAL\Schema\Table('DBAL202');
  22
+            $table->addColumn('id', 'integer');
  23
+            $table->setPrimaryKey(array('id'));
  24
+
  25
+            $this->_conn->getSchemaManager()->createTable($table);
  26
+        }
  27
+    }
  28
+
  29
+    public function testStatementRollback()
  30
+    {
  31
+        $stmt = $this->_conn->prepare('INSERT INTO DBAL202 VALUES (8)');
  32
+        $this->_conn->beginTransaction();
  33
+        $stmt->execute();
  34
+        $this->_conn->rollback();
  35
+
  36
+        $this->assertEquals(0, $this->_conn->query('SELECT COUNT(1) FROM DBAL202')->fetchColumn());
  37
+    }
  38
+
  39
+    public function testStatementCommit()
  40
+    {
  41
+        $stmt = $this->_conn->prepare('INSERT INTO DBAL202 VALUES (8)');
  42
+        $this->_conn->beginTransaction();
  43
+        $stmt->execute();
  44
+        $this->_conn->commit();
  45
+
  46
+        $this->assertEquals(1, $this->_conn->query('SELECT COUNT(1) FROM DBAL202')->fetchColumn());
  47
+    }
  48
+}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.