Files
paliad/internal/db/migrations/157_scenario_builder_foundation.up.sql
mAi 0f3c30a647
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
feat(scenario-builder): B0 schema foundation + minimal API (m/paliad#153)
t-paliad-340 — B0 of edison's 7-slice train (PRD §7.1). DB-only: schema
+ RLS land, dev-only test route exercises the surface, no user-facing
change. B1 wires the actual builder UI on top.

Migration 157 (additive on the legacy mig-145 scenarios table — 0 rows
in prod, safe to relax):
- paliad.scenarios gets owner_id / status / origin_project_id /
  promoted_project_id / stichtag / notes. spec drops NOT NULL and the
  scenarios_unique_per_scope constraint drops (the builder allows
  multiple scratch + Unbenanntes Szenario rows per user).
- New tables: scenario_proceedings, scenario_events, scenario_shares.
- paliad.projects.origin_scenario_id for the promote-to-project audit
  trail (the FK lands now; the wizard ships in B5).
- paliad.can_see_scenario(uuid) STABLE SECURITY DEFINER helper covering
  owner / share / global_admin / two legacy paths.
- Replacement RLS on scenarios + RLS on the three new tables; legacy
  service + handlers stay live and unchanged.

PRD §5.1 deviations called out in the migration header:
- proceeding_type_id is integer (live schema), not uuid (PRD draft).
- FK target is paliad.users, matching the rest of paliad's schema.

Go surface:
- ScenarioBuilderService — list/create/get-deep/patch scenarios,
  add/patch/delete proceedings, add/patch/delete events,
  add/delete shares. Writes wrap in transactions with set_config(
  paliad.audit_reason, ..., true) per event_choice_service.go pattern.
- /api/builder/scenarios/* — handlers register under a builder/
  prefix so the legacy /api/scenarios surface still works.
- /dev/scenario-builder — single-page HTML form gated to
  PaliadinOwnerEmail, exercises the B0 surface without Postman.
- Live-DB integration test (TEST_DATABASE_URL gated) covers
  create + list + deep-get + share + visibility negatives + patch.

Audit-first: every DDL block ran clean via BEGIN/ROLLBACK against
the live DB before commit; end-to-end sanity (insert chain + CHECK
constraints + CASCADE-on-delete) verified via the Supabase MCP.

bun build clean. go vet + go test -short ./... green.
2026-05-27 23:50:14 +02:00

501 lines
22 KiB
PL/PgSQL

-- 157_scenario_builder_foundation — t-paliad-340 / m/paliad#153 B0
--
-- Schema foundation for the Litigation Builder (PRD
-- docs/plans/prd-procedures-litigation-planner-2026-05-27.md §5.1 + §5.2).
-- Phase B0 of the 7-slice train described in PRD §7.1. DB-only — no UI
-- depends on these tables yet; B1 wires the builder shell on top.
--
-- What this migration adds:
--
-- 1. Six new columns on paliad.scenarios for the builder shape:
-- owner_id, status, origin_project_id, promoted_project_id,
-- stichtag, notes.
-- Two relaxations on existing columns:
-- - spec NOT NULL → NULL (the builder normalises spec contents
-- into scenario_proceedings / scenario_events; new rows skip
-- spec entirely. Legacy callers from mig 145 still provide it
-- explicitly, so they keep inserting valid rows.)
-- - DROP CONSTRAINT scenarios_unique_per_scope (the builder
-- allows multiple "Unbenanntes Szenario" + multiple scratch
-- scenarios per user — uniqueness on (project_id, created_by,
-- name) blocks that. The legacy service treated the constraint
-- as UX collision avoidance, not correctness.)
--
-- 2. Three new tables for the normalised builder shape:
-- - paliad.scenario_proceedings (one row per proceeding in a
-- scenario; multi-proceeding constellations + spawned children)
-- - paliad.scenario_events (one row per event card on the
-- canvas; planned / filed / skipped state + actual_date + notes
-- + per-card optional horizon)
-- - paliad.scenario_shares (read-only team shares; owner is
-- the sole editor)
--
-- 3. One new column on paliad.projects:
-- - origin_scenario_id — audit trail for promote-to-project
-- (B5; the column lands now so the FK is in place when the
-- wizard arrives).
--
-- 4. New helper function paliad.can_see_scenario(_scenario_id) that
-- mirrors paliad.can_see_project's STABLE SECURITY DEFINER shape.
-- Visibility logic:
-- - global_admin sees everything,
-- - owner_id = auth.uid() (builder-owned scenarios),
-- - scenario_shares.shared_with_user_id = auth.uid()
-- (read-only shared scenarios),
-- - legacy project-scoped scenarios (owner_id IS NULL AND
-- project_id IS NOT NULL) follow can_see_project(project_id),
-- - legacy abstract scenarios (owner_id IS NULL AND project_id
-- IS NULL) follow created_by = auth.uid().
--
-- 5. Replacement RLS policies on paliad.scenarios that fold builder
-- visibility together with the legacy shape. The legacy
-- project_* / abstract_* policies are dropped (they covered only
-- legacy paths) and rewritten as a single pair of policies that
-- treats owner_id, scenario_shares, and the legacy paths uniformly.
--
-- Builder-only RLS for the three new tables: read = scenario
-- visibility; write = scenario owner (or legacy editor) only.
--
-- PRD §5.1 deviations called out for the reader:
--
-- - PRD specs `proceeding_type_id uuid REFERENCES paliad.proceeding_types(id)`.
-- The live column is `integer` (see paliad.proceeding_types.id);
-- scenario_proceedings.proceeding_type_id is integer here to match
-- the real FK target. PRD authors did not check the column type;
-- this migration uses the truth on disk.
--
-- - PRD references `auth.users(id)` for owner_id and share columns;
-- the established paliad convention (see paliad.projects.created_by,
-- paliad.scenarios.created_by) uses `paliad.users(id)`. Same UUIDs
-- either way (paliad.users.id == auth.users.id), but the FK targets
-- paliad.users to stay consistent with project tables.
--
-- Audit-first: all DDL ran clean against a BEGIN/ROLLBACK probe on the
-- live DB before this file was committed. paliad.scenarios has 0 rows
-- (verified pre-mig), so the column additions and constraint relaxations
-- have no data impact.
BEGIN;
SELECT set_config(
'paliad.audit_reason',
'mig 157: Scenario builder foundation (t-paliad-340 / m/paliad#153 B0)',
true
);
-- ----------------------------------------------------------------
-- 1. paliad.scenarios — additive columns + constraint relaxations
-- ----------------------------------------------------------------
ALTER TABLE paliad.scenarios
ADD COLUMN owner_id uuid NULL REFERENCES paliad.users(id) ON DELETE CASCADE,
ADD COLUMN status text NOT NULL DEFAULT 'active'
CHECK (status IN ('active','archived','promoted')),
ADD COLUMN origin_project_id uuid NULL REFERENCES paliad.projects(id) ON DELETE SET NULL,
ADD COLUMN promoted_project_id uuid NULL REFERENCES paliad.projects(id) ON DELETE SET NULL,
ADD COLUMN stichtag date NULL,
ADD COLUMN notes text NULL;
ALTER TABLE paliad.scenarios ALTER COLUMN spec DROP NOT NULL;
ALTER TABLE paliad.scenarios DROP CONSTRAINT IF EXISTS scenarios_unique_per_scope;
CREATE INDEX scenarios_owner_status_idx
ON paliad.scenarios(owner_id, status)
WHERE owner_id IS NOT NULL;
CREATE INDEX scenarios_updated_idx
ON paliad.scenarios(owner_id, updated_at DESC)
WHERE owner_id IS NOT NULL;
COMMENT ON COLUMN paliad.scenarios.owner_id IS
'Litigation Builder owner (PRD §5.1). NULL = legacy composition-spec '
'scenario from m/paliad#124 Slice D (mig 145). Builder rows MUST have '
'owner_id set; the application enforces it via ScenarioBuilderService.';
COMMENT ON COLUMN paliad.scenarios.status IS
'Lifecycle: active (default; user-editable) / archived (soft-deleted, '
'still visible in side panel) / promoted (converted to project via '
'B5 wizard; read-only). Legacy mig-145 rows default to active.';
COMMENT ON COLUMN paliad.scenarios.origin_project_id IS
'Set when the scenario was exported from an existing project '
'("Im Builder öffnen" — Akte mode, PRD §2.3).';
COMMENT ON COLUMN paliad.scenarios.promoted_project_id IS
'Set after the scenario was promoted to a real project via the 3-step '
'wizard (PRD §5.4). Together with paliad.projects.origin_scenario_id, '
'forms the bidirectional audit link.';
COMMENT ON COLUMN paliad.scenarios.stichtag IS
'Scenario-level default Stichtag; per-proceeding overrides in '
'paliad.scenario_proceedings.stichtag take precedence.';
-- ----------------------------------------------------------------
-- 2. paliad.scenario_proceedings — one proceeding per scenario row
-- ----------------------------------------------------------------
CREATE TABLE paliad.scenario_proceedings (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scenario_id uuid NOT NULL
REFERENCES paliad.scenarios(id) ON DELETE CASCADE,
proceeding_type_id integer NOT NULL
REFERENCES paliad.proceeding_types(id),
primary_party text NULL
CHECK (primary_party IN ('claimant','defendant')),
scenario_flags jsonb NOT NULL DEFAULT '{}'::jsonb
CHECK (jsonb_typeof(scenario_flags) = 'object'),
parent_scenario_proceeding_id uuid NULL
REFERENCES paliad.scenario_proceedings(id) ON DELETE CASCADE,
spawn_anchor_event_id uuid NULL
REFERENCES paliad.sequencing_rules(id),
ordinal int NOT NULL DEFAULT 0,
stichtag date NULL,
detailgrad text NOT NULL DEFAULT 'selected'
CHECK (detailgrad IN ('selected','all_options')),
appeal_target text NULL,
collapsed boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX scenario_proceedings_scenario_idx
ON paliad.scenario_proceedings(scenario_id, ordinal);
CREATE INDEX scenario_proceedings_parent_idx
ON paliad.scenario_proceedings(parent_scenario_proceeding_id)
WHERE parent_scenario_proceeding_id IS NOT NULL;
COMMENT ON TABLE paliad.scenario_proceedings IS
'One proceeding inside a Litigation Builder scenario. Multiple rows '
'per scenario for multi-proceeding constellations. '
'parent_scenario_proceeding_id self-refs for spawned children '
'(e.g. upc.ccr.cfi spawned by with_ccr on upc.inf.cfi). '
'PRD §5.1, m/paliad#153 B0.';
COMMENT ON COLUMN paliad.scenario_proceedings.primary_party IS
'Per-proceeding perspective ("our side"). NULL = no perspective '
'picked yet (both party columns render with natural labels). '
'Per-proceeding so multi-jurisdiction constellations can flip side '
'independently (PRD §3.3).';
COMMENT ON COLUMN paliad.scenario_proceedings.scenario_flags IS
'Per-proceeding flags (e.g. {"with_ccr": true, "with_amend": false}). '
'Mirrors paliad.projects.scenario_flags shape but lives per-proceeding-'
'per-scenario. Validated by the application against '
'paliad.scenario_flag_catalog at write time.';
COMMENT ON COLUMN paliad.scenario_proceedings.spawn_anchor_event_id IS
'Which sequencing_rule of the parent proceeding caused this spawn. '
'NULL for root proceedings. Used by the UI to place the spawned child '
'triplet directly below the parent at the spawn node (PRD §3.6).';
COMMENT ON COLUMN paliad.scenario_proceedings.ordinal IS
'Stack order on canvas (top to bottom). Siblings under the same '
'parent (or top-level) are ordered by ordinal asc, then created_at.';
COMMENT ON COLUMN paliad.scenario_proceedings.detailgrad IS
'Per-proceeding optional-detail toggle: selected (only explicitly '
'chosen optionals + mandatories) or all_options (every optional '
'sequencing_rule surfaces). Matches today''s Verfahrensablauf pattern.';
-- ----------------------------------------------------------------
-- 3. paliad.scenario_events — one event card on the canvas
-- ----------------------------------------------------------------
CREATE TABLE paliad.scenario_events (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scenario_proceeding_id uuid NOT NULL
REFERENCES paliad.scenario_proceedings(id) ON DELETE CASCADE,
sequencing_rule_id uuid NULL
REFERENCES paliad.sequencing_rules(id),
procedural_event_id uuid NULL
REFERENCES paliad.procedural_events(id),
custom_label text NULL,
state text NOT NULL DEFAULT 'planned'
CHECK (state IN ('planned','filed','skipped')),
actual_date date NULL,
skip_reason text NULL,
notes text NULL,
horizon_optional int NOT NULL DEFAULT 0
CHECK (horizon_optional >= 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT scenario_events_one_anchor CHECK (
(sequencing_rule_id IS NOT NULL)::int +
(procedural_event_id IS NOT NULL)::int +
(custom_label IS NOT NULL)::int >= 1
)
);
CREATE INDEX scenario_events_proceeding_idx
ON paliad.scenario_events(scenario_proceeding_id);
-- A single proceeding can't carry two cards for the same sequencing rule
-- (each rule maps to one card). Free-form / procedural_event-only cards
-- skip this uniqueness — multiple custom cards per proceeding are OK.
CREATE UNIQUE INDEX scenario_events_rule_uniq_idx
ON paliad.scenario_events(scenario_proceeding_id, sequencing_rule_id)
WHERE sequencing_rule_id IS NOT NULL;
COMMENT ON TABLE paliad.scenario_events IS
'One event card on the Litigation Builder canvas. Captures state '
'(planned/filed/skipped), actual_date, notes, skip_reason, and the '
'per-card optional-horizon setting. At least one of '
'(sequencing_rule_id, procedural_event_id, custom_label) must be '
'set — sequencing-rule-backed cards are the common case; free-form '
'cards exist for events the catalog doesn''t cover yet. '
'PRD §3.4 / §5.1.';
COMMENT ON COLUMN paliad.scenario_events.state IS
'3-state machine: planned (default, future event with computed date) '
'/ filed (past event, actual_date set) / skipped (user chose not to '
'file; optional skip_reason). No "overdue" enum — that''s derived '
'(date < today AND state=planned), not stored. PRD Q10 / §3.4.';
COMMENT ON COLUMN paliad.scenario_events.actual_date IS
'Set when state=filed (real-world filing date) OR when state=planned '
'and the user overrode the computed date (court-set events, manual '
'tweaks). NULL when the computed date is canonical.';
COMMENT ON COLUMN paliad.scenario_events.horizon_optional IS
'Per-card "show N more optional follow-ups" affordance. Default 0 '
'(hidden). PRD Q4 / §3.4.';
-- ----------------------------------------------------------------
-- 4. paliad.scenario_shares — read-only team shares
-- ----------------------------------------------------------------
CREATE TABLE paliad.scenario_shares (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scenario_id uuid NOT NULL
REFERENCES paliad.scenarios(id) ON DELETE CASCADE,
shared_with_user_id uuid NOT NULL
REFERENCES paliad.users(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
created_by uuid NOT NULL REFERENCES paliad.users(id),
UNIQUE (scenario_id, shared_with_user_id)
);
CREATE INDEX scenario_shares_user_idx
ON paliad.scenario_shares(shared_with_user_id);
COMMENT ON TABLE paliad.scenario_shares IS
'Read-only team shares for Litigation Builder scenarios. Owner '
'(paliad.scenarios.owner_id) is the sole editor; rows here grant '
'view-only access to other paliad users. PRD Q12 / §5.1.';
-- ----------------------------------------------------------------
-- 5. paliad.projects.origin_scenario_id — promote-to-project trail
-- ----------------------------------------------------------------
ALTER TABLE paliad.projects
ADD COLUMN origin_scenario_id uuid NULL
REFERENCES paliad.scenarios(id) ON DELETE SET NULL;
CREATE INDEX projects_origin_scenario_idx
ON paliad.projects(origin_scenario_id)
WHERE origin_scenario_id IS NOT NULL;
COMMENT ON COLUMN paliad.projects.origin_scenario_id IS
'FK to the scenario this project was promoted from (B5 wizard). '
'NULL = project was created directly, not via Builder. Together with '
'paliad.scenarios.promoted_project_id, forms the bidirectional audit '
'link. PRD §5.2.';
-- ----------------------------------------------------------------
-- 6. paliad.can_see_scenario — visibility helper
-- ----------------------------------------------------------------
CREATE OR REPLACE FUNCTION paliad.can_see_scenario(_scenario_id uuid)
RETURNS boolean
LANGUAGE sql STABLE SECURITY DEFINER
SET search_path TO 'paliad', 'public'
AS $func$
SELECT EXISTS (
SELECT 1 FROM paliad.users u
WHERE u.id = auth.uid() AND u.global_role = 'global_admin'
)
OR EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = _scenario_id AND s.owner_id = auth.uid()
)
OR EXISTS (
SELECT 1 FROM paliad.scenario_shares sh
WHERE sh.scenario_id = _scenario_id
AND sh.shared_with_user_id = auth.uid()
)
-- Legacy project-scoped scenarios (mig 145) — visible via project
-- team membership.
OR EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = _scenario_id
AND s.owner_id IS NULL
AND s.project_id IS NOT NULL
AND paliad.can_see_project(s.project_id)
)
-- Legacy abstract scenarios (mig 145) — owner-only via created_by.
OR EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = _scenario_id
AND s.owner_id IS NULL
AND s.project_id IS NULL
AND s.created_by = auth.uid()
);
$func$;
COMMENT ON FUNCTION paliad.can_see_scenario(uuid) IS
'Returns true if the caller (auth.uid()) can see the given scenario. '
'Mirrors paliad.can_see_project. Covers builder-owned scenarios '
'(owner_id), read-only shares (scenario_shares), and the two legacy '
'paths from mig 145 (project-scoped via can_see_project, abstract '
'via created_by). Used by RLS on all four scenario_* tables.';
-- ----------------------------------------------------------------
-- 7. RLS — replace legacy scenarios policies + new tables
-- ----------------------------------------------------------------
-- Replace mig-145's four policies with a single pair that handles
-- builder + legacy shapes together.
DROP POLICY IF EXISTS scenarios_project_select ON paliad.scenarios;
DROP POLICY IF EXISTS scenarios_project_mutate ON paliad.scenarios;
DROP POLICY IF EXISTS scenarios_abstract_select ON paliad.scenarios;
DROP POLICY IF EXISTS scenarios_abstract_mutate ON paliad.scenarios;
CREATE POLICY scenarios_select ON paliad.scenarios
FOR SELECT USING (paliad.can_see_scenario(id));
-- Write rule: builder owner, legacy project team member (if no owner),
-- or legacy abstract creator (if no owner + no project). Shares are
-- read-only — they don't grant mutate.
CREATE POLICY scenarios_owner_mutate ON paliad.scenarios
FOR ALL
USING (
owner_id = auth.uid()
OR (owner_id IS NULL AND project_id IS NOT NULL AND paliad.can_see_project(project_id))
OR (owner_id IS NULL AND project_id IS NULL AND created_by = auth.uid())
)
WITH CHECK (
owner_id = auth.uid()
OR (owner_id IS NULL AND project_id IS NOT NULL AND paliad.can_see_project(project_id))
OR (owner_id IS NULL AND project_id IS NULL AND created_by = auth.uid())
);
-- scenario_proceedings — visibility piggybacks on the parent scenario.
ALTER TABLE paliad.scenario_proceedings ENABLE ROW LEVEL SECURITY;
CREATE POLICY scenario_proceedings_select ON paliad.scenario_proceedings
FOR SELECT USING (paliad.can_see_scenario(scenario_id));
CREATE POLICY scenario_proceedings_mutate ON paliad.scenario_proceedings
FOR ALL
USING (EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = scenario_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
))
WITH CHECK (EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = scenario_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
));
-- scenario_events — visibility piggybacks on the parent scenario via
-- the proceeding row.
ALTER TABLE paliad.scenario_events ENABLE ROW LEVEL SECURITY;
CREATE POLICY scenario_events_select ON paliad.scenario_events
FOR SELECT
USING (EXISTS (
SELECT 1 FROM paliad.scenario_proceedings sp
WHERE sp.id = scenario_proceeding_id
AND paliad.can_see_scenario(sp.scenario_id)
));
CREATE POLICY scenario_events_mutate ON paliad.scenario_events
FOR ALL
USING (EXISTS (
SELECT 1 FROM paliad.scenario_proceedings sp
JOIN paliad.scenarios s ON s.id = sp.scenario_id
WHERE sp.id = scenario_proceeding_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
))
WITH CHECK (EXISTS (
SELECT 1 FROM paliad.scenario_proceedings sp
JOIN paliad.scenarios s ON s.id = sp.scenario_id
WHERE sp.id = scenario_proceeding_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
));
-- scenario_shares — recipient can see their share rows; the scenario
-- owner (or legacy editor) can manage them.
ALTER TABLE paliad.scenario_shares ENABLE ROW LEVEL SECURITY;
CREATE POLICY scenario_shares_select ON paliad.scenario_shares
FOR SELECT
USING (
shared_with_user_id = auth.uid()
OR EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = scenario_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
)
);
CREATE POLICY scenario_shares_mutate ON paliad.scenario_shares
FOR ALL
USING (EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = scenario_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
))
WITH CHECK (EXISTS (
SELECT 1 FROM paliad.scenarios s
WHERE s.id = scenario_id
AND (s.owner_id = auth.uid()
OR (s.owner_id IS NULL AND s.project_id IS NOT NULL AND paliad.can_see_project(s.project_id))
OR (s.owner_id IS NULL AND s.project_id IS NULL AND s.created_by = auth.uid()))
));
-- ----------------------------------------------------------------
-- 8. updated_at triggers on the new tables (reuse the function mig 145
-- already created for paliad.scenarios).
-- ----------------------------------------------------------------
CREATE TRIGGER scenario_proceedings_touch_updated_at_trg
BEFORE UPDATE ON paliad.scenario_proceedings
FOR EACH ROW
EXECUTE FUNCTION paliad.scenarios_touch_updated_at();
CREATE TRIGGER scenario_events_touch_updated_at_trg
BEFORE UPDATE ON paliad.scenario_events
FOR EACH ROW
EXECUTE FUNCTION paliad.scenarios_touch_updated_at();
-- ----------------------------------------------------------------
-- 9. Informational NOTICE.
-- ----------------------------------------------------------------
DO $$
BEGIN
RAISE NOTICE '[mig 157] paliad.scenarios extended with builder columns (0 legacy rows affected)';
RAISE NOTICE '[mig 157] paliad.scenario_proceedings created';
RAISE NOTICE '[mig 157] paliad.scenario_events created';
RAISE NOTICE '[mig 157] paliad.scenario_shares created';
RAISE NOTICE '[mig 157] paliad.projects.origin_scenario_id added';
RAISE NOTICE '[mig 157] paliad.can_see_scenario(uuid) created';
END $$;
COMMIT;