From e0421d1592dc6271e2abf6a75c0a89d238e30fad Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sat, 9 May 2020 15:09:30 -0700 Subject: [PATCH 1/4] Update documentation to use the new fetch API --- docs/en/reference/caching.rst | 2 +- .../data-retrieval-and-manipulation.rst | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index 5a2daf0b317..5e6885deae8 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -42,7 +42,7 @@ object is closed: executeCacheQuery($query, $params, $types, new QueryCacheProfile(0, "some key")); - $data = $stmt->fetchAll(); + $data = $stmt->fetchAllAssociative(); $stmt->closeCursor(); // at this point the result is cached .. warning:: diff --git a/docs/en/reference/data-retrieval-and-manipulation.rst b/docs/en/reference/data-retrieval-and-manipulation.rst index 31397a532b9..e1afd8ec322 100644 --- a/docs/en/reference/data-retrieval-and-manipulation.rst +++ b/docs/en/reference/data-retrieval-and-manipulation.rst @@ -41,7 +41,7 @@ the query until there are no more rows: fetch()) { + while (($row = $stmt->fetchAssociative()) !== false) { echo $row['headline']; } @@ -308,7 +308,7 @@ Prepare a given SQL statement and return the prepare('SELECT * FROM user'); $statement->execute(); - $users = $statement->fetchAll(); + $users = $statement->fetchAllAssociative(); /* array( @@ -346,7 +346,7 @@ parameters to the execute method, then returning the statement: executeQuery('SELECT * FROM user WHERE username = ?', array('jwage')); - $user = $statement->fetch(); + $user = $statement->fetchAssociative(); /* array( @@ -360,15 +360,15 @@ to perform necessary type conversions between actual input parameters and expected database values. See the :ref:`Types ` section for more information. -fetchAll() -~~~~~~~~~~ +fetchAllAssociative() +~~~~~~~~~~~~~~~~~~~~~ Execute the query and fetch all results into an array: .. code-block:: php fetchAll('SELECT * FROM user'); + $users = $conn->fetchAllAssociative('SELECT * FROM user'); /* array( @@ -379,15 +379,15 @@ Execute the query and fetch all results into an array: ) */ -fetchArray() -~~~~~~~~~~~~ +fetchNumeric() +~~~~~~~~~~~~~~ Numeric index retrieval of first result row of the given query: .. code-block:: php fetchArray('SELECT * FROM user WHERE username = ?', array('jwage')); + $user = $conn->fetchNumeric('SELECT * FROM user WHERE username = ?', array('jwage')); /* array( @@ -396,26 +396,26 @@ Numeric index retrieval of first result row of the given query: ) */ -fetchColumn() -~~~~~~~~~~~~~ +fetchOne() +~~~~~~~~~~ -Retrieve only the given column of the first result row. +Retrieve only the value of the first column of the first result row. .. code-block:: php fetchColumn('SELECT username FROM user WHERE id = ?', array(1), 0); + $username = $conn->fetchOne('SELECT username FROM user WHERE id = ?', array(1), 0); echo $username; // jwage -fetchAssoc() -~~~~~~~~~~~~ +fetchAssociative() +~~~~~~~~~~~~~~~~~~ -Retrieve assoc row of the first result row. +Retrieve associative array of the first result row. .. code-block:: php fetchAssoc('SELECT * FROM user WHERE username = ?', array('jwage')); + $user = $conn->fetchAssociative('SELECT * FROM user WHERE username = ?', array('jwage')); /* array( 'username' => 'jwage', From 4af4999b4ab57362c19cdfd2740d8470a4576471 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sat, 9 May 2020 15:09:30 -0700 Subject: [PATCH 2/4] ResultCacheStatement::fetchAllNumeric() should return numeric arrays --- .../DBAL/Cache/ResultCacheStatement.php | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 1a4382dd178..28a7fa8162d 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -13,6 +13,7 @@ use InvalidArgumentException; use IteratorAggregate; use PDO; +use function array_map; use function array_merge; use function array_values; use function assert; @@ -55,7 +56,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement, Forwar */ private $emptied = false; - /** @var mixed[] */ + /** @var array> */ private $data; /** @var int */ @@ -247,7 +248,9 @@ public function fetchAllNumeric() : array $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); } - return $this->store($data); + $this->store($data); + + return array_map('array_values', $this->data); } /** @@ -261,7 +264,9 @@ public function fetchAllAssociative() : array $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); } - return $this->store($data); + $this->store($data); + + return $this->data; } /** @@ -311,19 +316,11 @@ private function doFetch() } /** - * @param array> $data - * - * @return array> + * @param array> $data */ - private function store(array $data) : array + private function store(array $data) : void { - foreach ($data as $key => $value) { - $data[$key] = [$value]; - } - $this->data = $data; $this->emptied = true; - - return $this->data; } } From 3eb6f0a2cbc0f44fa5ca2a96ddfd6e4cdf5b0716 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Wed, 27 May 2020 09:24:49 -0700 Subject: [PATCH 3/4] Refactored ResultCacheTest for better coverage --- .../Tests/DBAL/Functional/ResultCacheTest.php | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php index 6d03a736e13..3786ccc2955 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php @@ -102,13 +102,13 @@ public function testMixingFetch() : void $numExpectedResult[] = array_values($v); } - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $data = $this->hydrateStmt($stmt, FetchMode::ASSOCIATIVE); self::assertEquals($this->expectedResult, $data); - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $data = $this->hydrateStmt($stmt, FetchMode::NUMERIC); @@ -124,10 +124,10 @@ public function testIteratorFetch() : void private function assertStandardAndIteratorFetchAreEqual(int $fetchMode) : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $data = $this->hydrateStmt($stmt, $fetchMode); - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $dataIterator = $this->hydrateStmtIterator($stmt, $fetchMode); self::assertEquals($data, $dataIterator); @@ -135,7 +135,7 @@ private function assertStandardAndIteratorFetchAreEqual(int $fetchMode) : void public function testDontCloseNoCache() : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $data = []; @@ -143,7 +143,7 @@ public function testDontCloseNoCache() : void $data[] = $row; } - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $data = []; @@ -156,12 +156,12 @@ public function testDontCloseNoCache() : void public function testDontFinishNoCache() : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $stmt->fetch(FetchMode::ASSOCIATIVE); $stmt->closeCursor(); - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); $this->hydrateStmt($stmt, FetchMode::NUMERIC); @@ -171,7 +171,7 @@ public function testDontFinishNoCache() : void public function testFetchAllAndFinishSavesCache() : void { $layerCache = new ArrayCache(); - $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'testcachekey', $layerCache)); + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'testcachekey', $layerCache)); $stmt->fetchAll(); $stmt->closeCursor(); @@ -199,13 +199,13 @@ public function testFetchAllColumn() : void */ private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedResult, int $fetchMode) : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); self::assertEquals(2, $stmt->columnCount()); $data = $this->hydrateStmt($stmt, $fetchMode); self::assertEquals($expectedResult, $data); - $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); + $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(0, 'testcachekey')); self::assertEquals(2, $stmt->columnCount()); $data = $this->hydrateStmt($stmt, $fetchMode); @@ -215,23 +215,24 @@ private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedR public function testEmptyResultCache() : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey')); - $data = $this->hydrateStmt($stmt); + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey')); + $this->hydrateStmt($stmt); - $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey')); - $data = $this->hydrateStmt($stmt); + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey')); + $this->hydrateStmt($stmt); self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit'); } public function testChangeCacheImpl() : void { - $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey')); - $data = $this->hydrateStmt($stmt); + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey')); + $this->hydrateStmt($stmt); $secondCache = new ArrayCache(); - $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache)); - $data = $this->hydrateStmt($stmt); + + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(0, 'emptycachekey', $secondCache)); + $this->hydrateStmt($stmt); self::assertCount(2, $this->sqlLogger->queries, 'two hits'); self::assertCount(1, $secondCache->fetch('emptycachekey')); @@ -243,7 +244,8 @@ public function testChangeCacheImpl() : void private function hydrateStmt(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE) : array { $data = []; - while ($row = $stmt->fetch($fetchMode)) { + + foreach ($stmt->fetchAll($fetchMode) as $row) { $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row; } From e74b97c24bbe18651fbed967e2af29deeacdd13e Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Wed, 27 May 2020 15:57:59 -0700 Subject: [PATCH 4/4] ResultCacheStatement should always fetch in associative mode This will allow for subsequent fetches in different modes. --- .../DBAL/Cache/ResultCacheStatement.php | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 28a7fa8162d..5cbb59e337c 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -180,18 +180,26 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { - $data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); - - if ($fetchMode === FetchMode::COLUMN) { - foreach ($data as $key => $value) { - $data[$key] = [$value]; - } - } + $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE, $fetchArgument, $ctorArgs); $this->data = $data; $this->emptied = true; - return $this->data; + if ($fetchMode === FetchMode::NUMERIC) { + foreach ($data as $i => $row) { + $data[$i] = array_values($row); + } + } elseif ($fetchMode === FetchMode::MIXED) { + foreach ($data as $i => $row) { + $data[$i] = array_merge($row, array_values($row)); + } + } elseif ($fetchMode === FetchMode::COLUMN) { + foreach ($data as $i => $row) { + $data[$i] = reset($row); + } + } + + return $data; } /**