mysql/php: Fehlernummer abfragen
Jörg
- mysqli
- php
Hallo,
ich trage in einer Schleife Daten in eine Tabelle ein.
Damit alle Daten nur genau 1x drin stehen, habe ich einen kombinierten Index gesetzt, der unique sein muss.
Und damit mir das Script dann nicht abbricht, sondern schlicht nicht einträgt und alle Schleifendurchläufe durch geht, habe ich das bisher (php 7.4) so abgefangen:
while {
...
$result = mysqli_query($con,$query);
if($result == FALSE && mysqli_errno($con) != 1062) {
echo $con->errno;
error_log_datei($query);
}
}
Das scheint unter php 8.1 nicht mehr zu funktionieren.
Hat sich denn da irgendwas geändert, bzw, wie bekomme ich die Fehlernummer denn unter php 8.1 raus?
Jörg
Hi,
Das scheint unter php 8.1 nicht mehr zu funktionieren.
nicht ganz klar was genau nicht mehr funktioniert
Hat sich denn da irgendwas geändert, bzw, wie bekomme ich die Fehlernummer denn unter php 8.1 raus?
In PHP 8.1 hat sich das standardmäßige Fehlerbehandlungsverhalten der MySQLi-Erweiterung von der Stummschaltung von Fehlern dahingehend geändert, dass bei Fehlern eine Ausnahme ausgelöst wird.
Vor PHP 8.1 bestand der standardmäßige Fehlerberichtsmodus in MySQLi darin, die Fehler stumm zu schalten, was oft zu Code führte, der nicht der richtigen Ausnahme-/Fehlerbehandlung folgte. Ab PHP 8.1 und höher besteht der Standardmodus für die Fehlerberichterstattung darin, eine Ausnahme bei Fehlern auszulösen.
MYSQLI_REPORT_OFF wurde zu MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT
Also, wenn dies bei dir hineinspielt, könnte ein
mysqli_report(MYSQLI_REPORT_OFF);
ODER
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_OFF;
helfen.
Aber die Verwendung von Ausnahmen (Standardverhalten in PHP 8.1) wird empfohlen, da dies Fehler hervorbringt, die unbemerkt bleiben könnten. Um den Verlust sensibler Daten zu verhindern, konfiguriere den INI-Wert display_errors auf off. Bei MYSQLI_REPORT_OFF muss der Aufrufer die Fehler behandeln, indem er den Rückgabewert von mysqli_*-Funktionsaufrufen prüft oder indem er die Eigenschaft $mysqli->error prüft.
mysqli_connect_errno() - Gibt den Fehlercode vom letzten Verbindungsaufruf zurück mysqli_connect_error() - Gibt eine Beschreibung des letzten Verbindungsfehlers zurück mysqli_error() - Gibt eine String-Beschreibung des letzten Fehlers zurück
mysqli_sqlstate() - Gibt den SQLSTATE-Fehler aus der vorherigen MySQL-Operation zurück
Hoffe es hilft
Das scheint unter php 8.1 nicht mehr zu funktionieren.
Was sich beim Übergang von PHP 8.0 zu 8.1 geändert hat, steht u.a. hier
Da steht auch
Der Standard-Fehlerbehandlungsmodus wurde von "silent" auf "exceptions" geändert. Siehe die Seite MySQLi-Berichtsmodus für weitere Details darüber, was das bedeutet, und wie man dieses Attribut explizit setzt. Um das vorherige Verhalten wiederherzustellen, kann folgendes verwendet werden:
mysqli_report(MYSQLI_REPORT_OFF);
Das bedeutet: Dein Skript bricht im Fehlerfall wohl ab, bevor Du den Fehler auswertest und die Nachrichten landen im error.log des Servers.
Hallo Jörg,
wie Raketenwilli schon schrieb - wenn Du das in PHP 8.1 geänderte mysqli Error-Reporting von Exception auf Error oder Off zurückdrehst, bricht dein Script nicht mehr ab.
Die Fehlernummer 1062 sollte dann wieder kommen - die kommt auch bei MariaDB.
Man muss sich bei sowas nur bewusst sein, dass Fehlernummern spezifisch für die Datenbank sind. Tauscht man die aus, ist Code dieser Art zum Scheitern verurteilt.
Rolf
spezifisch für die Datenbank sind.
Naja. Jörg verwendet mysqli
. Ich glaube, das ist - anders als pdo
- schon so sehr spezifisch, dass er beim Wechsel der Datenbank eh „alles“ umschreiben muss. Da macht DAS dann auch nichts mehr aus.
Naja. Jörg verwendet
mysqli
. Ich glaube, das ist - anders alspdo
- schon so sehr spezifisch, dass er beim Wechsel der Datenbank eh „alles“ umschreiben muss. Da macht DAS dann auch nichts mehr aus.
Jain.
Ich verwende Beides, msqli und pdo.
Nicht wild durcheinander gemixt, aber ich wollte beides ausprobieren und habe dann ein paar Module mit pdo gemacht.
Eines von diesen ist, was Du wirklich willst:
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
INSERT INTO t1 (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
Hinter ON DUPLICATE KEY UPDATE
notiere was geschehen soll wenn der Tabellen-Key doppelt vergeben werden sollte.
https://dev.mysql.com/doc/refman/8.0/en/insert-select.html
INSERT IGNORE INTO t1 (a,b,c) VALUES (1,2,3);
Ignoriert den ganzen Befehl wenn der Tabellen-Key doppelt vergeben werden sollte. Wirft keine Exeption. Wenn Du wissen willst, ob etwas eingefügt wurde, frage nach den „affected rows“.
Mahlzeit Willi,
erstmal vorab danke auch an alle anderen, die mir geantwortet haben.👍
Leider komme ich erst jetzt gerade wieder dazu, selber zu antworten.
Daher, wie gesagt, an dieser Stelle einen Dank an die Aufklärung, warum mein bisheriger Code nicht mehr funktioniert hatte.
Eines von diesen ist, was Du wirklich willst:
Da hast Du unzweifelhaft Recht! 😉
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
INSERT INTO t1 (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
Hinter
ON DUPLICATE KEY UPDATE
notiere was geschehen soll wenn der Tabellen-Key doppelt vergeben werden sollte.
Nein, genau das kannte ich, brauchte es aber nicht, mal davon abgesehen, dass man das natürlich auch nutzen könnte, indem man einen "Fake-Update" einer unbedeutenden Spalte vornimmt.
https://dev.mysql.com/doc/refman/8.0/en/insert-select.html
INSERT IGNORE INTO t1 (a,b,c) VALUES (1,2,3);
Ignoriert den ganzen Befehl wenn der Tabellen-Key doppelt vergeben werden sollte. Wirft keine Exeption.
Ja und genau das kannte ich nicht, entspricht aber dem, was ich benötige. Zumindest in diesem Fall.
Ich habe noch an anderer Stelle mal vor Jahren mit dem 1062er Error gearbeitet, ich müsst mal schauen, ob ich dort auch eines der Beiden nutzen kann oder der Fall dort komplizierter liegt.
Ich meld' mich später hierzu nochmal. In diesem fall aber ist INSERT IGNORE sicher meine Lösung, ich hatte es jetzt kurzfristig über einen zusätzlichen SELECT geregelt, der das Vorhandensein eines eventuellen DUPLICATE ENTRY vorwegnimmt.
Jörg
Hi,
Ich habe noch an anderer Stelle mal vor Jahren mit dem 1062er Error gearbeitet, ich müsst mal schauen, ob ich dort auch eines der Beiden nutzen kann oder der Fall dort komplizierter liegt.
Ich meld' mich später hierzu nochmal.
Ja, habe den Part gefunden. Der wird aber wohl eher so bleiben, denn hier spielt ein Designfehler der DB mit hinein.
Ich bastle mir eine Nummerzusammen, die zwar letztlich iterativ ist, aber dann als "Gesamtwerk" in einer Spalte erscheint.
Da diese Nummernbastelei aber im worstcase in einem "Wettlauf verschiedener User" auf dieselbe Nummer kommen könnte, hatte ich dort die Nummernspalte auf "unique" gesetzt und die Zusammensetzung selbiger in eine do...while(mysqli_error($con) == 1062)-Schleife
gelegt.
Wäre die Spalte selber sinnvoller gestaltet (also den iterativen Teil als eigenständige Spalte(n)), dann wäre hier wieder ON DUPLICATE KEY UPDATE
korrekt gewesen. Aber ok, läuft jetzt seit Jahren problemlos, insofern bleibts jetzt und mysqli_report(MYSQLI_REPORT_ERROR)
verrichtet dort seinen Dienst.
Dazu mal ne Frage: Wenn ich in meiner Gesamtanwendung mysqli_report(MYSQLI_REPORT_ERROR)
anwende, bleibt das dann nur für die nächste Query bestehen oder für alle?
Jörg
Dazu mal ne Frage: Wenn ich in meiner Gesamtanwendung
mysqli_report(MYSQLI_REPORT_ERROR)
anwende, bleibt das dann nur für die nächste Query bestehen oder für alle?
Habs gerade mal ausprobiert.
Es bleibt bestehen.
Und anscheinend gibts auch keine Möglichkeit, das Verhalten innerhalb des Scriptes wieder auf Standard php 8.1 zurückzusetzen.
Vielleicht müsste man die Verbindung zu sql kappen und neu aufnehmen, dann würde es vermutlich zurückgestzt. Zwar ungetestet, aber erscheint mir logisch.
Vielleicht müsste man die Verbindung zu sql kappen und neu aufnehmen,
Ich wüsste nicht, warum man mitten in einem Skript sowas ändern sollte. Ich würde es sogar per Projekt (oder Libary) einstellen. Man kann das, was hinten rauskommt, mit beiden Methoden behandeln.
Immerhin lässt sich ja auch mysqli_query()
innerhalb von PHP in try {} catch {}
packen…
Hallo Jörg,
Habs gerade mal ausprobiert.
Es bleibt bestehen.
Nein.
Vielleicht müsste man die Verbindung zu sql kappen und neu aufnehmen, dann würde es vermutlich zurückgestzt.
Nein, das ist keine Eigenschaft der mysqli-Connection, sondern des mysqli-Treibers. Der Treiber ist ein Singleton, d.h. es gibt nur einen während der ganzen PHP Laufzeit. Und er wertet den Schalter für jedes Statement neu aus.
Die Lösung ist: REPORT_ERROR ist nur eine Hälfte der Medaille. Wenn Du diese Option setzt, wird für den Duplicate Key eine Warnung protokolliert. Wenn es auch noch eine Exception geben soll, muss man REPORT_STRICT hinzufügen. REPORT_STRICT allein reicht auch nicht, denn dann werden Fehler nicht protokolliert. Keine Ahnung, wann REPORT_STRICT alleine Sinn ergibt.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)
wäre also vonnöten. Wie das PHP Handbuch schreibt, ist das ab PHP 8.1 die Werkseinstellung.
Wenn Du mit dem Schalter nicht rumspielen willst, setze den query-Aufruf mit dem Insert-Befehl in einen try-catch Block. Der folgende Code setzt voraus, dass SQL Exceptions geworfen werden und fragt deshalb den Returncode von query nicht mehr ab. Also: Entweder PHP 8.1 verwenden oder die Exceptions vorsätzlich einschalten.
try {
$db->query("INSERT INTO foo (bar, baz) VALUES('pi', 'daumen')");
}
catch (myqli_sql_exception $sqx) {
if ($sqx->code != 1062) {
// real error
}
}
Rolf
Habs gerade mal ausprobiert. Es bleibt bestehen.
Nein.
Vielleicht müsste man die Verbindung zu sql kappen und neu aufnehmen, dann würde es vermutlich zurückgestzt.
Nein, das ist keine Eigenschaft der mysqli-Connection, sondern des mysqli-Treibers. Der Treiber ist ein Singleton, d.h. es gibt nur einen während der ganzen PHP Laufzeit. Und er wertet den Schalter für jedes Statement neu aus.
Uff! UAHHHHHH!
FALLS das stimmt (ich teste das jetzt nicht selbst) kann mir irgendwer, der eine Libary getippt hat, meine Einstellung ändern bzw. für mich eine setzen? Das gänge schwer in Richtung Katastrophe...
Hallo Raketenwilli,
ich hab's getestet. Mit PHP 8.1 und einem Mariechen dahinter.
Ein Library-Autor sollte tunlichst die Flossen von diesem Fisch lassen. Oder den alten Wert vorher sichern (das geht mit dem mysqli_driver Objekt).
Rolf
Hallo Raketenwilli,
INSERT IGNORE
den kannte ich auch noch nicht. Das ist aber auch MYSQL Spezial und mein Wissensschwerpunkt liegt bei Microsoft...
Rolf
Hallo Raketenwilli,
INSERT IGNORE
den kannte ich auch noch nicht. Das ist aber auch MYSQL Spezial und mein Wissensschwerpunkt liegt bei Microsoft...
Hi Rolf,
es scheint da, zumindest für MariaDB ein paar Eigenheiten zu geben, was INSERTS angeht. Die kannte ich größtenteils noch nicht.
Jörg
Hi,
den kannte ich auch noch nicht. Das ist aber auch MYSQL Spezial und mein Wissensschwerpunkt liegt bei Microsoft...
es scheint da, zumindest für MariaDB ein paar Eigenheiten zu geben, was INSERTS angeht. Die kannte ich größtenteils noch nicht.
SQL ist halt leider nicht wirklich standardisiert - jeder DB-Hersteller kocht da sein eigenes Süppchen.
Und wenn man dann immer wieder für mehrere verschiedene DBs was baut, muß man dann in der "fertigen" Procedure dann doch noch einiges umbauen, weil man mal wieder ein Feature bzw. eine Syntaxvariante einer anderen DB verwendet hat …
cu,
Andreas a/k/a MudGuard
Hallo,
https://dev.mysql.com/doc/refman/8.0/en/insert-select.html
INSERT IGNORE INTO t1 (a,b,c) VALUES (1,2,3);
Ignoriert den ganzen Befehl wenn der Tabellen-Key doppelt vergeben werden sollte. Wirft keine Exeption. Wenn Du wissen willst, ob etwas eingefügt wurde, frage nach den „affected rows“.
Eigentlich ist das im Zusammenhang mit den „affected rows“](https://www.php.net/manual/de/mysqli.affected-rows.php doch ein ganz guter Ersatz für meine "do-while-1062- Schleife, würde ich meinen.
Oder habe ich da einen Fallstrick übersehen?
Eigentlich ist das im Zusammenhang mit den „affected rows“](https://www.php.net/manual/de/mysqli.affected-rows.php doch ein ganz guter Ersatz für meine "do-while-1062- Schleife, würde ich meinen.
Oder habe ich da einen Fallstrick übersehen?
Na, zumindest klappt es noch nicht ganz so, wie gedacht:
include("myDB.php");
$i = 1;
$counter = 0;
do {
$insert = 1;
$query = "INSERT IGNORE INTO __test (ID) VALUES ($i)";
$result = mysqli_query($con,$query);
if(!$result) {
echo('Fehler');
}
if(mysqli_affected_rows($con) == 0) {
$counter++;
$insert = 0;
}
// Edit
$i++;
} while($insert == 0);
echo "Nach $counter Versuchen $insert Einträge eingesetzt";
landet in einer Endlosschleife und ich seh grad nicht, warum?
Edit: Oops, Iteration vergessen. 😄
$i++;
Jörg
Hello,
ich trage in einer Schleife Daten in eine Tabelle ein.
Damit alle Daten nur genau 1x drin stehen, habe ich einen kombinierten Index gesetzt, der unique sein muss.Und damit mir das Script dann nicht abbricht, sondern schlicht nicht einträgt und alle Schleifendurchläufe durch geht, habe ich das bisher (php 7.4) so abgefangen:
while { ... $result = mysqli_query($con,$query); if($result == FALSE && mysqli_errno($con) != 1062) { echo $con->errno; error_log_datei($query); } }
Alternativ lass die Zeilen einzeln per Stored Routine und Trigger eintragen. Der Trigger kann dann entweder eine Exception schmeißen oder eine Aktion ausführen. Das mit der Exception muss man eventuell auch "durch die Brust ins Auge" realisieren.
Ich habe keinen Überblick, ob das Manquo da schon abgebaut wurde. Aber zumindest gibt es da einen Weg.
Man sollte ohnehin eine Zusatzschicht mittels Stored Routines einfügen, um die komplette Kontrolle über die Geschäftsregeln in der Datenbank halten zu können. Der Direktzugriff auf die Tabellen wird dann gesperrt. Zugriffe finden nur noch über die Zwischenschicht statt.
Da kann man dann sauber regeln, welcher User was darf.
Glück Auf
Tom vom Berg
Hallo TS,
Man sollte ohnehin eine Zusatzschicht mittels Stored Routines einfügen,
Finde ich auch - aber nicht jeder Hoster lässt Routinen zu.
Rolf
Man sollte ohnehin eine Zusatzschicht mittels Stored Routines einfügen, um die komplette Kontrolle über die Geschäftsregeln in der Datenbank halten zu können. Der Direktzugriff auf die Tabellen wird dann gesperrt. Zugriffe finden nur noch über die Zwischenschicht statt.
Hm. Für $Anwendung
auf „eigenen“ Servern kann man das machen. Aber die meisten Leser hier sind mit der Situation konfrontiert, dass diese von $Hoster
genau $n | $n=[1..10]
Datenbanken mit jeweils genau einem Benutzer zur Verfügung gestellt bekommen...
Freilich sollte man sich genauer überlegen was man auf einem solchen shared server hostet und ob man so in der Lage ist, für die erforderliche Sicherheit zu sorgen oder ob man irgendwann „Montags in der Zeitung“ Negativwerbung für die eigene Firma lesen will.
Freilich sollte man sich genauer überlegen was man auf einem solchen shared server hostet und ob man so in der Lage ist, für die erforderliche Sicherheit zu sorgen oder ob man irgendwann „Montags in der Zeitung“ Negativwerbung für die eigene Firma lesen will.
Heißt, diese schlimmen Dinge passieren auf jeden Fall am Wochenende? 😜
Und so recht ich Dir gebe, so weiß doch jeder von uns, dass noch viel größere Sicherheitsrisiken viel profaner sind. Mach mal einem User klar, dass eine Anwendung bei Nichtnutzung einen automatischen Logout vorsieht, dass Userpasswörter eben nicht einfach "geheim" oder "qwertz" lauten dürfen oder es gar für jeden User ein und dasselbe "Firmenpasswort" genutzt werden darf, das zudem in jedem Programm, das die Firma nutzt, verwendet wird.
Originalantwort eines Users auf die Frage, ob er denn gerne haben würde, dass jemand Fremdes seine Daten stehlen könnte: "Ach wissen Sie, ich glaube, so interessant sind meine/unsere Daten nicht."
Kennt Ihr sowas aus Eurer Praxis nicht auch?😉
Heißt, diese schlimmen Dinge passieren auf jeden Fall am Wochenende?
Montags liest man meist darüber, was für idiotisches Zeug Leute mit Technik angestellt haben, welche diese nicht beherrschen und also nicht nutzen sollten:
Montags liest man meist darüber, was für idiotisches Zeug Leute mit Technik angestellt haben, welche diese nicht beherrschen und also nicht nutzen sollten:
Bericht vom 29.08.2022 19:25 Uhr, einem Donnerstag... 😅
Nix für Ungut, ich weiß, was Du sagen willst. 👍😉
Bericht vom 29.08.2022 19:25 Uhr, einem Donnerstag... 😅
Oops, ich nehm alles zurück und behaupte das Gegenteil.
Ich hatte mich um nen Monat vertan. 😆
Also doch Montag. 😇
Hallo,
Und so recht ich Dir gebe, so weiß doch jeder von uns, dass noch viel größere Sicherheitsrisiken viel profaner sind. Mach mal einem User klar, dass eine Anwendung bei Nichtnutzung einen automatischen Logout vorsieht, dass Userpasswörter eben nicht einfach "geheim" oder "qwertz" lauten dürfen
wieso fällt mir da spontan Spaceballs ein? 😉
oder es gar für jeden User ein und dasselbe "Firmenpasswort" genutzt werden darf
Das finde ich für manche Fälle[1] sogar akzeptabel oder sogar sinnvoll. Beispielsweise haben wir in der Firma sowohl in der Fertigung, als auch in der Entwicklung an einigen Arbeitsplätzen PCs zur Steuerung von Mess- und Prüfgeräten. Diese PCs sind nicht bestimmten Mitarbeitern zugeordnet, sondern dem jeweiligen Arbeitsplatz. Sie werden von mehreren Mitarbeitern benutzt, die an diesen Stationen alle dieselben Zugangsdaten verwenden.
das zudem in jedem Programm, das die Firma nutzt, verwendet wird.
Dasselbe Passwort für verschiedene Dienste oder Anwendungen ist generell keine gute Idee. Machen aber trotzdem sehr viele. "Ich will mir nicht ein Dutzend Passwörter merken müssen, deshalb habe ich überall das gleiche eingestellt. Clever, gell?"
Nein, clever ist das nicht. Eher leichtsinnig und dumm.
Originalantwort eines Users auf die Frage, ob er denn gerne haben würde, dass jemand Fremdes seine Daten stehlen könnte: "Ach wissen Sie, ich glaube, so interessant sind meine/unsere Daten nicht."
Der ändert seine Meinung bestimmt ganz schnell, wenn jemand anders in seinem Namen bei amazon bestellt und der Kaufpreis seinem eigenen Konto belastet wird. Oder wenn er plötzlich von einem Verlag eine Auftragsbestätigung für ein 24-Monate-Zeitschriftenabo bekommt.
Kennt Ihr sowas aus Eurer Praxis nicht auch?😉
Ja, reichlich.
Einen schönen Tag noch
Martin
nota bene: Für manche Fälle. Das sollte man aber für jeden Einzelfall gründlich überlegen. ↩︎
Und so recht ich Dir gebe, so weiß doch jeder von uns, dass noch viel größere Sicherheitsrisiken viel profaner sind. Mach mal einem User klar, dass eine Anwendung bei Nichtnutzung einen automatischen Logout vorsieht, dass Userpasswörter eben nicht einfach "geheim" oder "qwertz" lauten dürfen
wieso fällt mir da spontan Spaceballs ein? 😉
Ach. Man muss da weder die Phantasie noch das Universum bemühen: Eine deutsche Firma, die sich als „Marktführer bei Webseiten für den Mittelstand“ ansah und gerade kein eigenes Rechenzentrum in Bulgarien betrieb, bestanden die Passwörter für deren „Phoning-System“ aus zwei, aus dem Namen der Benutzer herleitbaren Buchstaben und bequemerweise einer für alle identischen Zahl, die wieder aus der Wiederholung immer derselben Ziffer.
Zudem waren etliche Dokumente auf diesem Server, die laut prozessualem Vortrag dieser (sich angeblich „stets rechtstreu“ verhaltenden) Firma gegen unberechtigte Abrufe durch Verlangen nach einem Benutzernamen und dem zugehörigen, „sicheren“ Passwort geschützt waren, sogar via Google auffindbar.
Realsatire aus der Akte 14c O 70/15 des LG Düsseldorf
😆 Ratet mal, wer gewonnen hat…