External Files

Hallo,

ich starteg erade mit Postgres und bin durchweg begeistert.

Mein Traum, auch größere Dateien im Filesystem des Servers über den DB-Server zu lesen und zu schreiben schien auch mit einer Extension für Externe Files in Erfüllung zu gehen: https://github.com/darold/external_file. Leider hat diese ein paar Schwächen, die ich mit meinen aktuellen Kenntnissen nicht lösen kann:

  1. Files ab 1GB führen zu “out of memory”. Könnte man scheibchenweise schreiben und lesen? Wie würde das gehen?
  2. Schreiben klappt durchgängig bis zum “out of memory”.
  3. Ich gehe mit Delphi 10 Seattle und UniDac auf die Datenbank.
  4. Lesen hängt manchmal beim Open eines “select readefile(‘pfad’)” für ewige Zeit. Ich vermute, dass es an der Art und Weise liegt, wie da blockweise gelesen wird. Danach klappt es schnell aber unzuverlässig.
  5. Die Datenbank bläht soch dabei extrem auf. Kann man das Logging dafür unterbinden?

Ich habe das Teil schon etwas umgeschrieben, um direkt einen Pfad angeben zu können. Vielleicht kann jemand der Pros hier helfen, wie man das Ganze und vor allem die Readefile-Routine verbessern kann. Würde Postgres nochmal um ein wirklich cooles Feature ergänzen.

Danke!

Michael Schumann

CREATE OR REPLACE FUNCTION public.writeefile(
    buffer bytea,
    filename character varying)
  RETURNS void AS
$BODY$
DECLARE
  l_oid oid;
  lfd integer;
  lsize integer;
BEGIN
  l_oid := lo_create(0);
  lfd := lo_open(l_oid,131072); --0x00020000 write mode
  lsize := lowrite(lfd,buffer);
  PERFORM lo_close(lfd);
  PERFORM lo_export(l_oid,filename);
  PERFORM lo_unlink(l_oid);
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION public.writeefile(bytea, character varying)
  OWNER TO itcms;

CREATE OR REPLACE FUNCTION public.readefile(filename character varying)
  RETURNS bytea AS
$BODY$
DECLARE
  l_oid oid;
  r record;
  buffer bytea;
BEGIN
  buffer := '';
  SELECT lo_import(filename) INTO l_oid;
  FOR r IN ( SELECT data 
             FROM pg_largeobject 
             WHERE loid = l_oid 
             ORDER BY pageno ) LOOP
    buffer = buffer || r.data;
  END LOOP;
  PERFORM lo_unlink(l_oid);
  return buffer;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION public.readefile(character varying)
  OWNER TO itcms;

Was willst Du machen, Dateien größer 1GB in einem feld speichern? Da triffst Du IIRC eine Grenze. Warum willst Du das machen? Davon wird allgemein abgeraten.

zu 5.: definiere das genauer. Wenn Du einen record löscht, so wird dieser nicht physisch gelöscht, sondern nur als gelöscht markiert. Vacuum gibt ihn dann für neue Records frei. Nur ein vacuum full würde den Platz physisch freigeben - und erfordert Sperren.

Hallo, ich will genau diese Daten nicht in der Datenbank, sondern im Filesystem speichern, daher External Files. Nur wenn der Server dies kann, brauche ich nicht noch parallel einen FTP Server, was das Deployment erheblich vereinfacht. Da ich mit den obigen Funktionen nicht in Tabellen speichere, wundert mich, dass das WAL anwächst. Das Aufblähen der Datenbank geht natürlich mit vacuum weg, das würde ich aber gerne auch vermeiden.

Ah, okay. Ich kenne die Extension nicht, kann Dir da jetzt auch nicht weiterhelfen, sorry.

Je nach Format das du verwendest, es gibt einen WIMRE ForeignDataWrapper der Textfiles einbinden kann. Falls das weiterhilft.

Gruß uwe