104 lines
5.3 KiB
PL/PgSQL
104 lines
5.3 KiB
PL/PgSQL
BEGIN;
|
|
-- Canonical rows are separate from the legacy snapshot. Adoption is an explicit,
|
|
-- version-checked transaction; installing this schema does not migrate user data.
|
|
CREATE TABLE canvas_model_projects (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
root_id uuid NOT NULL,
|
|
document_format_version integer NOT NULL DEFAULT 1 CHECK(document_format_version=1),
|
|
migration_id uuid NOT NULL,
|
|
source_versions jsonb NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY(project,workspace_id,root_id),
|
|
FOREIGN KEY(project,workspace_id,root_id) REFERENCES materials ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE canvas_model_canvases (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
root_id uuid NOT NULL,
|
|
canvas_id uuid NOT NULL,
|
|
parent_canvas_id uuid,
|
|
entry_element_id text,
|
|
version bigint NOT NULL CHECK(version>0),
|
|
archived boolean NOT NULL DEFAULT false,
|
|
PRIMARY KEY(project,workspace_id,canvas_id),
|
|
UNIQUE(project,canvas_id),
|
|
UNIQUE(project,workspace_id,root_id,canvas_id),
|
|
CHECK((parent_canvas_id IS NULL)=(entry_element_id IS NULL)),
|
|
CHECK(parent_canvas_id IS NOT NULL OR canvas_id=root_id),
|
|
FOREIGN KEY(project,workspace_id,root_id) REFERENCES canvas_model_projects ON DELETE CASCADE,
|
|
FOREIGN KEY(project,workspace_id,canvas_id) REFERENCES materials ON DELETE CASCADE,
|
|
FOREIGN KEY(project,workspace_id,root_id,parent_canvas_id) REFERENCES canvas_model_canvases(project,workspace_id,root_id,canvas_id) DEFERRABLE INITIALLY DEFERRED
|
|
);
|
|
CREATE UNIQUE INDEX canvas_model_active_entry ON canvas_model_canvases(project,workspace_id,entry_element_id) WHERE NOT archived;
|
|
CREATE TABLE canvas_model_elements (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
root_id uuid NOT NULL,
|
|
element_id text NOT NULL CHECK(element_id ~ '^[A-Za-z0-9_-]{1,128}$'),
|
|
owner_canvas_id uuid NOT NULL,
|
|
incarnation bigint NOT NULL CHECK(incarnation>0 AND incarnation<=9007199254740991),
|
|
deleted_incarnation bigint NOT NULL DEFAULT 0 CHECK(deleted_incarnation>=0 AND deleted_incarnation<=incarnation),
|
|
record jsonb NOT NULL,
|
|
deleted boolean NOT NULL DEFAULT false,
|
|
PRIMARY KEY(project,element_id),
|
|
UNIQUE(project,workspace_id,element_id),
|
|
CHECK(jsonb_typeof(record)='object' AND record->>'id'=element_id AND record->>'ownerCanvasId'=owner_canvas_id::text AND (record->>'incarnation')::bigint=incarnation),
|
|
CHECK(NOT deleted OR deleted_incarnation=incarnation),
|
|
FOREIGN KEY(project,workspace_id,root_id) REFERENCES canvas_model_projects ON DELETE CASCADE,
|
|
FOREIGN KEY(project,workspace_id,root_id,owner_canvas_id) REFERENCES canvas_model_canvases(project,workspace_id,root_id,canvas_id) DEFERRABLE INITIALLY DEFERRED
|
|
);
|
|
CREATE INDEX canvas_model_owner ON canvas_model_elements(project,workspace_id,root_id,owner_canvas_id) WHERE NOT deleted;
|
|
ALTER TABLE canvas_model_canvases ADD CONSTRAINT canvas_model_entry_fk FOREIGN KEY(project,workspace_id,entry_element_id) REFERENCES canvas_model_elements(project,workspace_id,element_id) DEFERRABLE INITIALLY DEFERRED;
|
|
CREATE TABLE canvas_model_identity_map (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
root_id uuid NOT NULL,
|
|
source_canvas_id uuid NOT NULL,
|
|
source_structure text NOT NULL CHECK(source_structure IN('node','connection')),
|
|
source_element_id text NOT NULL,
|
|
element_id text NOT NULL,
|
|
PRIMARY KEY(project,workspace_id,root_id,source_canvas_id,source_structure,source_element_id),
|
|
UNIQUE(project,element_id),
|
|
FOREIGN KEY(project,workspace_id,root_id) REFERENCES canvas_model_projects ON DELETE CASCADE,
|
|
FOREIGN KEY(project,workspace_id,element_id) REFERENCES canvas_model_elements(project,workspace_id,element_id) DEFERRABLE INITIALLY DEFERRED
|
|
);
|
|
-- Logical operations cannot acquire a second successful execution under a new request key.
|
|
CREATE TABLE canvas_model_requests (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
account_id uuid NOT NULL,
|
|
request_id uuid NOT NULL,
|
|
root_id uuid NOT NULL,
|
|
action text NOT NULL,
|
|
protocol_version integer NOT NULL,
|
|
payload_hash text NOT NULL,
|
|
response_status integer NOT NULL,
|
|
response_body jsonb NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY(project,workspace_id,account_id,request_id),
|
|
FOREIGN KEY(project,workspace_id,root_id) REFERENCES materials ON DELETE RESTRICT,
|
|
FOREIGN KEY(project,account_id) REFERENCES project_accounts
|
|
);
|
|
CREATE TABLE canvas_model_operations (
|
|
project text NOT NULL,
|
|
workspace_id uuid NOT NULL,
|
|
operation_id uuid NOT NULL,
|
|
request_id uuid NOT NULL,
|
|
account_id uuid NOT NULL,
|
|
PRIMARY KEY(project,workspace_id,operation_id),
|
|
FOREIGN KEY(project,workspace_id,account_id,request_id) REFERENCES canvas_model_requests DEFERRABLE INITIALLY DEFERRED,
|
|
FOREIGN KEY(project,account_id) REFERENCES project_accounts
|
|
);
|
|
CREATE FUNCTION foundry_guard_canonical_canvas() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
IF (NEW.canvas_document IS DISTINCT FROM OLD.canvas_document OR NEW.canvas_version IS DISTINCT FROM OLD.canvas_version)
|
|
AND EXISTS(SELECT 1 FROM canvas_model_canvases c WHERE c.project=NEW.project AND c.workspace_id=NEW.workspace_id AND c.canvas_id=NEW.id)
|
|
AND current_setting('foundry.canonical_writer',true) IS DISTINCT FROM 'on' THEN
|
|
RAISE EXCEPTION 'canonical_write_required' USING ERRCODE='55000';
|
|
END IF;
|
|
RETURN NEW;
|
|
END $$;
|
|
CREATE TRIGGER canonical_canvas_write_barrier BEFORE UPDATE OF canvas_document,canvas_version ON materials FOR EACH ROW EXECUTE FUNCTION foundry_guard_canonical_canvas();
|
|
COMMIT;
|