View Issue Details

IDProjectCategoryView StatusLast Update
0000193Adventure PHP FrameworkBugpublic2015-10-12 12:20
ReporterScreezeAssigned Todingsda 
PriorityurgentSeveritycrashReproducibilityalways
Status closedResolutionfixed 
Product Version2.0 
Target Version2.1Fixed in Version2.1 
Summary0000193: MySQLiHandler: Inkompatibel mit mehreren Stored Procedures
DescriptionDer MySQLiHandler hat einen Fehler, welcher es verhindert mehrere Stored Procedures auszuführen, die ein Ergebnis zurückgeben.

Der 1. Aufruf funktioniert noch, aber sobald ein 2. Aufruf gemacht wird, stürzt der mysqli treiber mit folgendem Fehler ab:
Packets out of order. Expected 1 received 7. Packet size=7 (Number: 2, File: E:\Entwicklung\xx\yy\APF\core\database\MySQLiHandler.php, Line: 336)


Das Problem ist, dass bei SPROCS mit Result noch einige weitere Befehle notwendig sind, wie hier im Beispiel 5 beschrieben:
http://www.php.net/manual/en/mysqli.quickstart.stored-procedures.php#example-1712

Im Detail fehlt um das abholen der Ergebnisse eine do { ... } while ($this->dbConn->more_results() && $this->dbConn->next_result());

Ich bin mir nicht 100%ig sicher ob das kompatibel zu normalen SELECT-Queries ist, Tests stehen noch aus. Daher wäre mein *vorläufiger* Fix-Vorschlag, folgenden Code für die fetchBindResult() Methode zu verwenden (es werden auch gleich noch 3 Array-Initialisierungen vorgenommen, die bisher fehlen):

private function fetchBindResult(&$query) {
      $metaData = $query->result_metadata();

      // in case the meta data is not present (e.g. for INSERT statements),
      // we cannot fetch any data. thus we return null to indicate no result
      if ($metaData === false) {
         return null;
      }
      
      $resultRow = array();
      $resultParams = array();
      while ($field = $metaData->fetch_field()) {
         $resultParams[] = &$resultRow[$field->name];
      }
      
      $bindResult = array();
      do {
        call_user_func_array(array(&$query, 'bind_result'), $resultParams);
        while ($query->fetch()) {
           $currentRow = array();
           foreach ($resultRow as $key => $val) {
              $currentRow[$key] = $val;
           }
           $bindResult[] = $currentRow;
        }
      } while ($this->dbConn->more_results() && $this->dbConn->next_result()); // for sprocs
      
      return $bindResult;
   }
Steps To Reproduce$user = $this->getDBConnector()->getUser('228Y097');
        var_dump($user);
        
        $deviceData = $this->getDBConnector()->createDevice('228Y097', 'DUMMY', 0);
        var_dump($deviceData);


---
public function createDevice($userIdent, $yy, $zz) {
        $result = $this->db->executeTextBindStatement('CALL insertDevice(?,?,?)', array($userIdent, $yy, $zz));
        return $result[0];
    }

public function getUser($userIdent) {
        $result = $this->db->executeTextBindStatement('CALL selectUserByIdent(?)', array($userIdent));
        if(count($result) === 0) {
            return null;
        }
        $result[0]['xx'] = base64_encode($result[0]['xx']);
        return $result[0];
    }
Tagsdatabase
Codereferenz: ([Datei]:[Zeile])
Namespacecore

Activities

Screeze

2014-05-15 09:12

developer   ~0000340

Ok, die Änderung scheint kompatibel zu sein mit normalen SELECTS, folgender Code funktioniert mit den Änderungen:

$user = $this->getDBConnector()->getUser('228Y097');
        var_dump($user);
        echo PHP_EOL;
        echo PHP_EOL;
        
        $deviceData = $this->getDBConnector()->createDevice('228Y097', 'DUMMY', 0);
        var_dump($deviceData);
        echo PHP_EOL;
        echo PHP_EOL;
        
        /* @var $mysqliHandler MySQLiHandler */
        $mysqliHandler = Singleton::getInstance('APF\core\database\ConnectionManager')->getConnection(DBConnector::DB_IDENT);
        
        $users = $mysqliHandler->executeTextBindStatement('SELECT * FROM user WHERE 1=?', array(1));
        var_dump($users);
        echo PHP_EOL;
        echo PHP_EOL;
        
        $devices = $mysqliHandler->executeTextBindStatement('SELECT * FROM device WHERE 1=?', array(1));
        var_dump($devices);
        echo PHP_EOL;
        echo PHP_EOL;
        
        $user1 = $this->getDBConnector()->getUser('228Y097');
        var_dump($user1);
        echo PHP_EOL;
        echo PHP_EOL;
        
        $deviceData2 = $this->getDBConnector()->createDevice('228Y097', 'DUMMxxxY', 1);
        var_dump($deviceData2);
        echo PHP_EOL;
        echo PHP_EOL;
