Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/9' into develop
Browse files Browse the repository at this point in the history
Forward port #9
  • Loading branch information
weierophinney committed Jul 23, 2015
2 parents da26e98 + 67c8f7b commit 60477cf
Show file tree
Hide file tree
Showing 14 changed files with 751 additions and 85 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ All notable changes to this project will be documented in this file, in reverse
bit on a regular file.
- [#3](https://github.com/zendframework/zend-db/pull/3) updates the code to use
closure binding (now that we're on 5.5+, this is possible).
- [#9](https://github.com/zendframework/zend-db/pull/9) thoroughly audits the
OCI8 (Oracle) driver, ensuring it provides feature parity with other drivers,
and fixes issues with subselects, limits, and offsets.
4 changes: 2 additions & 2 deletions src/Adapter/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ protected function createPlatform($parameters)
// PDO is only supported driver for quoting values in this platform
return new Platform\SqlServer(($this->driver instanceof Driver\Pdo\Pdo) ? $this->driver : null);
case 'Oracle':
// oracle does not accept a driver as an option, no driver specific quoting available
return new Platform\Oracle($options);
$driver = ($this->driver instanceof Driver\Oci8\Oci8 || $this->driver instanceof Driver\Pdo\Pdo) ? $this->driver : null;
return new Platform\Oracle($options, $driver);
case 'Sqlite':
// PDO is only supported driver for quoting values in this platform
return new Platform\Sqlite(($this->driver instanceof Driver\Pdo\Pdo) ? $this->driver : null);
Expand Down
75 changes: 75 additions & 0 deletions src/Adapter/Driver/Oci8/Feature/RowCounter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Db\Adapter\Driver\Oci8\Feature;

use Zend\Db\Adapter\Driver\Feature\AbstractFeature;
use Zend\Db\Adapter\Driver\Oci8\Statement;

/**
* Class for count of results of a select
*/
class RowCounter extends AbstractFeature
{
/**
* @return string
*/
public function getName()
{
return 'RowCounter';
}

/**
* @param Statement $statement
* @return null|int
*/
public function getCountForStatement(Statement $statement)
{
$countStmt = clone $statement;
$sql = $statement->getSql();
if ($sql == '' || stripos(strtolower($sql), 'select') === false) {
return;
}
$countSql = 'SELECT COUNT(*) as "count" FROM (' . $sql . ')';
$countStmt->prepare($countSql);
$result = $countStmt->execute();
$countRow = $result->current();
return $countRow['count'];
}

/**
* @param string $sql
* @return null|int
*/
public function getCountForSql($sql)
{
if (stripos(strtolower($sql), 'select') === false) {
return;
}
$countSql = 'SELECT COUNT(*) as "count" FROM (' . $sql . ')';
$result = $this->driver->getConnection()->execute($countSql);
$countRow = $result->current();
return $countRow['count'];
}

/**
* @param \Zend\Db\Adapter\Driver\Oci8\Statement|string $context
* @return callable
*/
public function getRowCountClosure($context)
{
$rowCounter = $this;
return function () use ($rowCounter, $context) {
/** @var $rowCounter RowCounter */
return ($context instanceof Statement)
? $rowCounter->getCountForStatement($context)
: $rowCounter->getCountForSql($context);
};
}
}
90 changes: 84 additions & 6 deletions src/Adapter/Driver/Oci8/Oci8.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
use Zend\Db\Adapter\Driver\DriverInterface;
use Zend\Db\Adapter\Exception;
use Zend\Db\Adapter\Profiler;
use Zend\Db\Adapter\Driver\Feature\AbstractFeature;

class Oci8 implements DriverInterface, Profiler\ProfilerAwareInterface
{
const FEATURES_DEFAULT = 'default';

/**
* @var Connection
*/
Expand All @@ -35,20 +38,46 @@ class Oci8 implements DriverInterface, Profiler\ProfilerAwareInterface
*/
protected $profiler = null;

/**
* @var array
*/
protected $options = [];

/**
* @var array
*/
protected $features = [];

/**
* @param array|Connection|\oci8 $connection
* @param null|Statement $statementPrototype
* @param null|Result $resultPrototype
* @param array $options
*/
public function __construct($connection, Statement $statementPrototype = null, Result $resultPrototype = null)
{
public function __construct(
$connection,
Statement $statementPrototype = null,
Result $resultPrototype = null,
array $options = [],
$features = self::FEATURES_DEFAULT
) {
if (!$connection instanceof Connection) {
$connection = new Connection($connection);
}

$options = array_intersect_key(array_merge($this->options, $options), $this->options);
$this->registerConnection($connection);
$this->registerStatementPrototype(($statementPrototype) ?: new Statement());
$this->registerResultPrototype(($resultPrototype) ?: new Result());
if (is_array($features)) {
foreach ($features as $name => $feature) {
$this->addFeature($name, $feature);
}
} elseif ($features instanceof AbstractFeature) {
$this->addFeature($features->getName(), $features);
} elseif ($features === self::FEATURES_DEFAULT) {
$this->setupDefaultFeatures();
}
}

/**
Expand Down Expand Up @@ -129,6 +158,48 @@ public function getResultPrototype()
return $this->resultPrototype;
}

/**
* Add feature
*
* @param string $name
* @param AbstractFeature $feature
* @return self
*/
public function addFeature($name, $feature)
{
if ($feature instanceof AbstractFeature) {
$name = $feature->getName(); // overwrite the name, just in case
$feature->setDriver($this);
}
$this->features[$name] = $feature;
return $this;
}

/**
* Setup the default features for Pdo
*
* @return self
*/
public function setupDefaultFeatures()
{
$this->addFeature(null, new Feature\RowCounter());
return $this;
}

/**
* Get feature
*
* @param string $name
* @return AbstractFeature|false
*/
public function getFeature($name)
{
if (isset($this->features[$name])) {
return $this->features[$name];
}
return false;
}

/**
* Get database platform name
*
Expand All @@ -146,7 +217,9 @@ public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCAS
public function checkEnvironment()
{
if (!extension_loaded('oci8')) {
throw new Exception\RuntimeException('The Oci8 extension is required for this adapter but the extension is not loaded');
throw new Exception\RuntimeException(
'The Oci8 extension is required for this adapter but the extension is not loaded'
);
}
}

Expand Down Expand Up @@ -185,13 +258,18 @@ public function createStatement($sqlOrResource = null)

/**
* @param resource $resource
* @param null $isBuffered
* @param null $context
* @return Result
*/
public function createResult($resource, $isBuffered = null)
public function createResult($resource, $context = null)
{
$result = clone $this->resultPrototype;
$result->initialize($resource, $this->connection->getLastGeneratedValue(), $isBuffered);
$rowCount = null;
// special feature, oracle Oci counter
if ($context && ($rowCounter = $this->getFeature('RowCounter')) && oci_num_fields($resource) > 0) {
$rowCount = $rowCounter->getRowCountClosure($context);
}
$result->initialize($resource, $rowCount);
return $result;
}

Expand Down
27 changes: 17 additions & 10 deletions src/Adapter/Driver/Oci8/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class Result implements Iterator, ResultInterface
protected $resource = null;

/**
* @var bool
* @var null|int
*/
protected $isBuffered = null;
protected $rowCount = null;

/**
* Cursor position
Expand All @@ -42,7 +42,7 @@ class Result implements Iterator, ResultInterface
* @var bool
*/
protected $currentComplete = false;

/**
* @var bool
*/
Expand All @@ -62,14 +62,18 @@ class Result implements Iterator, ResultInterface
/**
* Initialize
* @param resource $resource
* @param null|int $generatedValue
* @param null|int $rowCount
* @return Result
*/
public function initialize($resource /*, $generatedValue, $isBuffered = null*/)
public function initialize($resource, $generatedValue = null, $rowCount = null)
{
if (!is_resource($resource) && get_resource_type($resource) !== 'oci8 statement') {
throw new Exception\InvalidArgumentException('Invalid resource provided.');
}
$this->resource = $resource;
$this->generatedValue = $generatedValue;
$this->rowCount = $rowCount;
return $this;
}

Expand Down Expand Up @@ -134,7 +138,6 @@ public function current()
return false;
}
}

return $this->currentData;
}

Expand All @@ -147,7 +150,6 @@ protected function loadData()
{
$this->currentComplete = true;
$this->currentData = oci_fetch_assoc($this->resource);

if ($this->currentData !== false) {
$this->position++;
return true;
Expand Down Expand Up @@ -191,17 +193,22 @@ public function valid()
if ($this->currentComplete) {
return ($this->currentData !== false);
}

return $this->loadData();
}

/**
* Count
* @return int
* @return null|int
*/
public function count()
{
// @todo OCI8 row count in Driver Result
if (is_int($this->rowCount)) {
return $this->rowCount;
}
if (is_callable($this->rowCount)) {
$this->rowCount = (int) call_user_func($this->rowCount);
return $this->rowCount;
}
return;
}

Expand All @@ -214,7 +221,7 @@ public function getFieldCount()
}

/**
* @return mixed|null
* @return null
*/
public function getGeneratedValue()
{
Expand Down
15 changes: 14 additions & 1 deletion src/Adapter/Driver/Oci8/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public function execute($parameters = null)
throw new Exception\RuntimeException($e['message'], $e['code']);
}

$result = $this->driver->createResult($this->resource);
$result = $this->driver->createResult($this->resource, $this);
return $result;
}

Expand Down Expand Up @@ -313,4 +313,17 @@ protected function bindParametersFromContainer()
oci_bind_by_name($this->resource, $name, $value, $maxLength, $type);
}
}

/**
* Perform a deep clone
*/
public function __clone()
{
$this->isPrepared = false;
$this->parametersBound = false;
$this->resource = null;
if ($this->parameterContainer) {
$this->parameterContainer = clone $this->parameterContainer;
}
}
}
Loading

0 comments on commit 60477cf

Please sign in to comment.