_Frank_: Insert Into in SQL-Datenbank schlägt fehl (base64/utf-8)

Hallo Liebe selfhtml-Gemeinde,

ich habe ein echte Problem, wo ich nicht weiter komme.
Mir liegt ein String mit base64 Codierung vor. Der zugehörige Zeichensatz ist UTF-8 (der String stammt aus einer E-Mail (eml-Datei), welcher extrahiert wurde).
Dieser muss aber in Klartext in die Datenbank geschrieben werden, sprich ich muss eine base64 Decodierung machen. Das ist alles noch kein Problem.

Der Decodierte String enthält aber einige Zeichen, die es mir nicht möglich machen, diesen in die SQL-Datenbank zu schreiben, weil der Query immer unterbrochen wird.
Ich habe schon alles mögliche versucht mit str_replace oder mit Hilfe der Multibyte Funktionen. Alles erfolglos.

Vielleicht habt ihr einen Ansatz, wie ich es schaffe, dass der String später ohne Probleme in Klartext in der Datenbank enthalten ist.

Hier mein base64-String:

  
$text ="  
VGhpcyBpcyBhbiBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBEZWxpdmVyeSBTdGF0dXMgTm90aWZpY2F0aW9uLiAAIABEZWxpdmVyeSB0byB0aGUgZm9sbG93aW5nIHJlY2lwaWVudHMgZmFpbGVkLiAAIAAgICAgICAgdGVzdHRlc3R0QHRlc3Rlc3QuY28gACAAIAAgAA0KDQo=";  
$Text2 = base64_decode($text);  

Wird jetzt versucht, mittels SQL-Insert $Text2 in die DB zu schreiben, fällt das Statement hin und produziert folgende Fehlerausgabe:

Warning: PDOStatement::execute() [function.PDOStatement-execute]: SQLSTATE[HY000]: General error: 10007 Falsche Syntax in der Nähe von 'This is an automatically generated Delivery Status Notification. '. [10007] (severity 5) [(null)]

Ich wäre euch sehr dankbar, wenn ihr mir irgendwie helfen auf die Sprünge helfen könntet. Wenn Ihr noch ein paar Infos benötigt, nur raus damit.

Mit freundlichen Grüßen

