
- Data wpisu: 2009-03-09
- Tagi: Bazy danych, obrazki, PL/Python, plpythonu, PostgreSQL, usuwanie rekordów
Moja dzisiejsza notatka będzie pewnym przykładem rozwiązania problemu, z którym często się spotykam, ale z pewnych ograniczeń, postanowiłem to rozwiązać trochę w inny sposób. Metoda ta oczywiście znajduje się poniżej.
Często z baz danych usuwamy rekordy, które są odzwierciedleniem obrazków na dysku, ale jak usunąć rekord a wraz z nim plik zależny, który nie zawsze znajduje się fizycznie na tej samej maszynie? Dlaczego akurat uzależniać, usuwanie pliku od bazy danych? Powodów może być kilka, np wiele powtarzających się mechanizmów obsługujących ten sam mechanizm. Wydawać by się mogło że refaktoryzacja kody to załatwi ale niestety różnie to bywa i często do ideału daleka droga.
Przykład z życia – obliczamy punkty fotek w funkcji plpgsql a następnie niepotrzebne rekordy są usuwane wraz z fotkami zapisanymi fizycznie na dysku, oczywiście PHP w tym momencie nie ma dostępu do tego rekordu.
Konkrety: PostgreSQL i język proceduralny
Baza danych taka jak PostgreSQL, umożliwia obsługę wielu proceduralnych języków np. :
- pl/pgsql
- pl/tcl
- pl/python
- pl/perl
- pl/sh
Dla przykładu wykorzystam pl/python
Załóżmy, że mamy tabele images z polami, id, created_at, name, path, gdzie path bedzie scieżka na naszym serwerze
CREATE TABLE images ( id serial, created_at timestamp without time zone DEFAULT NOW(), image_name character varying(100) NOT NULL, path character varying(255) NOT NULL, CONSTRAINT pkey_id PRIMARY KEY (id) );
Usuwając rekord z tabeli images chcemy usunąć plik fizycznie z dysku. Pierwszą rzeczą jest dodanie obsługi języka plpython, oczywiście z poziomu superuser’a.
CREATE LANGUAGE plpythonu;
Pozostaje nam tylko stworzenie triggera, który wywoła funkcję usuwania pliku z dysku po usunięciu rekordu.
CREATE OR REPLACE FUNCTION delete_images() RETURNS trigger AS ' import os os.unlink( TD["old"]["path"] ) return "OK" ' LANGUAGE 'plpythonu' VOLATILE; CREATE TRIGGER delete_images_trigger BEFORE DELETE ON images FOR EACH ROW EXECUTE PROCEDURE delete_images();
Pozostaje nam jeszcze dodać przykładowy rekord a następnie go usunąć
INSERT INTO images (name, path) values ('test', '/home/songoq/pg_test/a')
Dla testu tworzymy plik na dysku
touch /home/songoq/pg_test/a
Usuwamy rekord
DELETE FROM images WHERE id = 1
Przykład można bardziej rozbudować o obsługę błędów, sprawdzanie czy plik istnieje lub poprzez wywołanie shella usunąć go z zewnętrznego serwera, ograniczeniem jest tylko wyobraźnia programisty ![]()
