Blobverarbeitung in php

Forum für Fragen rund um Firebird-Software von Drittanbietern.

Moderator: martin.koeditz

vr2
Beiträge: 214
Registriert: Fr 13. Apr 2018, 00:13

Hallo Martin,

in php ist es nicht möglich, einen Textblob > 32K einem update or insert- Statement zu übergeben. Stattdessen muss der blobcontent zu einem Blobobjekt gemacht werden, was dann einem parametrisierten Statement eingefügt wird:

Code: Alles auswählen

function data2blob($data, $aDB, $sql) { 
  $status = 'ok';
  // einfügen von daten > 32K in einen blob geht nicht literal, daher so:
  // muss auch transaktional sein und committed werden, sonst wird der blobimport von nachfolgenden transaktionen nicht gesehen
  $tx = ibase_trans(IBASE_WRITE|IBASE_COMMITTED|IBASE_NOWAIT|IBASE_REC_VERSION, $aDB); 
  
  $blh = ibase_blob_create($tx);
  ibase_blob_add($blh, $data);
  $blob = ibase_blob_close($blh);
  
  if (!is_string($blob)) {
    // import failed
    $status = 'blob-import gescheitert';  
  } else {
    $prepared = ibase_prepare($aDB, $tx, $sql);
    if (!ibase_execute($prepared, $blob)) {
      $status = 'insert gescheitert';
    }
    else {
      ibase_commit($tx);
    }  
  } 
  return $status;
}
Ist diese Einschränkung auf 32K Blobgröße, die noch literal übergeben werden kann, php-treiber-spezifisch? Wenn ja, ließe sich das ändern?

Grüße, Volker
Benutzeravatar
martin.koeditz
Beiträge: 443
Registriert: Sa 31. Mär 2018, 14:35

Hallo Volker,

das hängt mit der grundsätzlichen Beschränkung für Statements zusammen. Diese sind (nach meiner derzeitigen Kenntnis auf 32k) begrenzt. Deshalb müssen größere BLOBs anders behandelt werden.

Dies gilt auch für andere Treiber. Ich meine in Flamerobin sind wir auch mal auf dieses Problem für Text-BLOBs gestoßen.

Gruß
Martin
Martin Köditz
it & synergy GmbH
bfuerchau
Beiträge: 485
Registriert: Mo 7. Mai 2018, 18:09
Kontaktdaten:

Die Frage stellt sich eigentlich gar nicht, da parametrierte SQL's grundsätzlich sicherer und schneller, weil wiederholbar, sind (prepared Statements) und sie sind Typsicher.
Die Grenzen für ein Statement sind bei FB 2.5 noch 64K, ab 3.0 10MB.
Allerdings ist die max. Zeilenlänge eben auch nur 64K, und ein Char-Feld max. 32K, bei UTF8 sogar nur 8K.
Deshalb klappts auch nicht mit Blobs als Text.

Übrigens:
Parameter schützen grundsätzlich vor SQL-Injection und bei Strings spare ich mir die Hochkommaverdoppelung.

Ich artbeite seit über 30 Jahren zu 99,999% mit Parametern bei SQL.
vr2
Beiträge: 214
Registriert: Fr 13. Apr 2018, 00:13

Hallo ihr 2,

Ja, die max. Statementgröße ist 10MB ab Firebird 3. Ein Treiber sollte das nicht einschränken, da Firebird sich schon meldet, wenn es zu groß ist. Oder bei php der Webserver, je nachdem, wie er konfiguriert ist.
Man kann mit dem aktuellen Flamerobin ein Statement > 32K absetzen. Dh, IBPP (C++) kann es, IBX (Pascal) auch.

Man kann in php auch einem Prepared-Statement-Parameter keinen längeren Text übergeben als 32K, außer eben man erzeugt erst ein Blobobjekt.

Mir ging es nur darum, ob der php-Treiber die Beschränkung auf 32K ins Spiel bringt. Dass Firebird diverse Limits definiert, ist ja klar und auch ok. Firebird ab Version 3 kann 4GB Statementgröße, die 10 MB sind nur ein Vorsichtsmaßnahme. Eigentlich ein Parameter für firebird.conf.

