diff --git a/.ci/oracle_fixtures.sh b/.ci/oracle_fixtures.sh
new file mode 100644
index 0000000000..82f692d98c
--- /dev/null
+++ b/.ci/oracle_fixtures.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+echo "Configure Oracle test database"
+
+"CREATE DATABASE test DATAFILE 'zenddb_test' SIZE 10M"
diff --git a/.travis.yml b/.travis.yml
index e7b538f18c..dc3e958b94 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,6 +22,7 @@ matrix:
- LEGACY_DEPS="phpunit/phpunit zendframework/zend-hydrator"
- TEST_INTEGRATION=true
- TESTS_ZEND_DB_ADAPTER_DRIVER_MYSQL=true
+ - TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8=true
- php: 5.6
env:
- DEPS=latest
@@ -36,6 +37,14 @@ matrix:
- LEGACY_DEPS="phpunit/phpunit zendframework/zend-hydrator"
- TEST_INTEGRATION=true
- TESTS_ZEND_DB_ADAPTER_DRIVER_MYSQL=true
+ - php: 7.0
+ services:
+ - oci8
+ env:
+ - DEPS=locked
+ - LEGACY_DEPS="phpunit/phpunit zendframework/zend-hydrator"
+ - TEST_INTEGRATION=true
+ - TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8=true
- php: 7.0
env:
- DEPS=latest
@@ -52,6 +61,7 @@ matrix:
- TEST_COVERAGE=true
- TEST_INTEGRATION=true
- TESTS_ZEND_DB_ADAPTER_DRIVER_MYSQL=true
+ - TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8=true
- php: 7.1
env:
- DEPS=latest
@@ -65,6 +75,7 @@ matrix:
- DEPS=locked
- TEST_INTEGRATION=true
- TESTS_ZEND_DB_ADAPTER_DRIVER_MYSQL=true
+ - TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8=true
- php: 7.2
services:
- postgresql
@@ -74,6 +85,7 @@ matrix:
- DEPS=locked
- TEST_INTEGRATION=true
- TESTS_ZEND_DB_ADAPTER_DRIVER_PGSQL=true
+ - TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8=true
- php: 7.2
env:
- DEPS=latest
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index e58cf9e02c..5816ec07a0 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -43,11 +43,12 @@
-
-
+
+
+
diff --git a/src/Adapter/Driver/Oci8/Statement.php b/src/Adapter/Driver/Oci8/Statement.php
index 2d8ca1eef8..e7a8e6323a 100644
--- a/src/Adapter/Driver/Oci8/Statement.php
+++ b/src/Adapter/Driver/Oci8/Statement.php
@@ -289,11 +289,18 @@ protected function bindParametersFromContainer()
$type = SQLT_BIN;
break;
case ParameterContainer::TYPE_LOB:
+ case ParameterContainer::TYPE_CLOB:
$type = OCI_B_CLOB;
$clob = oci_new_descriptor($this->driver->getConnection()->getResource(), OCI_DTYPE_LOB);
$clob->writetemporary($value, OCI_TEMP_CLOB);
$value = $clob;
break;
+ case ParameterContainer::TYPE_BLOB:
+ $type = OCI_B_BLOB;
+ $blob = oci_new_descriptor($this->driver->getConnection()->getResource(), OCI_DTYPE_LOB);
+ $blob->writetemporary($value, OCI_TEMP_BLOB);
+ $value = $blob;
+ break;
case ParameterContainer::TYPE_STRING:
default:
$type = SQLT_CHR;
diff --git a/src/Adapter/ParameterContainer.php b/src/Adapter/ParameterContainer.php
index c630837752..acb9e9b9e0 100644
--- a/src/Adapter/ParameterContainer.php
+++ b/src/Adapter/ParameterContainer.php
@@ -22,6 +22,8 @@ class ParameterContainer implements Iterator, ArrayAccess, Countable
const TYPE_BINARY = 'binary';
const TYPE_STRING = 'string';
const TYPE_LOB = 'lob';
+ const TYPE_BLOB = 'blob';
+ const TYPE_CLOB = 'clob';
/**
* Data
diff --git a/test/integration/Adapter/Driver/Oci8/ConnectionTest.php b/test/integration/Adapter/Driver/Oci8/ConnectionTest.php
new file mode 100644
index 0000000000..0ac403f2c6
--- /dev/null
+++ b/test/integration/Adapter/Driver/Oci8/ConnectionTest.php
@@ -0,0 +1,29 @@
+variables);
+ $connection->connect();
+
+ self::assertTrue($connection->isConnected());
+ $connection->disconnect();
+ }
+}
diff --git a/test/integration/Adapter/Driver/Oci8/TableGatewayTest.php b/test/integration/Adapter/Driver/Oci8/TableGatewayTest.php
new file mode 100644
index 0000000000..0cf45d7d15
--- /dev/null
+++ b/test/integration/Adapter/Driver/Oci8/TableGatewayTest.php
@@ -0,0 +1,155 @@
+ 'OCI8',
+ 'connection_string' => $this->variables['connectionstring'],
+ 'username' => $this->variables['username'],
+ 'password' => $this->variables['password'],
+ 'character_set' => $this->variables['charset'],
+ 'options' => ['buffer_results' => true]
+ ]);
+ $tableGateway = new TableGateway('TEST', $adapter);
+ $rowset = $tableGateway->select('ID = 0');
+
+ $this->assertNull($rowset->current());
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/issues/330
+ */
+ public function testSelectWithEmptyCurrentWithoutBufferResult()
+ {
+ $adapter = new Adapter([
+ 'driver' => 'OCI8',
+ 'connection_string' => $this->variables['connectionstring'],
+ 'username' => $this->variables['username'],
+ 'password' => $this->variables['password'],
+ 'character_set' => $this->variables['charset'],
+ 'options' => ['buffer_results' => false]
+ ]);
+ $tableGateway = new TableGateway('TEST', $adapter);
+ $rowset = $tableGateway->select('ID = 0');
+
+ $this->assertNull($rowset->current());
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/pull/396
+ */
+ public function testBlobWithOci8()
+ {
+ $adapter = new Adapter([
+ 'driver' => 'OCI8',
+ 'connection_string' => $this->variables['connectionstring'],
+ 'username' => $this->variables['username'],
+ 'password' => $this->variables['password'],
+ 'character_set' => $this->variables['charset'],
+ 'options' => ['buffer_results' => false]
+ ]);
+ $tableGateway = new TableGateway('TEST', $adapter);
+
+ $blob = 'very long sentence that is in fact not very long that tests blob';
+
+ $data = new ParameterContainer();
+ $data->setFromArray(['CONTENT_BLOB' => $blob]);
+ $data->offsetSetErrata('CONTENT_BLOB', ParameterContainer::TYPE_BLOB);
+
+ $sql = 'UPDATE TEST SET CONTENT_BLOB = :CONTENT_BLOB WHERE ID = 1';
+ $stm = $tableGateway->getAdapter()->getDriver()->createStatement($sql);
+ $stm->setParameterContainer($data);
+ $stm->execute();
+
+ $rowset = $tableGateway->select('ID = 1')->current();
+
+ $this->assertInstanceOf('OCI-Lob', $rowset['CONTENT_BLOB']);
+ $value = $rowset['CONTENT_BLOB']->read($rowset['CONTENT_BLOB']->size());
+ $this->assertEquals($blob, $value);
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/pull/396
+ */
+ public function testClobWithOci8()
+ {
+ $adapter = new Adapter([
+ 'driver' => 'OCI8',
+ 'connection_string' => $this->variables['connectionstring'],
+ 'username' => $this->variables['username'],
+ 'password' => $this->variables['password'],
+ 'character_set' => $this->variables['charset'],
+ 'options' => ['buffer_results' => false]
+ ]);
+ $tableGateway = new TableGateway('TEST', $adapter);
+
+ $clob = 'very long sentence that is in fact not very long that tests clob';
+
+ $data = new ParameterContainer();
+ $data->setFromArray(['CONTENT_CLOB' => $clob]);
+ $data->offsetSetErrata('CONTENT_CLOB', ParameterContainer::TYPE_CLOB);
+
+ $sql = 'UPDATE TEST SET CONTENT_CLOB = :CONTENT_CLOB WHERE ID = 1';
+ $stm = $tableGateway->getAdapter()->getDriver()->createStatement($sql);
+ $stm->setParameterContainer($data);
+ $stm->execute();
+
+ $rowset = $tableGateway->select('ID = 1')->current();
+
+ $this->assertInstanceOf('OCI-Lob', $rowset['CONTENT_CLOB']);
+ $value = $rowset['CONTENT_CLOB']->read($rowset['CONTENT_CLOB']->size());
+ $this->assertEquals($clob, $value);
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/pull/396
+ */
+ public function testLobWithOci8()
+ {
+ $adapter = new Adapter([
+ 'driver' => 'OCI8',
+ 'connection_string' => $this->variables['connectionstring'],
+ 'username' => $this->variables['username'],
+ 'password' => $this->variables['password'],
+ 'character_set' => $this->variables['charset'],
+ 'options' => ['buffer_results' => false]
+ ]);
+ $tableGateway = new TableGateway('TEST', $adapter);
+
+ $clob = 'very long sentence that is in fact not very long that tests lob';
+
+ $data = new ParameterContainer();
+ $data->setFromArray(['CONTENT_CLOB' => $clob]);
+ $data->offsetSetErrata('CONTENT_CLOB', ParameterContainer::TYPE_LOB);
+
+ $sql = 'UPDATE TEST SET CONTENT_CLOB = :CONTENT_CLOB WHERE ID = 2';
+ $stm = $tableGateway->getAdapter()->getDriver()->createStatement($sql);
+ $stm->setParameterContainer($data);
+ $stm->execute();
+
+ $rowset = $tableGateway->select('ID = 2')->current();
+
+ $this->assertInstanceOf('OCI-Lob', $rowset['CONTENT_CLOB']);
+ $value = $rowset['CONTENT_CLOB']->read($rowset['CONTENT_CLOB']->size());
+ $this->assertEquals($clob, $value);
+ }
+}
diff --git a/test/integration/Adapter/Driver/Oci8/TraitSetup.php b/test/integration/Adapter/Driver/Oci8/TraitSetup.php
new file mode 100644
index 0000000000..f9aabed9b9
--- /dev/null
+++ b/test/integration/Adapter/Driver/Oci8/TraitSetup.php
@@ -0,0 +1,44 @@
+ 'TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CONNECTIONSTRING',
+ 'username' => 'TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_USERNAME',
+ 'password' => 'TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_PASSWORD',
+ 'charset' => 'TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CHARSET',
+ 'database' => 'TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_DATABASE',
+ ];
+
+ /**
+ * Sets up the fixture, for example, opens a network connection.
+ * This method is called before a test is executed.
+ */
+ protected function setUp()
+ {
+ if (!getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8')) {
+ $this->markTestSkipped('Oci8 integration test disabled');
+ }
+
+ if (!extension_loaded('oci8')) {
+ $this->fail('The phpunit group integration-oci8 was enabled, but the extension is not loaded.');
+ }
+
+ foreach ($this->variables as $name => $value) {
+ if (!getenv($value)) {
+ $this->markTestSkipped(sprintf(
+ 'Missing required variable %s from phpunit.xml for this integration test',
+ $value
+ ));
+ }
+ $this->variables[$name] = getenv($value);
+ }
+ }
+}
diff --git a/test/integration/Adapter/Platform/Oci8Test.php b/test/integration/Adapter/Platform/Oci8Test.php
new file mode 100644
index 0000000000..9b56b0e1f4
--- /dev/null
+++ b/test/integration/Adapter/Platform/Oci8Test.php
@@ -0,0 +1,77 @@
+markTestSkipped(__CLASS__ . ' integration tests are not enabled!');
+ }
+ if (extension_loaded('oci8')) {
+ $this->adapters['oci8'] = oci_connect(
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_USERNAME'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_PASSWORD'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CONNECTIONSTRING'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CHARSET'),
+ null
+ );
+ }
+ if (extension_loaded('pdo_oci')) {
+ $this->adapters['pdo_oci'] = new \PDO(
+ 'oci:dbname=' . getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CONNECTIONSTRING'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_USERNAME'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_PASSWORD')
+ );
+ }
+ }
+
+ public function testQuoteValueWithOci8()
+ {
+ if (!isset($this->adapters['oci8'])
+ || !$this->adapters['oci8'] instanceof \Oracle
+ ) {
+ $this->markTestSkipped('Oracle (oci8) not configured in unit test configuration file');
+ }
+ $oracle = new Oracle($this->adapters['oci8']);
+ $value = $oracle->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+
+ $oracle = new Oracle(new Oci8\Oci8(new Oci8\Connection($this->adapters['oci8'])));
+ $value = $oracle->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+ }
+
+ public function testQuoteValueWithPdoOci()
+ {
+ if (!isset($this->adapters['pdo_oci'])
+ || !$this->adapters['pdo_oci'] instanceof \PDO
+ ) {
+ $this->markTestSkipped('Oracle (pdo_oci) not configured in unit test configuration file');
+ }
+ $oracle = new Pdo($this->adapters['pdo_oci']);
+ $value = $oracle->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+
+ $oracle = new Pdo(new Pdo\Pdo(new Pdo\Connection($this->adapters['pdo_oci'])));
+ $value = $oracle->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+ }
+}
diff --git a/test/integration/IntegrationTestListener.php b/test/integration/IntegrationTestListener.php
index 9bb4dcaa31..9908f8120b 100644
--- a/test/integration/IntegrationTestListener.php
+++ b/test/integration/IntegrationTestListener.php
@@ -9,9 +9,7 @@
namespace ZendIntegrationTest\Db;
-use Exception;
use PDO;
-use PDOException;
use PHPUnit\Framework\BaseTestListener;
use PHPUnit_Framework_TestSuite as TestSuite;
use ZendIntegrationTest\Db\Platform\FixtureLoader;
@@ -40,6 +38,9 @@ public function startTestSuite(TestSuite $suite)
if (getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_PGSQL')) {
$this->fixtureLoader = new \ZendIntegrationTest\Db\Platform\PgsqlFixtureLoader();
}
+ if (getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8')) {
+ $this->fixtureLoader = new \ZendIntegrationTest\Db\Platform\Oci8FixtureLoader();
+ }
if (! isset($this->fixtureLoader)) {
return;
diff --git a/test/integration/Platform/Oci8FixtureLoader.php b/test/integration/Platform/Oci8FixtureLoader.php
new file mode 100644
index 0000000000..43aa666db7
--- /dev/null
+++ b/test/integration/Platform/Oci8FixtureLoader.php
@@ -0,0 +1,79 @@
+oci8 = oci_pconnect(
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_USERNAME'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_PASSWORD'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CONNECTIONSTRING'),
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_CHARSET'),
+ null
+ );
+
+ $this->dropDatabase();
+
+ $sql = file_get_contents($this->fixtureFile);
+ $sql2 = <<oci8, $sql2);
+ $ret = oci_execute($resource);
+ if (false === $ret) {
+ throw new RuntimeException(sprintf(
+ 'I cannot create the table for database %s. Check the %s file. %s',
+ getenv('TESTS_ZEND_DB_ADAPTER_DRIVER_OCI8_DATABASE'),
+ $this->fixtureFile,
+ print_r(oci_error($this->oci8), true)
+ ));
+ }
+ }
+
+ public function dropDatabase()
+ {
+ if (!$this->initialRun) {
+ // Not possible to drop database in Oracle.
+
+ return;
+ }
+ $this->initialRun = false;
+
+ $resource = oci_parse($this->oci8, 'DROP TABLE TEST CASCADE CONSTRAINTS');
+ oci_execute($resource);
+
+ $resource = oci_parse($this->oci8, 'DROP TABLE TEST_CHARSET CASCADE CONSTRAINTS');
+ oci_execute($resource);
+
+ $resource = oci_parse($this->oci8, 'DROP SEQUENCE test_sequence');
+ oci_execute($resource);
+
+ $resource = oci_parse($this->oci8, 'DROP SEQUENCE testcharset_sequence');
+ oci_execute($resource);
+
+ $resource = oci_parse($this->oci8, 'DROP TRIGGER TEST_ON_INSERT');
+ oci_execute($resource);
+
+ $resource = oci_parse($this->oci8, 'DROP TRIGGER TESTCHARSET_ON_INSERT');
+ oci_execute($resource);
+ }
+}
diff --git a/test/integration/TestFixtures/oci8.sql b/test/integration/TestFixtures/oci8.sql
new file mode 100644
index 0000000000..fb10007efa
--- /dev/null
+++ b/test/integration/TestFixtures/oci8.sql
@@ -0,0 +1,55 @@
+CREATE TABLE TEST
+(
+ ID NUMBER(3, 0) NOT NULL ENABLE,
+ NAME VARCHAR2(255 CHAR) NOT NULL ENABLE,
+ VALUE VARCHAR2(255 CHAR) NOT NULL ENABLE,
+ CONTENT_BLOB BLOB,
+ CONTENT_CLOB CLOB,
+ CONSTRAINT TEST_PK PRIMARY KEY (ID)
+);
+
+CREATE SEQUENCE test_sequence;
+
+CREATE OR REPLACE TRIGGER TEST_ON_INSERT
+ BEFORE INSERT
+ ON test
+ FOR EACH ROW
+ BEGIN
+ SELECT test_sequence.nextval
+ INTO :new.id
+ FROM dual;
+ END;
+/
+
+ALTER TRIGGER TEST_ON_INSERT ENABLE;
+
+INSERT INTO TEST (NAME, VALUE) VALUES ('foo', 'bar');
+INSERT INTO TEST (NAME, VALUE) VALUES ('bar', 'baz');
+
+CREATE TABLE TEST_CHARSET
+(
+ ID NUMBER(3, 0) NOT NULL ENABLE,
+ FIELD$ VARCHAR2(255 CHAR) NOT NULL ENABLE,
+ FIELD_ VARCHAR2(255 CHAR) NOT NULL ENABLE,
+ CONSTRAINT TESTCHARSET_PK PRIMARY KEY (ID)
+);
+
+CREATE SEQUENCE testcharset_sequence;
+
+CREATE OR REPLACE TRIGGER TESTCHARSET_ON_INSERT
+ BEFORE INSERT
+ ON TEST_CHARSET
+ FOR EACH ROW
+ BEGIN
+ SELECT testcharset_sequence.nextval
+ INTO :new.id
+ FROM dual;
+ END;
+/
+
+ALTER TRIGGER TESTCHARSET_ON_INSERT ENABLE;
+
+INSERT INTO TEST_CHARSET (FIELD$, FIELD_) VALUES ('foo', 'bar');
+INSERT INTO TEST_CHARSET (FIELD$, FIELD_) VALUES ('bar', 'baz');
+
+COMMIT;