Binäre Daten von max 30 Bytes einfügen

Hallo alle miteinander!

Ich habe schon einiges mit postgresql an Daten gespeichert, aber mit binären Daten musste ich mich bisher nicht auseinandersetzen. Jetzt muss ich aber binäre Daten speichern die pro Datensatz maximal 30 Byte lang sind. Es muss kein Index drauf oder ähnliches. Was nimmt man als Datentyp dafür und wie muss ich das mit ANSI C in die Tabelle einfügen?

Gruß Ronny

Du nimmst entweder ByteA als Datentyp und schreibst das direkt in die Tabelle oder du encodest das irgendwie (z. B. base64) und speicherst das in einem Feld vom Typ TEXT.

Ach ja, unter Umständen hilft dir noch ein:

ALTER TABLE ... ALTER COLUMN ... SET STORAGE EXTERNAL;[/quote]
Das verhindert, dass die Daten von PG noch mal komprimiert werden.

Für den Zugriff aus C heraus gibt es PQescapeByteaConn. In ISBN 978-3-937514-69-7 ist die Anwendung ab Seite 284 beschrieben.

Also nachdem ich weis nach was ich suchen muss hab ich derzeit folgende Lösung(?) gefunden: Testtabelle anlegen:

CREATE TABLE testtbl (name text, daten bytea);

Daten einfügen:

uchar buf[255];
unsigned char *to;
int len;

// .......
// Verarbeitung der Daten und ermitteln der Länge usw.
// .......
// Hiermit einfügen

to = PQescapeByteaConn(con, buf, len, &to_len);
sprintf(sqlquery, "INSERT INTO testtbl (name, daten) VALUES ('test', E'%s');", to);

Wieso 255 Zeichen für “buf”?

Dachte ich reserviere schon genug vor, weil die Querys evtl so lang sein könnten. Was könnte evtl besser sein? In der späteren Anwendung werden bis zu 8 INSERT Befehle pro Sekunde zur Datenbank. Deshalb war ich der Meinung ohne malloc/free arbeiten zu können.

Ich freue mich natürlich auch über Verbesserungen. Mit C hab ich es nicht so sehr (arbeite sonst mit Delphi und PHP). Aber in diesem Fall geht es leider nicht anders.

Gruß Ronny

Nun, “buf” ist in deinem Fall der Speicherplatz für die Daten, die du in die Datenbank schreiben möchtest. Laut deiner Aussage sind das maximal 30 Bytes. Warum reservierst du dann 255 Bytes?

Derartige Programme packe ich eher in die Ecke: ich weiß noch nicht, was letztendlich draus wird, also reserviere ich mal etwas mehr. Später vergesse ich das dann und handle mir einen Segfault samt Absturz und möglicher Code Injection ein.

Ja richtig. Weil die 30 Byte ja “escaped” werden somit im ungünstigsten Fall 150 Bytes belegen. Wenn mein INSERT noch ein wenig komplexer wird, ist das nicht zu übertrieben. Lieber ein paar Bytes zu viel reserviert, als das es hinterher nicht “reinpasst” :wink:

Nein, die 30 Bytes in “buf” bleiben 30 Bytes.

Es wäre fahrlässig, den übergebeben Speicherbereich einfach zu überschreiben, weil die Funktion gar nicht weiß, wie groß der Bereich reserviert wurde.

Nein, für das Ergebnis wird neuer Speicher reserviert, den du später wieder freigeben musst. Den Zeiger darauf bekommst du als Rückgabewert. Steht auch so alles in der Dokumentation.

Selbst wenn jedes einzelne Zeichen escaped wird und selbst wenn der Source-Bereich überschrieben werden würde, kommst du auf maximal 120 Zeichen Länge.


Vielleicht wäre es praktisch, wenn du dich vorab mit C beschäftigen würdest. Das ist eine Sprache, in der man sich schnell und gründlich in den Fuß schießen kann. Abgesehen von Assembler und einigen esoterischen Sprachen nimmt keine andere Sprache Programmierfehler so übel wie C.

Was für Informationen sollen denn in den 30 Byte gespeichert werden - wenn ich fragen darf?!

