Was will mir diese PDO-Meldung sagen? "SQLSTATE[HY000]: General error"
Felix Riesterer
- datenbank
- php
- programmiertechnik
Liebe Mitlesende,
ich habe mir für ein Projekt eine Klasse gebaut, die PHPs PDO-Klasse nutzt. Nun möchte ich ein SQL-UPDATE an einer Tabelle durchführen, und erhalte dabei diese ominöse Fehlermeldung:
SQLSTATE[HY000]: General error
Was will mir wer (PHP, PDO oder MySQL?) damit sagen?
Mein PHP-Code, der die Anfrage stellt:
class MyDB {
...
public function __construct ($settings) {
$t = $this;
$t->errors = array();
try {
$t->pdo = new \PDO(
sprintf(
'mysql:dbname=%1$s;host=%2$s;charset=UTF8;',
$settings['db-name'],
$settings['db-host']
),
$settings['db-user'],
$settings['db-pw'],
array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
)
);
} catch (\PDOException $e) {
$t->errors[] = $t->get_error($e);
}
}
/**
* wrapper for \PDO::prepare, \PDOStatement::execute and \PDOStatement::fetchAll
*
* This function passes the parameters through to the PDO object.
*
* @param string SQL
* @param array parameters list
* @param int column number for \PDO::FETCH_COLUMN fetch mode
* @return array result sets
*/
public function get ($sql = '', $params = array(), $col = null) {
$t = $this;
$r = array();
if (!empty($sql)) {
$st = $t->pdo->prepare($sql);
$st->execute($params); // getestet: ergibt in allen Fällen true
try {
$r = (
!is_null($col) && is_numeric($col)
? $st->fetchAll(\PDO::FETCH_COLUMN, $col)
: $st->fetchAll(\PDO::FETCH_ASSOC)
);
} catch (\PDOException $ex) {
$t->errors[] = $t->get_error(
$ex,
array('sql' => $sql, 'params' => $params)
);
}
}
return $r;
}
}
Da ich sowohl den SQL-Code mit Platzhaltern, als auch die Parameter logge, hier meine Debug-Ausgabe, was ich an Daten füttern wollte:
UPDATE `people` SET
`call`=:call,
`first_name`=:first_name,
`family_name`=:family_name,
`email`=:email,
`sex`=:sex,
`titles`=:titles
WHERE `first_name`=:first_name AND `family_name`=:family_name
Die Platzhalter wurden mit diesem Array befüllt:
[params] => Array
(
[:call] => Herr
[:first_name] => Fritzchen
[:family_name] => Müller
[:email] => fritzchenmueller@example.org
[:sex] => m
[:titles] => Prof. Dr.
)
Mit ist völlig unklar, wo hier ein Fehler liegen könnte. Anscheinend wird mein SQL-Code korrekt erstellt (sehe keinen Syntax-Fehler darin) und mit der korrekten Anzahl an passenden Ersetzungsparametern an MyDB::get
übertragen.
Wer weiß Rat?
Liebe Grüße,
Felix Riesterer.
Tach!
Was will mir wer (PHP, PDO oder MySQL?) damit sagen?
Vielleicht, dass es beim Update nichts zu fetchen gibt.
Da muss doch auch eine Zeile angezeigt werden, für die der Fehler ausgelöst wird. Diese Information hast du unterschlagen.
dedlfix.
Lieber dedlfix,
Vielleicht, dass es beim Update nichts zu fetchen gibt.
soll das heißen, dass ich in meiner MyDB-Klasse den SQL-Code nach UPDATE
sniffen sollte, um statt fetchAll nur fetch aufzurufen? Dass ich am besten gleich so lange fetche, bis ich auch bei SELECT
alle betroffenen Zeilen ermittelt habe?
Zu was würdest Du mir da raten?
Ist schon einige Jahre her, dass ich was mit SQL gemacht habe. Damals hatte ich noch die alten mysql_*-Funktionen verwendet. Das Kapseln mit PDO ist mir noch sehr neu.
Jedenfalls vielen herzlichen Dank für Deine sehr hilfreiche Antwort!
Liebe Grüße,
Felix Riesterer.
Tach!
Vielleicht, dass es beim Update nichts zu fetchen gibt.
soll das heißen, dass ich in meiner MyDB-Klasse den SQL-Code nach
UPDATE
sniffen sollte, um statt fetchAll nur fetch aufzurufen? Dass ich am besten gleich so lange fetche, bis ich auch beiSELECT
alle betroffenen Zeilen ermittelt habe?Zu was würdest Du mir da raten?
Es gibt noch mehr Statements, die keine Ergebnismenge produzieren. Anderswo werden die Statement-Abschick-Funktionen unterschieden (sprich: es gibt zwei Funktionen), ob du ein Ergebnis haben möchtest oder nicht. Demzufolge gibt es ein befragbares Resultset zurück oder nur eine Statusinformation (affected Rows zum Beispiel).
Ist schon einige Jahre her, dass ich was mit SQL gemacht habe. Damals hatte ich noch die alten mysql_*-Funktionen verwendet. Das Kapseln mit PDO ist mir noch sehr neu.
Auch da kannst du nicht auf keine Ergebnismenge fetchen, weil in dem Fall das Ergebnis von mysql_query() nur ein Integer und keine Resource ist.
dedlfix.
Hallo Felix,
ich habe mir für ein Projekt eine Klasse gebaut, die PHPs PDO-Klasse nutzt. Nun möchte ich ein SQL-UPDATE an einer Tabelle durchführen, und erhalte dabei diese ominöse Fehlermeldung:
SQLSTATE[HY000]: General error
Was will mir wer (PHP, PDO oder MySQL?) damit sagen?
Du versuchst, wenn ich deinen Code richtig interpretiere (du hast ja nicht alles gepostet), ein fetchAll
auf ein UPDATE
-Statement. Das geht, wenn du keine RETURNING
-Klausel hast, schief, denn es gibt kein Resultset.
LG,
CK
Tach!
Das geht, wenn du keine
RETURNING
-Klausel hast, schief, denn es gibt kein Resultset.
MySQL kennt keine RETURNING-Klausel oder ein Äquivalent dazu.
dedlfix.
Hallo dedlfix,
Das geht, wenn du keine
RETURNING
-Klausel hast, schief, denn es gibt kein Resultset.MySQL kennt keine RETURNING-Klausel oder ein Äquivalent dazu.
Das ist mir bewusst, aber für andere DBMS gilt das nicht. Deshalb war der Einschub notwendig.
LG,
CK
Lieber Christian,
Du versuchst, wenn ich deinen Code richtig interpretiere (du hast ja nicht alles gepostet), ein
fetchAll
auf einUPDATE
-Statement. Das geht, wenn du keineRETURNING
-Klausel hast, schief, denn es gibt kein Resultset.
also stimmt meine Logik in meinem Code noch nicht ganz. Empfiehlst Du mir, dass ich in den SQL-Code hineinsniffe, um UPDATE
zu erkennen und dann kein Resultset holen zu wollen?
Beträfe das noch mehr, als nur UPDATE
? Gibt es sowieso nur bei SELECT
ein Resultset? Dann könnte ich ja darauf prüfen, ob am Anfang meines SQL-Code ein SELECT
steht, um nur dann fetchAll
aufzurufen.
Was meinst Du dazu?
Liebe Grüße,
Felix Riesterer.
Hallo Felix,
also stimmt meine Logik in meinem Code noch nicht ganz. Empfiehlst Du mir, dass ich in den SQL-Code hineinsniffe, um
UPDATE
zu erkennen und dann kein Resultset holen zu wollen?
Nein, ich empfehle zwei verschiedene Varianten zu verwenden, etwa exec
und get
.
Beträfe das noch mehr, als nur
UPDATE
?
Ja: REPLACE INTO
, INSERT
, DELETE
, …
Gibt es sowieso nur bei
SELECT
ein Resultset?
Funktionen können auch ein Resultset haben. Aber im wesentlichen betrifft es SELECT
, ja.
Was meinst Du dazu?
Ich würde davon abraten, das erscheint mir error prone. Etwa im Falle von INSERT … SELECT
. Verwende einfach zwei Methoden, exec
und get
oder so ;-)
LG,
CK
Lieber Christian,
Verwende einfach zwei Methoden,
exec
undget
oder so ;-)
OK. Habe nun eine Methode send
ergänzt:
/**
* function to send a query to the DB
*
* This function passes the parameters through to the PDO object.
*
* @param string SQL
* @param array parameters list
*/
public function send ($sql = '', $params = array()) {
$t = $this;
if (!empty($sql)) {
if (empty($params)) {
try {
$t->pdo->query($sql);
} catch (\PDOException $ex) {
$t->errors[] = $t->get_error(
$ex,
array('sql' => $sql, 'params' => $params)
);
}
} else {
try {
$st = $t->pdo->prepare($sql);
$st->execute($params);
} catch (\PDOException $ex) {
$t->errors[] = $t->get_error(
$ex,
array('sql' => $sql, 'params' => $params)
);
}
}
}
}
Was meinst Du dazu? Geht das auch eleganter? Mir war es wichtig, die Parameter-Substitution zu erhalten. Dann kann ich mein eingangs erwähntes UPDATE
nun mit MyDB::send
ausführen lassen, inklusive der Ersetzungsparameter.
Liebe Grüße,
Felix Riesterer.
Tach!
if (!empty($sql)) {
Nesting reduzieren! Logik umdrehen und return.
if (empty($params)) {
Extrawurst reduzieren! Probier mal, ob execute() ein leeres Array oder alternativ null schluckt (Voraussetzung, keine Platzhalter im SQL-Statement). Dann lass query() weg und nimm nur execute().
dedlfix.
Lieber dedlfix,
habe Deine Vorschläge jetzt so umgesetzt:
public function send ($sql = '', $params = array()) {
$t = $this;
if (empty($sql)) {
return;
}
$st = null;
try {
$st = $t->pdo->prepare($sql);
$st->execute($params);
} catch (\PDOException $ex) {
$t->errors[] = $t->get_error(
$ex,
array('sql' => $sql, 'params' => $params)
);
}
if (is_object($st) && method_exists($st, 'rowCount')) {
return $st->rowCount();
}
}
Es sollte auch nicht erwähnt bleiben, dass der Rückgabewert von rowCount
nicht immer eine Zahl > 0 zurück gibt, vor allem dann, wenn beim UPDATE
identische Werte zurückgeschrieben werden. Abhilfe schafft PDO::MYSQL_ATTR_FOUND_ROWS => true
bei der Parameter-Übergabe im Konstruktor-Aufruf, den ich nun so geändert habe:
public function __construct ($settings) {
$t = $this;
$t->errors = array();
try {
$t->pdo = new \PDO(
sprintf(
'mysql:dbname=%1$s;host=%2$s;charset=UTF8;',
$settings['db-name'],
$settings['db-host']
),
$settings['db-user'],
$settings['db-pw'],
array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::MYSQL_ATTR_FOUND_ROWS => true,
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
)
);
} catch (\PDOException $e) {
$t->errors[] = $t->get_error($e);
}
}
Nun klappt bisher alles so, wie ich mir das wünsche. Supi!
Liebe Grüße,
Felix Riesterer.
Tja, lieber Felix,
wie macht man das dann mit den "affected rows"? Ein PDOStatement
kennt die Methode rowCount
, die es nocht zu nutzen gilt:
if (!empty($sql)) {
$st = null;
if (empty($params)) {
try {
$st = $t->pdo->query($sql);
} catch (\PDOException $ex) { ... }
} else {
try {
$st = $t->pdo->prepare($sql);
$st->execute($params);
} catch (\PDOException $ex) { ... }
}
if (is_object($st) && method_exists($st, 'rowCount')) {
return $st->rowCount();
}
}
So sollte eine Information über "affected Rows" möglich sein. Wer Verbesserungsvorschläge hat, bitte immer her damit!
Liebe Grüße,
Felix Riesterer.