A. Meier: Wie kann ich die existenz eines variablennamens innerhalb der Datenbank prüfen

Hallo, ich lese über $_POST übergebene Variablen aus. Dann erfolgt ein UPDATE über die Datenbank.

Problem, nicht alle übergebenen Variablen existieren in der Datenbank als (Feldnamen)!

Daher Fehler und Abbruch der PHP ausführung. (Fehler erst seit Umstellung von PHP 8.0 auf 8.1!!) (Ausgabe: ...Uncaught mysqli_sql_exception: Unknown column 'Feldnamen'...) Fehlerzeile: ...mysqli_query...

Frage: Wie kann ich die in der Datenbank existierenden Feldnamen mit den übergebenen Variablen (durch $_POST) vergleichen. (nicht die Inhalte!!)

Gruß und Danke

akzeptierte Antworten

  1. Hm. Normalerweise macht man einfach das INSERT, SELECT, UPDATE oder sonstwas und schaut in PHP, ob der Datenbankserver meckert(PDO). (Variante für mysqli)

    Aber Du kannst Dir und dem Server natürlich (was sinnfrei erscheint…) Extra-Arbeit aufhalsen:

    https://dev.mysql.com/doc/refman/8.0/en/show-columns.html

    sagt:

    SHOW COLUMNS FROM mytable FROM mydb;
    SHOW COLUMNS FROM mydb.mytable;
    

    Das Ergebnis ist eine Tabelle, sollte also wie andere Tabellen ausgewertet werden kann und hat die Spalten

    • Field
    • Type
    • Null
    • Key
    • Default
    • Extra

    Und ich fürchte, Du bist fürchterlich auf dem Holzweg, denn normalerweise sind die Tabellen, einem sorgfältigen Plan folgend, sorgfältig definiert und damit sind die Spalten dem Programmmierer bekannt.

    Schreibst Du etwa gerade etwas wie PHPMyAdmin? Das ist ziemlich heftiges Vorhaben…

    1. Hm. Normalerweise macht man einfach das INSERT, SELECT, UPDATE oder sonstwas und schaut in PHP, ob der Datenbankserver meckert(PDO). (Variante für mysqli)

      Nachtrag:

      Das Problem ist offenbar das abbrechende Skript. Die Lösung lautet:

      Fehler mit try { … } catch { … } abfangen wie unter „4. Fehler abfangen“ gezeigt:

      https://www.peterkropff.de/site/php/mysqli_fehler.htm

      Weitere Probleme – im Hinblick auf mehrere schreibende, aber logisch zusammenhängende Zugriffe auf die Datenbank – lassen sich mit Transaktionen abfangen. Bei einem - offenbar gewünschtem - Nichtabbruch des Skriptes ist es auch notwendig, das ROLLBACK-Kommando an die Datenbank zu senden. Denn sonst wird die normalerweise eröffnete Transaktion (mit den bis dahin erfolgreichen, aber insgesamt unvollständigen Änderungen) am Ende der Verbindung, also des PHP-Skriptes automatisch „comittet“, also übernommen: „autocommit = ON“.

      Einstieg:

      Die Aussage, dass normalerweise der Programmierer (und also das Skript) die Spaltennamen kennt und benennt (und nicht etwa der Benutzer eines vermeintlichen Webformulares), die bleibt natürlich. Es gibt da nur die eine Ausnahme, dass ein solcher Benutzer - sorgfältig authentifiziert - eine Datentabelle via Webformular anlegen/ändern soll.

      1. Hallo Raketenwilli,

        ROLLBACK-Kommando

        Ist PHP automatisch transaktional?

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Boah! Muss man hier wieder GENAU sein…

          Hallo Raketenwilli,

          ROLLBACK-Kommando

          Ist PHP automatisch transaktional?

          PHP an sich kann es nicht sein, denn „(nicht) transaktional“ ist eine Eigenschaft von DatenBank(ManagementSystemen) - genauer gesagt, der verwendeten Speicher-Engine. Immerhin können sogar einzelne, große Transaktionen durchaus auch sehr viel mehr Platz brauchen als der Arbeitsspeicher bietet…

          MySQL/MariuaDB können Transaktionen, sind also prinzipiell „transaktional“ (s.o.), starten für jede Sitzung auch automatisch eine Transaktion - haben aber einen aktiven autocommit, so dass alle Änderungen sofort „committed“ (von der sitzungsprivaten Transaktion in den Hauptspeicher der Datenbank übertragen und damit für alle anderen Sitzungen sichtbar) werden:

          → Auch PHP „baut“ als Client mit PDO oder mysqli eine solche Sitzung - womit die Datenbank die Transaktion also eröffnet - eber eben auch jede Änderung sofort comittet.

          Man kann das abschalten (autocommit=0, s.u.) oder eine Transaktion in der Transaktion starten - das ist es was das PHP Handbuch vorschlägt, wobei die Beschreibung für PHP-PDO eigentlich sogar falsch ist:

          PHP::PDO:

          https://www.php.net/manual/de/pdo.begintransaction.php

          „Deaktiviert den Autocommit-Modus. Wenn der Autocommit-Modus deaktiviert ist, werden Änderungen, die über die Instanz eines PDO-Objekts an der Datenbank vorgenommen werden, erst dann bestätigt, wenn die Transaktion durch den Aufruf von PDO::commit() beendet wird.“

          Das ist falsch, denn das startet tatsächlich eine Transaktion in der Transaktion - nur eben ohne autocommit.

          Die Beschreibung für PHP::mysqli klingt ähnlich, ist aber nicht so falsch:

          https://www.php.net/manual/de/mysqli.begin-transaction.php

          „Beginnt eine Transaktion; benötigt die InnoDB-Engine“

          Beginnt eine Transaktion in der Transaktion - auch ohne autocommit.

          Das gilt freilich nur dann wenn die gewählte oder automatisch gesetzte Speicher-Engine Transaktionen unterstützt... Das seit Jahren als default benutzte InnoDB tut es.

          In der Mysql/MariaDB-Shell erreicht man das, in dem man vor den nicht automatisch zu „comittenden“ Aktionen einmalig mit

          SET autocommit=0;
          

          die Variable setzt. Das geht freilich auch von PHP aus. Als ganz normale SQL-Anweisung…

          Danach werden die Änderungen erst nach einem explizitem COMMIT in den übrigen Sitzungen sichtbar bzw. beim Beenden übernommen. Wenn es bei schreibenden Zugriffen darauf ankommt, dass eine ganze Gruppe solcher Aktionen entweder übernommen wird oder nicht, ist das Deaktivieren des autocommits - für die übrigens stets mit jeder Sitzung automatisch (im Hintergrund) gestartete Transaktion - die beste Idee.

          Man kann Transaktionen sogar selbst verschachteln und ihnen Spitznamen geben...

          1. Hallo Raketenwilli,

            Autocommit

            Autsch, ich habe offenbar viel zu selten mit MyISAM gearbeitet. Dass MyISAM auf Kosten von Tempo und Speicher auf vieles verzichtet, wusste ich, aber es weiß ja von ACID wirklich gar nichts und ist damit nichts weiter als dBASE mit SQL-Frontend.

            Mit "automatisch transaktional" meinte ich das Verhalten von Transaktionsmonitoren (meine ersten Programme liefen auf IBM Großrechnern), die sich im Prinzip wie ein Webserver mit einem sehr dummen Browser verhalten: Drückt man auf dem Terminal ENTER oder eine Funktionstaste, wird ein Request an den TM geschickt (der dann dummerweise ebenfalls Transaktion heißt), von ihm verarbeitet und ein neuer Bildschirminhalt zurückgeschickt. TMs starten automatisch beim Beginn der Terminal-Transaktion eine DB Transaktion und committen die DB automatisch, wenn sie ohne Fehler durchläuft. Bei einem Abbruch (wegen Bug oder wegen Absicht) gibt's den DB Rollback.

            Es hätte ja sein können, dass PHP sowas automatisch tut. Es hätte mich gewundert, aber beim lieben Rasmus weiß man ja nie.

            Angesichts der (Nicht-)Fähigkeiten von MyISAM sehe ich jetzt aber, dass diese Möglichkeit einfach nicht besteht.

            Nachdem wir das geklärt haben, möchte ich diesem Satz von Dir widersprechen:

            Bei einem - offenbar gewünschtem - Nichtabbruch des Skriptes ist es auch notwendig, das ROLLBACK-Kommando an die Datenbank zu senden. Denn sonst wird die normalerweise eröffnete Transaktion (mit den bis dahin erfolgreichen, aber insgesamt unvollständigen Änderungen) am Ende der Verbindung, also des PHP-Skriptes automatisch „comittet“, also übernommen: „autocommit = ON“.

            MyISAM kann keine Transaktionen. Da gibt's kein Autocommit und kein ROLLBACK. Wenn ich mit MyISAM 3 Statements durchführe und das dritte kackt ab, hilft nur ein RECOVER der DB. Oder ein manuelles Undo der bereits durchgeführten Änderungen.

            InnoDB kann Transaktionen. Es hat per Default Autocommit, und das findet nach jedem einzelnen Statement statt. Läuft ein Statement auf einen Error, gibt's automatisch einen Rollback auf dieses Statement. Handbuch:

            By default, MySQL runs with autocommit mode enabled. This means that, when not otherwise inside a transaction, each statement is atomic, as if it were surrounded by START TRANSACTION and COMMIT. You cannot use ROLLBACK to undo the effect; however, if an error occurs during statement execution, the statement is rolled back.

            D.h. bei MyISAM würde ein ROLLBACK auf einen Error laufen ("Hä? Was's das?!") und bei InnoDB würde er nichts tun. Es sei denn, man hätte vorher mit START TRANSACTION eine Transaktion als Klammer um alle Statements geöffnet:

            To disable autocommit mode implicitly for a single series of statements, use the START TRANSACTION statement

            Falls eins von mehreren Statements in einer Transaktion auf einen Error läuft:

            If a single SQL statement within a transaction rolls back as a result of an error…

            Das steht bei Locking, aber es impliziert, dass auch bei einer manuell gestarteten Transaktion jedes einzelne Statement entweder vollständig oder gar nicht ausgeführt wurd.

            Dann schriebst Du:

            „(begintransaktion) Deaktiviert den Autocommit-Modus. Wenn der Autocommit-Modus deaktiviert ist, werden Änderungen, die über die Instanz eines PDO-Objekts an der Datenbank vorgenommen werden, erst dann bestätigt, wenn die Transaktion durch den Aufruf von PDO::commit() beendet wird.“

            Das ist falsch, denn das startet tatsächlich eine Transaktion in der Transaktion - nur eben ohne autocommit.

            Entweder verstehe ich Dich miss, oder Du irrst. Auch das MySQL Handbuch schreibt, dass es bei abgeschaltetem Autocommit steht eine aktive Transaktion gibt. Man kann darin mit START TRANSACTION eine Subtransaktion starten, ja. Muss man aber nicht, und wenn man dann am Ende den COMMIT oder ROLLBACK vergisst und die Session schließt (15.7.2.2):

            If autocommit mode is disabled within a session with SET autocommit = 0, the session always has a transaction open. A COMMIT or ROLLBACK statement ends the current transaction and a new one starts.
            If a session that has autocommit disabled ends without explicitly committing the final transaction, MySQL rolls back that transaction.

            D.h. InnoDB verhält sich per Default so, wie ich es von Datenbanken wie MS SQL Server oder IBM DB2 kenne, und der er von Dir vorgeschlagene ROLLBACK ist nur dann nötig, wenn A. Meier vorher explizit eine Transaktion gestartet, bereits andere Statements ausgeführt und dann trotz Errors einen COMMIT programmiert hätte.

            Man kann diesen Error übrigens entweder mit try/catch fangen, oder man schaltet den

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Autsch, ich habe offenbar viel zu selten mit MyISAM gearbeitet. Dass MyISAM auf Kosten von Tempo und Speicher auf vieles verzichtet, wusste ich, aber es weiß ja von ACID wirklich gar nichts und ist damit nichts weiter als dBASE mit SQL-Frontend.

              So ähnlich ... aber MyISAM ist so „raus“ wie Minix als Linux-Dateisystem.

              Default-Type ist seit Jahren InnoDB, das kann Transaktionen.

              InnoDB kann Transaktionen. Es hat per Default Autocommit, und das findet nach jedem einzelnen Statement statt. Läuft ein Statement auf einen Error, gibt's automatisch einen Rollback auf dieses Statement. Handbuch:

              Stell Dir das so vor, als gäbe es eine Super-Transaktion - das passt auch ganz gut.

              In dieser wird für jede Verbindung eine Kind-Transaktion gestartet, in welcher das autocommit auf On steht: Alle Änderungen werden sofort der Eltern-Transaktion, in dem Fall der Super-Transaktion verpetzt, die natürlich gleich einen Zettel an den Kühlschrank hängt, damit alle Kinder (und weil die als Eltern auch einen Zettel an den Kühlschrank hän Kindeskinder davon wissen.

              Um das automatische Committen abzustellen hast Du ZWEI Möglichkeiten:

              Entweder SET autocommit=0;, also durch die Variable in der Kind-Transaktion oder durch Starten einer Enkeltransaktion. In der ist das autocommit automatisch abgeschaltet.

              Der Effekt (Isolation) ist bei beiden Varianten gleich. Aber die Variante mit der Subtransaktion („Enkeltransaktion“) zieht einen etwas höheren Aufwand auf der Systemseite nach sich:

              „Supertransaktion“
                     |
                     +- Kindtransaktion für Verbindung
                     |  automatisch: autocommit=1;
                     |       |
                     |       + Enkeltransaktion
                     |         automatisch: autocommit=0; 
                     |
                     +- Kindtransaktion für Verbindung
                        automatisch:     autocommit=1;
                        manuell gesetzt: autocommit=0;
              
              1. Moin,

                So ähnlich ... aber MyISAM ist so „raus“ wie Minix als Linux-Dateisystem.

                ich kannte bis gerade eben Minix nur als eigenes Betriebssystem, aber nicht als Dateisystem. Was habe ich verpasst?

                Viele Grüße
                Robert

                1. Was habe ich verpasst?

                  Linus Thorwald hat seinen ersten Linux-Kernel darauf kompiliert / damit zum Laufen gebracht... Insoweit ist Minix das erste Linux-Dateisystem.

                  Zitat:

                  „Dieses Dateisystem wurde von Linux bis zur Einführung des extended filesystem im April 1992 als Standard benutzt und wurde auch lange danach noch häufig bei (bootfähigen) Disketten, teilweise für RAM-Disks und auf anderen Anwendungsgebieten wegen des geringen für die Verwaltung benötigten Speicherplatzes verwendet.“

                  Das ist aber schon eine ganze Weile her…

      2. Hab jetzt mal gesucht, wer oder was die Verwirrung verursacht hat.

        Wer hat es verbockt?

        Denn sonst wird die normalerweise eröffnete Transaktion (mit den bis dahin erfolgreichen, aber insgesamt unvollständigen Änderungen) am Ende der Verbindung, also des PHP-Skriptes automatisch „comittet“, also übernommen: „autocommit = ON“.

        Ah. Da war ich richtig böse falsch. Ohne eigene Transaktion oder set autocommit=0 wird jede Änderung einzeln, sofort, dauerhaft und für alle übernommen („commited“).

        Bei einem - offenbar gewünschtem - Nichtabbruch des Skriptes ist es auch notwendig, das ROLLBACK-Kommando an die Datenbank zu senden.

        Das setzt also das voraus, was ich tun würde, wenn ich mehrere Änderungen an der Datenbank nur dann vornehmen wöllte, wenn alles fehlerfrei durchläuft: automit „ausnullen“ oder eigene Transaktion starten.

        Das hätte ich brav gleich dazu schreiben sollen,.

  2. Moin,

    Hallo, ich lese über $_POST übergebene Variablen aus. Dann erfolgt ein UPDATE über die Datenbank.

    Problem, nicht alle übergebenen Variablen existieren in der Datenbank als (Feldnamen)!

    Das sollte dem Script egal sein. Du darfst auf keinen Fall einen Query anhand der übergebenen Parameter zusammenbauen (Ausnahme: du programmierst ein Tool zur Verwaltung der Datenbank) – das wäre eine gravierende Sicherheitslücke: ein Angreifer könnte zusätzliche Werte übertragen und damit u.U. Spalten überschreiben die nicht geändert werden dürfen.

    Daher Fehler und Abbruch der PHP ausführung. (Fehler erst seit Umstellung von PHP 8.0 auf 8.1!!)

    Einen Fehler hat das auch vorher schon geworfen. Allerdings wurde mit dem Update auf PHP 8.1 der Standardwert für die Fehlerbehandlung auf „exceptions“ geändert – bisher wurde dir der Fehler wohl einfach nicht angezeigt.

    Frage: Wie kann ich die in der Datenbank existierenden Feldnamen mit den übergebenen Variablen (durch $_POST) vergleichen. (nicht die Inhalte!!)

    Die Spalten einer Tabelle kannst du über die Tabelle INFORMATION_SCHEMA.COLUMNS abrufen, aber wie gesagt: mach es nicht, das Script muss selbst wissen welche Spalten geändert werden dürfen, das darf nicht vom Inhalt von $_POST abhängen!

    Gruß
    Tobias