----

Kann da jemand nochmal drüberschauen und das ins GIT einchecken?

dingsda

2014-05-16 08:31

developer   ~0000341

Last edited: 2014-05-16 09:07

View 4 revisions

gut, dass du das thema angesprochen hast. hätte sonst auch vergessen auch stored-procedures-support in das refactoring der datenbanktreiber einzubauen.

da man dort die selben befehle zum fetchen nutzen muss wie multiquery dürften im moment auch bei executeTextStatement die befehle, die der mysqliHandler anbieten nicht ausreichen um alle resultsets abzuholen.
die nicht abgeholten resultsets führen dann zu der fehlermeldung, weil sie noch ungebuffered vorliegen.

die kompatibilität mit normalen select-statements dürfte wirklich kein problem sein, da bei einem normalen select more_results einfach false zurückliefern würde und daher die schleife nur einmal durchlaufern wird.

ich werd mir das aber am WE nochmal genauer anschauen.

dingsda

2014-05-16 22:22

developer   ~0000344

Last edited: 2014-05-16 23:44

View 2 revisions

habs mir nochmal angeschaut.

das funktioniert nur, solange die procedur nur ein resultset zurückgibt oder alle resultsets den selben aufbau haben.

diese procedur funktioniert z.b. nicht

CREATE PROCEDURE (IN param1 INT, IN param2 INT)
    BEGIN
     SELECT param1 as blub;
     SELECT param2 as bla;
     SELECT param1 as blub, param2 as bla;
    END;

so funktionierts:

   private function fetchBindResult(\mysqli_stmt $query) {

      $result = null;

      do {

         $metaData = $query->result_metadata();

         // in case the meta data is not present (e.g. for INSERT statements),
         // we cannot fetch any data. thus we return null to indicate no result
         if ($metaData === false) {
            break;
         }

         $resultRow = array();
         $resultParams = array();
         while ($field = $metaData->fetch_field()) {
            $resultParams[] = & $resultRow[$field->name];
         }

         $bindResult = array();

         call_user_func_array(array(&$query, 'bind_result'), $resultParams);
         while ($query->fetch()) {
            $currentRow = array();
            foreach ($resultRow as $key => $val) {
               $currentRow[$key] = $val;
            }
            $bindResult[] = $currentRow;
         }
         $result[] = $bindResult;
      } while ($query->more_results() && $query->next_result()); // for sprocs

      return (count($result) === 1) ? $result[0] : $result;
   }

rückgabe ungefähr

array (size=3)
  0 =>
    array (size=1)
      0 =>
        array (size=1)
          'blub' => int 1
  1 =>
    array (size=1)
      0 =>
        array (size=1)
          'bla' => int 2
  2 =>
    array (size=1)
      0 =>
        array (size=2)
          'blub' => int 1
          'bla' => int 2

bei normalen statements ist die rückgabe wie bisher ein 2 dimensionales array. proceduren mit nur einem resultset auch.

passt das so auch bei dir?

hab mal ein pull-request bei github gemacht

ChristianAchatz

2014-05-17 09:05

administrator   ~0000345

Danke euch beiden! Habe die Inhalte in den master-Branch gemerged.

Issue History

Date Modified Username Field Change
2014-05-15 09:05 Screeze New Issue
2014-05-15 09:06 Screeze Description Updated View Revisions
2014-05-15 09:06 Screeze Description Updated View Revisions
2014-05-15 09:07 Screeze Tag Attached: database
2014-05-15 09:12 Screeze Note Added: 0000340
2014-05-16 08:31 dingsda Note Added: 0000341
2014-05-16 08:50 dingsda Note Edited: 0000341 View Revisions
2014-05-16 08:56 dingsda Note Edited: 0000341 View Revisions
2014-05-16 09:07 dingsda Note Edited: 0000341 View Revisions
2014-05-16 22:22 dingsda Note Added: 0000344
2014-05-16 23:44 dingsda Note Edited: 0000344 View Revisions
2014-05-17 09:05 ChristianAchatz Note Added: 0000345
2014-05-17 09:06 ChristianAchatz Status new => resolved
2014-05-17 09:06 ChristianAchatz Fixed in Version => 2.1
2014-05-17 09:06 ChristianAchatz Resolution open => fixed
2014-05-17 09:06 ChristianAchatz Assigned To => dingsda
2015-10-12 12:20 ChristianAchatz Status resolved => closed