How can I buffer an unbuffered ResultSet in ZF2 ze

2019-09-11 20:36发布

问题:

My Atlas class performs a recursive data exploration given a non-contiguous but sequential set of col1 values, read from MySQL table1 beginning with $startingVal. Limit $totalVal is a throttle for work units during any given session. On each recursion, Atlas will retrieve between zero and eight (0-8) rows from a different table.

Fatal error: Uncaught exception 'Zend\Db\Adapter\Exception\RuntimeException'
with message 'Row count is not available in unbuffered result sets.'
in /path/vendor/Zend/zend-db/src/Adapter/Driver/Mysqli/Result.php:316
Stack trace:
#0 /path/vendor/Zend/zend-db/src/ResultSet/AbstractResultSet.php(65):
Zend\Db\Adapter\Driver\Mysqli\Result->count()
#1 /path/vendor/Zend/zend-db/src/Adapter/Adapter.php(195):
Zend\Db\ResultSet\AbstractResultSet->
initialize(Object(Zend\Db\Adapter\Driver\Mysqli\Result))
#2 /path/modules/Flightplan/src/Flightplan/Atlas.php(110): Zend\Db\Adapter\Adapter->query('SELECT col1...', Array)
#3 /path/utility.php(46):
Flightplan\Atlas->shoulder_routes('30000001', '1')
#4 {main} thrown in /path/vendor/Zend/zend-db/src/Adapter/Driver/Mysqli/Result.php
on line 316

If I am reading the error and Zend source correctly, Adapter is initializing an AbstractResultSet (buffering by default) with a Mysqli\Result resource (non buffering by default), and immediately after, tries to count it, which throws the error. This is happening the during my first query through Adapter, before recursion.

AbstractResultSet.php

public function initialize($dataSource)
{
    // reset buffering
    if (is_array($this->buffer)) {
        $this->buffer = array();
    }

    if ($dataSource instanceof ResultInterface) {
        $this->count = $dataSource->count();         // line 65

Atlas.php:

use Zend\Db\Adapter\Adapter;

public function __construct( array $configArray = null ){
    ...
    $this->configArray = $configArray;
    $this->adapter = new Adapter( $this->configArray );
}
public function shoulder_routes( $startingVal, $totalVal ){
    ...
    $result = $this->adapter->query( 'SELECT col1 FROM table1 
        WHERE col1 >= ? LIMIT ?', array( $startingVal, $totalVal ));  // line 110
    $result->buffer(); // ADDED AFTER 1st error occurrence 
    ...
}

I added $result->buffer(); which is unfortunately useless since the error occurs before execution returns to Atlas.

How can I rectify this?

  1. Mysqli\Result CAN be set to buffer at initialization. Do I extend Result with buffering on by default, and would I specify that in Atlas, like:

$this->adapter = new Adapter( $this->configArray,null,new MyMysqliResult() );

Edit: Supplying a ResultInterface as a parameter to Adapter threw an error, as Adapter is expecting a ResultSet. However, it may be possible to extend AbstractResultSet so that IT calls $result->buffer() before calling $result->count() on the ResultSet passed to it from Adaper\Driver\Mysqli...

  1. (I think) I read that Zend\Adapter PDO_Mysql driver/platform/result DOES buffer by default. Is there any reason not to switch to PDO?

Edit: Switching to PDO_Mysql Adapter\Driver resulted in following error, which I imagine may be resolved if I prepare a driver-supplied statement

Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' at line 1' in /path/vendor/Zend/zend-db/src/Adapter/Driver/Pdo/Statement.php:239

  1. I have also seen in this answer where result->getResource()->fetchAll(); may help later in Atlas but again the error is occurring before I get this chance.

What am I missing? Thanks -

回答1:

After coming and going from this problem and project for a few months, I stumbled upon an answer here which lead me to the same answer here.

    $config => array(
       ...
      'options' => array(
        'buffer_results' => true,
    ),

I was looking for this kind of answer, and obviously stared too hard.