-- SQL support for fake lobs for ACS/Postgres. -- Don Baccus February 2000 -- for each user table my_table in which you want to stuff large -- amounts of data: -- define a column "lob integer references lobs" -- do "create trigger my_table_lob_trig before delete or update or insert -- on my_table for each row execute procedure on_lob_ref()" -- to initialize a row's lob column, use empty_lob(): -- insert into my_table (lob) values(empty_lob()); -- deletes and updates on my_table use reference count information -- to delete data from lobs and lob_data when appropriate. create sequence lob_sequence; create table lobs ( lob_id integer not null constraint lobs_lob_id_pk primary key, refcount integer not null default 0 ); CREATE OR REPLACE FUNCTION lobs_delete_tr() RETURNS trigger AS $$ BEGIN delete from lob_data where lob_id = old.lob_id; return old; END; $$ LANGUAGE plpgsql; create trigger lobs_delete_tr before delete on lobs for each row execute procedure lobs_delete_tr(); create table lob_data ( lob_id integer not null constraint lob_data_lob_id_fk references lobs on delete cascade, segment integer not null, byte_len integer not null, data bytea not null, constraint lob_data_lob_id_segment_pk primary key (lob_id, segment) ); create index lob_data_index on lob_data(lob_id); -- Note - race conditions might cause problems here, but I -- couldn't get locking to work consistently between PG 6.5 -- and PG 7.0. The ACS doesn't share LOBs between tables -- or rows within a table anyway, I don't think/hope. CREATE OR REPLACE FUNCTION on_lob_ref() RETURNS trigger AS $$ BEGIN if TG_OP = 'UPDATE' then if new.lob = old.lob then return new; end if; end if; if TG_OP = 'INSERT' or TG_OP = 'UPDATE' then if new.lob is not null then insert into lobs select new.lob, 0 where 0 = (select count(*) from lobs where lob_id = new.lob); update lobs set refcount = refcount + 1 where lob_id = new.lob; end if; end if; if TG_OP <> 'INSERT' then if old.lob is not null then update lobs set refcount = refcount - 1 where lob_id = old.lob; delete from lobs where lob_id = old.lob and refcount = 0; end if; end if; if TG_OP = 'INSERT' or TG_OP = 'UPDATE' then return new; else return old; end if; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION empty_lob() RETURNS integer AS $$ BEGIN return nextval('lob_sequence'); END; $$ LANGUAGE plpgsql; -- added select define_function_args('lob_get_data','lob_id'); -- -- procedure lob_get_data/1 -- CREATE OR REPLACE FUNCTION lob_get_data( p_lob_id integer ) RETURNS text AS $$ DECLARE v_rec record; v_data text default ''; BEGIN for v_rec in select data, segment from lob_data where lob_id = p_lob_id order by segment loop v_data := v_data || v_rec.data; end loop; return v_data; END; $$ LANGUAGE plpgsql; -- added select define_function_args('lob_copy','from_id,to_id'); -- -- procedure lob_copy/2 -- CREATE OR REPLACE FUNCTION lob_copy( from_id integer, to_id integer ) RETURNS integer AS $$ DECLARE BEGIN if from_id is null then raise exception 'lob_copy: attempt to copy null from_id to % to_id',to_id; end if; insert into lobs (lob_id,refcount) values (to_id,0); insert into lob_data select to_id as lob_id, segment, byte_len, data from lob_data where lob_id = from_id; return null; END; $$ LANGUAGE plpgsql; -- added select define_function_args('lob_length','id'); -- -- procedure lob_length/1 -- CREATE OR REPLACE FUNCTION lob_length( id integer ) RETURNS integer AS $$ DECLARE BEGIN return sum(byte_len) from lob_data where lob_id = id; END; $$ LANGUAGE plpgsql;