Grüße, Volker
bfuerchau
Beiträge: 485
Registriert: Mo 7. Mai 2018, 18:09
Kontaktdaten:

Meine Tests bzgl. Bulkload ergibt aber auf jeden Fall die Beschränkung, dass die Summe aller Parameterinhalte (z.B. für Prozedur/execute Block) ca. 64K nicht überschreiten darf, excl. Blob, also die max. Zeilenlänge einer Tabelle.
Auch die Anzahl der Inserts/Updates innherhalb einer Prozedur/execute Block darf 255 nicht überschreiten (egal ob Parameter oder als String), da max. nur 255 Tabellenreferenzen möglich sind.
Hier sollte ggf. mal eine Erweiterung stattfinden.

Zum PHP-Treiber kann ich nun nichts sagen, da gibts hier ja einen Entwickler, der das wissen müsste. Aber ein Blob ist als Parameter bzgl. Firebird nur auf die Zielgröße beschränkt.
Benutzeravatar
martin.koeditz
Beiträge: 443
Registriert: Sa 31. Mär 2018, 14:35

Ich habe nochmals in der Doku recherchiert: Die maximale Größe für Zeichen-Literale beträgt seit Firebird 3.0 64 kb. Wenn also ein String über Verkettung aufgebaut wird, kann diese Grenze schnell erreicht werden. Bei Prepared Statements gilt diese Beschränkung nicht.

Evtl. spielt auch eine alte fbclient mit und meldet die 32kb-Verletzung.

Was den PHP-Treiber angeht: das muss ich prüfen. Die Daten habe ich nicht im Kopf. Wäre aber in der Tat merkwürdig dies Treiberseitig zu beschränken.

Gruß
Martin
Martin Köditz
it & synergy GmbH
vr2
Beiträge: 214
Registriert: Fr 13. Apr 2018, 00:13

Hallo ihr 2,

Ich hab angeregt durch eure Kommentare mal versucht, in php die Grenzen der parametrisierten Übergabe auszuloten, und konnte einen Textblob von ca 27 MB übergeben, ganz normal, ohne explizite Erzeugung eines Blobobjekts. Das hab ich mehrmals wiederholt, mal mit, mal ohne Verwendung eines Blobobjekts. Die Zeiten und die Speicherauslastung sind dabei nicht wahrnehmbar unterschiedlich.

Jetzt frage ich mich, wieso man überhaupt ein Blobobjekt erzeugen sollte. Ich könnte mir vorstellen, dass bei Verwendung eines Blobobjekts intern ein BlobReader erzeugt wird, der streamt, anstatt den ganzen materialisierten Text am Stück zu übertragen. Aber die Speicherauslastung hat darüber keinen Aufschluss gegeben. Oder das execute eines parametrisierten Statements legt intern selber ein Blobobjekt an, wenn ein Limit überschritten ist. Die beiden Arten der Parameterübergabe verhielten sich jedenfalls bei meinen Tests gleich, von außen betrachtet.

Martin, kannst Du das vielleicht erhellen?

Die Rolle unterschiedlicher Treiberversionen untersuche ich noch.

Grüße, Volker
bfuerchau
Beiträge: 485
Registriert: Mo 7. Mai 2018, 18:09
Kontaktdaten:

Du musst da unterscheiden zwischen BLOB (Binary, also ein Byte-Array) oder Textblob, was in der FB-Tabellenbeschreibung auch ersichtlich ist.
Ein Byte-Arry muss natürlich, um Verlustfrei zu sein, anders übertragen werden als ein String von entsprechender Größe.
Dabei ist es dann Sache des Treibers auf Grund des Spaltenzieles als ganz normales Textfeld oder als UTF8-Text zu übertragen.
Für Textblobs ist daher auch kein Blob-Objekt erforderlich.

