From a3a0f1b91dafd95c700cab1386678b5960f0ab81 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc kop@karlpinc.com" Date: Fri, 5 Jun 2026 01:22:25 +0000 Subject: [PATCH] Automatically maintain ARRIVALS.Seq --- db/schemas/lib/triggers/create/arrivals.m4 | 438 +++++++++++++++++- db/schemas/lib/triggers/create/events.m4 | 148 +++++- db/schemas/lib/triggers/drop/arrivals.m4 | 3 + db/schemas/lib/triggers/drop/events.m4 | 1 + db/schemas/sokwedb/indexes/create/arrivals.m4 | 3 + db/schemas/sokwedb/indexes/create/events.m4 | 7 + db/schemas/sokwedb/tables/create/arrivals.m4 | 1 + 7 files changed, 596 insertions(+), 5 deletions(-) diff --git a/db/schemas/lib/triggers/create/arrivals.m4 b/db/schemas/lib/triggers/create/arrivals.m4 index 92b9db8..b1d2196 100644 --- a/db/schemas/lib/triggers/create/arrivals.m4 +++ b/db/schemas/lib/triggers/create/arrivals.m4 @@ -23,18 +23,64 @@ include(`constants.m4')dnl include(`macros.m4')dnl -RAISE INFO 'arrivals_func'; -CREATE OR REPLACE FUNCTION arrivals_func () +dnl Macro to update Seq of other rows in the follow in a fashion +dnl consistent with either the new row or the old row. +dnl +dnl Syntax: _update_seqs(what) +dnl +dnl Input: +dnl what Either "NEW" or "OLD". +dnl +changequote([,]) +define([_update_seqs], [ + -- Update the Seq of the next event. + UPDATE arrivals + SET seq = NULL -- Re-compute + WHERE arrivals.eid = + (-- Find the next arrival event (of the individual in the follow). + SELECT next_events.eid + FROM roles AS this_role + , events AS this_event + JOIN events AS next_events + ON (next_events.fid = this_event.fid + AND next_events.behavior = 'sdb_arrival') + JOIN roles AS next_roles + ON (next_roles.eid = next_events.eid) + WHERE this_role.eid = $1.eid + AND this_event.eid = $1.eid + AND (next_events.start > this_event.start + OR (next_events.start = this_event.start + AND (next_events.stop > this_event.stop + OR (next_events.stop = this_event.stop + AND next_events.eid > $1.eid)))) + AND next_roles.participant = this_role.participant + -- Find earliest and break ties + ORDER BY next_events.start, next_events.stop, next_events.eid + LIMIT 1);])dnl +changequote(`,')dnl See above + + +RAISE INFO 'arrivals_before_func'; +CREATE OR REPLACE FUNCTION arrivals_before_func () RETURNS trigger LANGUAGE plpgsql sdb_function_set_search_path AS $$ + DECLARE + a_seq arrivals.seq%TYPE; + a_fid follows.fid%TYPE; + BEGIN - -- Function for arrivals insert and update triggers + -- Function for arrivals before insert and before update triggers -- -- AGPL_notice(` --', `2026', `The Meme Factory, Inc., www.karlpinc.com') + -- + -- Validate that the necessary conditions are established to + -- compute Seq values. + -- + IF TG_OP = 'UPDATE' THEN cannot_change(`ARRIVALS', `EID') END IF; @@ -47,7 +93,6 @@ CREATE OR REPLACE FUNCTION arrivals_func () a_start events.start%TYPE; a_stop events.stop%TYPE; -- FOLLOWS - a_fid follows.fid%TYPE; a_focal follows.focal%TYPE; a_date follows.date%TYPE; -- ROLES @@ -148,6 +193,139 @@ CREATE OR REPLACE FUNCTION arrivals_func () END; END IF; + -- + -- Validation complete, do the work of automatically computing data values + -- + + -- Compute Seq + IF TG_OP = 'INSERT' + OR (NEW.seq IS NOT NULL + AND NEW.seq <> OLD.seq) + OR NEW.seq IS NULL THEN + -- Automatically set the Seq value + -- Use the Start time value to sequence arrivals. + + -- Find the right sequence number to use + SELECT MAX(arrivals.seq) + 1, follows.fid + INTO a_seq , a_fid + FROM events + JOIN follows + ON (follows.fid = events.fid) + JOIN events AS prior_aevents + ON (prior_aevents.fid = events.fid + AND prior_aevents.behavior = 'sdb_arrival') + JOIN roles AS prior_roles + ON (prior_roles.eid = prior_aevents.eid) + JOIN arrivals + ON (arrivals.eid = prior_aevents.eid) + , roles + WHERE events.eid = NEW.eid + AND (prior_aevents.start < events.start + OR (prior_aevents.start = events.start + AND (prior_aevents.stop < events.stop + OR (prior_aevents.stop = events.stop + AND prior_aevents.eid < NEW.eid)))) + AND roles.eid = NEW.eid + AND prior_roles.participant = roles.participant + GROUP BY follows.fid; -- Which does nothing, really + IF NOT FOUND THEN + a_seq := 1; + SELECT events.fid + INTO a_fid + FROM events + WHERE events.eid = NEW.eid; + END IF; + + -- Complain if the user is attempting to set the Seq to + -- an incorrect value. + IF NEW.seq IS NOT NULL + AND NEW.seq <> a_seq THEN + DECLARE + -- EVENTS + a_behavior events.behavior%TYPE; + a_start events.start%TYPE; + a_stop events.stop%TYPE; + -- ROLES + a_pid roles.pid%TYPE; + a_role roles.role%TYPE; + a_participant roles.participant%TYPE; + -- FOLLOWS + a_focal follows.focal%TYPE; + a_date follows.date%TYPE; + + BEGIN + SELECT events.behavior, events.start, events.stop + , roles.pid, roles.role, roles.participant + , follows.focal, follows.date + INTO a_behavior , a_start , a_stop + , a_pid , a_role , a_participant + , a_focal , a_date + FROM events + JOIN follows ON (follows.fid = events.fid) + JOIN roles ON (roles.eid = NEW.eid) + WHERE events.eid = NEW.eid + ORDER BY roles.pid; -- Consistent error message + + RAISE EXCEPTION integrity_constraint_violation USING + MESSAGE = 'Error on ' || TG_OP || ' of ARRIVALS' + , DETAIL = 'Attempt to set the Seq to an incorrect value (' + || NEW.seq + || '); the correct value is (' + || a_seq + || '): Key (EID = (' + || NEW.eid + || '): Value (Seq) = (' + || NEW.seq + || '): Value (NestStart) = (' + || NEW.neststart + || '): Value (NestEnd) = (' + || NEW.nestend + || '): Value (Cycle) = (' + || NEW.cycle + || '): Key (ROLES.PID) = (' + || a_pid + || '), Value (ROLES.Role) = (' + || a_role + || '), Value (ROLES.Participant) = (' + || a_participant + || '): Key (EVENTS.EID) = (' + || NEW.eid + || '): Value (EVENTS.Behavior) = (' + || a_behavior + || '), Value (EVENTS.Start) = (' + || a_start + || '), Value (EVENTS.Stop) = (' + || a_stop + || '): Key (FOLLOWS.FID) = (' + || a_fid + || '), Value (FOLLOWS.Focal) = (' + || a_focal + || '), Value (FOLLOWS.Date) = (' + || a_date + || ')'; + END; + END IF; + + NEW.seq := a_seq; + END IF; + + RETURN NEW; + END; +$$; + + +RAISE INFO 'arrivals_func'; +CREATE OR REPLACE FUNCTION arrivals_func () + RETURNS trigger + LANGUAGE plpgsql + sdb_function_set_search_path + AS $$ + BEGIN + -- Function for arrivals insert and update triggers + -- + -- AGPL_notice(` --', `2025', + `The Meme Factory, Inc., www.karlpinc.com') + -- BIOGRAPHY_DATA IF TG_OP = 'INSERT' OR NEW.cycle <> OLD.cycle THEN @@ -480,6 +658,238 @@ CREATE OR REPLACE FUNCTION arrivals_func () END; END IF; + -- + -- Done with validation. Update things. + -- + + -- Update the Seq of the event(s) that need their sequence number + -- recomputed (and through that, recursively update all events + -- subsequent to that). + IF TG_OP = 'INSERT' + OR (NEW.seq IS NOT NULL + AND NEW.seq <> OLD.seq) THEN + + -- If we need to adjust earlier sequence numbers, do that first because + -- that might adjust the sequence numbers that fall after the current + -- one. + IF TG_OP = 'UPDATE' + AND NEW.seq > OLD.seq THEN + -- There is a gap in the sequencing, shift the post-gap sequence "down". + _update_seqs(`OLD') + END IF; + + IF TG_OP = 'INSERT' + OR NEW.seq < OLD.seq THEN + -- The seqeuence numbers that are "after" the current one + -- need to be increased. + _update_seqs(`NEW') + END IF; + END IF; + + RETURN NULL; + END; +$$; + + +RAISE INFO 'arrivals_delete_func'; +CREATE OR REPLACE FUNCTION arrivals_delete_func () + RETURNS trigger + LANGUAGE plpgsql + sdb_function_set_search_path + AS $$ + BEGIN + -- Function for arrivals delete trigger + -- + -- AGPL_notice(` --', `2026', + `The Meme Factory, Inc., www.karlpinc.com') + + -- Update the row that follows the gap created when OLD + -- row was deleted. + _update_seqs(`OLD') + + RETURN NULL; + END; +$$; + + +RAISE INFO 'arrivals_commit_func'; +CREATE OR REPLACE FUNCTION arrivals_commit_func () + RETURNS trigger + LANGUAGE plpgsql + sdb_function_set_search_path + AS $$ + DECLARE + -- EVENTS + a_behavior events.behavior%TYPE; + a_start events.start%TYPE; + a_stop events.stop%TYPE; + -- FOLLOWS + a_fid follows.fid%TYPE; + a_focal follows.focal%TYPE; + a_date follows.date%TYPE; + -- ROLES + a_pid roles.pid%TYPE; + a_role roles.role%TYPE; + a_participant roles.participant%TYPE; + + BEGIN + -- Function for arrivals on-commit insert and update triggers + -- + -- AGPL_notice(` --', `2026', + `The Meme Factory, Inc., www.karlpinc.com') + -- + -- Remarks: + -- See the EVENTS on-commit delete trigger for deletion checks. + + -- The Seq must start with 1 and can't have gaps, per follow, + -- per arriving individual. + IF (TG_OP = 'INSERT' + OR NEW.seq <> OLD.seq) THEN + + IF NEW.seq > 1 THEN + -- Does the expected NEW.seq - 1 row exist? + SELECT events.behavior, events.start, events.stop + , follows.fid, follows.focal, follows.date + , roles.pid, roles.role, roles.participant + INTO a_behavior , a_start , a_stop + , a_fid , a_focal , a_date + , a_pid , a_role , a_participant + FROM roles + , events + JOIN follows ON (follows.fid = events.fid) + WHERE roles.eid = NEW.eid + AND events.eid = NEW.eid + AND NOT EXISTS + (SELECT 1 + FROM events AS pevents + JOIN arrivals AS parrivals ON (parrivals.eid = pevents.eid) + JOIN roles AS proles ON (proles.eid = pevents.eid) + WHERE pevents.fid = follows.fid + AND pevents.behavior = 'sdb_arrival' + AND proles.participant = roles.participant + AND parrivals.seq = NEW.seq - 1); + + IF FOUND THEN + RAISE EXCEPTION integrity_constraint_violation USING + MESSAGE = 'Error on ' || TG_OP || ' of ARRIVALS' + , DETAIL = 'Arrival events must have Seq values that increase' + || ' without gaps, with respect to their related' + || ' EVENTS row; but there is no such ARRIVALS row' + || ' with a Seq of (' + || NEW.seq - 1 + || ') to pair with the ARRIVALS row:' + || ' Key (EID = (' + || NEW.eid + || '): Value (Seq) = (' + || NEW.seq + || '): Value (NestStart) = (' + || NEW.neststart + || '): Value (NestEnd) = (' + || NEW.nestend + || '): Value (Cycle) = (' + || NEW.cycle + || '): Value (datasource) = (' + || NEW.datasource + || '): Key (ROLES.PID) = (' + || a_pid + || '), Value (ROLES.Role) = (' + || a_role + || '), Value (ROLES.Participant) = (' + || a_participant + || '): Key (EVENTS.EID) = (' + || NEW.eid + || '): Value (EVENTS.Behavior) = (' + || a_behavior + || '), Value (EVENTS.Start) = (' + || a_start + || '), Value (EVENTS.Stop) = (' + || a_stop + || '): Key (FOLLOWS.FID) = (' + || a_fid + || '), Value (FOLLOWS.Focal) = (' + || a_focal + || '), Value (FOLLOWS.Date) = (' + || a_date + || ')'; + END IF; + END IF; + + IF TG_OP = 'UPDATE' THEN + -- When there is an OLD.Seq + 1 row, + -- does the expected OLD.seq row exist? + SELECT events.behavior, events.start, events.stop + , follows.fid, follows.focal, follows.date + , roles.pid, roles.role, roles.participant + INTO a_behavior , a_start , a_stop + , a_fid , a_focal , a_date + , a_pid , a_role , a_participant + FROM roles + , events + JOIN follows ON (follows.fid = events.fid) + JOIN events AS oldslot ON (oldslot.fid = events.fid) + JOIN arrivals ON (arrivals.eid = oldslot.eid) + WHERE roles.eid = OLD.eid + AND events.eid = OLD.eid + AND oldslot.behavior = 'sdb_arrival' + AND arrivals.seq = OLD.seq + 1 + AND NOT EXISTS + (SELECT 1 + FROM events AS pevents + JOIN arrivals AS parrivals + ON (parrivals.eid = pevents.eid) + JOIN roles AS proles + ON (proles.eid = pevents.eid) + WHERE pevents.fid = follows.fid + AND pevents.behavior = 'sdb_arrival' + AND proles.participant = roles.participant + AND parrivals.seq = OLD.seq); + + IF FOUND THEN + RAISE EXCEPTION integrity_constraint_violation USING + MESSAGE = 'Error on ' || TG_OP || ' of ARRIVALS' + , DETAIL = 'Arrival events must have Seq values that increase' + || ' without gaps, with respect to their related' + || ' EVENTS row; but there is no such ARRIVALS row' + || ' with a Seq of (' + || OLD.seq + || ') to pair with the ARRIVALS row:' + || ' Key (EID = (' + || NEW.eid + || '): Value (Seq) = (' + || NEW.seq + || '): Value (NestStart) = (' + || NEW.neststart + || '): Value (NestEnd) = (' + || NEW.nestend + || '): Value (Cycle) = (' + || NEW.cycle + || '): Value (datasource) = (' + || NEW.datasource + || '): Key (ROLES.PID) = (' + || a_pid + || '), Value (ROLES.Role) = (' + || a_role + || '), Value (ROLES.Participant) = (' + || a_participant + || '): Key (EVENTS.EID) = (' + || NEW.eid + || '): Value (EVENTS.Behavior) = (' + || a_behavior + || '), Value (EVENTS.Start) = (' + || a_start + || '), Value (EVENTS.Stop) = (' + || a_stop + || '): Key (FOLLOWS.FID) = (' + || a_fid + || '), Value (FOLLOWS.Focal) = (' + || a_focal + || '), Value (FOLLOWS.Date) = (' + || a_date + || ')'; + END IF; + END IF; + END IF; + RETURN NULL; END; $$; @@ -490,3 +900,23 @@ CREATE TRIGGER arrivals_trigger AFTER INSERT OR UPDATE ON arrivals FOR EACH ROW EXECUTE PROCEDURE arrivals_func(); + +RAISE INFO 'arrivals_delete_trigger'; +CREATE TRIGGER arrivals_delete_trigger + AFTER DELETE + ON arrivals FOR EACH ROW + EXECUTE PROCEDURE arrivals_delete_func(); + +RAISE INFO 'arrivals_before_trigger'; +CREATE TRIGGER arrivals_before_trigger + BEFORE INSERT OR UPDATE + ON arrivals FOR EACH ROW + EXECUTE PROCEDURE arrivals_before_func(); + +RAISE INFO 'arrivals_commit_trigger'; +CREATE CONSTRAINT TRIGGER arrivals_commit_trigger + AFTER INSERT OR UPDATE + ON arrivals + DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE arrivals_commit_func(); diff --git a/db/schemas/lib/triggers/create/events.m4 b/db/schemas/lib/triggers/create/events.m4 index 7c4cd95..bbe26b7 100644 --- a/db/schemas/lib/triggers/create/events.m4 +++ b/db/schemas/lib/triggers/create/events.m4 @@ -56,6 +56,143 @@ CREATE OR REPLACE FUNCTION events_update_func () -- require the old rows be deleted and then new ones added. cannot_change(`EVENTS', `Behavior') + IF NEW.behavior = 'sdb_arrival' + AND NEW.start <> OLD.start THEN + -- Changing the start time can change the ARRIVALS.Seq value + UPDATE arrivals + SET seq = NULL -- Recompute + WHERE arrivals.eid = NEW.eid; + END IF; + + RETURN NULL; + END; +$$; + + +RAISE INFO 'events_delete_commit_func'; +CREATE OR REPLACE FUNCTION events_delete_commit_func () + RETURNS trigger + LANGUAGE plpgsql + sdb_function_set_search_path + AS $$ + BEGIN + -- Function for events on-commit delete trigger + -- + -- AGPL_notice(` --', `2026', + `The Meme Factory, Inc., www.karlpinc.com') + -- + -- Remarks: + -- What we're relying on here is that events are deleted + -- when their related ARRIVALS row is deleted. + -- (At time of this writing, this is enforced with a warning, not a trigger.) + -- If this is the case, as it should be, the trigger cannot + -- be on the ARRIVALS table because the event may no longer + -- exist. In which case we can't find the follow that is + -- being validated. + + IF OLD.behavior = 'sdb_arrival' THEN + -- ARRIVALS.Seq can't have gaps, per follow, per arriving individual + DECLARE + -- ARRIVALS + a_seq arrivals.seq%TYPE; + a_neststart arrivals.neststart%TYPE; + a_nestend arrivals.nestend%TYPE; + a_cycle arrivals.cycle%TYPE; + a_datasource arrivals.datasource%TYPE; + -- EVENTS + a_behavior events.behavior%TYPE; + a_start events.start%TYPE; + a_stop events.stop%TYPE; + -- FOLLOWS + a_fid follows.fid%TYPE; + a_focal follows.focal%TYPE; + a_date follows.date%TYPE; + -- ROLES + a_pid roles.pid%TYPE; + a_role roles.role%TYPE; + a_participant roles.participant%TYPE; + + BEGIN + + -- Within the follow, for N > 1 Is there a Seq = N+1 row, + -- but not a Seq = N row? + SELECT arrivals.seq, arrivals.neststart + , arrivals.nestend, arrivals.cycle, arrivals.datasource + , events.behavior, events.start, events.stop + , follows.fid, follows.focal, follows.date + , roles.pid, roles.role, roles.participant + INTO a_seq , a_neststart + , a_nestend , a_cycle , a_datasource + , a_behavior , a_start , a_stop + , a_fid , a_focal , a_date + , a_pid , a_role , a_participant + FROM follows + , events + JOIN arrivals ON (arrivals.eid = events.eid) + JOIN roles ON (roles.eid = events.eid) + WHERE follows.fid = OLD.fid + AND events.fid = OLD.fid + AND events.behavior = 'sdb_arrival' + AND arrivals.seq > 1 + AND NOT EXISTS + (SELECT 1 + FROM events AS p1events + JOIN roles AS p1roles + ON (p1roles.eid = p1events.eid) + JOIN arrivals AS p1arrivals + ON (p1arrivals.eid = p1events.eid) + WHERE p1events.fid = OLD.fid + AND p1roles.participant = roles.participant + AND p1arrivals.seq = arrivals.seq - 1) + -- Produce a consistent error message + ORDER BY roles.participant, arrivals.seq; + + IF FOUND THEN + RAISE EXCEPTION integrity_constraint_violation USING + MESSAGE = 'Error on DELETE of ARRIVALS' + , DETAIL = 'Arrival events must have Seq values that increase' + || ' without gaps, with respect to their related' + || ' EVENTS row; but there is no such ARRIVALS row' + || ' with a Seq of (' + || a_seq - 1 + || ') to pair with the following ARRIVALS row:' + || ' Key (EID = (' + || a_eid + || '): Value (Seq) = (' + || a_seq + || '): Value (NestStart) = (' + || a_neststart + || '): Value (NestEnd) = (' + || a_nestend + || '): Value (Cycle) = (' + || a_cycle + || '): Value (datasource) = (' + || a_datasource + || '): Key (ROLES.PID) = (' + || a_pid + || '), Value (ROLES.Role) = (' + || a_role + || '), Value (ROLES.Participant) = (' + || a_participant + || '): Key (EVENTS.EID) = (' + || OLD.eid + || '): Value (EVENTS.Behavior) = (' + || a_behavior + || '), Value (EVENTS.Start) = (' + || a_start + || '), Value (EVENTS.Stop) = (' + || a_stop + || '): Key (FOLLOWS.FID) = (' + || a_fid + || '), Value (FOLLOWS.Focal) = (' + || a_focal + || '), Value (FOLLOWS .Date) = (' + || a_date + || ')'; + END IF; + END; + END IF; + RETURN NULL; END; $$; @@ -63,6 +200,15 @@ $$; RAISE INFO 'events_update_trigger'; CREATE TRIGGER events_update_trigger - AFTER INSERT OR UPDATE + AFTER UPDATE ON events FOR EACH ROW EXECUTE PROCEDURE events_update_func(); + +RAISE INFO 'events_delete_commit_trigger'; +CREATE CONSTRAINT TRIGGER events_delete_commit_trigger + AFTER DELETE + ON events + DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + Execute PROCEDURE events_delete_commit_func(); + diff --git a/db/schemas/lib/triggers/drop/arrivals.m4 b/db/schemas/lib/triggers/drop/arrivals.m4 index ec10cfc..5704078 100644 --- a/db/schemas/lib/triggers/drop/arrivals.m4 +++ b/db/schemas/lib/triggers/drop/arrivals.m4 @@ -21,3 +21,6 @@ dnl m4 includes include(`copyright.m4')dnl DROP FUNCTION IF EXISTS arrivals_func() CASCADE; +DROP FUNCTION IF EXISTS arrivals_delete_func() CASCADE; +DROP FUNCTION IF EXISTS arrivals_before_func() CASCADE; +DROP FUNCTION IF EXISTS arrivals_commit_func() CASCADE; diff --git a/db/schemas/lib/triggers/drop/events.m4 b/db/schemas/lib/triggers/drop/events.m4 index 41f2c9b..8b8d433 100644 --- a/db/schemas/lib/triggers/drop/events.m4 +++ b/db/schemas/lib/triggers/drop/events.m4 @@ -21,3 +21,4 @@ dnl m4 includes include(`copyright.m4')dnl DROP FUNCTION IF EXISTS events_update_func() CASCADE; +DROP FUNCTION IF EXISTS events_delete_commit_func() CASCADE; diff --git a/db/schemas/sokwedb/indexes/create/arrivals.m4 b/db/schemas/sokwedb/indexes/create/arrivals.m4 index 206b9b9..49cf3d6 100644 --- a/db/schemas/sokwedb/indexes/create/arrivals.m4 +++ b/db/schemas/sokwedb/indexes/create/arrivals.m4 @@ -21,6 +21,9 @@ include(`copyright.m4')dnl include(`constants.m4')dnl include(`indexmacros.m4')dnl +-- We _could_ make a covering index on EID+Seq for Seq maintenance. +-- But the ARRIVALS row is going to be in RAM anyway, so why? + CREATE INDEX IF NOT EXISTS arrivals_cycle ON arrivals (cycle); CREATE INDEX IF NOT EXISTS arrivals_datasource ON arrivals diff --git a/db/schemas/sokwedb/indexes/create/events.m4 b/db/schemas/sokwedb/indexes/create/events.m4 index ae13f27..040ace5 100644 --- a/db/schemas/sokwedb/indexes/create/events.m4 +++ b/db/schemas/sokwedb/indexes/create/events.m4 @@ -33,3 +33,10 @@ CREATE INDEX IF NOT EXISTS events_start ON events (start); CREATE INDEX IF NOT EXISTS events_stop ON events (stop); + +-- If we felt like it, we could create a covering index to support +-- arrival event sequencing. But it does not seem worth doing. +-- CREATE INDEX IF NOT EXISTS events_fid_start ON events +-- (fid, start) +-- WHERE fid IS NOT NULL +-- AND behavior = 'sdb_arrival'; diff --git a/db/schemas/sokwedb/tables/create/arrivals.m4 b/db/schemas/sokwedb/tables/create/arrivals.m4 index 0756ace..57af2d8 100644 --- a/db/schemas/sokwedb/tables/create/arrivals.m4 +++ b/db/schemas/sokwedb/tables/create/arrivals.m4 @@ -27,6 +27,7 @@ CREATE TABLE arrivals ( eid INTEGER NOT NULL REFERENCES events ,seq INTEGER + positive_check(`Seq') ,neststart BOOLEAN NOT NULL ,nestend BOOLEAN NOT NULL ,cycle TEXT NOT NULL -- 2.34.1