Home > Tipps & Tricks > Datenbanken, Schnittstellen > mysqli: Commands out of sync; you can't run this command now

mysqli: Commands out of sync; you can't run this command now

Geschrieben von   admin on    Februar 21, 2010

Es scheint einige Konfusion im Zusammenhang mit der PHP-Datenbankschnittstelle php_mysqli zu geben, was den Umgang mit Prepared Statements und Queries angeht. Speziell, wenn alle Operationen auf einer einzigen Connection ausgeführt werden sollen. Wenn man in der Suchmaschine seiner Wahl nach der Zeichenkette "Commands out of sync; you can't run this command now" und mysqli sucht, dann bekommt man eine fast unüberschaubare Anzahl von Fundstellen geliefert. Darunter sind sogar einige von Live-Sites. Ähem, da hat wohl jemand auf dem Produktivsystem mal wieder die ini-Direktive display_errors auf On stehen lassen. Bitte unbedingt abstellen! Für Hacker, die nichts Gutes im Schilde führen, ist sowas ein gefundenes Fressen!

Ungewöhnlicher Workflow im Zusammenhang mit Prepared Statements

Wie dem auch sei: Viele Entwickler stehen vor dem Problem, dass mysqli es angeblich nicht erlaubt, mehr als ein datenabrufendes Prepared Statement mit einer Datenbank-Connection zu verwenden. Hin und wieder wird als Lösung vorgeschlagen, jeweils eine eigene Connection zu verwenden. Oder die Connection zu schließen und für das nachfolgende Statement wieder zu öffnen.

Das kann natürlich so nicht stimmen:

  1. Eine moderne Datenbankschnittstelle - das i bei mysqli steht schließlich für improved - wäre keine solche, wenn man nicht mehrere, auch lesende, Statements über eine Connection absetzen könnte.
  2. Wenn zwei Connections verwendet werden, dann gibt es ein Problem, wenn man mit Transaktionen arbeitet. Gerade, wenn man auf in der Transaktion geänderte Daten zugreifen muss.
  3. Bei parallel geöffneten Connections mit gleicher Datenbank-Instanz können wesentlich früher Probleme mit Connection-Limits auftauchen. Beispielsweise wenn die Website gut besucht ist.
  4. Schließen und anschließendes Neuöffnen einer Connection ist Ressourcenverschwendung.

Was ich etwas verblüffend finde, ist, dass das PHP-Manual auf den im Folgenden beschriebenen Stolperstein nur unzureichend hinweist. Und auch in der MySQL-Dokumentation kommt das Thema zu kurz. In Anhang B.5.2.14 des MySQL Manuals der Version 5.0 gibt es zwar eine kurze Beschreibung - für diejenigen, die sie finden - aber sie ist reichlich kurz. Und sie geht auf das Hauptproblem überhaupt nicht ein. Nämlich, dass mysqli ein weiteres, leeres Resultset mit zurückliefert, in dem der Status (OK, ERR) der Operation transportiert wird. Dieses Resultset muss abgerufen werden, damit es nachfolgend nicht zu dem angesprochenen Fehler kommt.

Was nun?

Hilfreich ist ein Nutzerkommentar unterhalb der Beschreibung von mysqli_stmt_execute(): Nach dem Holen der Daten "überliest" man einfach weitere Resultsets:

while(mysqli_more_results($conn)) mysqli_next_result($conn);


Gut, vorausgesetzt, es gibt nur ein Resultset, was einen interessiert. Was allerdings der Standardfall sein dürfte.

Das Manual sagt auch: "When using mysqli_stmt_execute(), the mysqli_stmt_fetch() function must be used to fetch the data prior to performing any additional queries." Auch das kann man so nicht stehen lassen. Wie in einem weiteren Nutzerkommentar angegeben, führt mysqli_stmt_execute() intern nicht mysqli_stmt_store_result() aus. Wenn man nun aber mysqli_stmt_store_result() direkt nach ..._execute() absetzt, so wird die gesamte Datenmenge zum Client - also in Richtung PHP-Skript - abgerufen. Was natürlich nicht in allen Situationen angesagt ist. Aber in vielen. Und dann klappt es auch mit dem nachfolgenden Statement.