Ich benutze z.B. den .NET-Treiber, der mir die obigen Grenzen durch eine automatisierte Prüfung, je nach DB-Version, liefert da sie nicht direkt abfragbar sind und vor allem der interne Verwaltungsoverhead nicht bekannt ist.
So erstelle ich also entsprechend viele parametrierte Inserts in einem execute Block bis der Treiber mir beim Prepare sein OK gibt.
Da die Anzahl Tabellen eben auf 255 beschränkt ist, kann ich per binärem Verfahren das Maximum berechnen.
Dabei stellt sich eben heraus, dass die Datenlänge eben 64K nicht überschreiten kann.

Was die Textkonstanten angeht, so ist diese auf das Maximum des Zielfeldes beschränkt. Und Charvariablen bleiben bei 32K.

Ein Insert mit Parametern dauert im Schnitt 10 Millisekunden, ohne Parameter 50 Millisiekunden. Daher kann ich im Bulkload mit Parametern 5x mehr Sätze laden als mit Texten.
Dies erklärt sich eben auch durch die zusätzlichen Zeiten der SQL-Textanalyse sowie der Umwandlung und vor allem Gültigkeitsprüfung von Zahl- und Datumwerten.

I.Ü. gilt dies für jede DB und ich kann es bis heute nicht verstehen, warum in vielen Anwendungen immer noch mit Text- statt Parameterwerten gearbeitet wird. Als ob Parameter vollkommen unbekannt wären.
Bei Programmiersprachen verwendet man doch auch typisierte Parameter und nicht mit Strings.
Benutzeravatar
martin.koeditz
Beiträge: 443
Registriert: Sa 31. Mär 2018, 14:35

I.Ü. gilt dies für jede DB und ich kann es bis heute nicht verstehen, warum in vielen Anwendungen immer noch mit Text- statt Parameterwerten gearbeitet wird. Als ob Parameter vollkommen unbekannt wären.
Bei Programmiersprachen verwendet man doch auch typisierte Parameter und nicht mit Strings.
Ich fürchte, dass dies häufig doch unbekannt ist. Viele ausgelernte Fachinformatiker können hiermit nichts anfangen. Die SQL-Anweisungen werden einfach verkettet. Ist ja auch viel simpler und es wird schon nichts passieren...

Wie bfuerchau schon geschrieben hat: Die Stärke von Prepared Statements sind, neben dem Problem SQL-Injection, Stapelverarbeitungen. Je mehr Datensätze hintereinander geschrieben werden, desto mehr merkt man die Performance.

Grundsätzlich stimmt es natürlich, dass ich bei Texten nicht unbedingt ein BLOB-Objekt erzeugen muss. Habe ich jedoch längere Texte, sieht die Sache schon etwas anders aus. Die Feldlänge für VARCHARs ist eben auf 32Kb beschränkt. Bei UTF-8 sind das etwa 8000 Zeichen.

Mit etwas Glück kann ich mir den Treiber im Laufe der nächsten Woche mal ansehen.

Gruß
Martin
Martin Köditz
it & synergy GmbH
vr2
Beiträge: 214
Registriert: Fr 13. Apr 2018, 00:13

Hi,

ich hab es wohl nicht verständlich genug erklärt. Dass große Textblobs nicht am Stück in ein varchar-Feld passen, ist klar. Wofür parametrisierte Abfragen gut sind, auch ;-)

Hier ging es um lange Texte (zb csv-Daten), die in ein Blobfeld eingetragen werden, und ob in php ein Blobobjekt erzeugt werden muss oder nicht oder ob das irgendwelche Vorteile hat. Und wo in beiden Fällen die Limits sind. Ich würde diese Dinge nicht fragen, wenn die php-Doku an der Stelle nicht gewohnt unterirdisch wäre, im Vergleich zu Dokus wie beim python-Firebird-Treiber: https://fdb.readthedocs.io/en/latest/us ... onversions, siehe Abschnitt 'Working with BLOBs'. Das ist eine Doku, nicht so ein Ranz wie https://www.php.net/manual/de/function. ... ob-add.php. Martin, das geht nicht gegen Dich, ich hab ja miterlebt, wie zugeknöpft die bei php sind, als es um den neuen Firebird-Treiber ging oder auch nur um Schreibrechte für die grottenschlechten Dokuseiten. Die hüten den Gral, selbst wenn sie inhaltlich keine Ahnung haben.

Grüße, Volker
Antworten