diff --git a/UPGRADE.md b/UPGRADE.md
index acf1846c16c..cf712fedb7b 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -1,5 +1,16 @@
# Upgrade to 3.0
+## BC BREAK changes in fetching statement results
+
+1. The `Statement` interface no longer extends `ResultStatement`.
+2. The `ResultStatement` interface has been renamed to `Result`.
+3. Instead of returning `bool`, `Statement::execute()` now returns a `Result` that should be used for fetching the result data and metadata.
+4. The functionality previously available via `Statement::closeCursor()` is now available via `Result::free()`. The behavior of fetching data from a freed result is no longer portable. In this case, some drivers will return `false` while others may throw an exception.
+
+Additional related changes:
+
+1. The `ArrayStatement` and `ResultCacheStatement` classes from the `Cache` package have been renamed to `ArrayResult` and `CachingResult` respectively and marked `@internal`.
+
## BC BREAK `Statement::rowCount()` is moved.
`Statement::rowCount()` has been moved to the `ResultStatement` interface where it belongs by definition.
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 390aa2c76d9..a9607273e9d 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -97,7 +97,7 @@
- src/Driver/SQLSrv/SQLSrvStatement.php
+ src/Driver/SQLSrv/Result.php
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index c38e5eacdfa..b7e33c6fd16 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -58,7 +58,9 @@ parameters:
- '~unknown class OCI-(Lob|Collection)~'
# https://github.com/JetBrains/phpstorm-stubs/pull/766
- - '~^Method Doctrine\\DBAL\\Driver\\Mysqli\\MysqliStatement::_fetch\(\) never returns null so it can be removed from the return typehint\.$~'
+ -
+ message: '~^Strict comparison using === between true and null will always evaluate to false\.$~'
+ path: %currentWorkingDirectory%/src/Driver/Mysqli/Result.php
# The ReflectionException in the case when the class does not exist is acceptable and does not need to be handled
- '~^Parameter #1 \$argument of class ReflectionClass constructor expects class-string\|T of object, string given\.$~'
diff --git a/src/Cache/ArrayStatement.php b/src/Cache/ArrayResult.php
similarity index 85%
rename from src/Cache/ArrayStatement.php
rename to src/Cache/ArrayResult.php
index 288e30b3221..f8fa83442c8 100644
--- a/src/Cache/ArrayStatement.php
+++ b/src/Cache/ArrayResult.php
@@ -4,16 +4,15 @@
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
-use Doctrine\DBAL\Driver\ResultStatement;
use function array_values;
use function count;
use function reset;
/**
- * @deprecated
+ * @internal The class is internal to the caching layer implementation.
*/
-class ArrayStatement implements ResultStatement, Result
+final class ArrayResult implements Result
{
/** @var mixed[] */
private $data;
@@ -37,35 +36,6 @@ public function __construct(array $data)
$this->columnCount = count($data[0]);
}
- /**
- * {@inheritdoc}
- *
- * @deprecated Use free() instead.
- */
- public function closeCursor()
- {
- $this->free();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- return $this->columnCount;
- }
-
- public function rowCount(): int
- {
- if ($this->data === null) {
- return 0;
- }
-
- return count($this->data);
- }
-
/**
* {@inheritdoc}
*/
@@ -126,6 +96,20 @@ public function fetchFirstColumn(): array
return FetchUtils::fetchFirstColumn($this);
}
+ public function rowCount(): int
+ {
+ if ($this->data === null) {
+ return 0;
+ }
+
+ return count($this->data);
+ }
+
+ public function columnCount(): int
+ {
+ return $this->columnCount;
+ }
+
public function free(): void
{
$this->data = [];
diff --git a/src/Cache/ResultCacheStatement.php b/src/Cache/CachingResult.php
similarity index 72%
rename from src/Cache/ResultCacheStatement.php
rename to src/Cache/CachingResult.php
index d1180f499bf..c7c208e7e03 100644
--- a/src/Cache/ResultCacheStatement.php
+++ b/src/Cache/CachingResult.php
@@ -6,14 +6,11 @@
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
-use Doctrine\DBAL\Driver\ResultStatement;
use function array_map;
use function array_values;
/**
- * Cache statement for SQL results.
- *
* A result is saved in multiple cache keys, there is the originally specified
* cache key which is just pointing to result rows by key. The following things
* have to be ensured:
@@ -24,12 +21,12 @@
* Also you have to realize that the cache will load the whole result into memory at once to ensure 2.
* This means that the memory usage for cached results might increase by using this feature.
*
- * @deprecated
+ * @internal The class is internal to the caching layer implementation.
*/
-class ResultCacheStatement implements ResultStatement, Result
+class CachingResult implements Result
{
/** @var Cache */
- private $resultCache;
+ private $cache;
/** @var string */
private $cacheKey;
@@ -40,8 +37,8 @@ class ResultCacheStatement implements ResultStatement, Result
/** @var int */
private $lifetime;
- /** @var ResultStatement */
- private $statement;
+ /** @var Result */
+ private $result;
/** @var array>|null */
private $data;
@@ -51,38 +48,13 @@ class ResultCacheStatement implements ResultStatement, Result
* @param string $realKey
* @param int $lifetime
*/
- public function __construct(ResultStatement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime)
- {
- $this->statement = $stmt;
- $this->resultCache = $resultCache;
- $this->cacheKey = $cacheKey;
- $this->realKey = $realKey;
- $this->lifetime = $lifetime;
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated Use free() instead.
- */
- public function closeCursor()
- {
- $this->free();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
+ public function __construct(Result $result, Cache $cache, $cacheKey, $realKey, $lifetime)
{
- return $this->statement->columnCount();
- }
-
- public function rowCount(): int
- {
- return $this->statement->rowCount();
+ $this->result = $result;
+ $this->cache = $cache;
+ $this->cacheKey = $cacheKey;
+ $this->realKey = $realKey;
+ $this->lifetime = $lifetime;
}
/**
@@ -121,7 +93,7 @@ public function fetchOne()
public function fetchAllNumeric(): array
{
$this->store(
- $this->statement->fetchAllAssociative()
+ $this->result->fetchAllAssociative()
);
return array_map('array_values', $this->data);
@@ -133,7 +105,7 @@ public function fetchAllNumeric(): array
public function fetchAllAssociative(): array
{
$this->store(
- $this->statement->fetchAllAssociative()
+ $this->result->fetchAllAssociative()
);
return $this->data;
@@ -147,6 +119,16 @@ public function fetchFirstColumn(): array
return FetchUtils::fetchFirstColumn($this);
}
+ public function rowCount(): int
+ {
+ return $this->result->rowCount();
+ }
+
+ public function columnCount(): int
+ {
+ return $this->result->columnCount();
+ }
+
public function free(): void
{
$this->data = null;
@@ -163,7 +145,7 @@ private function fetch()
$this->data = [];
}
- $row = $this->statement->fetchAssociative();
+ $row = $this->result->fetchAssociative();
if ($row !== false) {
$this->data[] = $row;
@@ -192,7 +174,7 @@ private function saveToCache(): void
return;
}
- $data = $this->resultCache->fetch($this->cacheKey);
+ $data = $this->cache->fetch($this->cacheKey);
if ($data === false) {
$data = [];
@@ -200,6 +182,6 @@ private function saveToCache(): void
$data[$this->realKey] = $this->data;
- $this->resultCache->save($this->cacheKey, $data, $this->lifetime);
+ $this->cache->save($this->cacheKey, $data, $this->lifetime);
}
}
diff --git a/src/Connection.php b/src/Connection.php
index 1297c201609..6e430e7fca0 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -4,13 +4,14 @@
use Closure;
use Doctrine\Common\EventManager;
-use Doctrine\DBAL\Cache\ArrayStatement;
+use Doctrine\DBAL\Abstraction\Result as AbstractionResult;
+use Doctrine\DBAL\Cache\ArrayResult;
use Doctrine\DBAL\Cache\CacheException;
+use Doctrine\DBAL\Cache\CachingResult;
use Doctrine\DBAL\Cache\QueryCacheProfile;
-use Doctrine\DBAL\Cache\ResultCacheStatement;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\PingableConnection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\Exception\InvalidArgumentException;
@@ -900,9 +901,9 @@ public function fetchFirstColumn(string $query, array $params = [], array $types
public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable
{
try {
- $stmt = $this->executeQuery($query, $params, $types);
+ $result = $this->executeQuery($query, $params, $types);
- while (($row = $stmt->fetchNumeric()) !== false) {
+ while (($row = $result->fetchNumeric()) !== false) {
yield $row;
}
} catch (Throwable $e) {
@@ -924,9 +925,9 @@ public function iterateNumeric(string $query, array $params = [], array $types =
public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable
{
try {
- $stmt = $this->executeQuery($query, $params, $types);
+ $result = $this->executeQuery($query, $params, $types);
- while (($row = $stmt->fetchAssociative()) !== false) {
+ while (($row = $result->fetchAssociative()) !== false) {
yield $row;
}
} catch (Throwable $e) {
@@ -948,9 +949,9 @@ public function iterateAssociative(string $query, array $params = [], array $typ
public function iterateColumn(string $query, array $params = [], array $types = []): Traversable
{
try {
- $stmt = $this->executeQuery($query, $params, $types);
+ $result = $this->executeQuery($query, $params, $types);
- while (($value = $stmt->fetchOne()) !== false) {
+ while (($value = $result->fetchOne()) !== false) {
yield $value;
}
} catch (Throwable $e) {
@@ -985,12 +986,14 @@ public function prepare(string $sql): DriverStatement
* @param int[]|string[] $types The types the previous parameters are in.
* @param QueryCacheProfile|null $qcp The query cache profile, optional.
*
- * @return ResultStatement The executed statement.
- *
* @throws DBALException
*/
- public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): ResultStatement
- {
+ public function executeQuery(
+ string $query,
+ array $params = [],
+ $types = [],
+ ?QueryCacheProfile $qcp = null
+ ): AbstractionResult {
if ($qcp !== null) {
return $this->executeCacheQuery($query, $params, $types, $qcp);
}
@@ -1009,22 +1012,22 @@ public function executeQuery(string $query, array $params = [], $types = [], ?Qu
$stmt = $connection->prepare($query);
if (count($types) > 0) {
$this->_bindTypedValues($stmt, $params, $types);
- $stmt->execute();
+ $result = $stmt->execute();
} else {
- $stmt->execute($params);
+ $result = $stmt->execute($params);
}
} else {
- $stmt = $connection->query($query);
+ $result = $connection->query($query);
}
+
+ return new Result($result, $this);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types));
+ } finally {
+ if ($logger !== null) {
+ $logger->stopQuery();
+ }
}
-
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
- return $stmt;
}
/**
@@ -1036,8 +1039,9 @@ public function executeQuery(string $query, array $params = [], $types = [], ?Qu
* @param QueryCacheProfile $qcp The query cache profile.
*
* @throws CacheException
+ * @throws DBALException
*/
- public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp): ResultStatement
+ public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp): Result
{
$resultCache = $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl();
@@ -1056,20 +1060,26 @@ public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qc
if ($data !== false) {
// is the real key part of this row pointers map or is the cache only pointing to other cache keys?
if (isset($data[$realKey])) {
- $stmt = new ArrayStatement($data[$realKey]);
+ $result = new ArrayResult($data[$realKey]);
} elseif (array_key_exists($realKey, $data)) {
- $stmt = new ArrayStatement([]);
+ $result = new ArrayResult([]);
}
}
- if (! isset($stmt)) {
- $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime());
+ if (! isset($result)) {
+ $result = new CachingResult(
+ $this->executeQuery($query, $params, $types),
+ $resultCache,
+ $cacheKey,
+ $realKey,
+ $qcp->getLifetime()
+ );
}
- return $stmt;
+ return new Result($result, $this);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): DriverResult
{
$connection = $this->getWrappedConnection();
@@ -1079,16 +1089,14 @@ public function query(string $sql): ResultStatement
}
try {
- $statement = $connection->query($sql);
+ return $connection->query($sql);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $sql);
+ } finally {
+ if ($logger !== null) {
+ $logger->stopQuery();
+ }
}
-
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
- return $statement;
}
/**
@@ -1120,24 +1128,23 @@ public function executeUpdate(string $query, array $params = [], array $types =
if (count($types) > 0) {
$this->_bindTypedValues($stmt, $params, $types);
- $stmt->execute();
+
+ $result = $stmt->execute();
} else {
- $stmt->execute($params);
+ $result = $stmt->execute($params);
}
- $result = $stmt->rowCount();
- } else {
- $result = $connection->exec($query);
+ return $result->rowCount();
}
+
+ return $connection->exec($query);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types));
+ } finally {
+ if ($logger !== null) {
+ $logger->stopQuery();
+ }
}
-
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
- return $result;
}
public function exec(string $statement): int
@@ -1150,16 +1157,14 @@ public function exec(string $statement): int
}
try {
- $result = $connection->exec($statement);
+ return $connection->exec($statement);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement);
+ } finally {
+ if ($logger !== null) {
+ $logger->stopQuery();
+ }
}
-
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
- return $result;
}
/**
@@ -1643,8 +1648,8 @@ private function getBindingInfo($value, $type)
* @internal This is a purely internal method. If you rely on this method, you are advised to
* copy/paste the code as this method may change, or be removed without prior notice.
*
- * @param mixed[] $params
- * @param int[]|string[] $types
+ * @param mixed[] $params
+ * @param array $types
*
* @return mixed[]
*/
diff --git a/src/Connections/MasterSlaveConnection.php b/src/Connections/MasterSlaveConnection.php
index 9bcea1e8563..25d5ac99a80 100644
--- a/src/Connections/MasterSlaveConnection.php
+++ b/src/Connections/MasterSlaveConnection.php
@@ -7,7 +7,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
@@ -341,7 +341,7 @@ public function rollbackSavepoint($savepoint)
parent::rollbackSavepoint($savepoint);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): Result
{
$this->connect('master');
assert($this->_conn instanceof DriverConnection);
diff --git a/src/Driver/Connection.php b/src/Driver/Connection.php
index 3f1840ea972..e972c896dd5 100644
--- a/src/Driver/Connection.php
+++ b/src/Driver/Connection.php
@@ -23,7 +23,7 @@ public function prepare(string $sql): Statement;
*
* @throws DBALException
*/
- public function query(string $sql): ResultStatement;
+ public function query(string $sql): Result;
/**
* Quotes a string for use in a query.
diff --git a/src/Driver/IBMDB2/DB2Connection.php b/src/Driver/IBMDB2/DB2Connection.php
index 93565bab70c..c6a120cc916 100644
--- a/src/Driver/IBMDB2/DB2Connection.php
+++ b/src/Driver/IBMDB2/DB2Connection.php
@@ -2,7 +2,7 @@
namespace Doctrine\DBAL\Driver\IBMDB2;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
@@ -86,12 +86,9 @@ public function prepare(string $sql): DriverStatement
return new DB2Statement($stmt);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
- $stmt = $this->prepare($sql);
- $stmt->execute();
-
- return $stmt;
+ return $this->prepare($sql)->execute();
}
/**
diff --git a/src/Driver/IBMDB2/DB2Statement.php b/src/Driver/IBMDB2/DB2Statement.php
index 0bab901c7b4..57c566c561d 100644
--- a/src/Driver/IBMDB2/DB2Statement.php
+++ b/src/Driver/IBMDB2/DB2Statement.php
@@ -2,19 +2,13 @@
namespace Doctrine\DBAL\Driver\IBMDB2;
-use Doctrine\DBAL\Driver\FetchUtils;
-use Doctrine\DBAL\Driver\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
use function assert;
use function db2_bind_param;
use function db2_execute;
-use function db2_fetch_array;
-use function db2_fetch_assoc;
-use function db2_free_result;
-use function db2_num_fields;
-use function db2_num_rows;
use function db2_stmt_errormsg;
use function error_get_last;
use function fclose;
@@ -32,7 +26,7 @@
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
-class DB2Statement implements Statement, Result
+class DB2Statement implements Statement
{
/** @var resource */
private $stmt;
@@ -48,13 +42,6 @@ class DB2Statement implements Statement, Result
*/
private $lobs = [];
- /**
- * Indicates whether the statement is in the state when fetching results is possible
- *
- * @var bool
- */
- private $result = false;
-
/**
* @param resource $stmt
*/
@@ -122,40 +109,8 @@ private function bind($position, &$variable, int $parameterType, int $dataType):
/**
* {@inheritdoc}
- *
- * @deprecated Use free() instead.
*/
- public function closeCursor()
- {
- $this->bindParam = [];
-
- if (! db2_free_result($this->stmt)) {
- return false;
- }
-
- $this->result = false;
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- $count = db2_num_fields($this->stmt);
-
- if ($count !== false) {
- return $count;
- }
-
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($params = null)
+ public function execute($params = null): ResultInterface
{
if ($params === null) {
ksort($this->bindParam);
@@ -177,7 +132,7 @@ public function execute($params = null)
$this->writeStringToStream($source, $target);
}
- $retval = db2_execute($this->stmt, $params);
+ $result = db2_execute($this->stmt, $params);
foreach ($this->lobs as [, $handle]) {
fclose($handle);
@@ -185,85 +140,11 @@ public function execute($params = null)
$this->lobs = [];
- if ($retval === false) {
+ if ($result === false) {
throw new DB2Exception(db2_stmt_errormsg());
}
- $this->result = true;
-
- return $retval;
- }
-
- /**
- * {@inheritDoc}
- */
- public function fetchNumeric()
- {
- if (! $this->result) {
- return false;
- }
-
- return db2_fetch_array($this->stmt);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- // do not try fetching from the statement if it's not expected to contain the result
- // in order to prevent exceptional situation
- if (! $this->result) {
- return false;
- }
-
- return db2_fetch_assoc($this->stmt);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return FetchUtils::fetchOne($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return FetchUtils::fetchAllNumeric($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return FetchUtils::fetchAllAssociative($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
- {
- return FetchUtils::fetchFirstColumn($this);
- }
-
- public function rowCount(): int
- {
- return @db2_num_rows($this->stmt);
- }
-
- public function free(): void
- {
- $this->bindParam = [];
-
- db2_free_result($this->stmt);
-
- $this->result = false;
+ return new Result($this->stmt);
}
/**
diff --git a/src/Driver/IBMDB2/Result.php b/src/Driver/IBMDB2/Result.php
new file mode 100644
index 00000000000..2cb756fd0d9
--- /dev/null
+++ b/src/Driver/IBMDB2/Result.php
@@ -0,0 +1,111 @@
+statement = $statement;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ $row = @db2_fetch_array($this->statement);
+
+ if ($row === false && db2_stmt_error($this->statement) !== '02000') {
+ throw new DB2Exception(db2_stmt_errormsg($this->statement));
+ }
+
+ return $row;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ $row = @db2_fetch_assoc($this->statement);
+
+ if ($row === false && db2_stmt_error($this->statement) !== '02000') {
+ throw new DB2Exception(db2_stmt_errormsg($this->statement));
+ }
+
+ return $row;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return FetchUtils::fetchOne($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return FetchUtils::fetchAllNumeric($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return FetchUtils::fetchAllAssociative($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return FetchUtils::fetchFirstColumn($this);
+ }
+
+ public function rowCount(): int
+ {
+ return @db2_num_rows($this->statement);
+ }
+
+ public function columnCount(): int
+ {
+ $count = db2_num_fields($this->statement);
+
+ if ($count !== false) {
+ return $count;
+ }
+
+ return 0;
+ }
+
+ public function free(): void
+ {
+ db2_free_result($this->statement);
+ }
+}
diff --git a/src/Driver/Mysqli/MysqliConnection.php b/src/Driver/Mysqli/MysqliConnection.php
index b2fcd70be6f..6da2e729152 100644
--- a/src/Driver/Mysqli/MysqliConnection.php
+++ b/src/Driver/Mysqli/MysqliConnection.php
@@ -3,7 +3,7 @@
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\PingableConnection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
@@ -136,12 +136,9 @@ public function prepare(string $sql): DriverStatement
return new MysqliStatement($this->conn, $sql);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
- $stmt = $this->prepare($sql);
- $stmt->execute();
-
- return $stmt;
+ return $this->prepare($sql)->execute();
}
/**
diff --git a/src/Driver/Mysqli/MysqliStatement.php b/src/Driver/Mysqli/MysqliStatement.php
index 30af1604e7b..44168f8e251 100644
--- a/src/Driver/Mysqli/MysqliStatement.php
+++ b/src/Driver/Mysqli/MysqliStatement.php
@@ -2,28 +2,25 @@
namespace Doctrine\DBAL\Driver\Mysqli;
-use Doctrine\DBAL\Driver\FetchUtils;
-use Doctrine\DBAL\Driver\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\ParameterType;
use mysqli;
use mysqli_stmt;
-use function array_combine;
use function array_fill;
use function assert;
use function count;
use function feof;
use function fread;
use function get_resource_type;
-use function is_array;
use function is_int;
use function is_resource;
use function sprintf;
use function str_repeat;
-class MysqliStatement implements Statement, Result
+class MysqliStatement implements Statement
{
/** @var string[] */
protected static $_paramTypeMap = [
@@ -41,12 +38,6 @@ class MysqliStatement implements Statement, Result
/** @var mysqli_stmt */
protected $_stmt;
- /** @var string[]|false|null */
- protected $_columnNames;
-
- /** @var mixed[] */
- protected $_rowBindedValues = [];
-
/** @var mixed[] */
protected $_bindedValues;
@@ -60,13 +51,6 @@ class MysqliStatement implements Statement, Result
*/
protected $_values = [];
- /**
- * Indicates whether the statement is in the state when fetching results is possible
- *
- * @var bool
- */
- private $result = false;
-
/**
* @param string $prepareString
*
@@ -131,7 +115,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING)
/**
* {@inheritdoc}
*/
- public function execute($params = null)
+ public function execute($params = null): ResultInterface
{
if ($this->_bindedValues !== null) {
if ($params !== null) {
@@ -147,57 +131,7 @@ public function execute($params = null)
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
- if ($this->_columnNames === null) {
- $meta = $this->_stmt->result_metadata();
- if ($meta !== false) {
- $fields = $meta->fetch_fields();
- assert(is_array($fields));
-
- $columnNames = [];
- foreach ($fields as $col) {
- $columnNames[] = $col->name;
- }
-
- $meta->free();
-
- $this->_columnNames = $columnNames;
- } else {
- $this->_columnNames = false;
- }
- }
-
- if ($this->_columnNames !== false) {
- // Store result of every execution which has it. Otherwise it will be impossible
- // to execute a new statement in case if the previous one has non-fetched rows
- // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
- $this->_stmt->store_result();
-
- // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
- // it will have to allocate as much memory as it may be needed for the given column type
- // (e.g. for a LONGBLOB field it's 4 gigabytes)
- // @link https://bugs.php.net/bug.php?id=51386#1270673122
- //
- // Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been
- // previously called on the statement, the values are unbound making the statement unusable.
- //
- // It's also important that row values are bound after _each_ call to store_result(). Otherwise,
- // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
- // to the length of the ones fetched during the previous execution.
- $this->_rowBindedValues = array_fill(0, count($this->_columnNames), null);
-
- $refs = [];
- foreach ($this->_rowBindedValues as $key => &$value) {
- $refs[$key] =& $value;
- }
-
- if (! $this->_stmt->bind_result(...$refs)) {
- throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
- }
- }
-
- $this->result = true;
-
- return true;
+ return new Result($this->_stmt);
}
/**
@@ -281,132 +215,4 @@ private function bindUntypedValues(array $values)
return $this->_stmt->bind_param($types, ...$params);
}
-
- /**
- * @return mixed[]|false|null
- */
- private function _fetch()
- {
- $ret = $this->_stmt->fetch();
-
- if ($ret === true) {
- $values = [];
- foreach ($this->_rowBindedValues as $v) {
- $values[] = $v;
- }
-
- return $values;
- }
-
- return $ret;
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchNumeric()
- {
- // do not try fetching from the statement if it's not expected to contain the result
- // in order to prevent exceptional situation
- if (! $this->result) {
- return false;
- }
-
- $values = $this->_fetch();
-
- if ($values === null) {
- return false;
- }
-
- if ($values === false) {
- throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
- }
-
- return $values;
- }
-
- /**
- * {@inheritDoc}
- */
- public function fetchAssociative()
- {
- $values = $this->fetchNumeric();
-
- if ($values === false) {
- return false;
- }
-
- assert(is_array($this->_columnNames));
- $row = array_combine($this->_columnNames, $values);
- assert(is_array($row));
-
- return $row;
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return FetchUtils::fetchOne($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return FetchUtils::fetchAllNumeric($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return FetchUtils::fetchAllAssociative($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
- {
- return FetchUtils::fetchFirstColumn($this);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated Use free() instead.
- */
- public function closeCursor()
- {
- $this->free();
-
- return true;
- }
-
- public function rowCount(): int
- {
- if ($this->_columnNames === false) {
- return $this->_stmt->affected_rows;
- }
-
- return $this->_stmt->num_rows;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- return $this->_stmt->field_count;
- }
-
- public function free(): void
- {
- $this->_stmt->free_result();
- $this->result = false;
- }
}
diff --git a/src/Driver/Mysqli/Result.php b/src/Driver/Mysqli/Result.php
new file mode 100644
index 00000000000..0833503a227
--- /dev/null
+++ b/src/Driver/Mysqli/Result.php
@@ -0,0 +1,186 @@
+
+ */
+ private $columnNames = [];
+
+ /** @var mixed[] */
+ private $boundValues = [];
+
+ /**
+ * @throws MysqliException
+ */
+ public function __construct(mysqli_stmt $statement)
+ {
+ $this->statement = $statement;
+
+ $meta = $statement->result_metadata();
+
+ if ($meta === false) {
+ return;
+ }
+
+ $this->hasColumns = true;
+
+ $fields = $meta->fetch_fields();
+ assert(is_array($fields));
+
+ $this->columnNames = array_map(static function (stdClass $field): string {
+ return $field->name;
+ }, $fields);
+
+ $meta->free();
+
+ // Store result of every execution which has it. Otherwise it will be impossible
+ // to execute a new statement in case if the previous one has non-fetched rows
+ // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
+ $this->statement->store_result();
+
+ // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
+ // it will have to allocate as much memory as it may be needed for the given column type
+ // (e.g. for a LONGBLOB field it's 4 gigabytes)
+ // @link https://bugs.php.net/bug.php?id=51386#1270673122
+ //
+ // Make sure that the values are bound after each execution. Otherwise, if free() has been
+ // previously called on the result, the values are unbound making the statement unusable.
+ //
+ // It's also important that row values are bound after _each_ call to store_result(). Otherwise,
+ // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
+ // to the length of the ones fetched during the previous execution.
+ $this->boundValues = array_fill(0, count($this->columnNames), null);
+
+ $refs = [];
+ foreach ($this->boundValues as &$value) {
+ $refs[] =& $value;
+ }
+
+ if (! $this->statement->bind_result(...$refs)) {
+ throw new MysqliException($this->statement->error, $this->statement->sqlstate, $this->statement->errno);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchNumeric()
+ {
+ $ret = $this->statement->fetch();
+
+ if ($ret === false) {
+ throw new MysqliException($this->statement->error, $this->statement->sqlstate, $this->statement->errno);
+ }
+
+ if ($ret === null) {
+ return false;
+ }
+
+ $values = [];
+
+ foreach ($this->boundValues as $v) {
+ $values[] = $v;
+ }
+
+ return $values;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ $values = $this->fetchNumeric();
+
+ if ($values === false) {
+ return false;
+ }
+
+ $row = array_combine($this->columnNames, $values);
+ assert(is_array($row));
+
+ return $row;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchOne()
+ {
+ return FetchUtils::fetchOne($this);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return FetchUtils::fetchAllNumeric($this);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return FetchUtils::fetchAllAssociative($this);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return FetchUtils::fetchFirstColumn($this);
+ }
+
+ public function rowCount(): int
+ {
+ if ($this->hasColumns) {
+ return $this->statement->num_rows;
+ }
+
+ return $this->statement->affected_rows;
+ }
+
+ public function columnCount(): int
+ {
+ return $this->statement->field_count;
+ }
+
+ public function free(): void
+ {
+ $this->statement->free_result();
+ }
+}
diff --git a/src/Driver/OCI8/OCI8Connection.php b/src/Driver/OCI8/OCI8Connection.php
index e7460df289d..b02199789c2 100644
--- a/src/Driver/OCI8/OCI8Connection.php
+++ b/src/Driver/OCI8/OCI8Connection.php
@@ -3,7 +3,7 @@
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Connection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
@@ -107,12 +107,9 @@ public function prepare(string $sql): DriverStatement
return new OCI8Statement($this->dbh, $sql, $this);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
- $stmt = $this->prepare($sql);
- $stmt->execute();
-
- return $stmt;
+ return $this->prepare($sql)->execute();
}
/**
@@ -131,10 +128,7 @@ public function quote($value, $type = ParameterType::STRING)
public function exec(string $statement): int
{
- $stmt = $this->prepare($statement);
- $stmt->execute();
-
- return $stmt->rowCount();
+ return $this->prepare($statement)->execute()->rowCount();
}
/**
diff --git a/src/Driver/OCI8/OCI8Statement.php b/src/Driver/OCI8/OCI8Statement.php
index dea91df3e0b..87619bbd247 100644
--- a/src/Driver/OCI8/OCI8Statement.php
+++ b/src/Driver/OCI8/OCI8Statement.php
@@ -2,8 +2,7 @@
namespace Doctrine\DBAL\Driver\OCI8;
-use Doctrine\DBAL\Driver\FetchUtils;
-use Doctrine\DBAL\Driver\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
@@ -13,29 +12,18 @@
use function is_int;
use function is_resource;
use function oci_bind_by_name;
-use function oci_cancel;
use function oci_error;
use function oci_execute;
-use function oci_fetch_all;
-use function oci_fetch_array;
use function oci_new_descriptor;
-use function oci_num_fields;
-use function oci_num_rows;
use function oci_parse;
use function preg_match;
use function preg_quote;
use function sprintf;
use function substr;
-use const OCI_ASSOC;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_D_LOB;
-use const OCI_FETCHSTATEMENT_BY_COLUMN;
-use const OCI_FETCHSTATEMENT_BY_ROW;
-use const OCI_NUM;
-use const OCI_RETURN_LOBS;
-use const OCI_RETURN_NULLS;
use const OCI_TEMP_BLOB;
use const PREG_OFFSET_CAPTURE;
use const SQLT_CHR;
@@ -43,7 +31,7 @@
/**
* The OCI8 implementation of the Statement interface.
*/
-class OCI8Statement implements Statement, Result
+class OCI8Statement implements Statement
{
/** @var resource */
protected $_dbh;
@@ -73,13 +61,6 @@ class OCI8Statement implements Statement, Result
*/
private $boundValues = [];
- /**
- * Indicates whether the statement is in the state when fetching results is possible
- *
- * @var bool
- */
- private $result = false;
-
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
@@ -315,31 +296,7 @@ private function convertParameterType(int $type): int
*
* @deprecated Use free() instead.
*/
- public function closeCursor()
- {
- $this->free();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- $count = oci_num_fields($this->_sth);
-
- if ($count !== false) {
- return $count;
- }
-
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($params = null)
+ public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
@@ -356,118 +313,6 @@ public function execute($params = null)
throw OCI8Exception::fromErrorInfo(oci_error($this->_sth));
}
- $this->result = true;
-
- return $ret;
- }
-
- public function rowCount(): int
- {
- $count = oci_num_rows($this->_sth);
-
- if ($count !== false) {
- return $count;
- }
-
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchNumeric()
- {
- return $this->fetch(OCI_NUM);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- return $this->fetch(OCI_ASSOC);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return FetchUtils::fetchOne($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return $this->fetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
- {
- return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0];
- }
-
- public function free(): void
- {
- // not having the result means there's nothing to close
- if (! $this->result) {
- return;
- }
-
- oci_cancel($this->_sth);
-
- $this->result = false;
- }
-
- /**
- * @return mixed|false
- */
- private function fetch(int $mode)
- {
- // do not try fetching from the statement if it's not expected to contain the result
- // in order to prevent exceptional situation
- if (! $this->result) {
- return false;
- }
-
- return oci_fetch_array(
- $this->_sth,
- $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS
- );
- }
-
- /**
- * @return array
- */
- private function fetchAll(int $mode, int $fetchStructure): array
- {
- // do not try fetching from the statement if it's not expected to contain the result
- // in order to prevent exceptional situation
- if (! $this->result) {
- return [];
- }
-
- oci_fetch_all(
- $this->_sth,
- $result,
- 0,
- -1,
- $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS
- );
-
- return $result;
+ return new Result($this->_sth);
}
}
diff --git a/src/Driver/OCI8/Result.php b/src/Driver/OCI8/Result.php
new file mode 100644
index 00000000000..f3e69e1f242
--- /dev/null
+++ b/src/Driver/OCI8/Result.php
@@ -0,0 +1,137 @@
+statement = $statement;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ return $this->fetch(OCI_NUM);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ return $this->fetch(OCI_ASSOC);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return FetchUtils::fetchOne($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return $this->fetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0];
+ }
+
+ public function rowCount(): int
+ {
+ $count = oci_num_rows($this->statement);
+
+ if ($count !== false) {
+ return $count;
+ }
+
+ return 0;
+ }
+
+ public function columnCount(): int
+ {
+ $count = oci_num_fields($this->statement);
+
+ if ($count !== false) {
+ return $count;
+ }
+
+ return 0;
+ }
+
+ public function free(): void
+ {
+ oci_cancel($this->statement);
+ }
+
+ /**
+ * @return mixed|false
+ */
+ private function fetch(int $mode)
+ {
+ return oci_fetch_array(
+ $this->statement,
+ $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS
+ );
+ }
+
+ /**
+ * @return array
+ */
+ private function fetchAll(int $mode, int $fetchStructure): array
+ {
+ oci_fetch_all(
+ $this->statement,
+ $result,
+ 0,
+ -1,
+ $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS
+ );
+
+ return $result;
+ }
+}
diff --git a/src/Driver/PDO/Result.php b/src/Driver/PDO/Result.php
new file mode 100644
index 00000000000..bacf335837e
--- /dev/null
+++ b/src/Driver/PDO/Result.php
@@ -0,0 +1,131 @@
+statement = $statement;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ return $this->fetch(PDO::FETCH_NUM);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ return $this->fetch(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return $this->fetch(PDO::FETCH_COLUMN);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return $this->fetchAll(PDO::FETCH_NUM);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return $this->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return $this->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ public function rowCount(): int
+ {
+ try {
+ return $this->statement->rowCount();
+ } catch (\PDOException $exception) {
+ throw new PDOException($exception);
+ }
+ }
+
+ public function columnCount(): int
+ {
+ try {
+ return $this->statement->columnCount();
+ } catch (\PDOException $exception) {
+ throw new PDOException($exception);
+ }
+ }
+
+ public function free(): void
+ {
+ try {
+ $this->statement->closeCursor();
+ } catch (\PDOException $exception) {
+ throw new PDOException($exception);
+ }
+ }
+
+ /**
+ * @return mixed|false
+ *
+ * @throws PDOException
+ */
+ private function fetch(int $mode)
+ {
+ try {
+ return $this->statement->fetch($mode);
+ } catch (\PDOException $exception) {
+ throw new PDOException($exception);
+ }
+ }
+
+ /**
+ * @return array
+ *
+ * @throws PDOException
+ */
+ private function fetchAll(int $mode): array
+ {
+ try {
+ $data = $this->statement->fetchAll($mode);
+ } catch (\PDOException $exception) {
+ throw new PDOException($exception);
+ }
+
+ assert(is_array($data));
+
+ return $data;
+ }
+}
diff --git a/src/Driver/PDOConnection.php b/src/Driver/PDOConnection.php
index 5d6c16982c4..e604c03457b 100644
--- a/src/Driver/PDOConnection.php
+++ b/src/Driver/PDOConnection.php
@@ -2,6 +2,8 @@
namespace Doctrine\DBAL\Driver;
+use Doctrine\DBAL\Driver\PDO\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\ParameterType;
use PDO;
@@ -63,13 +65,13 @@ public function prepare(string $sql): Statement
}
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
try {
$stmt = $this->connection->query($sql);
assert($stmt instanceof \PDOStatement);
- return $this->createStatement($stmt);
+ return new Result($stmt);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
diff --git a/src/Driver/PDOSqlsrv/Connection.php b/src/Driver/PDOSqlsrv/Connection.php
index 60f77f6947f..4d22a98e139 100644
--- a/src/Driver/PDOSqlsrv/Connection.php
+++ b/src/Driver/PDOSqlsrv/Connection.php
@@ -23,10 +23,9 @@ public function lastInsertId($name = null)
return parent::lastInsertId($name);
}
- $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
- $stmt->execute([$name]);
-
- return $stmt->fetchOne();
+ return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?')
+ ->execute([$name])
+ ->fetchOne();
}
/**
diff --git a/src/Driver/PDOStatement.php b/src/Driver/PDOStatement.php
index 38bf60d5ce5..617019f1cb9 100644
--- a/src/Driver/PDOStatement.php
+++ b/src/Driver/PDOStatement.php
@@ -2,14 +2,14 @@
namespace Doctrine\DBAL\Driver;
+use Doctrine\DBAL\Driver\PDO\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use PDO;
use function array_slice;
-use function assert;
use function func_get_args;
-use function is_array;
/**
* The PDO implementation of the Statement interface.
@@ -71,120 +71,15 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l
/**
* {@inheritdoc}
*/
- public function closeCursor()
+ public function execute($params = null): ResultInterface
{
try {
- return $this->stmt->closeCursor();
- } catch (\PDOException $exception) {
- // Exceptions not allowed by the interface.
- // In case driver implementations do not adhere to the interface, silence exceptions here.
- return true;
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- return $this->stmt->columnCount();
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($params = null)
- {
- try {
- return $this->stmt->execute($params);
+ $this->stmt->execute($params);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
- }
-
- public function rowCount(): int
- {
- return $this->stmt->rowCount();
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchNumeric()
- {
- return $this->fetch(PDO::FETCH_NUM);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- return $this->fetch(PDO::FETCH_ASSOC);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return $this->fetch(PDO::FETCH_COLUMN);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return $this->fetchAll(PDO::FETCH_NUM);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return $this->fetchAll(PDO::FETCH_ASSOC);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
- {
- return $this->fetchAll(PDO::FETCH_COLUMN);
- }
-
- /**
- * @return mixed|false
- *
- * @throws PDOException
- */
- private function fetch(int $mode)
- {
- try {
- return $this->stmt->fetch($mode);
- } catch (\PDOException $exception) {
- throw new PDOException($exception);
- }
- }
-
- /**
- * @return array
- *
- * @throws PDOException
- */
- private function fetchAll(int $mode): array
- {
- try {
- $data = $this->stmt->fetchAll($mode);
- } catch (\PDOException $exception) {
- throw new PDOException($exception);
- }
-
- assert(is_array($data));
- return $data;
+ return new Result($this->stmt);
}
/**
diff --git a/src/Driver/Result.php b/src/Driver/Result.php
index 3818755b11e..c38ebc2df14 100644
--- a/src/Driver/Result.php
+++ b/src/Driver/Result.php
@@ -5,7 +5,7 @@
namespace Doctrine\DBAL\Driver;
/**
- * Driver-level result statement execution result.
+ * Driver-level statement execution result.
*/
interface Result
{
@@ -72,7 +72,7 @@ public function fetchFirstColumn(): array;
*
* @return int The number of rows.
*/
- public function rowCount();
+ public function rowCount(): int;
/**
* Returns the number of columns in the result
@@ -80,16 +80,7 @@ public function rowCount();
* @return int The number of columns in the result. If the columns cannot be counted,
* this method must return 0.
*/
- public function columnCount();
-
- /**
- * Closes the cursor, enabling the statement to be executed again.
- *
- * @deprecated Use Result::free() instead.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function closeCursor();
+ public function columnCount(): int;
/**
* Discards the non-fetched portion of the result, enabling the originating statement to be executed again.
diff --git a/src/Driver/ResultStatement.php b/src/Driver/ResultStatement.php
deleted file mode 100644
index dabe08bb82c..00000000000
--- a/src/Driver/ResultStatement.php
+++ /dev/null
@@ -1,94 +0,0 @@
-|false
- *
- * @throws DriverException
- */
- public function fetchNumeric();
-
- /**
- * Returns the next row of a result set as an associative array or FALSE if there are no more rows.
- *
- * @return array|false
- *
- * @throws DriverException
- */
- public function fetchAssociative();
-
- /**
- * Returns the first value of the next row of a result set or FALSE if there are no more rows.
- *
- * @return mixed|false
- *
- * @throws DriverException
- */
- public function fetchOne();
-
- /**
- * Returns an array containing all of the result set rows represented as numeric arrays.
- *
- * @return array>
- *
- * @throws DriverException
- */
- public function fetchAllNumeric(): array;
-
- /**
- * Returns an array containing all of the result set rows represented as associative arrays.
- *
- * @return array>
- *
- * @throws DriverException
- */
- public function fetchAllAssociative(): array;
-
- /**
- * Returns an array containing the values of the first column of the result set.
- *
- * @return array
- *
- * @throws DriverException
- */
- public function fetchFirstColumn(): array;
-}
diff --git a/src/Driver/SQLAnywhere/Result.php b/src/Driver/SQLAnywhere/Result.php
new file mode 100644
index 00000000000..1881513d082
--- /dev/null
+++ b/src/Driver/SQLAnywhere/Result.php
@@ -0,0 +1,96 @@
+statement = $statement;
+ $this->result = sasql_stmt_result_metadata($statement);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ return sasql_fetch_row($this->result);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ return sasql_fetch_assoc($this->result);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return FetchUtils::fetchOne($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return FetchUtils::fetchAllNumeric($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return FetchUtils::fetchAllAssociative($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return FetchUtils::fetchFirstColumn($this);
+ }
+
+ public function rowCount(): int
+ {
+ return sasql_stmt_affected_rows($this->statement);
+ }
+
+ public function columnCount(): int
+ {
+ return sasql_stmt_field_count($this->statement);
+ }
+
+ public function free(): void
+ {
+ sasql_stmt_reset($this->statement);
+ }
+}
diff --git a/src/Driver/SQLAnywhere/SQLAnywhereConnection.php b/src/Driver/SQLAnywhere/SQLAnywhereConnection.php
index b29954598be..9690203941e 100644
--- a/src/Driver/SQLAnywhere/SQLAnywhereConnection.php
+++ b/src/Driver/SQLAnywhere/SQLAnywhereConnection.php
@@ -2,7 +2,7 @@
namespace Doctrine\DBAL\Driver\SQLAnywhere;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
@@ -124,12 +124,9 @@ public function prepare(string $sql): Statement
return new SQLAnywhereStatement($this->connection, $sql);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
- $stmt = $this->prepare($sql);
- $stmt->execute();
-
- return $stmt;
+ return $this->prepare($sql)->execute();
}
/**
diff --git a/src/Driver/SQLAnywhere/SQLAnywhereStatement.php b/src/Driver/SQLAnywhere/SQLAnywhereStatement.php
index 9d764590973..54043ec9477 100644
--- a/src/Driver/SQLAnywhere/SQLAnywhereStatement.php
+++ b/src/Driver/SQLAnywhere/SQLAnywhereStatement.php
@@ -2,9 +2,7 @@
namespace Doctrine\DBAL\Driver\SQLAnywhere;
-use Doctrine\DBAL\Driver\DriverException;
-use Doctrine\DBAL\Driver\FetchUtils;
-use Doctrine\DBAL\Driver\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
@@ -12,27 +10,15 @@
use function assert;
use function is_int;
use function is_resource;
-use function sasql_fetch_assoc;
-use function sasql_fetch_row;
-use function sasql_prepare;
-use function sasql_stmt_affected_rows;
-use function sasql_stmt_bind_param_ex;
-use function sasql_stmt_execute;
-use function sasql_stmt_field_count;
-use function sasql_stmt_reset;
-use function sasql_stmt_result_metadata;
/**
* SAP SQL Anywhere implementation of the Statement interface.
*/
-class SQLAnywhereStatement implements Statement, Result
+class SQLAnywhereStatement implements Statement
{
/** @var resource The connection resource. */
private $conn;
- /** @var resource|null The result set resource to fetch. */
- private $result;
-
/** @var resource The prepared SQL statement to execute. */
private $stmt;
@@ -112,29 +98,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING)
*
* @throws SQLAnywhereException
*/
- public function closeCursor()
- {
- if (! sasql_stmt_reset($this->stmt)) {
- throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- return sasql_stmt_field_count($this->stmt);
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws SQLAnywhereException
- */
- public function execute($params = null)
+ public function execute($params = null): ResultInterface
{
if ($params !== null) {
$hasZeroIndex = array_key_exists(0, $params);
@@ -152,80 +116,7 @@ public function execute($params = null)
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
- $this->result = sasql_stmt_result_metadata($this->stmt);
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws SQLAnywhereException
- */
- public function fetchNumeric()
- {
- if (! is_resource($this->result)) {
- return false;
- }
-
- return sasql_fetch_row($this->result);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- if (! is_resource($this->result)) {
- return false;
- }
-
- return sasql_fetch_assoc($this->result);
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DriverException
- */
- public function fetchOne()
- {
- return FetchUtils::fetchOne($this);
- }
-
- /**
- * @return array>
- *
- * @throws DriverException
- */
- public function fetchAllNumeric(): array
- {
- return FetchUtils::fetchAllNumeric($this);
- }
-
- /**
- * @return array>
- *
- * @throws DriverException
- */
- public function fetchAllAssociative(): array
- {
- return FetchUtils::fetchAllAssociative($this);
- }
-
- /**
- * @return array
- *
- * @throws DriverException
- */
- public function fetchFirstColumn(): array
- {
- return FetchUtils::fetchFirstColumn($this);
- }
-
- public function rowCount(): int
- {
- return sasql_stmt_affected_rows($this->stmt);
+ return new Result($this->stmt);
}
public function free(): void
diff --git a/src/Driver/SQLSrv/Result.php b/src/Driver/SQLSrv/Result.php
new file mode 100644
index 00000000000..4e7a383e568
--- /dev/null
+++ b/src/Driver/SQLSrv/Result.php
@@ -0,0 +1,126 @@
+statement = $stmt;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ return $this->fetch(SQLSRV_FETCH_NUMERIC);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ return $this->fetch(SQLSRV_FETCH_ASSOC);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return FetchUtils::fetchOne($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return FetchUtils::fetchAllNumeric($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return FetchUtils::fetchAllAssociative($this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return FetchUtils::fetchFirstColumn($this);
+ }
+
+ public function rowCount(): int
+ {
+ if ($this->statement === null) {
+ return 0;
+ }
+
+ $count = sqlsrv_rows_affected($this->statement);
+
+ if ($count !== false) {
+ return $count;
+ }
+
+ return 0;
+ }
+
+ public function columnCount(): int
+ {
+ if ($this->statement === null) {
+ return 0;
+ }
+
+ $count = sqlsrv_num_fields($this->statement);
+
+ if ($count !== false) {
+ return $count;
+ }
+
+ return 0;
+ }
+
+ public function free(): void
+ {
+ // emulate it by fetching and discarding rows, similarly to what PDO does in this case
+ // @link http://php.net/manual/en/pdostatement.closecursor.php
+ // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075
+ // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them
+ while (sqlsrv_fetch($this->statement)) {
+ }
+ }
+
+ /**
+ * @return mixed|false
+ */
+ private function fetch(int $fetchType)
+ {
+ return sqlsrv_fetch_array($this->statement, $fetchType) ?? false;
+ }
+}
diff --git a/src/Driver/SQLSrv/SQLSrvConnection.php b/src/Driver/SQLSrv/SQLSrvConnection.php
index 2824919f672..30f27272f0b 100644
--- a/src/Driver/SQLSrv/SQLSrvConnection.php
+++ b/src/Driver/SQLSrv/SQLSrvConnection.php
@@ -2,7 +2,7 @@
namespace Doctrine\DBAL\Driver\SQLSrv;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
@@ -76,12 +76,9 @@ public function prepare(string $sql): DriverStatement
return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): ResultInterface
{
- $stmt = $this->prepare($sql);
- $stmt->execute();
-
- return $stmt;
+ return $this->prepare($sql)->execute();
}
/**
@@ -123,13 +120,13 @@ public function exec(string $statement): int
public function lastInsertId($name = null)
{
if ($name !== null) {
- $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
- $stmt->execute([$name]);
+ $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?')
+ ->execute([$name]);
} else {
- $stmt = $this->query('SELECT @@IDENTITY');
+ $result = $this->query('SELECT @@IDENTITY');
}
- return $stmt->fetchOne();
+ return $result->fetchOne();
}
/**
diff --git a/src/Driver/SQLSrv/SQLSrvStatement.php b/src/Driver/SQLSrv/SQLSrvStatement.php
index 5556ff8bc2e..a89b77cc77d 100644
--- a/src/Driver/SQLSrv/SQLSrvStatement.php
+++ b/src/Driver/SQLSrv/SQLSrvStatement.php
@@ -2,8 +2,7 @@
namespace Doctrine\DBAL\Driver\SQLSrv;
-use Doctrine\DBAL\Driver\FetchUtils;
-use Doctrine\DBAL\Driver\Result;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
@@ -11,26 +10,21 @@
use function is_numeric;
use function sqlsrv_execute;
use function sqlsrv_fetch;
-use function sqlsrv_fetch_array;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
-use function sqlsrv_num_fields;
use function SQLSRV_PHPTYPE_STREAM;
use function SQLSRV_PHPTYPE_STRING;
use function sqlsrv_prepare;
-use function sqlsrv_rows_affected;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
use const SQLSRV_ENC_BINARY;
-use const SQLSRV_FETCH_ASSOC;
-use const SQLSRV_FETCH_NUMERIC;
use const SQLSRV_PARAM_IN;
/**
* SQL Server Statement.
*/
-final class SQLSrvStatement implements Statement, Result
+final class SQLSrvStatement implements Statement
{
/**
* The SQLSRV Resource.
@@ -74,13 +68,6 @@ final class SQLSrvStatement implements Statement, Result
*/
private $lastInsertId;
- /**
- * Indicates whether the statement is in the state when fetching results is possible
- *
- * @var bool
- */
- private $result = false;
-
/**
* Append to any INSERT query to retrieve the last insert id.
*
@@ -145,35 +132,7 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l
*
* @deprecated Use free() instead.
*/
- public function closeCursor()
- {
- $this->free();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- if ($this->stmt === null) {
- return 0;
- }
-
- $count = sqlsrv_num_fields($this->stmt);
-
- if ($count !== false) {
- return $count;
- }
-
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($params = null)
+ public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
@@ -199,9 +158,7 @@ public function execute($params = null)
$this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0));
}
- $this->result = true;
-
- return true;
+ return new Result($this->stmt);
}
/**
@@ -248,98 +205,4 @@ private function prepare()
return $stmt;
}
-
- /**
- * {@inheritdoc}
- */
- public function fetchNumeric()
- {
- return $this->fetch(SQLSRV_FETCH_NUMERIC);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- return $this->fetch(SQLSRV_FETCH_ASSOC);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return FetchUtils::fetchOne($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return FetchUtils::fetchAllNumeric($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return FetchUtils::fetchAllAssociative($this);
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
- {
- return FetchUtils::fetchFirstColumn($this);
- }
-
- public function rowCount(): int
- {
- if ($this->stmt === null) {
- return 0;
- }
-
- $count = sqlsrv_rows_affected($this->stmt);
-
- if ($count !== false) {
- return $count;
- }
-
- return 0;
- }
-
- public function free(): void
- {
- // not having the result means there's nothing to close
- if ($this->stmt === null || ! $this->result) {
- return;
- }
-
- // emulate it by fetching and discarding rows, similarly to what PDO does in this case
- // @link http://php.net/manual/en/pdostatement.closecursor.php
- // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075
- // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them
- while (sqlsrv_fetch($this->stmt)) {
- }
-
- $this->result = false;
- }
-
- /**
- * @return mixed|false
- */
- private function fetch(int $fetchType)
- {
- // do not try fetching from the statement if it's not expected to contain the result
- // in order to prevent exceptional situation
- if ($this->stmt === null || ! $this->result) {
- return false;
- }
-
- return sqlsrv_fetch_array($this->stmt, $fetchType) ?? false;
- }
}
diff --git a/src/Driver/Statement.php b/src/Driver/Statement.php
index 2bd1e879470..ed8b2774c5f 100644
--- a/src/Driver/Statement.php
+++ b/src/Driver/Statement.php
@@ -5,12 +5,9 @@
use Doctrine\DBAL\ParameterType;
/**
- * Statement interface.
- * Drivers must implement this interface.
- *
- * This resembles (a subset of) the PDOStatement interface.
+ * Driver-level statement
*/
-interface Statement extends ResultStatement
+interface Statement
{
/**
* Binds a value to a corresponding named (not supported by mysqli driver, see comment below) or positional
@@ -69,7 +66,7 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l
* @param mixed[]|null $params A numeric array of values with as many elements as there are
* bound parameters in the SQL statement being executed.
*
- * @return bool TRUE on success or FALSE on failure.
+ * @throws DriverException
*/
- public function execute($params = null);
+ public function execute($params = null): Result;
}
diff --git a/src/Portability/Connection.php b/src/Portability/Connection.php
index 8e2f607af00..78ba390846b 100644
--- a/src/Portability/Connection.php
+++ b/src/Portability/Connection.php
@@ -2,11 +2,13 @@
namespace Doctrine\DBAL\Portability;
+use Doctrine\DBAL\Abstraction\Result as AbstractionResult;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Driver\PDOConnection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
+use Doctrine\DBAL\Result as DBALResult;
use PDO;
use const CASE_LOWER;
@@ -86,9 +88,11 @@ public function connect()
/**
* {@inheritdoc}
*/
- public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): ResultStatement
+ public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): AbstractionResult
{
- return new Statement(parent::executeQuery($query, $params, $types, $qcp), $this->converter);
+ return $this->wrapResult(
+ parent::executeQuery($query, $params, $types, $qcp)
+ );
}
public function prepare(string $sql): DriverStatement
@@ -96,8 +100,18 @@ public function prepare(string $sql): DriverStatement
return new Statement(parent::prepare($sql), $this->converter);
}
- public function query(string $sql): ResultStatement
+ public function query(string $sql): DriverResult
{
- return new Statement(parent::query($sql), $this->converter);
+ return $this->wrapResult(
+ parent::query($sql)
+ );
+ }
+
+ private function wrapResult(DriverResult $result): AbstractionResult
+ {
+ return new DBALResult(
+ new Result($result, $this->converter),
+ $this
+ );
}
}
diff --git a/src/Portability/Result.php b/src/Portability/Result.php
new file mode 100644
index 00000000000..dd56c327d8d
--- /dev/null
+++ b/src/Portability/Result.php
@@ -0,0 +1,97 @@
+result = $result;
+ $this->converter = $converter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchNumeric()
+ {
+ return $this->converter->convertNumeric(
+ $this->result->fetchNumeric()
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAssociative()
+ {
+ return $this->converter->convertAssociative(
+ $this->result->fetchAssociative()
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ return $this->converter->convertOne(
+ $this->result->fetchOne()
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllNumeric(): array
+ {
+ return $this->converter->convertAllNumeric(
+ $this->result->fetchAllNumeric()
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchAllAssociative(): array
+ {
+ return $this->converter->convertAllAssociative(
+ $this->result->fetchAllAssociative()
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFirstColumn(): array
+ {
+ return $this->converter->convertFirstColumn(
+ $this->result->fetchFirstColumn()
+ );
+ }
+
+ public function rowCount(): int
+ {
+ return $this->result->rowCount();
+ }
+
+ public function columnCount(): int
+ {
+ return $this->result->columnCount();
+ }
+
+ public function free(): void
+ {
+ $this->result->free();
+ }
+}
diff --git a/src/Portability/Statement.php b/src/Portability/Statement.php
index eb01fd6dd81..c50dd30b722 100644
--- a/src/Portability/Statement.php
+++ b/src/Portability/Statement.php
@@ -2,18 +2,16 @@
namespace Doctrine\DBAL\Portability;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
-use function assert;
-
/**
* Portability wrapper for a Statement.
*/
class Statement implements DriverStatement
{
- /** @var DriverStatement|ResultStatement */
+ /** @var DriverStatement */
private $stmt;
/** @var Converter */
@@ -21,10 +19,8 @@ class Statement implements DriverStatement
/**
* Wraps Statement and applies portability measures.
- *
- * @param DriverStatement|ResultStatement $stmt
*/
- public function __construct($stmt, Converter $converter)
+ public function __construct(DriverStatement $stmt, Converter $converter)
{
$this->stmt = $stmt;
$this->converter = $converter;
@@ -35,8 +31,6 @@ public function __construct($stmt, Converter $converter)
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
- assert($this->stmt instanceof DriverStatement);
-
return $this->stmt->bindParam($column, $variable, $type, $length);
}
@@ -45,101 +39,17 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
- assert($this->stmt instanceof DriverStatement);
-
return $this->stmt->bindValue($param, $value, $type);
}
/**
* {@inheritdoc}
*/
- public function closeCursor()
- {
- return $this->stmt->closeCursor();
- }
-
- /**
- * {@inheritdoc}
- */
- public function columnCount()
- {
- return $this->stmt->columnCount();
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($params = null)
- {
- assert($this->stmt instanceof DriverStatement);
-
- return $this->stmt->execute($params);
- }
-
- public function rowCount(): int
- {
- assert($this->stmt instanceof DriverStatement);
-
- return $this->stmt->rowCount();
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchNumeric()
- {
- return $this->converter->convertNumeric(
- $this->stmt->fetchNumeric()
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAssociative()
- {
- return $this->converter->convertAssociative(
- $this->stmt->fetchAssociative()
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- return $this->converter->convertOne(
- $this->stmt->fetchOne()
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllNumeric(): array
- {
- return $this->converter->convertAllNumeric(
- $this->stmt->fetchAllNumeric()
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchAllAssociative(): array
- {
- return $this->converter->convertAllAssociative(
- $this->stmt->fetchAllAssociative()
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchFirstColumn(): array
+ public function execute($params = null): ResultInterface
{
- return $this->converter->convertFirstColumn(
- $this->stmt->fetchFirstColumn()
+ return new Result(
+ $this->stmt->execute($params),
+ $this->converter
);
}
}
diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php
index edf348f83ba..5884ef6e48f 100644
--- a/src/Query/QueryBuilder.php
+++ b/src/Query/QueryBuilder.php
@@ -3,7 +3,7 @@
namespace Doctrine\DBAL\Query;
use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
@@ -200,7 +200,7 @@ public function getState()
* Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
* for insert, update and delete statements.
*
- * @return ResultStatement|int
+ * @return Result|int
*/
public function execute()
{
diff --git a/src/Result.php b/src/Result.php
new file mode 100644
index 00000000000..f015eba032f
--- /dev/null
+++ b/src/Result.php
@@ -0,0 +1,170 @@
+result = $result;
+ $this->connection = $connection;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DBALException
+ */
+ public function fetchNumeric()
+ {
+ try {
+ return $this->result->fetchNumeric();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DBALException
+ */
+ public function fetchAssociative()
+ {
+ try {
+ return $this->result->fetchAssociative();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchOne()
+ {
+ try {
+ return $this->result->fetchOne();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DBALException
+ */
+ public function fetchAllNumeric(): array
+ {
+ try {
+ return $this->result->fetchAllNumeric();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DBALException
+ */
+ public function fetchAllAssociative(): array
+ {
+ try {
+ return $this->result->fetchAllAssociative();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DBALException
+ */
+ public function fetchFirstColumn(): array
+ {
+ try {
+ return $this->result->fetchFirstColumn();
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * @return Traversable>
+ *
+ * @throws DBALException
+ */
+ public function iterateNumeric(): Traversable
+ {
+ try {
+ while (($row = $this->result->fetchNumeric()) !== false) {
+ yield $row;
+ }
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * @return Traversable>
+ *
+ * @throws DBALException
+ */
+ public function iterateAssociative(): Traversable
+ {
+ try {
+ while (($row = $this->result->fetchAssociative()) !== false) {
+ yield $row;
+ }
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ /**
+ * @return Traversable
+ *
+ * @throws DBALException
+ */
+ public function iterateColumn(): Traversable
+ {
+ try {
+ while (($value = $this->result->fetchOne()) !== false) {
+ yield $value;
+ }
+ } catch (DriverException $e) {
+ throw DBALException::driverException($this->connection->getDriver(), $e);
+ }
+ }
+
+ public function rowCount(): int
+ {
+ return $this->result->rowCount();
+ }
+
+ public function columnCount(): int
+ {
+ return $this->result->columnCount();
+ }
+
+ public function free(): void
+ {
+ $this->result->free();
+ }
+}
diff --git a/src/Statement.php b/src/Statement.php
index 053aa783794..8b9196942f0 100644
--- a/src/Statement.php
+++ b/src/Statement.php
@@ -2,13 +2,11 @@
namespace Doctrine\DBAL;
-use Doctrine\DBAL\Abstraction\Result;
-use Doctrine\DBAL\Driver\DriverException;
+use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Throwable;
-use Traversable;
use function is_string;
@@ -16,7 +14,7 @@
* A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support
* for logging, DBAL mapping types, etc.
*/
-class Statement implements DriverStatement, Result
+class Statement implements DriverStatement
{
/**
* The SQL statement.
@@ -42,7 +40,7 @@ class Statement implements DriverStatement, Result
/**
* The underlying driver statement.
*
- * @var \Doctrine\DBAL\Driver\Statement
+ * @var DriverStatement
*/
protected $stmt;
@@ -136,11 +134,9 @@ public function bindParam($name, &$var, $type = ParameterType::STRING, $length =
*
* @param mixed[]|null $params
*
- * @return bool TRUE on success, FALSE on failure.
- *
* @throws DBALException
*/
- public function execute($params = null)
+ public function execute($params = null): DriverResult
{
if ($params !== null) {
$this->params = $params;
@@ -152,213 +148,31 @@ public function execute($params = null)
}
try {
- $stmt = $this->stmt->execute($params);
+ return new Result(
+ $this->stmt->execute($params),
+ $this->conn
+ );
} catch (Throwable $ex) {
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
throw DBALException::driverExceptionDuringQuery(
$this->conn->getDriver(),
$ex,
$this->sql,
$this->conn->resolveParams($this->params, $this->types)
);
- }
-
- if ($logger !== null) {
- $logger->stopQuery();
- }
-
- $this->params = [];
- $this->types = [];
-
- return $stmt;
- }
-
- /**
- * Closes the cursor, freeing the database resources used by this statement.
- *
- * @deprecated Use Result::free() instead.
- *
- * @return bool TRUE on success, FALSE on failure.
- */
- public function closeCursor()
- {
- return $this->stmt->closeCursor();
- }
-
- /**
- * Returns the number of columns in the result set.
- *
- * @return int
- */
- public function columnCount()
- {
- return $this->stmt->columnCount();
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DBALException
- */
- public function fetchNumeric()
- {
- try {
- return $this->stmt->fetchNumeric();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DBALException
- */
- public function fetchAssociative()
- {
- try {
- return $this->stmt->fetchAssociative();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function fetchOne()
- {
- try {
- return $this->stmt->fetchOne();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DBALException
- */
- public function fetchAllNumeric(): array
- {
- try {
- return $this->stmt->fetchAllNumeric();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DBALException
- */
- public function fetchAllAssociative(): array
- {
- try {
- return $this->stmt->fetchAllAssociative();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws DBALException
- */
- public function fetchFirstColumn(): array
- {
- try {
- return $this->stmt->fetchFirstColumn();
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @return Traversable>
- *
- * @throws DBALException
- */
- public function iterateNumeric(): Traversable
- {
- try {
- while (($row = $this->stmt->fetchNumeric()) !== false) {
- yield $row;
- }
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @return Traversable>
- *
- * @throws DBALException
- */
- public function iterateAssociative(): Traversable
- {
- try {
- while (($row = $this->stmt->fetchAssociative()) !== false) {
- yield $row;
- }
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @return Traversable
- *
- * @throws DBALException
- */
- public function iterateColumn(): Traversable
- {
- try {
- while (($value = $this->stmt->fetchOne()) !== false) {
- yield $value;
+ } finally {
+ if ($logger !== null) {
+ $logger->stopQuery();
}
- } catch (DriverException $e) {
- throw DBALException::driverException($this->conn->getDriver(), $e);
- }
- }
- /**
- * Returns the number of rows affected by the last execution of this statement.
- *
- * @return int The number of affected rows.
- */
- public function rowCount(): int
- {
- return $this->stmt->rowCount();
- }
-
- public function free(): void
- {
- if ($this->stmt instanceof Result) {
- $this->stmt->free();
-
- return;
+ $this->params = [];
+ $this->types = [];
}
-
- $this->stmt->closeCursor();
}
/**
* Gets the wrapped driver statement.
*
- * @return \Doctrine\DBAL\Driver\Statement
+ * @return DriverStatement
*/
public function getWrappedStatement()
{
diff --git a/tests/Connection/LoggingTest.php b/tests/Connection/LoggingTest.php
index 7ffc32b6e06..1f75cc49b3a 100644
--- a/tests/Connection/LoggingTest.php
+++ b/tests/Connection/LoggingTest.php
@@ -15,8 +15,6 @@ final class LoggingTest extends TestCase
public function testLogExecuteQuery(): void
{
$driverConnection = $this->createStub(DriverConnection::class);
- $driverConnection->method('query')
- ->willReturn($this->createStub(Statement::class));
$this->createConnection($driverConnection, 'SELECT * FROM table')
->executeQuery('SELECT * FROM table');
diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php
index 010e635c757..7a35d980b8c 100644
--- a/tests/ConnectionTest.php
+++ b/tests/ConnectionTest.php
@@ -4,7 +4,7 @@
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\EventManager;
-use Doctrine\DBAL\Cache\ArrayStatement;
+use Doctrine\DBAL\Abstraction\Result;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
@@ -13,7 +13,6 @@
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
-use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Exception\InvalidArgumentException;
@@ -545,140 +544,100 @@ public function testDeleteWithIsNull(): void
);
}
- public function testFetchAssociative(): void
- {
- $statement = 'SELECT * FROM foo WHERE bar = ?';
- $params = [666];
- $types = [ParameterType::INTEGER];
- $result = [];
-
- $driverMock = $this->createMock(Driver::class);
-
- $driverMock->expects(self::any())
- ->method('connect')
- ->will(self::returnValue(
- $this->createMock(DriverConnection::class)
- ));
-
- $driverStatementMock = $this->createMock(Statement::class);
-
- $driverStatementMock->expects(self::once())
- ->method('fetchAssociative')
- ->will(self::returnValue($result));
-
- $conn = $this->getMockBuilder(Connection::class)
- ->onlyMethods(['executeQuery'])
- ->setConstructorArgs([[], $driverMock])
- ->getMock();
-
- $conn->expects(self::once())
- ->method('executeQuery')
- ->with($statement, $params, $types)
- ->will(self::returnValue($driverStatementMock));
-
- self::assertSame($result, $conn->fetchAssociative($statement, $params, $types));
- }
-
- public function testFetchNumeric(): void
- {
- $statement = 'SELECT * FROM foo WHERE bar = ?';
- $params = [666];
- $types = [ParameterType::INTEGER];
- $result = [];
-
- $driverMock = $this->createMock(Driver::class);
-
- $driverMock->expects(self::any())
- ->method('connect')
- ->will(self::returnValue(
- $this->createMock(DriverConnection::class)
- ));
-
- $driverStatementMock = $this->createMock(Statement::class);
-
- $driverStatementMock->expects(self::once())
- ->method('fetchNumeric')
- ->will(self::returnValue($result));
-
- $conn = $this->getMockBuilder(Connection::class)
- ->onlyMethods(['executeQuery'])
- ->setConstructorArgs([[], $driverMock])
- ->getMock();
-
- $conn->expects(self::once())
- ->method('executeQuery')
- ->with($statement, $params, $types)
- ->will(self::returnValue($driverStatementMock));
-
- self::assertSame($result, $conn->fetchNumeric($statement, $params, $types));
- }
-
- public function testFetchOne(): void
+ /**
+ * @param mixed $expected
+ *
+ * @dataProvider fetchModeProvider
+ */
+ public function testFetch(string $method, callable $invoke, $expected): void
{
- $statement = 'SELECT * FROM foo WHERE bar = ?';
- $params = [666];
- $types = [ParameterType::INTEGER];
- $result = [];
-
- $driverMock = $this->createMock(Driver::class);
-
- $driverMock->expects(self::any())
- ->method('connect')
- ->will(self::returnValue(
- $this->createMock(DriverConnection::class)
- ));
+ $query = 'SELECT * FROM foo WHERE bar = ?';
+ $params = [666];
+ $types = [ParameterType::INTEGER];
- $driverStatementMock = $this->createMock(Statement::class);
+ $result = $this->createMock(Result::class);
+ $result->expects(self::once())
+ ->method($method)
+ ->willReturn($expected);
- $driverStatementMock->expects(self::once())
- ->method('fetchOne')
- ->will(self::returnValue($result));
+ $driver = $this->createConfiguredMock(Driver::class, [
+ 'connect' => $this->createMock(DriverConnection::class),
+ ]);
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
- ->setConstructorArgs([[], $driverMock])
+ ->setConstructorArgs([[], $driver])
->getMock();
$conn->expects(self::once())
->method('executeQuery')
- ->with($statement, $params, $types)
- ->will(self::returnValue($driverStatementMock));
+ ->with($query, $params, $types)
+ ->willReturn($result);
- self::assertSame($result, $conn->fetchOne($statement, $params, $types));
+ self::assertSame($expected, $invoke($conn, $query, $params, $types));
}
- public function testFetchAllAssociative(): void
+ /**
+ * @return iterable>
+ */
+ public static function fetchModeProvider(): iterable
{
- $statement = 'SELECT * FROM foo WHERE bar = ?';
- $params = [666];
- $types = [ParameterType::INTEGER];
- $result = [];
-
- $driverMock = $this->createMock(Driver::class);
-
- $driverMock->expects(self::any())
- ->method('connect')
- ->will(self::returnValue(
- $this->createMock(DriverConnection::class)
- ));
+ yield 'numeric' => [
+ 'fetchNumeric',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchNumeric($query, $params, $types);
+ },
+ ['bar'],
+ ];
- $driverStatementMock = $this->createMock(Statement::class);
+ yield 'associative' => [
+ 'fetchAssociative',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchAssociative($query, $params, $types);
+ },
+ ['foo' => 'bar'],
+ ];
- $driverStatementMock->expects(self::once())
- ->method('fetchAllAssociative')
- ->will(self::returnValue($result));
+ yield 'one' => [
+ 'fetchOne',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchOne($query, $params, $types);
+ },
+ 'bar',
+ ];
- $conn = $this->getMockBuilder(Connection::class)
- ->onlyMethods(['executeQuery'])
- ->setConstructorArgs([[], $driverMock])
- ->getMock();
+ yield 'all-numeric' => [
+ 'fetchAllNumeric',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchAllNumeric($query, $params, $types);
+ },
+ [
+ ['bar'],
+ ['baz'],
+ ],
+ ];
- $conn->expects(self::once())
- ->method('executeQuery')
- ->with($statement, $params, $types)
- ->will(self::returnValue($driverStatementMock));
+ yield 'all-associative' => [
+ 'fetchAllAssociative',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchAllAssociative($query, $params, $types);
+ },
+ [
+ ['foo' => 'bar'],
+ ['foo' => 'baz'],
+ ],
+ ];
- self::assertSame($result, $conn->fetchAllAssociative($statement, $params, $types));
+ yield 'first-column' => [
+ 'fetchFirstColumn',
+ static function (Connection $connection, string $query, array $params, array $types) {
+ return $connection->fetchFirstColumn($query, $params, $types);
+ },
+ [
+ 'bar',
+ 'baz',
+ ],
+ ];
}
public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException(): void
@@ -766,10 +725,7 @@ public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCach
$driver = $this->createMock(Driver::class);
- self::assertInstanceOf(
- ArrayStatement::class,
- (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
- );
+ (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock);
}
/**
diff --git a/tests/Driver/AbstractMySQLDriverTest.php b/tests/Driver/AbstractMySQLDriverTest.php
index a03e637d4ed..c6c1e1ebd12 100644
--- a/tests/Driver/AbstractMySQLDriverTest.php
+++ b/tests/Driver/AbstractMySQLDriverTest.php
@@ -5,7 +5,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
@@ -26,9 +26,9 @@ public function testReturnsDatabaseName(): void
'password' => 'bar',
];
- $statement = $this->createMock(ResultStatement::class);
+ $result = $this->createMock(Result::class);
- $statement->expects(self::once())
+ $result->expects(self::once())
->method('fetchOne')
->will(self::returnValue($database));
@@ -40,7 +40,7 @@ public function testReturnsDatabaseName(): void
$connection->expects(self::once())
->method('query')
- ->will(self::returnValue($statement));
+ ->will(self::returnValue($result));
self::assertSame($database, $this->driver->getDatabase($connection));
}
diff --git a/tests/Driver/AbstractPostgreSQLDriverTest.php b/tests/Driver/AbstractPostgreSQLDriverTest.php
index ca429405750..d00bd11c847 100644
--- a/tests/Driver/AbstractPostgreSQLDriverTest.php
+++ b/tests/Driver/AbstractPostgreSQLDriverTest.php
@@ -5,7 +5,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
@@ -24,9 +24,9 @@ public function testReturnsDatabaseName(): void
'password' => 'bar',
];
- $statement = $this->createMock(ResultStatement::class);
+ $result = $this->createMock(Result::class);
- $statement->expects(self::once())
+ $result->expects(self::once())
->method('fetchOne')
->will(self::returnValue($database));
@@ -38,7 +38,7 @@ public function testReturnsDatabaseName(): void
$connection->expects(self::once())
->method('query')
- ->will(self::returnValue($statement));
+ ->will(self::returnValue($result));
self::assertSame($database, $this->driver->getDatabase($connection));
}
diff --git a/tests/Functional/DataAccessTest.php b/tests/Functional/DataAccessTest.php
index f62b5d09aca..bd312a161bb 100644
--- a/tests/Functional/DataAccessTest.php
+++ b/tests/Functional/DataAccessTest.php
@@ -58,9 +58,9 @@ public function testPrepareWithBindValue(): void
$stmt->bindValue(1, 1);
$stmt->bindValue(2, 'foo');
- $stmt->execute();
- $row = $stmt->fetchAssociative();
+ $row = $stmt->execute()->fetchAssociative();
+
self::assertIsArray($row);
$row = array_change_key_case($row, CASE_LOWER);
self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
@@ -77,9 +77,9 @@ public function testPrepareWithBindParam(): void
$stmt->bindParam(1, $paramInt);
$stmt->bindParam(2, $paramStr);
- $stmt->execute();
- $row = $stmt->fetchAssociative();
+ $row = $stmt->execute()->fetchAssociative();
+
self::assertIsArray($row);
$row = array_change_key_case($row, CASE_LOWER);
self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
@@ -96,9 +96,8 @@ public function testPrepareWithFetchAllAssociative(): void
$stmt->bindParam(1, $paramInt);
$stmt->bindParam(2, $paramStr);
- $stmt->execute();
- $rows = $stmt->fetchAllAssociative();
+ $rows = $stmt->execute()->fetchAllAssociative();
$rows[0] = array_change_key_case($rows[0], CASE_LOWER);
self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
}
@@ -114,34 +113,11 @@ public function testPrepareWithFetchOne(): void
$stmt->bindParam(1, $paramInt);
$stmt->bindParam(2, $paramStr);
- $stmt->execute();
- $column = $stmt->fetchOne();
+ $column = $stmt->execute()->fetchOne();
self::assertEquals(1, $column);
}
- public function testPrepareWithIterator(): void
- {
- $paramInt = 1;
- $paramStr = 'foo';
-
- $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
- $stmt = $this->connection->prepare($sql);
- self::assertInstanceOf(Statement::class, $stmt);
-
- $stmt->bindParam(1, $paramInt);
- $stmt->bindParam(2, $paramStr);
- $stmt->execute();
-
- $rows = [];
-
- foreach ($stmt->iterateAssociative() as $row) {
- $rows[] = array_change_key_case($row, CASE_LOWER);
- }
-
- self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
- }
-
public function testPrepareWithQuoted(): void
{
$table = 'fetch_table';
@@ -165,9 +141,9 @@ public function testPrepareWithExecuteParams(): void
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
$stmt = $this->connection->prepare($sql);
self::assertInstanceOf(Statement::class, $stmt);
- $stmt->execute([$paramInt, $paramStr]);
+ $result = $stmt->execute([$paramInt, $paramStr]);
- $row = $stmt->fetchAssociative();
+ $row = $result->fetchAssociative();
self::assertNotFalse($row);
$row = array_change_key_case($row, CASE_LOWER);
self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
@@ -392,14 +368,13 @@ public function testFetchOneWithMissingTypes(): void
*/
public function testExecuteQueryBindDateTimeType(): void
{
- $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
- $stmt = $this->connection->executeQuery(
- $sql,
+ $value = $this->connection->fetchOne(
+ 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
[1 => new DateTime('2010-01-01 10:10:10')],
[1 => Types::DATETIME_MUTABLE]
);
- self::assertEquals(1, $stmt->fetchOne());
+ self::assertEquals(1, $value);
}
/**
@@ -436,9 +411,9 @@ public function testPrepareQueryBindValueDateTimeType(): void
$sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
$stmt = $this->connection->prepare($sql);
$stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Types::DATETIME_MUTABLE);
- $stmt->execute();
+ $result = $stmt->execute();
- self::assertEquals(1, $stmt->fetchOne());
+ self::assertEquals(1, $result->fetchOne());
}
/**
@@ -450,23 +425,23 @@ public function testNativeArrayListSupport(): void
$this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
}
- $stmt = $this->connection->executeQuery(
+ $result = $this->connection->executeQuery(
'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
[[100, 101, 102, 103, 104]],
[Connection::PARAM_INT_ARRAY]
);
- $data = $stmt->fetchAllNumeric();
+ $data = $result->fetchAllNumeric();
self::assertCount(5, $data);
self::assertEquals([[100], [101], [102], [103], [104]], $data);
- $stmt = $this->connection->executeQuery(
+ $result = $this->connection->executeQuery(
'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
[['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
[Connection::PARAM_STR_ARRAY]
);
- $data = $stmt->fetchAllNumeric();
+ $data = $result->fetchAllNumeric();
self::assertCount(5, $data);
self::assertEquals([[100], [101], [102], [103], [104]], $data);
}
@@ -670,8 +645,7 @@ public function testBitComparisonExpressionSupport(): void
. ', ' . $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and'
. ' FROM fetch_table';
- $stmt = $this->connection->executeQuery($sql);
- $data = $stmt->fetchAllAssociative();
+ $data = $this->connection->fetchAllAssociative($sql);
self::assertCount(4, $data);
self::assertEquals(count($bitmap), count($data));
@@ -728,8 +702,7 @@ public function testEmptyFetchOneReturnsFalse(): void
public function testEmptyParameters(): void
{
$sql = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
- $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
- $rows = $stmt->fetchAllAssociative();
+ $rows = $this->connection->fetchAllAssociative($sql, [[]], [Connection::PARAM_INT_ARRAY]);
self::assertEquals([], $rows);
}
diff --git a/tests/Functional/Driver/OCI8/StatementTest.php b/tests/Functional/Driver/OCI8/StatementTest.php
index b5d42c0df5d..680d272a9d7 100644
--- a/tests/Functional/Driver/OCI8/StatementTest.php
+++ b/tests/Functional/Driver/OCI8/StatementTest.php
@@ -48,12 +48,11 @@ public function testQueryConversion(string $query, array $params, array $expecte
*/
public function testStatementBindParameters(string $query, array $params, array $expected): void
{
- $stmt = $this->connection->prepare($query);
- $stmt->execute($params);
-
self::assertEquals(
$expected,
- $stmt->fetchAssociative()
+ $this->connection->prepare($query)
+ ->execute($params)
+ ->fetchAssociative()
);
}
diff --git a/tests/Functional/Driver/PDOPgSql/DriverTest.php b/tests/Functional/Driver/PDOPgSql/DriverTest.php
index a05b8133cd8..e5db1a085c2 100644
--- a/tests/Functional/Driver/PDOPgSql/DriverTest.php
+++ b/tests/Functional/Driver/PDOPgSql/DriverTest.php
@@ -83,10 +83,9 @@ public function testConnectsWithApplicationNameParameter(): void
$connection = $this->driver->connect($parameters, $user, $password);
- $hash = microtime(true); // required to identify the record in the results uniquely
- $sql = sprintf('SELECT * FROM pg_stat_activity WHERE %d = %d', $hash, $hash);
- $statement = $connection->query($sql);
- $records = $statement->fetchAllAssociative();
+ $hash = microtime(true); // required to identify the record in the results uniquely
+ $sql = sprintf('SELECT * FROM pg_stat_activity WHERE %d = %d', $hash, $hash);
+ $records = $connection->query($sql)->fetchAllAssociative();
foreach ($records as $record) {
// The query column is named "current_query" on PostgreSQL < 9.2
diff --git a/tests/Functional/LikeWildcardsEscapingTest.php b/tests/Functional/LikeWildcardsEscapingTest.php
index 5a7078e06b4..d283a2ad63f 100644
--- a/tests/Functional/LikeWildcardsEscapingTest.php
+++ b/tests/Functional/LikeWildcardsEscapingTest.php
@@ -13,7 +13,8 @@ public function testFetchLikeExpressionResult(): void
$string = '_25% off_ your next purchase \o/ [$̲̅(̲̅5̲̅)̲̅$̲̅] (^̮^)';
$escapeChar = '!';
$databasePlatform = $this->connection->getDatabasePlatform();
- $stmt = $this->connection->prepare(
+
+ $result = $this->connection->prepare(
$databasePlatform->getDummySelectSQL(
sprintf(
"(CASE WHEN '%s' LIKE '%s' ESCAPE '%s' THEN 1 ELSE 0 END)",
@@ -22,8 +23,8 @@ public function testFetchLikeExpressionResult(): void
$escapeChar
)
)
- );
- $stmt->execute();
- self::assertTrue((bool) $stmt->fetchOne());
+ )->execute();
+
+ self::assertTrue((bool) $result->fetchOne());
}
}
diff --git a/tests/Functional/MasterSlaveConnectionTest.php b/tests/Functional/MasterSlaveConnectionTest.php
index 6e442fecc44..b465d531f7c 100644
--- a/tests/Functional/MasterSlaveConnectionTest.php
+++ b/tests/Functional/MasterSlaveConnectionTest.php
@@ -3,7 +3,6 @@
namespace Doctrine\DBAL\Tests\Functional;
use Doctrine\DBAL\Connections\MasterSlaveConnection;
-use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
@@ -197,14 +196,12 @@ public function testQueryOnMaster(): void
$query = 'SELECT count(*) as num FROM master_slave_table';
- $statement = $conn->query($query);
-
- self::assertInstanceOf(Statement::class, $statement);
+ $result = $conn->query($query);
//Query must be executed only on Master
self::assertTrue($conn->isConnectedToMaster());
- $data = $statement->fetchAllAssociative();
+ $data = $result->fetchAllAssociative();
self::assertArrayHasKey(0, $data);
self::assertArrayHasKey('num', $data[0]);
@@ -221,14 +218,12 @@ public function testQueryOnSlave(): void
$query = 'SELECT count(*) as num FROM master_slave_table';
- $statement = $conn->query($query);
-
- self::assertInstanceOf(Statement::class, $statement);
+ $result = $conn->query($query);
//Query must be executed only on Master, even when we connect to the slave
self::assertTrue($conn->isConnectedToMaster());
- $data = $statement->fetchAllAssociative();
+ $data = $result->fetchAllAssociative();
self::assertArrayHasKey(0, $data);
self::assertArrayHasKey('num', $data[0]);
diff --git a/tests/Functional/NamedParametersTest.php b/tests/Functional/NamedParametersTest.php
index fc6f50f384a..07ef1063b46 100644
--- a/tests/Functional/NamedParametersTest.php
+++ b/tests/Functional/NamedParametersTest.php
@@ -213,13 +213,12 @@ protected function setUp(): void
*/
public function testTicket(string $query, array $params, array $types, array $expected): void
{
- $stmt = $this->connection->executeQuery($query, $params, $types);
- $result = $stmt->fetchAllAssociative();
+ $data = $this->connection->fetchAllAssociative($query, $params, $types);
- foreach ($result as $k => $v) {
- $result[$k] = array_change_key_case($v, CASE_LOWER);
+ foreach ($data as $k => $v) {
+ $data[$k] = array_change_key_case($v, CASE_LOWER);
}
- self::assertEquals($result, $expected);
+ self::assertEquals($data, $expected);
}
}
diff --git a/tests/Functional/PortabilityTest.php b/tests/Functional/PortabilityTest.php
index 1b9a47e8db3..7c9b597c3f4 100644
--- a/tests/Functional/PortabilityTest.php
+++ b/tests/Functional/PortabilityTest.php
@@ -66,16 +66,17 @@ public function testFullFetchMode(): void
$rows = $this->getPortableConnection()->fetchAllAssociative('SELECT * FROM portability_table');
$this->assertFetchResultRows($rows);
- $stmt = $this->getPortableConnection()->query('SELECT * FROM portability_table');
+ $result = $this->getPortableConnection()->query('SELECT * FROM portability_table');
- while (($row = $stmt->fetchAssociative())) {
+ while (($row = $result->fetchAssociative())) {
$this->assertFetchResultRow($row);
}
- $stmt = $this->getPortableConnection()->prepare('SELECT * FROM portability_table');
- $stmt->execute();
+ $result = $this->getPortableConnection()
+ ->prepare('SELECT * FROM portability_table')
+ ->execute();
- while (($row = $stmt->fetchAssociative())) {
+ while (($row = $result->fetchAssociative())) {
$this->assertFetchResultRow($row);
}
}
@@ -87,14 +88,15 @@ public function testConnFetchMode(): void
$rows = $conn->fetchAllAssociative('SELECT * FROM portability_table');
$this->assertFetchResultRows($rows);
- $stmt = $conn->query('SELECT * FROM portability_table');
- while (($row = $stmt->fetchAssociative())) {
+ $result = $conn->query('SELECT * FROM portability_table');
+ while (($row = $result->fetchAssociative())) {
$this->assertFetchResultRow($row);
}
- $stmt = $conn->prepare('SELECT * FROM portability_table');
- $stmt->execute();
- while (($row = $stmt->fetchAssociative())) {
+ $result = $conn->prepare('SELECT * FROM portability_table')
+ ->execute();
+
+ while (($row = $result->fetchAssociative())) {
$this->assertFetchResultRow($row);
}
}
@@ -133,10 +135,10 @@ public function assertFetchResultRow(array $row): void
*/
public function testfetchColumn(string $field, array $expected): void
{
- $conn = $this->getPortableConnection();
- $stmt = $conn->query('SELECT ' . $field . ' FROM portability_table');
+ $conn = $this->getPortableConnection();
+ $result = $conn->query('SELECT ' . $field . ' FROM portability_table');
- $column = $stmt->fetchFirstColumn();
+ $column = $result->fetchFirstColumn();
self::assertEquals($expected, $column);
}
@@ -159,10 +161,10 @@ public static function fetchColumnProvider(): iterable
public function testFetchAllNullColumn(): void
{
- $conn = $this->getPortableConnection();
- $stmt = $conn->query('SELECT Test_Null FROM portability_table');
+ $conn = $this->getPortableConnection();
+ $result = $conn->query('SELECT Test_Null FROM portability_table');
- $column = $stmt->fetchFirstColumn();
+ $column = $result->fetchFirstColumn();
self::assertSame([null, null], $column);
}
}
diff --git a/tests/Functional/ResultCacheTest.php b/tests/Functional/ResultCacheTest.php
index 1be834fe23d..56f84a8eb1b 100644
--- a/tests/Functional/ResultCacheTest.php
+++ b/tests/Functional/ResultCacheTest.php
@@ -4,7 +4,7 @@
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\DBAL\Cache\QueryCacheProfile;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
@@ -61,8 +61,8 @@ public function testCacheFetchAssociative(): void
{
$this->assertCacheNonCacheSelectSameFetchModeAreEqual(
$this->expectedResult,
- static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ static function (Result $result) {
+ return $result->fetchAssociative();
}
);
}
@@ -76,8 +76,8 @@ public function testFetchNumeric(): void
$this->assertCacheNonCacheSelectSameFetchModeAreEqual(
$expectedResult,
- static function (ResultStatement $stmt) {
- return $stmt->fetchNumeric();
+ static function (Result $result) {
+ return $result->fetchNumeric();
}
);
}
@@ -91,8 +91,8 @@ public function testFetchOne(): void
$this->assertCacheNonCacheSelectSameFetchModeAreEqual(
$expectedResult,
- static function (ResultStatement $stmt) {
- return $stmt->fetchOne();
+ static function (Result $result) {
+ return $result->fetchOne();
}
);
}
@@ -106,16 +106,16 @@ public function testMixingFetch(): void
$stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- $data = $this->hydrateViaFetchAll($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAllAssociative();
+ $data = $this->hydrateViaFetchAll($stmt, static function (Result $result) {
+ return $result->fetchAllAssociative();
});
self::assertEquals($this->expectedResult, $data);
$stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- $data = $this->hydrateViaFetchAll($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAllNumeric();
+ $data = $this->hydrateViaFetchAll($stmt, static function (Result $result) {
+ return $result->fetchAllNumeric();
});
self::assertEquals($numExpectedResult, $data);
@@ -137,19 +137,15 @@ public function testFetchViaIteration(callable $fetch, callable $fetchAll): void
public function testFetchAndFinishSavesCache(): void
{
- $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
-
- $data = [];
+ $result = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- while (($row = $stmt->fetchAssociative()) !== false) {
+ while (($row = $result->fetchAssociative()) !== false) {
$data[] = $row;
}
- $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
+ $result = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- $data = [];
-
- while (($row = $stmt->fetchNumeric()) !== false) {
+ while (($row = $result->fetchNumeric()) !== false) {
$data[] = $row;
}
@@ -158,14 +154,14 @@ public function testFetchAndFinishSavesCache(): void
public function testDontFinishNoCache(): void
{
- $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
+ $result = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- $stmt->fetchAssociative();
+ $result->fetchAssociative();
- $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
+ $result = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey'));
- $this->hydrateViaIteration($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchNumeric();
+ $this->hydrateViaIteration($result, static function (Result $result) {
+ return $result->fetchNumeric();
});
self::assertCount(2, $this->sqlLogger->queries);
@@ -174,8 +170,13 @@ public function testDontFinishNoCache(): void
public function testFetchAllSavesCache(): void
{
$layerCache = new ArrayCache();
- $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'testcachekey', $layerCache));
- $stmt->fetchAllAssociative();
+ $result = $this->connection->executeQuery(
+ 'SELECT * FROM caching WHERE test_int > 500',
+ [],
+ [],
+ new QueryCacheProfile(0, 'testcachekey', $layerCache)
+ );
+ $result->fetchAllAssociative();
self::assertCount(1, $layerCache->fetch('testcachekey'));
}
@@ -187,12 +188,12 @@ public function testFetchColumn(): void
$qcp = new QueryCacheProfile(0, 0, new ArrayCache());
- $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
- $stmt->fetchFirstColumn();
+ $result = $this->connection->executeCacheQuery($query, [], [], $qcp);
+ $result->fetchFirstColumn();
- $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
+ $query = $this->connection->executeCacheQuery($query, [], [], $qcp);
- self::assertEquals([1], $stmt->fetchFirstColumn());
+ self::assertEquals([1], $query->fetchFirstColumn());
}
/**
@@ -217,13 +218,13 @@ private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedR
public function testEmptyResultCache(): void
{
$stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey'));
- $this->hydrateViaIteration($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ $this->hydrateViaIteration($stmt, static function (Result $result) {
+ return $result->fetchAssociative();
});
$stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey'));
- $this->hydrateViaIteration($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ $this->hydrateViaIteration($stmt, static function (Result $result) {
+ return $result->fetchAssociative();
});
self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
@@ -232,15 +233,15 @@ public function testEmptyResultCache(): void
public function testChangeCacheImpl(): void
{
$stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey'));
- $this->hydrateViaIteration($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ $this->hydrateViaIteration($stmt, static function (Result $result) {
+ return $result->fetchAssociative();
});
$secondCache = new ArrayCache();
$stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey', $secondCache));
- $this->hydrateViaIteration($stmt, static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ $this->hydrateViaIteration($stmt, static function (Result $result) {
+ return $result->fetchAssociative();
});
self::assertCount(2, $this->sqlLogger->queries, 'two hits');
@@ -253,29 +254,29 @@ public function testChangeCacheImpl(): void
public static function fetchProvider(): iterable
{
yield 'associative' => [
- static function (ResultStatement $stmt) {
- return $stmt->fetchAssociative();
+ static function (Result $result) {
+ return $result->fetchAssociative();
},
- static function (ResultStatement $stmt) {
- return $stmt->fetchAllAssociative();
+ static function (Result $result) {
+ return $result->fetchAllAssociative();
},
];
yield 'numeric' => [
- static function (ResultStatement $stmt) {
- return $stmt->fetchNumeric();
+ static function (Result $result) {
+ return $result->fetchNumeric();
},
- static function (ResultStatement $stmt) {
- return $stmt->fetchAllNumeric();
+ static function (Result $result) {
+ return $result->fetchAllNumeric();
},
];
yield 'column' => [
- static function (ResultStatement $stmt) {
- return $stmt->fetchOne();
+ static function (Result $result) {
+ return $result->fetchOne();
},
- static function (ResultStatement $stmt) {
- return $stmt->fetchFirstColumn();
+ static function (Result $result) {
+ return $result->fetchFirstColumn();
},
];
}
@@ -283,11 +284,11 @@ static function (ResultStatement $stmt) {
/**
* @return array
*/
- private function hydrateViaFetchAll(ResultStatement $stmt, callable $fetchAll): array
+ private function hydrateViaFetchAll(Result $result, callable $fetchAll): array
{
$data = [];
- foreach ($fetchAll($stmt) as $row) {
+ foreach ($fetchAll($result) as $row) {
$data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
}
@@ -297,11 +298,11 @@ private function hydrateViaFetchAll(ResultStatement $stmt, callable $fetchAll):
/**
* @return array
*/
- private function hydrateViaIteration(ResultStatement $stmt, callable $fetch): array
+ private function hydrateViaIteration(Result $result, callable $fetch): array
{
$data = [];
- while (($row = $fetch($stmt)) !== false) {
+ while (($row = $fetch($result)) !== false) {
$data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
}
diff --git a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php
index 28f00a0eaa1..aa92bf9ab0d 100644
--- a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php
+++ b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php
@@ -1566,17 +1566,17 @@ public function testPrimaryKeyAutoIncrement(): void
$this->connection->insert('test_pk_auto_increment', ['text' => '1']);
- $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'1\'');
- $query->execute();
- $lastUsedIdBeforeDelete = (int) $query->fetchOne();
+ $result = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'1\'');
+
+ $lastUsedIdBeforeDelete = (int) $result->fetchOne();
$this->connection->query('DELETE FROM test_pk_auto_increment');
$this->connection->insert('test_pk_auto_increment', ['text' => '2']);
- $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'2\'');
- $query->execute();
- $lastUsedIdAfterDelete = (int) $query->fetchOne();
+ $result = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = \'2\'');
+
+ $lastUsedIdAfterDelete = (int) $result->fetchOne();
self::assertGreaterThan($lastUsedIdBeforeDelete, $lastUsedIdAfterDelete);
}
diff --git a/tests/Functional/Schema/SqliteSchemaManagerTest.php b/tests/Functional/Schema/SqliteSchemaManagerTest.php
index caa4ddb8a3f..0ea1a4e4433 100644
--- a/tests/Functional/Schema/SqliteSchemaManagerTest.php
+++ b/tests/Functional/Schema/SqliteSchemaManagerTest.php
@@ -273,9 +273,9 @@ public function testPrimaryKeyNoAutoIncrement(): void
$this->connection->insert('test_pk_auto_increment', ['text' => '2']);
- $query = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = "2"');
- $query->execute();
- $lastUsedIdAfterDelete = (int) $query->fetchOne();
+ $result = $this->connection->query('SELECT id FROM test_pk_auto_increment WHERE text = "2"');
+
+ $lastUsedIdAfterDelete = (int) $result->fetchOne();
// with an empty table, non autoincrement rowid is always 1
self::assertEquals(1, $lastUsedIdAfterDelete);
diff --git a/tests/Functional/StatementTest.php b/tests/Functional/StatementTest.php
index abf2356b36f..ee01f17eb8c 100644
--- a/tests/Functional/StatementTest.php
+++ b/tests/Functional/StatementTest.php
@@ -3,11 +3,12 @@
namespace Doctrine\DBAL\Tests\Functional;
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
-use Doctrine\DBAL\Driver\Statement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Type;
+use Throwable;
use function base64_decode;
use function stream_get_contents;
@@ -24,7 +25,7 @@ protected function setUp(): void
$this->connection->getSchemaManager()->dropAndCreateTable($table);
}
- public function testStatementIsReusableAfterClosingCursor(): void
+ public function testStatementIsReusableAfterFreeingResult(): void
{
if ($this->connection->getDriver() instanceof PDOOracleDriver) {
self::markTestIncomplete('See https://bugs.php.net/bug.php?id=77181');
@@ -35,18 +36,16 @@ public function testStatementIsReusableAfterClosingCursor(): void
$stmt = $this->connection->prepare('SELECT id FROM stmt_test ORDER BY id');
- $stmt->execute();
+ $result = $stmt->execute();
- $id = $stmt->fetchOne();
+ $id = $result->fetchOne();
self::assertEquals(1, $id);
- $stmt->closeCursor();
+ $result->free();
- $stmt->execute();
- $id = $stmt->fetchOne();
- self::assertEquals(1, $id);
- $id = $stmt->fetchOne();
- self::assertEquals(2, $id);
+ $result = $stmt->execute();
+ self::assertEquals(1, $result->fetchOne());
+ self::assertEquals(2, $result->fetchOne());
}
public function testReuseStatementWithLongerResults(): void
@@ -67,11 +66,11 @@ public function testReuseStatementWithLongerResults(): void
];
$this->connection->insert('stmt_longer_results', $row1);
- $stmt = $this->connection->prepare('SELECT param, val FROM stmt_longer_results ORDER BY param');
- $stmt->execute();
+ $stmt = $this->connection->prepare('SELECT param, val FROM stmt_longer_results ORDER BY param');
+ $result = $stmt->execute();
self::assertEquals([
['param1', 'X'],
- ], $stmt->fetchAllNumeric());
+ ], $result->fetchAllNumeric());
$row2 = [
'param' => 'param2',
@@ -79,11 +78,11 @@ public function testReuseStatementWithLongerResults(): void
];
$this->connection->insert('stmt_longer_results', $row2);
- $stmt->execute();
+ $result = $stmt->execute();
self::assertEquals([
['param1', 'X'],
['param2', 'A bit longer value'],
- ], $stmt->fetchAllNumeric());
+ ], $result->fetchAllNumeric());
}
public function testFetchLongBlob(): void
@@ -122,12 +121,12 @@ public function testFetchLongBlob(): void
$this->connection->insert('stmt_long_blob', ['contents' => $contents], [ParameterType::LARGE_OBJECT]);
- $stmt = $this->connection->prepare('SELECT contents FROM stmt_long_blob');
- $stmt->execute();
+ $result = $this->connection->prepare('SELECT contents FROM stmt_long_blob')
+ ->execute();
$stream = Type::getType('blob')
->convertToPHPValue(
- $stmt->fetchOne(),
+ $result->fetchOne(),
$this->connection->getDatabasePlatform()
);
@@ -139,19 +138,20 @@ public function testIncompletelyFetchedStatementDoesNotBlockConnection(): void
$this->connection->insert('stmt_test', ['id' => 1]);
$this->connection->insert('stmt_test', ['id' => 2]);
- $stmt1 = $this->connection->prepare('SELECT id FROM stmt_test');
- $stmt1->execute();
- $stmt1->fetchAssociative();
- $stmt1->execute();
+ $stmt1 = $this->connection->prepare('SELECT id FROM stmt_test');
+ $result = $stmt1->execute();
+ $result->fetchAssociative();
+
+ $result = $stmt1->execute();
// fetching only one record out of two
- $stmt1->fetchAssociative();
+ $result->fetchAssociative();
- $stmt2 = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
- $stmt2->execute([1]);
- self::assertEquals(1, $stmt2->fetchOne());
+ $stmt2 = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
+ $result = $stmt2->execute([1]);
+ self::assertEquals(1, $result->fetchOne());
}
- public function testReuseStatementAfterClosingCursor(): void
+ public function testReuseStatementAfterFreeingResult(): void
{
if ($this->connection->getDriver() instanceof PDOOracleDriver) {
self::markTestIncomplete('See https://bugs.php.net/bug.php?id=77181');
@@ -162,14 +162,16 @@ public function testReuseStatementAfterClosingCursor(): void
$stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
- $stmt->execute([1]);
- $id = $stmt->fetchOne();
+ $result = $stmt->execute([1]);
+
+ $id = $result->fetchOne();
self::assertEquals(1, $id);
- $stmt->closeCursor();
+ $result->free();
+
+ $result = $stmt->execute([2]);
- $stmt->execute([2]);
- $id = $stmt->fetchOne();
+ $id = $result->fetchOne();
self::assertEquals(2, $id);
}
@@ -182,12 +184,14 @@ public function testReuseStatementWithParameterBoundByReference(): void
$stmt->bindParam(1, $id);
$id = 1;
- $stmt->execute();
- self::assertEquals(1, $stmt->fetchOne());
+
+ $result = $stmt->execute();
+ self::assertEquals(1, $result->fetchOne());
$id = 2;
- $stmt->execute();
- self::assertEquals(2, $stmt->fetchOne());
+
+ $result = $stmt->execute();
+ self::assertEquals(2, $result->fetchOne());
}
public function testReuseStatementWithReboundValue(): void
@@ -198,12 +202,12 @@ public function testReuseStatementWithReboundValue(): void
$stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
$stmt->bindValue(1, 1);
- $stmt->execute();
- self::assertEquals(1, $stmt->fetchOne());
+ $result = $stmt->execute();
+ self::assertEquals(1, $result->fetchOne());
$stmt->bindValue(1, 2);
- $stmt->execute();
- self::assertEquals(2, $stmt->fetchOne());
+ $result = $stmt->execute();
+ self::assertEquals(2, $result->fetchOne());
}
public function testReuseStatementWithReboundParam(): void
@@ -215,44 +219,13 @@ public function testReuseStatementWithReboundParam(): void
$x = 1;
$stmt->bindParam(1, $x);
- $stmt->execute();
- self::assertEquals(1, $stmt->fetchOne());
+ $result = $stmt->execute();
+ self::assertEquals(1, $result->fetchOne());
$y = 2;
$stmt->bindParam(1, $y);
- $stmt->execute();
- self::assertEquals(2, $stmt->fetchOne());
- }
-
- /**
- * @param mixed $expected
- *
- * @dataProvider emptyFetchProvider
- */
- public function testFetchFromNonExecutedStatement(callable $fetch, $expected): void
- {
- $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
-
- self::assertSame($expected, $fetch($stmt));
- }
-
- public function testCloseCursorOnNonExecutedStatement(): void
- {
- $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
- self::assertTrue($stmt->closeCursor());
- }
-
- /**
- * @group DBAL-2637
- */
- public function testCloseCursorAfterCursorEnd(): void
- {
- $stmt = $this->connection->prepare('SELECT name FROM stmt_test');
-
- $stmt->execute();
- $stmt->fetchAssociative();
-
- self::assertTrue($stmt->closeCursor());
+ $result = $stmt->execute();
+ self::assertEquals(2, $result->fetchOne());
}
/**
@@ -260,28 +233,25 @@ public function testCloseCursorAfterCursorEnd(): void
*
* @dataProvider emptyFetchProvider
*/
- public function testFetchFromNonExecutedStatementWithClosedCursor(callable $fetch, $expected): void
+ public function testFetchFromExecutedStatementWithFreedResult(callable $fetch, $expected): void
{
- $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
- $stmt->closeCursor();
+ $this->connection->insert('stmt_test', ['id' => 1]);
- self::assertSame($expected, $fetch($stmt));
- }
+ $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
+ $result = $stmt->execute();
+ $result->free();
- /**
- * @param mixed $expected
- *
- * @dataProvider emptyFetchProvider
- */
- public function testFetchFromExecutedStatementWithClosedCursor(callable $fetch, $expected): void
- {
- $this->connection->insert('stmt_test', ['id' => 1]);
+ try {
+ $value = $fetch($result);
+ } catch (Throwable $e) {
+ // The drivers that enforce the command sequencing internally will throw an exception
+ $this->expectNotToPerformAssertions();
- $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
- $stmt->execute();
- $stmt->closeCursor();
+ return;
+ }
- self::assertSame($expected, $fetch($stmt));
+ // Other drivers will silently return an empty result
+ self::assertSame($expected, $value);
}
/**
@@ -291,20 +261,20 @@ public static function emptyFetchProvider(): iterable
{
return [
'fetch' => [
- static function (Statement $stmt) {
- return $stmt->fetchAssociative();
+ static function (Result $result) {
+ return $result->fetchAssociative();
},
false,
],
'fetch-column' => [
- static function (Statement $stmt) {
- return $stmt->fetchOne();
+ static function (Result $result) {
+ return $result->fetchOne();
},
false,
],
'fetch-all' => [
- static function (Statement $stmt): array {
- return $stmt->fetchAllAssociative();
+ static function (Result $result): array {
+ return $result->fetchAllAssociative();
},
[],
],
diff --git a/tests/Functional/Ticket/DBAL421Test.php b/tests/Functional/Ticket/DBAL421Test.php
index 9779f016dbe..8ed1bd45021 100644
--- a/tests/Functional/Ticket/DBAL421Test.php
+++ b/tests/Functional/Ticket/DBAL421Test.php
@@ -41,13 +41,10 @@ public function testGuidShouldBeRandom(): void
$guids = [];
for ($i = 0; $i < 99; $i++) {
- $statement->execute();
- $guid = $statement->fetchFirstColumn();
+ $guid = $statement->execute()->fetchFirstColumn();
self::assertNotContains($guid, $guids, 'Duplicate GUID detected');
$guids[] = $guid;
}
-
- $statement->closeCursor();
}
private function getSelectGuidSql(): string
diff --git a/tests/Functional/WriteTest.php b/tests/Functional/WriteTest.php
index 793c5631e67..51b0ec58a6a 100644
--- a/tests/Functional/WriteTest.php
+++ b/tests/Functional/WriteTest.php
@@ -74,24 +74,22 @@ public function testPrepareRowCountReturnsAffectedRows(): void
$stmt->bindValue(1, 1);
$stmt->bindValue(2, 'foo');
- $stmt->execute();
- self::assertEquals(1, $stmt->rowCount());
+ self::assertEquals(1, $stmt->execute()->rowCount());
}
- public function testPrepareWithPdoTypes(): void
+ public function testPrepareWithPrimitiveTypes(): void
{
$sql = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
$stmt = $this->connection->prepare($sql);
$stmt->bindValue(1, 1, ParameterType::INTEGER);
$stmt->bindValue(2, 'foo', ParameterType::STRING);
- $stmt->execute();
- self::assertEquals(1, $stmt->rowCount());
+ self::assertEquals(1, $stmt->execute()->rowCount());
}
- public function testPrepareWithDbalTypes(): void
+ public function testPrepareWithDoctrineMappingTypes(): void
{
$sql = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
$stmt = $this->connection->prepare($sql);
@@ -99,12 +97,11 @@ public function testPrepareWithDbalTypes(): void
self::assertInstanceOf(Statement::class, $stmt);
$stmt->bindValue(1, 1, Type::getType('integer'));
$stmt->bindValue(2, 'foo', Type::getType('string'));
- $stmt->execute();
- self::assertEquals(1, $stmt->rowCount());
+ self::assertEquals(1, $stmt->execute()->rowCount());
}
- public function testPrepareWithDbalTypeNames(): void
+ public function testPrepareWithDoctrineMappingTypeNames(): void
{
$sql = 'INSERT INTO write_table (test_int, test_string) VALUES (?, ?)';
$stmt = $this->connection->prepare($sql);
@@ -112,9 +109,8 @@ public function testPrepareWithDbalTypeNames(): void
self::assertInstanceOf(Statement::class, $stmt);
$stmt->bindValue(1, 1, 'integer');
$stmt->bindValue(2, 'foo', 'string');
- $stmt->execute();
- self::assertEquals(1, $stmt->rowCount());
+ self::assertEquals(1, $stmt->execute()->rowCount());
}
public function insertRows(): void
@@ -178,8 +174,8 @@ public function testLastInsertIdSequence(): void
return strtolower($sequence->getName()) === 'write_table_id_seq';
}));
- $stmt = $this->connection->query($this->connection->getDatabasePlatform()->getSequenceNextValSQL('write_table_id_seq'));
- $nextSequenceVal = $stmt->fetchOne();
+ $result = $this->connection->query($this->connection->getDatabasePlatform()->getSequenceNextValSQL('write_table_id_seq'));
+ $nextSequenceVal = $result->fetchOne();
$lastInsertId = $this->lastInsertId('write_table_id_seq');
diff --git a/tests/Portability/ResultTest.php b/tests/Portability/ResultTest.php
new file mode 100644
index 00000000000..30a3bfb22c4
--- /dev/null
+++ b/tests/Portability/ResultTest.php
@@ -0,0 +1,54 @@
+createMock(DriverResult::class);
+ $driverResult->expects(self::once())
+ ->method('rowCount')
+ ->willReturn(666);
+
+ $result = $this->newResult($driverResult);
+
+ self::assertSame(666, $result->rowCount());
+ }
+
+ public function testColumnCount(): void
+ {
+ $driverResult = $this->createMock(DriverResult::class);
+ $driverResult->expects(self::once())
+ ->method('columnCount')
+ ->willReturn(666);
+
+ $result = $this->newResult($driverResult);
+
+ self::assertSame(666, $result->columnCount());
+ }
+
+ public function testFree(): void
+ {
+ $driverResult = $this->createMock(DriverResult::class);
+ $driverResult->expects(self::once())
+ ->method('free');
+
+ $this->newResult($driverResult)->free();
+ }
+
+ private function newResult(DriverResult $driverResult): Result
+ {
+ return new Result(
+ $driverResult,
+ new Converter(false, false, null)
+ );
+ }
+}
diff --git a/tests/Portability/StatementTest.php b/tests/Portability/StatementTest.php
index c34f2ddf25f..d69074db52d 100644
--- a/tests/Portability/StatementTest.php
+++ b/tests/Portability/StatementTest.php
@@ -57,26 +57,6 @@ public function testBindValue(): void
self::assertTrue($this->stmt->bindValue($param, $value, $type));
}
- public function testCloseCursor(): void
- {
- $this->wrappedStmt->expects(self::once())
- ->method('closeCursor')
- ->will(self::returnValue(true));
-
- self::assertTrue($this->stmt->closeCursor());
- }
-
- public function testColumnCount(): void
- {
- $columnCount = 666;
-
- $this->wrappedStmt->expects(self::once())
- ->method('columnCount')
- ->will(self::returnValue($columnCount));
-
- self::assertSame($columnCount, $this->stmt->columnCount());
- }
-
public function testExecute(): void
{
$params = [
@@ -86,21 +66,9 @@ public function testExecute(): void
$this->wrappedStmt->expects(self::once())
->method('execute')
- ->with($params)
- ->will(self::returnValue(true));
-
- self::assertTrue($this->stmt->execute($params));
- }
-
- public function testRowCount(): void
- {
- $rowCount = 666;
-
- $this->wrappedStmt->expects(self::once())
- ->method('rowCount')
- ->will(self::returnValue($rowCount));
+ ->with($params);
- self::assertSame($rowCount, $this->stmt->rowCount());
+ $this->stmt->execute($params);
}
/**