Frank

  1. Hallo,

    Der Decodierte String enthält aber einige Zeichen, die es mir nicht möglich machen, diesen in die SQL-Datenbank zu schreiben, weil der Query immer unterbrochen wird.
    Vielleicht habt ihr einen Ansatz, wie ich es schaffe, dass der String später ohne Probleme in Klartext in der Datenbank enthalten ist.

    das ist ganz einfach. Behandle Deine Zeichenkette kontextgemäß. Du bringst sie in einen SQL-Kontext, somit ist für den Fall, dass Du die althergebrachten mysql_*-Funktionen verwendest, mysql_real_escape_string() auf Deine Daten anzuwenden, bei mysqli halt mysqli_real_escape_string() bzw. mysqli::real_escape_string().

    Benutzt Du eine andere Datenbankerweiterung, so wäre es nett, uns diese mitzuteilen, damit wir Dir helfen können.

    Freundliche Grüße

    Vinzenz

    1. Hallo,

      das ist ganz einfach. Behandle Deine Zeichenkette kontextgemäß. Du bringst sie in einen SQL-Kontext, somit ist für den Fall, dass Du die althergebrachten mysql_*-Funktionen verwendest, mysql_real_escape_string() auf Deine Daten anzuwenden, bei mysqli halt mysqli_real_escape_string() bzw. mysqli::real_escape_string().

      Oh das hätte ich vll. erwähnen sollen, dass ich dies schon versucht habe.
      Da bin ich natürlich als erstes drauf gekommen, aber hat mir leider auch nicht weiter geholfen.

      Benutzt Du eine andere Datenbankerweiterung, so wäre es nett, uns diese mitzuteilen, damit wir Dir helfen können.

      Am Ende muss ich es in eine MSSQL-Datenbank bekommen.

      Habe es sowohl mit den zugehörigen Funktionen versucht (mssql_*) als auch zuletzt mit der PDO-Extension für mssql.
      Beides war aber ohne Erfolg.

      Wende ich auf den o.g. String mysql_real_escape_string an, so wird mir später der Wert "0" in die Datenbank gespeichert, was überhaupt nicht korrekt ist.

      Also ich bin weiterhin für Vorschlänge offen und versuche alles, was ihr mir vorschlagt. ;-)

      Mit freundlichen Grüßen

      Frank

      1. Hallo,

        » das ist ganz einfach. Behandle Deine Zeichenkette kontextgemäß. Du bringst sie in einen SQL-Kontext, somit ist für den Fall, dass Du die althergebrachten mysql_*-Funktionen verwendest, mysql_real_escape_string() auf Deine Daten anzuwenden, bei mysqli halt mysqli_real_escape_string() bzw. mysqli::real_escape_string().

        Oh das hätte ich vll. erwähnen sollen, dass ich dies schon versucht habe.
        Am Ende muss ich es in eine MSSQL-Datenbank bekommen.

        sorry, das hättest Du erwähnen müssen.

        Habe es sowohl mit den zugehörigen Funktionen versucht (mssql_*) als auch zuletzt mit der PDO-Extension für mssql.

        Nutze: prepared-Statements mit PDO::prepare und gebundenem Parameter (Ausführen bitte nicht vergessen) oder als Alternative baue Dein Statement mit Hilfe von PDO::quote zusammen. Beachte die Hinweise dort :-)

        Es ist klar, dass Du mit Funktionen, die Daten für MySQL aufbereiten, nichts anfangen kannst, wenn Du nicht MySQL benutzt.

        Freundliche Grüße

        Vinzenz

        1. Hallo Vinzenz,

          schon einmal Vielen Dank für deine Mühe.

          Nutze: prepared-Statements mit PDO::prepare und gebundenem Parameter (Ausführen bitte nicht vergessen) oder als Alternative baue Dein Statement mit Hilfe von PDO::quote zusammen. Beachte die Hinweise dort :-)

          Soweit war ich auch schon ... gut, mittels Quote habe ich nicht gearbeitet, aber selbst das hat nichts geholfen. Ich poste dir einfach mal meinen Test, vll. habe ich ja dort einen Fehler drin.

            
          $text = "VGhpcyBpcyBhbiBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBEZWxpdmVyeSBTdGF0dXMgTm90aWZpY2F0aW9uLiAAIABEZWxpdmVyeSB0byB0aGUgZm9sbG93aW5nIHJlY2lwaWVudHMgZmFpbGVkLiAAIAAgICAgICAgdGVzdHRlc3R0QHRlc3Rlc3QuY28gACAAIAAgAA0KDQo=";  
          $Text2 = base64_decode($text);  
            
          try {  
             	$dbh = new PDO('mssql:host=NB_SQLSRV;dbname=sqltest', "sqltest", "sqltest");  
             	$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );  
             	$stmt = $dbh->prepare("insert into dbo.test2 (inhalt) values (:inhalt)");  
          	$stmt->bindParam(':inhalt', $name);  
            
          	$name = $dbh->quote($Text2);  
            
          	if ( !$stmt->execute() ) {  
          		echo "statement nicht ausgeführt";  
          		  
          	} else {  
          		echo "alles ok";  
          	}  
          	  
             $dbh = null;  
          } catch (PDOException $e) {  
             print "Error!: " . $e->getMessage() . "<br/>";  
             die();  
          } catch (PDOStatement $e) {  
          	print "Error!: " . $e->errorInfo() . "<br/>";  
          }  
          
          

          Vielleicht noch ein Hinweis:
          Speicher ich mir den decodierten Text in eine TXT-Datei und öffne Sie mit Wordpad, so werden mir Nicht-Ausgefüllte-Rechtecke angezeigt, die wohl das Problem verursachen. Was das aber für Zeichen sind, ist mir leider völlig unklar.
          Im Browser selbst sieht man das Zeichen nicht, entweder wird dies als Umbruch gewertet oder als nicht darstellbares Zeichen (so ein schwarz-gefüllter Kreis mit einem Fragezeichen).

          Hoffe die Infos helfen noch ein wenig weiter, um das Problem lösen zu können.

          Mit freundlichen Grüßen

          Frank

          1. Hallo,

            $text = "VGhpcyBpcyBhbiBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBEZWxpdmVyeSBTdGF0dXMgTm90aWZpY2F0aW9uLiAAIABEZWxpdmVyeSB0byB0aGUgZm9sbG93aW5nIHJlY2lwaWVudHMgZmFpbGVkLiAAIAAgICAgICAgdGVzdHRlc3R0QHRlc3Rlc3QuY28gACAAIAAgAA0KDQo=";
            $Text2 = base64_decode($text);

            try {
                $dbh = new PDO('mssql:host=NB_SQLSRV;dbname=sqltest', "sqltest", "sqltest");
                $dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
                $stmt = $dbh->prepare("insert into dbo.test2 (inhalt) values (:inhalt)");

            Welchen Datentyp hat Deine Spalte inhalt?

            $stmt->bindParam(':inhalt', $name);

            Binde an $Text2

            und entferne die folgende Zeile

            $name = $dbh->quote($Text2);

            Da Du Prepared Statements verwendest, darfst Du nicht mehr quoten, das macht Dir Deine Daten kaputt.

            if ( !$stmt->execute() ) {
            echo "statement nicht ausgeführt";

            [...]

              
              
            Freundliche Grüße  
              
            Vinzenz
            
            1. Hallo,

              Welchen Datentyp hat Deine Spalte inhalt?

              »» $stmt->bindParam(':inhalt', $name);

              Die Datenbankspalte ist als >text< definiert. Wollte es so nah an dem Echtsystem abbilden, wie es nur geht. ;-)

              Binde an $Text2

              und entferne die folgende Zeile

              »» $name = $dbh->quote($Text2);

              Die hatte ich vorhin reingemacht, als du mir den Hinweis damit gegeben hast.
              Aber hatte ja auch keine Besserung gebracht, sondern den selben Fehler wie am Anfang auch.

              Mit freundlichen Grüßen

              Frank

              1. Hallo Frank,

                nur zur Info: mit VB.NET und dem Native Client läuft das wunderbar. Der resultierende Text ist übrigens ein reiner ASCII-Text :-)

                Ich schau' mal, ob ich irgendwo eine VM mit 'ner PHP-Installation finde.

                Freundliche Grüße

                Vinzenz

                1. Hallo Vinzenz,

                  vielen Dank für deine Mühe.

                  VB.NET und Native Client ist schön und gut, hab ich wenigstens wieder was gelernt. Aber php wäre mir lieber. :)

                  Wäre klasse, wenn wir das irgendwie gelöst bekommen. Bin mit meinem Wissen echt am Ende. ;-)

                  Ich warte dann mal auf eine Antwort von dir.

                  Schönen Tag noch.

                  Mit freundlichen Grüßen

                  Frank

                2. Hallo Vinzenz,

                  ich konnte das Problem beheben.
                  Zeigt man sich den decodierten String mittels eines Hex-Editors an, sieht man das Zeichen, welches mit 00 definiert ist.
                  Sprich es ist das String-Trennungszeichen, somit hat sich das Programm eigentlich richtig verhalten.

                  Lösung des Problems:
                  $String = str_replace(chr(0), "", $String);

                  Anschließend ist alles OK.

                  Danke für deine Bemühungen, es funktioniert jetzt tadellos.

                  1. echo $begrüßung;

                    Zeigt man sich den decodierten String mittels eines Hex-Editors an, sieht man das Zeichen, welches mit 00 definiert ist.
                    Sprich es ist das String-Trennungszeichen, somit hat sich das Programm eigentlich richtig verhalten.

                    Nicht für PHP-Verhältnisse, da wird ein String nicht mit einem Null-Byte beendet. Allerdings kann es sich um eine fehlerhafte Implementierung handeln, die nicht binary-safe ist. Es wäre fatal, könnte man keine Null-Bytes in binären Daten speichern. Es kann aber auch eine Einschränkung seitens der von PDO verwendeten MSSQL-API handeln.

                    Lösung des Problems:
                    $String = str_replace(chr(0), "", $String);
                    Danke für deine Bemühungen, es funktioniert jetzt tadellos.

                    Hoffentlich nicht nur beim Speichern sondern auch beim Verarbeiten nach dem Auslesen. Da du hier Kürzungen vornimmst, verändert sich die String-Länge. Nicht dass die noch irgendwo hinterlegt ist und nun nicht mehr stimmt.

                    echo "$verabschiedung $name";