Fallstrick bei mysqli_query()

Wie im PHP Manual angegeben, ist mysqli_query() funktionell identisch mit einem Aufruf von mysqli_real_query() gefolgt von entweder mysqli_store_result() oder mysqli_use_result(). Unterhalb der Beispiele steht wie üblich die Ausgabe derselben. Ich weiß jetzt nicht, ob es ein Witz sein soll, oder unfreiwillig komisch ist. Die im Beispiel präsentierte Ausgabe präsentiert jedenfalls - völlig korrekt -

... Error: Commands out of sync;  You can't run this command now


Hier ist die Feinheit, die aber diesmal tatsächlich auch in der Beschreibung aufgeführt wird (sieh an, geht ja doch...), dass mysqli_query() einen zusätzlichen Parameter $resultmode hat, der per Default auf MYSQLI_STORE_RESULT gesetzt wird. Ich bin mir nicht ganz sicher, ob dies schon immer so war, denn ich kann mich erinneren, bei frühen Experimenten auf Probleme gestossen zu sein, die nahelegen, dass nicht von Anfang an ein internes automatisches mysql_store_result() ausgeführt wird (hier: sofern man den Parameter nicht anders belegt). Nun gut, man sollte nur wissen, dass - wie beschrieben - ein Setzen auf MYSQLI_USE_RESULT von einem mysqli_free_result() gefolgt sein muss, damit nachfolgende Aufrufe (auf der gleichen Connection) nicht zu der aufgeführten Fehlermeldung führen.

Was nicht nur mich hier stören dürfte, ist die Tatsache, das mysqli, sprich "myslq improved" nicht auch von einer "improved" Beschreibung begleitet wird. Ich will gar nicht wissen, wieviele PHP-Programmierer stundenlang im Internet nach Informationen gesucht haben, um "rätselhaften" Phänomenen dieser Art auf die Schliche zu kommen.

Übrigens: Es ist in der Tat nicht so, dass mysqli_query nicht im Zusammenhang mit Stored Procedures (Gespeicherten Prozeduren) verwendet werden könnte. Auch wenn man es immer wieder lesen kann. Wen es interessiert: Dieser externe Blogbeitrag geht im unteren Abschnitt darauf ein.

Fazit

Schön ist, dass mit mysqli ein "aufgebohrter" Nachfolger der traditionellen mysql-Schnittstelle vorliegt. Schlecht ist, dass diese Schnittstelle etwas schrullig geraten ist. Auch wenn ich in meinem Arbeitsleben mit mehr Datenbank-Schnittstellen zu tun hatte, als mir lieb ist: mysqli gehört zum Schrulligsten, was mir in diesem Bereich jemals untergekommen ist. Ein Beleg dafür ist, dass selbst pdo_mysql diese "Schrulligkeit" nicht abfängt. Siehe diese externe Seite im unteren Bereich.

Kommentare

Geschrieben von Jürgen am
Danke für den Tip mit dem Einzeiler.
Habe auch eine stored procedure, die ich sonst nicht in einer Schleife zum Laufen bekommen würde.
Geschrieben von admin am
Schön, dass ich Ihnen helfen konnte. Insgesamt überrascht mich immer wieder, wieviele Leute auf das gleiche Problem stoßen, obwohl dieser Tipp schon ein paar Jahre alt ist. Laut den Webmaster Tools von Google ist dieser Tipp nämlich die meistbesuchte Seite auf kumarisoft.de. Ich verstehe wirklich nicht, warum dieses fundamentale Problem bei php.net nicht besser dokumentiert ist.
Kommentar schreiben



(Ihre E-Mail-Adresse wird nicht angezeigt.)


Ungültiger Sicherheitscode

Bitte klicken Sie das Bild an, um einen neuen Sicherheitscode zu laden.