Okay das mit den 255 Bytes reserviert hab ich verstanden. :slight_smile: Nur wo der Speicher von dem Zeiger reserviert und freigegeben wird, ist mir derzeit völlig unklar. In der Beschreibung steht ja

PQescapeBytea ergibt eine Version der Binärdaten in from, in der alle Sonderzeichen durch Fluchtfolgen ersetzt wurden, sodass sie vom Zeichenkettenparser und von der bytea-Eingabefunktion ordnungsgemäß verarbeitet werden können. Der Ausgabepuffer wird mit > malloc() > angelegt. Ein abschließendes Null-Byte wird angehängt.

Würde ja bedeuten, dass ich nach meinem sprintf Befehl den Speicher direkt (selbst) freigeben muss, da sonst beim nächsten Aufruf neuer Speicher reserviert wird.
Ja ich weis das ist zwar der Befehl PQescapeBytea, aber er ist fast genauso wie PQescapeByteaConn, steht nur leider nicht in meinem Postgresql Handbuch drin.

Was für Informationen sollen denn in den 30 Byte gespeichert werden - wenn ich fragen darf?!

In den maximal 30 Bytes (meistens 9-12) werden die einzelnen Bustelegramme erfasst. Da hat fast jedes einzelne Bit eine Bedeutung zuzüglich Prüfsummen etc. Bisher hatte ich die Daten komplett in eine Struktur zerlegt in die Datenbank geschrieben. Dann hab ich festgestellt, dass das übertragen von 1 Mio Daten nach extern (selbst über DSL) viel zu lange dauert. Wenn ich die Daten binär übermittle und dann lokal wieder zerlege, könnte das deutlich schneller sein, als “reine ASCII” Zeichen zu übertragen.

Wichtig sind in der Datenbank eigentlich nur 4 Werte zum Eingrenzen oder Sortieren (Datum, Sender, Befehltyp, Datenwert). Die restlichen Infos sind nicht unwichtig, müssen aber nicht als einzelne Felder in die Datenbank. Somit finde ich es einfacher nur wenig Felder zu nutzen und bei Bedarf die eigentlichen Daten aus dem binären Feld herauszuholen.

Hatte ich früher schon mal geschrieben: Reservieren tut den die Funktion für dich, freigeben musst du ihn selbst.


Würde ja bedeuten, dass ich nach meinem sprintf Befehl den Speicher direkt (selbst) freigeben muss, da sonst beim nächsten Aufruf neuer Speicher reserviert wird.

Es wird sowieso neuer Speicher reserviert, egal ob du den freigibst oder nicht.



Ja ich weis das ist zwar der Befehl > PQescapeBytea> , aber er ist fast genauso wie > PQescapeByteaConn> , steht nur leider nicht in meinem Postgresql Handbuch drin.

Du solltest trotz allem PQescapeByteaConn verwenden, weil diese Funktion die Einstellungen deiner aktuellen Datenbankverbindung berücksichtigt. Die andere wird vielleicht sogar irgendwann entfernt, darum steht der wohl nicht in deinem Handbuch.

Noch etwas: du solltest jetzt dringend Grundlagen C lernen. Schon anhand deiner Aussagen zeigt sich, dass du einige sehr wichtige Grundlagen der Programmiersprache nicht verstanden hast.

das riecht danach, als wäre es durchaus denkbar diesen ominösen binär-typ (der wahrscheinlich einem strukturierten typ entstammt ???) doch besser als seine bestandteile zu speichern … wenn ich das recht verstehe, dann besteht dieser aus “komponenten” …

dann speicher doch diese komponenten in jeweils ein geeignetes feld … das sollte sich positiv auf das volumen auswirken - da du ja auch “nullfaehige” komponenten im wert vorzufinden vorgibst … vielleicht sind diese auch noch numerisch … leg doch felder mit den entsprechenden numerischen typen an … davon gibts ja reichlich … und wenn du ein einen teil als bitfeld verwendest der vielleicht 16, oder 32, od 64 bit beansprucht dann nimmst halt den entsprechenden int61, 32, 64 …

denk da mal drueber nach …

dann kannst du dir den ganzen unsinn nämlich schenken

