Problem with PDO DBlib (MSSQL, Sybase, FreeTDS) - nested fetches clear outer ones #5244

Open
opened 2026-01-22 15:02:27 +01:00 by admin · 2 comments
Owner

Originally created by @r0ssIV on GitHub (Sep 7, 2016).

There is a known issue with PDO DBLib nested pdo->prepare()->execute statements (they work differently from for e.x. MySQL). Issue is described here https://bugs.php.net/bug.php?id=65945, and the explanation from PHP team is the following

This is the behavior of MSSQL (TDS), DBLIB and FreeTDS. One statement per connection rule. If you initiate another statement, the previous statement is cancelled.

The previous versions buffered the entire result set in memory leading to OOM errors on large results sets.

The previous behavior can be replicated using fetchAll() and a loop if desired. Another workaround is to open 2 connection objects, one per statement.

For some reason, in latest Doctrine 2.5 this issue fired up. See ObjectHydrator.php\hydrateAllData()

while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
        $this->hydrateRowData($row, $result);
}

Sometimes for OneToOne associations $this->hydrateRowData() can cause nested calls to $this->_stmt->fetch which cause the outer results buffer cleared, so only part of the records are returned (like only the first record).

The solution for this problem is using fetchAll:

$rows =  $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
   $this->hydrateRowData($row, $result);
}

or using separate connections.

Originally created by @r0ssIV on GitHub (Sep 7, 2016). There is a known issue with PDO DBLib nested pdo->prepare()->execute statements (they work differently from for e.x. MySQL). Issue is described here https://bugs.php.net/bug.php?id=65945, and the explanation from PHP team is the following > This is the behavior of MSSQL (TDS), DBLIB and FreeTDS. One statement per connection rule. If you initiate another statement, the previous statement is cancelled. > > The previous versions buffered the entire result set in memory leading to OOM errors on large results sets. > > The previous behavior can be replicated using fetchAll() and a loop if desired. Another workaround is to open 2 connection objects, one per statement. For some reason, in latest Doctrine 2.5 this issue fired up. See ObjectHydrator.php\hydrateAllData() ``` while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { $this->hydrateRowData($row, $result); } ``` Sometimes for OneToOne associations $this->hydrateRowData() can cause nested calls to $this->_stmt->fetch which cause the outer results buffer cleared, so only part of the records are returned (like only the first record). The solution for this problem is using fetchAll: ``` $rows = $this->_stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($rows as $row) { $this->hydrateRowData($row, $result); } ``` or using separate connections.
admin added the BugMissing Tests labels 2026-01-22 15:02:27 +01:00
Author
Owner

@Ocramius commented on GitHub (Sep 7, 2016):

a fetchAll may work there, but a functional test case is to be written to expose the issue, first.

It is still problematic code though, as it denies lazy-hydration, should we switch to generators in future.

@Ocramius commented on GitHub (Sep 7, 2016): a `fetchAll` may work there, but a functional test case is to be written to expose the issue, first. It is still problematic code though, as it denies lazy-hydration, should we switch to generators in future.
Author
Owner

@salimimani commented on GitHub (Feb 21, 2019):

Hello,

In a project I'm working on, we have encountered the exact same issue.

As soon as hydrateAllData() fetches the first row, hydrateRowData() clears the buffered fetch, and proceeds to retrieve additional data with another query.

When we move on to the next iteration of the while loop, all has been cleared and we cannot collect the next rows. $row is set to false.

The workaround we used is the following :
First collect all result rows with fetchAll and store in $rows
Then replace the while loop with a foreach ($rows as $row)

==> I'm about to commit and make a pull request

My team is preparing a simple test case to reproduce the issue easily.
Meanwhile, here is the server config :

  • CentOS 7 (openshift on the PaaS cloud and docker or the local dev machine)
  • PDO_dblib
  • Freetds
  • MSSQL 2016 database
  • php71 with Silex in latest version (we are working on symfony flex migration)
@salimimani commented on GitHub (Feb 21, 2019): Hello, In a project I'm working on, we have encountered the exact same issue. As soon as hydrateAllData() fetches the first row, hydrateRowData() clears the buffered fetch, and proceeds to retrieve additional data with another query. When we move on to the next iteration of the while loop, all has been cleared and we cannot collect the next rows. $row is set to false. The workaround we used is the following : First collect all result rows with fetchAll and store in $rows Then replace the while loop with a foreach ($rows as $row) ==> I'm about to commit and make a pull request My team is preparing a simple test case to reproduce the issue easily. Meanwhile, here is the server config : - CentOS 7 (openshift on the PaaS cloud and docker or the local dev machine) - PDO_dblib - Freetds - MSSQL 2016 database - php71 with Silex in latest version (we are working on symfony flex migration)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5244