-- -- OSSP as -- Accounting System -- Copyright (c) 2002-2003 Cable & Wireless Deutschland -- Copyright (c) 2002-2003 Ralf S. Engelschall -- -- This file is part of OSSP as, an accounting system which can be -- found at http://www.ossp.org/pkg/tool/as/ -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- as published by the Free Software Foundation; either version -- 2.0 of the License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -- USA, or contact The OSSP Project . -- -- bres-history.sql -- History Tracking Basic Research -- -- TODO: -- - delete entries from as_hs_ut for expiring history: does it still not correctly -- and especially should only allow the oldest entry to be deleted or implicitly -- delete also all older entries. -- ## _____________________________________________________________________ -- ## -- ## Database Schema -- ## _____________________________________________________________________ -- ## -- cascadingly drop existing schema -- (just ignore errors on console) DROP SEQUENCE as_hs_st_seq CASCADE; DROP TABLE as_hs_st CASCADE; DROP TABLE as_hs_ut CASCADE; DROP TABLE as_hs_ut_no CASCADE; DROP TABLE as_account CASCADE; -- System Time Sequence CREATE SEQUENCE as_hs_st_seq INCREMENT 1 MINVALUE 1 START 1; -- System Time Table CREATE TABLE as_hs_st ( st_oid BIGINT NOT NULL, -- REFERENCES as_oid (id_oid) FIXME -- MATCH FULL DEFERRABLE, FIXME -- object identifier -- [1] (rw) st_hid BIGINT UNIQUE NOT NULL DEFAULT nextval('as_hs_st_seq'), -- history id associated with system time (discrete monoton increasing) -- [742] (rw) st_beg TIMESTAMP (4) WITH TIME ZONE NOT NULL DEFAULT 'now' -- system time of point in history (not monoton increasing, could backstep) -- [2002-01-01 00:00:00.0000] (rw) ); -- User Time Table CREATE TABLE as_hs_ut ( ut_oid BIGINT NOT NULL, -- REFERENCES as_oid (id_oid) FIXME -- MATCH FULL DEFERRABLE, FIXME -- object identifier -- [1] (rw) ut_beg TIMESTAMP (4) WITH TIME ZONE NOT NULL DEFAULT 'now', -- user time begin (inclusive) -- [2002-01-01 00:00:00.0000] (rw) ut_end TIMESTAMP (4) WITH TIME ZONE NOT NULL DEFAULT 'infinity', -- user time end (exclusive) -- [2002-01-01 00:00:00.0000] (rw) ut_hid BIGINT NOT NULL REFERENCES as_hs_st (st_hid) MATCH FULL DEFERRABLE -- user time history identifier -- [42] (rw) ); -- User Time Table (non-overlapping) CREATE TABLE as_hs_ut_no ( ut_oid BIGINT NOT NULL, -- REFERENCES as_oid (id_oid) FIXME -- MATCH FULL DEFERRABLE, FIXME -- object identifier -- [1] (rw) ut_beg TIMESTAMP (4) WITH TIME ZONE NOT NULL DEFAULT 'now', -- user time begin (inclusive) -- [2002-01-01 00:00:00.0000] (rw) ut_end TIMESTAMP (4) WITH TIME ZONE NOT NULL DEFAULT 'infinity', -- user time end (exclusive) -- [2002-01-01 00:00:00.0000] (rw) ut_hid BIGINT NOT NULL REFERENCES as_hs_st (st_hid) MATCH FULL DEFERRABLE, -- user time history identifier -- [42] (rw) ut_hid_max BIGINT NOT NULL -- REFERENCES as_hs_st (st_hid) FIXME -- MATCH FULL DEFERRABLE FIXME -- user time history identifier (maximum value until which this entry is valid) -- [42] (rw) ); -- Account Table CREATE TABLE as_account( ac_oid BIGINT NOT NULL, -- REFERENCES as_oid (id_oid) -- MATCH FULL DEFERRABLE, -- object identifier -- [1] (rw) ac_name TEXT NOT NULL, -- name of account -- [foo] (rw) ac_hid BIGINT NOT NULL REFERENCES as_hs_st (st_hid) MATCH FULL DEFERRABLE -- account history identifier -- [42] (rw) ); -- ## _____________________________________________________________________ -- ## -- ## Automated Update of as_hs_ut_no Table -- ## _____________________________________________________________________ -- ## -- as_hs_ut update function DROP FUNCTION as_hs_ut_update(BIGINT, BIGINT) CASCADE; CREATE FUNCTION as_hs_ut_update(BIGINT, BIGINT) RETURNS VOID LANGUAGE 'plpgsql' STABLE AS ' DECLARE oid BIGINT; hid BIGINT; pos_prev TIMESTAMP (4) WITH TIME ZONE; pos_this TIMESTAMP (4) WITH TIME ZONE; pos_next TIMESTAMP (4) WITH TIME ZONE; alt_this BIGINT; alt_prev BIGINT; BEGIN oid := $1; hid := $2; -- delete all old entries corresponding to OID DELETE FROM as_hs_ut_no WHERE ut_oid = oid AND ut_hid_max = hid; -- initialize iteration variables pos_prev := ''-infinity''; pos_this := ''-infinity''; pos_next := NULL; alt_this := NULL; alt_prev := NULL; LOOP -- determine next position from existing begin/end positions pos_next := ( SELECT min(v) FROM ( (SELECT min(ut_beg) FROM as_hs_ut WHERE ut_beg > pos_this AND ut_oid = oid AND ut_hid <= hid) UNION (SELECT min(ut_end) FROM as_hs_ut WHERE ut_end > pos_this AND ut_oid = oid AND ut_hid <= hid) ) AS result (v) ); IF pos_next IS NULL THEN pos_next := ''infinity''; END IF; -- determine current altidude alt_this := ( SELECT max(ut_hid) FROM as_hs_ut WHERE ut_beg <= pos_this AND pos_this < ut_end AND ut_oid = oid AND ut_hid <= hid ); IF alt_this IS NULL THEN alt_this := -1; END IF; -- RAISE NOTICE ''pos_this = %, pos_next = %, alt_this = %'', pos_this, pos_next, alt_this; IF alt_this = -1 THEN -- RAISE NOTICE ''hit ground - pruning entry''; ELSE IF alt_this = alt_prev THEN -- RAISE NOTICE ''keeping altitude - consolidating entry''; UPDATE as_hs_ut_no SET ut_end = pos_next WHERE ut_hid = alt_this AND ut_beg = pos_prev AND ut_end = pos_this AND ut_oid = oid AND ut_hid_max = hid; ELSE -- RAISE NOTICE ''changing altitude - new entry''; INSERT INTO as_hs_ut_no (ut_hid, ut_oid, ut_beg, ut_end, ut_hid_max) VALUES (alt_this, oid, pos_this, pos_next, hid); pos_prev := pos_this; END IF; END IF; -- stop if maximum is reached IF pos_next >= ''infinity'' THEN EXIT; END IF; -- move one step pos_this := pos_next; alt_prev := alt_this; END LOOP; RETURN; END; '; -- as_hs_ut trigger stub function DROP FUNCTION ut_trigger() CASCADE; CREATE FUNCTION ut_trigger() RETURNS TRIGGER LANGUAGE 'plpgsql' STABLE AS ' BEGIN IF TG_OP = ''INSERT'' OR TG_OP = ''UPDATE'' THEN PERFORM as_hs_ut_update(NEW.ut_oid, NEW.ut_hid); ELSIF TG_OP = ''DELETE'' THEN PERFORM as_hs_ut_update(OLD.ut_oid, OLD.ut_hid); END IF; RETURN NULL; END; '; -- as_hs_ut trigger --DROP TRIGGER ut_trigger ON as_hs_ut CASCADE; CREATE TRIGGER ut_trigger AFTER INSERT OR UPDATE OR DELETE ON as_hs_ut FOR EACH ROW EXECUTE PROCEDURE ut_trigger (); -- ## _____________________________________________________________________ -- ## -- ## Convinience History Tracking Constructor/Destructor Functions -- ## hid := as_hs_make([oid[, ut_beg, ut_end[, st_beg]]]); -- ## as_hs_delete(oid, st_hid); -- ## _____________________________________________________________________ -- ## -- hid := as_hs_make(oid, ut_beg, ut_end, st_beg) CREATE OR REPLACE FUNCTION as_hs_make (BIGINT, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS BIGINT LANGUAGE 'plpgsql' STABLE AS ' DECLARE var_oid BIGINT; var_ut_beg TIMESTAMP (4) WITH TIME ZONE; var_ut_end TIMESTAMP (4) WITH TIME ZONE; var_st_beg TIMESTAMP (4) WITH TIME ZONE; var_hid BIGINT; BEGIN var_oid := $1; var_ut_beg := $2; IF var_ut_beg IS NULL THEN var_ut_beg := now(); END IF; var_ut_end := $3; IF var_ut_end IS NULL THEN var_ut_end := ''infinity''; END IF; var_st_beg := $4; IF var_st_beg IS NULL THEN var_st_beg := now(); END IF; var_hid := NULL; IF var_oid IS NOT NULL THEN -- create new system time entry INSERT INTO as_hs_st (st_oid, st_beg) VALUES (var_oid, var_st_beg); var_hid := currval(''as_hs_st_seq''); -- create new user time entry INSERT INTO as_hs_ut (ut_oid, ut_beg, ut_end, ut_hid) VALUES (var_oid, var_ut_beg, var_ut_end, var_hid); ELSE -- return just the current/maximum history id var_hid := currval(''as_hs_st_seq''); END IF; RETURN var_hid; END; '; -- convenience function overloading CREATE OR REPLACE FUNCTION as_hs_make (BIGINT, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS BIGINT LANGUAGE 'plpgsql' STABLE AS ' BEGIN RETURN as_hs_make($1, $2, $3, NULL); END; '; CREATE OR REPLACE FUNCTION as_hs_make (BIGINT, TIMESTAMP (4) WITH TIME ZONE) RETURNS BIGINT LANGUAGE 'plpgsql' STABLE AS ' BEGIN RETURN as_hs_make($1, $2, NULL, NULL); END; '; CREATE OR REPLACE FUNCTION as_hs_make(BIGINT) RETURNS BIGINT LANGUAGE 'plpgsql' STABLE AS ' BEGIN RETURN as_hs_make($1, NULL, NULL, NULL); END; '; CREATE OR REPLACE FUNCTION as_hs_make() RETURNS BIGINT LANGUAGE 'plpgsql' STABLE AS ' BEGIN RETURN as_hs_make(NULL, NULL, NULL, NULL); END; '; -- as_hs_delete(oid, st_hid); CREATE OR REPLACE FUNCTION as_hs_delete (BIGINT, BIGINT) RETURNS VOID LANGUAGE 'plpgsql' STABLE AS ' DECLARE var_oid BIGINT; var_hid BIGINT; BEGIN var_oid := $1; var_hid := $2; DELETE FROM as_hs_ut WHERE ut_oid = var_oid AND ut_hid = var_hid; DELETE FROM as_hs_st WHERE st_oid = var_oid AND st_hid = var_hid; RETURN; END; '; -- ## _____________________________________________________________________ -- ## -- ## Parametrized History Table View -- ## * := as_hs(ut_beg[, ut_end[, st_beg[, st_end[, st_max]]]]) -- ## _____________________________________________________________________ -- ## -- This finds (optionally only maximum) oid/hid combinations between -- st_beg and st_end (both inclusive) system time, for which the -- user time touches the time range between ut_beg and ut_end (both -- inclusive). -- parametrized view return type DROP TYPE as_hs_t CASCADE; CREATE TYPE as_hs_t AS ( hs_oid BIGINT, hs_hid BIGINT ); CREATE OR REPLACE FUNCTION as_hs (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, INTEGER) RETURNS SETOF as_hs_t LANGUAGE 'plpgsql' STABLE AS ' DECLARE arg_ut_beg TIMESTAMP (4) WITH TIME ZONE; -- user time begin (including) arg_ut_end TIMESTAMP (4) WITH TIME ZONE; -- user time end (including) arg_st_beg TIMESTAMP (4) WITH TIME ZONE; -- system time begin (including) arg_st_end TIMESTAMP (4) WITH TIME ZONE; -- system time end (including) arg_st_max INTEGER; -- whether to take only maximum system time into account rec RECORD; BEGIN -- pre-process input arguments by providing reasonable defaults arg_ut_beg := $1; IF arg_ut_beg IS NULL THEN arg_ut_beg := ''-infinity''; END IF; arg_ut_end := $2; IF arg_ut_end IS NULL THEN arg_ut_end := ''infinity''; END IF; arg_st_beg := $3; IF arg_st_beg IS NULL THEN arg_st_beg := ''-infinity''; END IF; arg_st_end := $4; IF arg_st_end IS NULL THEN arg_st_end := ''infinity''; END IF; arg_st_max := $5; IF arg_st_max IS NULL THEN arg_st_max := 1; END IF; -- perform the query operation IF arg_st_max = 1 THEN -- variant 1: take only maximum system time for each user time and oid FOR rec IN SELECT ut_oid, ut_hid FROM as_hs_ut_no, ( SELECT st_oid, max(st_hid) AS st_hid FROM as_hs_st WHERE (arg_st_beg <= st_beg AND st_beg <= arg_st_end) GROUP BY st_oid ) AS as_hs_st WHERE ut_oid = st_oid AND ut_hid_max = st_hid AND (ut_beg <= arg_ut_end AND arg_ut_beg < ut_end) LOOP RETURN NEXT rec; END LOOP; ELSE -- variant 2: take all system times for each user time and oid FOR rec IN SELECT DISTINCT ut_oid, ut_hid FROM as_hs_ut_no, ( SELECT st_oid, st_hid FROM as_hs_st WHERE (arg_st_beg <= st_beg AND st_beg <= arg_st_end) ) AS as_hs_st WHERE ut_oid = st_oid AND ut_hid_max = st_hid AND (ut_beg <= arg_ut_end AND arg_ut_beg < ut_end) LOOP RETURN NEXT rec; END LOOP; END IF; RETURN; END; '; -- convenience overloading functions CREATE OR REPLACE FUNCTION as_hs (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_hs_t LANGUAGE 'sql' STABLE AS ' SELECT * FROM as_hs($1, $2, $3, $4, NULL); '; CREATE OR REPLACE FUNCTION as_hs (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_hs_t LANGUAGE 'sql' STABLE AS ' SELECT * FROM as_hs($1, $2, $3, NULL, NULL); '; CREATE OR REPLACE FUNCTION as_hs (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_hs_t LANGUAGE 'sql' STABLE AS ' SELECT * FROM as_hs($1, $2, NULL, NULL, NULL); '; CREATE OR REPLACE FUNCTION as_hs (TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_hs_t LANGUAGE 'sql' STABLE AS ' SELECT * FROM as_hs($1, NULL, NULL, NULL, NULL); '; CREATE OR REPLACE FUNCTION as_hs () RETURNS SETOF as_hs_t LANGUAGE 'sql' STABLE AS ' SELECT * FROM as_hs(NULL, NULL, NULL, NULL, NULL); '; -- ## _____________________________________________________________________ -- ## -- ## Account Table Convinience Manipulation Triggers -- ## _____________________________________________________________________ -- ## -- as_account trigger function CREATE OR REPLACE FUNCTION as_account_trigger() RETURNS TRIGGER LANGUAGE 'plpgsql' STABLE AS ' DECLARE hid INTEGER; BEGIN IF TG_WHEN = ''BEFORE'' AND TG_OP = ''INSERT'' THEN IF new.ac_hid IS NULL THEN -- auto-generate history id if not explicitly set by user new.ac_hid := as_hs_make(new.ac_oid); END IF; RETURN new; ELSIF TG_WHEN = ''BEFORE'' AND TG_OP = ''UPDATE'' THEN -- clone old entry to allow old entry to be overwritten as it would be new entry INSERT INTO as_account VALUES (old.ac_oid, old.ac_name, old.ac_hid); -- give new entry a new history id if not explicity set by user IF (new.ac_hid = old.ac_hid) THEN new.ac_hid := as_hs_make(new.ac_oid); END IF; RETURN new; ELSIF TG_WHEN = ''AFTER'' AND TG_OP = ''DELETE'' THEN -- delete corresponding history entries PERFORM as_hs_delete(old.ac_oid, old.ac_hid); RETURN old; END IF; END; '; -- as_account triggers CREATE TRIGGER as_account_trigger_before BEFORE INSERT OR UPDATE ON as_account FOR EACH ROW EXECUTE PROCEDURE as_account_trigger(); CREATE TRIGGER as_account_trigger_after AFTER DELETE ON as_account FOR EACH ROW EXECUTE PROCEDURE as_account_trigger(); -- ## _____________________________________________________________________ -- ## -- ## Parametrized Account Table View -- ## * := as_account([ut_beg[, ut_end[, st_beg[, st_end[, st_max]]]]]) -- ## _____________________________________________________________________ -- ## CREATE OR REPLACE FUNCTION as_account () RETURNS SETOF as_account LANGUAGE 'sql' STABLE AS ' SELECT ac.* FROM as_account ac, as_hs() WHERE ac_hid = hs_hid; '; CREATE OR REPLACE FUNCTION as_account (TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_account LANGUAGE 'sql' STABLE AS ' SELECT ac.* FROM as_account ac, as_hs($1) WHERE ac_hid = hs_hid; '; CREATE OR REPLACE FUNCTION as_account (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_account LANGUAGE 'sql' STABLE AS ' SELECT ac.* FROM as_account ac, as_hs($1, $2, $3) WHERE ac_hid = hs_hid; '; CREATE OR REPLACE FUNCTION as_account (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE) RETURNS SETOF as_account LANGUAGE 'sql' STABLE AS ' SELECT ac.* FROM as_account ac, as_hs($1, $2, $3, $4) WHERE ac_hid = hs_hid; '; CREATE OR REPLACE FUNCTION as_account (TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, TIMESTAMP (4) WITH TIME ZONE, INTEGER) RETURNS SETOF as_account LANGUAGE 'sql' STABLE AS ' SELECT ac.* FROM as_account ac, as_hs($1, $2, $3, $4, $5) WHERE ac_hid = hs_hid; '; -- ## _____________________________________________________________________ -- ## -- ## Test Data -- ## _____________________________________________________________________ -- ## -- whole test data as view -- # ^ ^ -- # | | -- # as_hs_st hid | | | | -- # 2007-01-01 #6 | [==20:bar3=[ | -- # 2006-01-01 #5 | [==20:bar2===========[ -- # 2005-01-01 #4 [=============20:bar1===========[ -- # 2004-01-01 #3 [=============10:foo============[ -- # 2002-01-01 #2 | [==10:bar==[ | -- # 2001-01-01 #1 [==10:foo==[ | | -- # as_hs_ut--> 0100-01-01 0200-01-01 0300-01-01 infinity -- whole test data as raw data -- # 1st (systime hid=1) create record for oid=10, name="foo", 100 <= usertime < 200 -- # 2nd (systime hid=2) create record for oid=10, name="bar", 200 <= usertime < 300 -- # 3rd (systime hid=3) create record for oid=10, name="foo", 100 <= usertime < oo -- # 4th (systime hid=4) create record for oid=20, name="bar1", 100 <= usertime < oo -- # 5th (systime hid=5) create record for oid=20, name="bar2", 200 <= usertime < oo -- # 6th (systime hid=6) create record for oid=20, name="bar3", 200 <= usertime < 300 -- whole test data in prosa -- # 1st create a new record "foo" containing payload valid between usertime 100 upto not including 200 -- # 2nd rename "foo" to "bar" and move usertime frame to 200 upto n. i. 300 -- # 3rd rename "bar" back to "foo", extend usertime begin back to original 100 and extend usertime end to infinity -- # 4th create a new record "bar1" containing payload valid between usertime 100 and infinity -- # 5th give "bar1" a new name "bar2" beginning with usertime 200 -- # 6th another new name "bar3" valid for usertime frame 200 upto n. i. 300 -- whole test data injected INSERT INTO as_account VALUES (10, 'foo', as_hs_make(10, '0100-01-01', '0200-01-01', '2001-01-01')); INSERT INTO as_account VALUES (10, 'bar', as_hs_make(10, '0200-01-01', '0300-01-01', '2002-01-01')); INSERT INTO as_account VALUES (10, 'foo', as_hs_make(10, '0100-01-01', 'infinity', '2004-01-01')); INSERT INTO as_account VALUES (20, 'bar1', as_hs_make(20, '0100-01-01', 'infinity', '2005-01-01')); INSERT INTO as_account VALUES (20, 'bar2', as_hs_make(20, '0200-01-01', 'infinity', '2006-01-01')); INSERT INTO as_account VALUES (20, 'bar3', as_hs_make(20, '0200-01-01', '0300-01-01', '2007-01-01')); -- ## _____________________________________________________________________ -- ## -- ## Test Data Dumping -- ## _____________________________________________________________________ -- ## select * from as_account; -- # ac_oid | ac_name | ac_hid -- # --------+---------+-------- -- # 10 | foo | 1 -- # 10 | bar | 2 -- # 10 | foo | 3 -- # 20 | bar1 | 4 -- # 20 | bar2 | 5 -- # 20 | bar3 | 6 -- # (6 rows) select * from as_hs_ut; -- # ut_oid | ut_beg | ut_end | ut_hid -- # --------+---------------------+---------------------+-------- -- # 10 | 0100-01-01 00:00:00 | 0200-01-01 00:00:00 | 1 -- # 10 | 0200-01-01 00:00:00 | 0300-01-01 00:00:00 | 2 -- # 10 | 0100-01-01 00:00:00 | infinity | 3 -- # 20 | 0100-01-01 00:00:00 | infinity | 4 -- # 20 | 0200-01-01 00:00:00 | infinity | 5 -- # 20 | 0200-01-01 00:00:00 | 0300-01-01 00:00:00 | 6 -- # (6 rows) select * from as_hs_ut_no; -- # ut_oid | ut_beg | ut_end | ut_hid | ut_hid_max -- # --------+---------------------+---------------------+--------+------------ -- # 10 | 0100-01-01 00:00:00 | 0200-01-01 00:00:00 | 1 | 1 -- # 10 | 0100-01-01 00:00:00 | 0200-01-01 00:00:00 | 1 | 2 -- # 10 | 0200-01-01 00:00:00 | 0300-01-01 00:00:00 | 2 | 2 -- # 10 | 0100-01-01 00:00:00 | infinity | 3 | 3 -- # 20 | 0100-01-01 00:00:00 | infinity | 4 | 4 -- # 20 | 0100-01-01 00:00:00 | 0200-01-01 00:00:00 | 4 | 5 -- # 20 | 0200-01-01 00:00:00 | infinity | 5 | 5 -- # 20 | 0100-01-01 00:00:00 | 0200-01-01 00:00:00 | 4 | 6 -- # 20 | 0200-01-01 00:00:00 | 0300-01-01 00:00:00 | 6 | 6 -- # 20 | 0300-01-01 00:00:00 | infinity | 5 | 6 -- # (10 rows) select * from as_hs_st; -- # st_oid | st_hid | st_beg -- # --------+--------+------------------------ -- # 10 | 1 | 2001-01-01 00:00:00+01 -- # 10 | 2 | 2002-01-01 00:00:00+01 -- # 10 | 3 | 2004-01-01 00:00:00+01 -- # 20 | 4 | 2005-01-01 00:00:00+01 -- # 20 | 5 | 2006-01-01 00:00:00+01 -- # 20 | 6 | 2007-01-01 00:00:00+01 -- # (6 rows) -- ## -- ## Test Usage -- ## -- whole test data retrieved SELECT * FROM as_account; -- # ac_oid | ac_name | ac_hid -- # --------+---------+-------- -- # 10 | foo | 1 -- # 10 | bar | 2 -- # 10 | foo | 3 -- # 20 | bar1 | 4 -- # 20 | bar2 | 5 -- # 20 | bar3 | 6 -- # (6 rows) -- for any usertime and any systime, list any valid oid for every point in systime (all oids with full history) SELECT * FROM as_hs('-infinity','infinity','-infinity','infinity',0); -- # hs_oid | hs_hid -- # --------+-------- -- # 10 | 1 -- # 10 | 2 -- # 10 | 3 -- # 20 | 4 -- # 20 | 5 -- # 20 | 6 -- # (6 rows) -- for any usertime and any systime, list max valid oid for every point in systime (oids with highest altitude) SELECT * FROM as_hs('-infinity','infinity','-infinity','infinity',1); -- same as SELECT FROM as_hs(); -- # hs_oid | hs_hid -- # --------+-------- -- # 10 | 3 -- # 20 | 4 -- # 20 | 6 -- # 20 | 5 -- # (4 rows) -- same as previous but join with payload to retrieve more details (details from oids with hightes altitude) SELECT * FROM as_account, as_hs() WHERE ac_hid = hs_hid; -- # ac_oid | ac_name | ac_hid | hs_oid | hs_hid -- # --------+---------+--------+--------+-------- -- # 10 | foo | 3 | 10 | 3 -- # 20 | bar1 | 4 | 20 | 4 -- # 20 | bar2 | 5 | 20 | 5 -- # 20 | bar3 | 6 | 20 | 6 -- # (4 rows) -- same as previous but we are only interested in oids valid for usertime point 222 SELECT * FROM as_account, as_hs('0222-01-01','0222-01-01','-infinity','infinity',1) WHERE ac_hid = hs_hid; -- # ac_oid | ac_name | ac_hid | hs_oid | hs_hid -- # --------+---------+--------+--------+-------- -- # 10 | foo | 3 | 10 | 3 -- # 20 | bar3 | 6 | 20 | 6 -- # (2 rows) -- same as previous but we look back in systime 2006 SELECT * FROM as_account, as_hs('0222-01-01','0222-01-01','-infinity','2006-01-01',1) WHERE ac_hid = hs_hid; -- # ac_oid | ac_name | ac_hid | hs_oid | hs_hid -- # --------+---------+--------+--------+-------- -- # 10 | foo | 3 | 10 | 3 -- # 20 | bar2 | 5 | 20 | 5 -- # (2 rows)