Hatte ich auch so geschrieben. Ist vielleicht missverstanden worden, da ich ja PQescapeByteaConn benutze. Ich wollte nur damit sagen, das egal ob PQescapeByteaConn oder PQescapeBytea immer ein Zeiger auf den Speicher herauskommt, und ich denke damit liege ich nicht falsch.

Ja einige Sachen sind mir wirklich noch nicht ganz klar, aber das heißt nicht das ich darüber nichts lesen/lernen muss. Da stimme ich dir völlig zu. C kann schon ganz schön anstrengend sein mit der Speicherverwaltung, zuweisen von Strukturen, Arrayverwaltung usw. weshalb ich die großen Programme lieber mit Delphi schreibe. Da kommt es auch nicht auf jedes Byte - wie in diesem Fall an :smiley:

Ja dann müsste ich davon 30 Bits einzeln ablegen und die anderen in 2 bit oder 3 bit. Dabei bin ich mir nicht sicher, ob das wirklich effizienter ist. Zum Schluss müsste ich diese 30 einzelnen Bits als einzelne Felder auslesen, was das ganze nicht beschleunigt. Oder lieg ich da daneben?

musst du das? lassen die sich nicht in sinnvolle gruppen zusammenfassen?

bla_flags INT16,
blubb_flags INT32,
foo_flags BYTE,
bar_flags INT64

oder sowas

Für das TEXT-Feld werden einmalig 4 Bytes in der eigentlichen Tabelle verwendet und zusätzlich ein Eintrag variabler (also hier maximal 30 Bytes) Länge in der TOAST-Tabelle.

Für die verschiedenen Spalten sind diverse Einträge in der Haupttabelle notwendig.

Ich sehe hier keinen Vorteil.



The TOAST code is triggered only when a row value to be stored in a table is wider than TOAST_TUPLE_THRESHOLD bytes (normally 2 kB).



Ich sehe hier keinen Vorteil.

Und ich kein Toastbrot :wink:

Andreas

welches TEXT-feld?

kein Wunder, solange Du TEXT-Felder siehst :slight_smile:

[edit]

ach so du beziehst dich auf eine andere variante mit TEXT … na meinetwegen … ich habe mir wie so oft die freiheit genommen einfach zu schreiben, was mir dazu einfaellt …

aber wenn du moechtest - du bekommst ein bienchen - fuer besonders tolle TEXT-Felder

[edit2]

der vorteil in der variante mit den komponenten basiert ja nur auf der vermutung, dass seine flags sich sinnvoll gruppieren lassen … gerade mit seinen bit-operationen laesst es sich doch viel leichter arbeiten, wenn man bei einfachen datentypen bleibt … hat PG nicht selbst auch funktionen und operatoren fuer so kram? … vielleicht liegt dort der “vorteil” … naja … egal

Nun ja, mein Fehler. Für Bytea gilt aber das gleiche. Ist auch ein Feld dynamischer Länge und wird in die TOAST-Tabelle ausgelagert.

solche dinge kamen mir in den sinn:

select (blubb_flags & 2)::boolean from ...



select .. from ... where (blubb_flags & 2)::boolean

z.B. um zu schauen ob das 2. bit gesetzt ist … da kann man das auch gleich die DB erledigen lassen … oder manipulation usw.

update ... set blubb_flags = blubb_flags # 2 ...

XOR 2. bit

ja PG macht manchmal spass :slight_smile:

komme-aus-dem-Staunen-nicht-mehr-raus :astonished:

Das mit den Bitoperatoren unter Postgresql kannte ich wirklich noch nicht. Wenn das schneller geht, könnte ich meine 3 “Gruppierfelder” evtl damit machen, es sind 2x jeweils 15 bits (4, 3 und 8). Zum Anzeigen, wenn ich mal schnell auf der Konsole was sehen möchte, müsste ich dann sicher eine Funktion schreiben, die mir beim SELECT das korrekt anzeigt/umwandelt. Alle anderen Daten werden im Client extern ausgewertet. Ich meine es stimmt schon, dass wenn ich es im Feldtyp “character varying (8)” speichere und der Wert im ungünstigsten Fall “15.7.254” ist, dann belegt es ja mindestens 8 Byte anstatt 2 für ein 16 bit Feld.