View Issue Details

IDProjectCategoryView StatusLast Update
0005622CaseTalk ModelerOtherpublic2026-09-01 21:59
ReporterMarco Wobben Assigned To 
PrioritynormalSeverityfeatureReproducibilityhave not tried
Status newResolutionopen 
Summary0005622: Generate SQL that hardens state-machine transitions
Description

RESEARCH / ENHANCEMENT

So-called "temporal" conceptual models (e.g. role transitions Candidate -> Tenderer -> Winner, "a tenderer must exist before being designated winner") are, structurally, state machines: the states are roles/subtypes and the "X before Y" rules are simply which edges exist in the transition graph. The opportunity is to generate SQL that enforces these transitions, so the database itself guarantees state integrity rather than relying on application code.

Two generation targets, in priority order:

  1. STATELESS TRANSITION CHECK (primary, low cost)

    • Treat the set of allowed transitions as a population of a binary fact type: "transition (from-state) to (to-state)".
    • Generate an allowed_transitions(from_state, to_state) lookup table from that population.
    • Generate a trigger on the entity's status/role column that rejects any (old_state, new_state) pair not present in the lookup table.
    • No history required. Fits the existing fact-oriented, multi-dialect SQL generator: the transitions are just a constraint over a populated fact type, generated the same way other constraints are.
  2. HISTORY-BASED ORDERING (secondary, opt-in, dialect-specific)

    • Needed only when a rule must assert that a prior state actually occurred ("this entity WAS a Tenderer at some earlier point"), not merely that the current move is legal.
    • Requires a history to query: system-versioned / temporal tables (SQL:2011, SQL Server, MariaDB, Oracle flashback) or an explicit event/audit log.
    • The rule becomes an EXISTS (... valid_from < current.valid_from) assertion. Storage-heavy and varies a lot per dialect.

Recommended scope: start with target 1 (covers the large majority of real cases and drops cleanly into current SQL generation). Treat target 2 as a separate opt-in feature for dialects that support system versioning.

Reference: Keet, "Can temporal conceptual modelling be useful in industry?" (2026-05-28) - EU e-procurement (ePO) examples.

Tagsexport, sql, state-machine
CaseTalk EditionCorporate

Relationships

related to 0005623 new Generate a SQL state table for state-machine states 

Activities

Marco Wobben

Marco Wobben

2026-05-29 19:20

administrator   ~0006213

EXAMPLE SQL SNIPPET

The article (Keet, 2026-05-28) does not transcribe its generated SQL; it only states the rule: "the start of tenderer must be before the start of winner, else that organisation won't be inserted in the winner table." The snippet below illustrates that exact rule in both forms discussed in the description.

-- (1) STATELESS TRANSITION TABLE -- the allowed edges of the state machine,
-- populated from the binary fact type "transition (from-state) to (to-state)".
CREATE TABLE allowed_transition (
from_state VARCHAR(20) NOT NULL,
to_state VARCHAR(20) NOT NULL,
CONSTRAINT pk_allowed_transition PRIMARY KEY (from_state, to_state)
);

INSERT INTO allowed_transition (from_state, to_state) VALUES
('Candidate', 'Tenderer'),
('Tenderer', 'Winner');

-- Trigger: reject any status change whose (old, new) pair is not an allowed edge.
CREATE TRIGGER trg_org_status_transition
BEFORE UPDATE OF status ON organisation
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
BEGIN
SELECT CASE WHEN NOT EXISTS (
SELECT 1 FROM allowed_transition
WHERE from_state = OLD.status AND to_state = NEW.status
) THEN RAISE(ABORT, 'Illegal state transition') END;
END;

-- (2) HISTORY-BASED ORDERING -- the article's actual "tenderer before winner"
-- rule, which needs a history of states to assert a prior state occurred.
-- (organisation_role keeps one row per role episode with a start timestamp.)
CREATE TRIGGER trg_winner_requires_prior_tenderer
BEFORE INSERT ON organisation_role
FOR EACH ROW
WHEN (NEW.role = 'Winner')
BEGIN
SELECT CASE WHEN NOT EXISTS (
SELECT 1 FROM organisation_role t
WHERE t.org_id = NEW.org_id
AND t.role = 'Tenderer'
AND t.start_ts < NEW.start_ts
) THEN RAISE(ABORT, 'Winner requires a prior Tenderer episode') END;
END;

Notes:

  • Form (1) is dialect-light and covers "legal next step" enforcement.
  • Form (2) is the article's exact temporal ordering; on SQL:2011 system-versioned tables the history table is implicit and the EXISTS queries the period view.
  • Syntax above is SQLite-flavoured for readability; the generator would emit the per-dialect equivalent (SQL Server, Oracle, PostgreSQL, etc.).

Issue History

Date Modified Username Field Change
2026-05-29 19:18 Marco Wobben New Issue
2026-05-29 19:18 Marco Wobben Tag Attached: sql
2026-05-29 19:18 Marco Wobben Tag Attached: state-machine
2026-05-29 19:18 Marco Wobben Tag Attached: export
2026-05-29 19:20 Marco Wobben Note Added: 0006213
2026-09-01 21:59 Marco Wobben Relationship added related to 0005623