diff --git a/ddl/Tables/Scripts/ddl_org_seq_ireland.sql b/ddl/Tables/Scripts/ddl_org_seq_ireland.sql new file mode 100644 index 0000000..092ca4a --- /dev/null +++ b/ddl/Tables/Scripts/ddl_org_seq_ireland.sql @@ -0,0 +1,52 @@ +-- ============================================================================ +-- alter_org_seq_ireland.sql (MIGRATION: old PR format -> new format) +-- ---------------------------------------------------------------------------- +-- Moves an existing deployment from ORG-00-001-XXX-XXX-XXX (PR #243, Ireland = DR for Virginia) +-- to the reviewed format ORG-XXX-XXX-XXX-XXXX. +-- +-- IMPORTANT — this changes BOTH the format AND the sequence range, so a plain +-- "create if missing" cannot update the already-deployed sequence. This script +-- therefore DROPS and RECREATES the sequence, which RESETS the counter. +-- * SAFE pre-launch / in dev, when NO org_id values have been issued yet. +-- * NOT SAFE if ORG ids already exist: resetting would re-issue them, AND the +-- already-stored old-format ids (ORG-00-001-...) would need a separate +-- data back-fill. In that case, migrate data first, then run this. +-- +-- Run as the OWNER of the organizations table. +-- ============================================================================ + +-- 1) Remove old objects (order: trigger -> function -> sequence) -------------- +DROP TRIGGER IF EXISTS before_insert_organizations ON ireland_dev_saayam_rdbms.organizations; +DROP FUNCTION IF EXISTS ireland_dev_saayam_rdbms.generate_org_id(); +DROP SEQUENCE IF EXISTS ireland_dev_saayam_rdbms.org_id_dr_seq; -- resets counter (see note) + +-- 2) Recreate with the new range + format ------------------------------------ +CREATE SEQUENCE ireland_dev_saayam_rdbms.org_id_dr_seq + START WITH 1000000000000 INCREMENT BY 1 MINVALUE 1000000000000 MAXVALUE 1999999999999 NO CYCLE; + +CREATE OR REPLACE FUNCTION ireland_dev_saayam_rdbms.generate_org_id() +RETURNS TRIGGER AS $$ +DECLARE + seq_id BIGINT; + padded TEXT; +BEGIN + IF NEW.org_id IS NOT NULL THEN + RETURN NEW; + END IF; + seq_id := nextval('ireland_dev_saayam_rdbms.org_id_dr_seq'); + padded := LPAD(seq_id::TEXT, 13, '0'); + IF length(padded) > 13 THEN + RAISE EXCEPTION 'org_id_dr_seq value % exceeds 13 digits; format would corrupt', seq_id; + END IF; + NEW.org_id := 'ORG-' || + SUBSTRING(padded FROM 1 FOR 3) || '-' || + SUBSTRING(padded FROM 4 FOR 3) || '-' || + SUBSTRING(padded FROM 7 FOR 3) || '-' || + SUBSTRING(padded FROM 10 FOR 4); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER before_insert_organizations + BEFORE INSERT ON ireland_dev_saayam_rdbms.organizations + FOR EACH ROW EXECUTE FUNCTION ireland_dev_saayam_rdbms.generate_org_id(); diff --git a/ddl/Tables/Scripts/ddl_organizations_ireland.sql b/ddl/Tables/Scripts/ddl_organizations_ireland.sql new file mode 100644 index 0000000..305f0b0 --- /dev/null +++ b/ddl/Tables/Scripts/ddl_organizations_ireland.sql @@ -0,0 +1,44 @@ +WHAT CHANGED vs PR #243: +-- - Format: ORG-00-001-XXX-XXX-XXX -> ORG-XXX-XXX-XXX-XXXX (13 digits, 3-3-3-4) +-- * dropped the constant "00" marker (differentiated nothing) +-- * dropped the explicit "001" region sub-block (region now lives in the range) +-- - Formatting: FLOOR/MOD math -> LPAD + SUBSTRING (string slicing) +-- - Sequence range: START 2,000,000,000,000 -> START 1,000,000,000,000, MAXVALUE 1,999,999,999,999 +-- (Ireland-DR owns the 100-band; Virginia owns the 000-band) +-- KEPT / ADDED (not in the raw feedback snippet): +-- - Trust-hook: a pre-supplied org_id (replication / DR copy-back) is not re-minted +-- - Width guard: loud error if a value ever exceeds 13 digits (SUBSTRING contract) +-- ============================================================================ + +CREATE SEQUENCE IF NOT EXISTS ireland_dev_saayam_rdbms.org_id_dr_seq + START WITH 1000000000000 INCREMENT BY 1 MINVALUE 1000000000000 MAXVALUE 1999999999999 NO CYCLE; + +CREATE OR REPLACE FUNCTION ireland_dev_saayam_rdbms.generate_org_id() +RETURNS TRIGGER AS $$ +DECLARE + seq_id BIGINT; + padded TEXT; +BEGIN + IF NEW.org_id IS NOT NULL THEN -- replication / DR copy-back -> keep id + RETURN NEW; + END IF; + + seq_id := nextval('ireland_dev_saayam_rdbms.org_id_dr_seq'); + padded := LPAD(seq_id::TEXT, 13, '0'); + + IF length(padded) > 13 THEN -- SUBSTRING width contract + RAISE EXCEPTION 'org_id_dr_seq value % exceeds 13 digits; format would corrupt', seq_id; + END IF; + + NEW.org_id := 'ORG-' || + SUBSTRING(padded FROM 1 FOR 3) || '-' || + SUBSTRING(padded FROM 4 FOR 3) || '-' || + SUBSTRING(padded FROM 7 FOR 3) || '-' || + SUBSTRING(padded FROM 10 FOR 4); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER before_insert_organizations + BEFORE INSERT ON ireland_dev_saayam_rdbms.organizations + FOR EACH ROW EXECUTE FUNCTION ireland_dev_saayam_rdbms.generate_org_id(); diff --git a/ddl/Tables/ddl_org_seq_virginia.sql b/ddl/Tables/ddl_org_seq_virginia.sql new file mode 100644 index 0000000..b18b65d --- /dev/null +++ b/ddl/Tables/ddl_org_seq_virginia.sql @@ -0,0 +1,52 @@ +-- ============================================================================ +-- alter_org_seq_virginia.sql (MIGRATION: old PR format -> new format) +-- ---------------------------------------------------------------------------- +-- Moves an existing deployment from ORG-00-001-XXX-XXX-XXX (PR #243) +-- to the reviewed format ORG-XXX-XXX-XXX-XXXX. +-- +-- IMPORTANT — this changes BOTH the format AND the sequence range, so a plain +-- "create if missing" cannot update the already-deployed sequence. This script +-- therefore DROPS and RECREATES the sequence, which RESETS the counter. +-- * SAFE pre-launch / in dev, when NO org_id values have been issued yet. +-- * NOT SAFE if ORG ids already exist: resetting would re-issue them, AND the +-- already-stored old-format ids (ORG-00-001-...) would need a separate +-- data back-fill. In that case, migrate data first, then run this. +-- +-- Run as the OWNER of the organizations table. +-- ============================================================================ + +-- 1) Remove old objects (order: trigger -> function -> sequence) -------------- +DROP TRIGGER IF EXISTS before_insert_organizations ON virginia_dev_saayam_rdbms.organizations; +DROP FUNCTION IF EXISTS virginia_dev_saayam_rdbms.generate_org_id(); +DROP SEQUENCE IF EXISTS virginia_dev_saayam_rdbms.org_id_seq; -- resets counter (see note) + +-- 2) Recreate with the new range + format ------------------------------------ +CREATE SEQUENCE virginia_dev_saayam_rdbms.org_id_seq + START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 999999999999 NO CYCLE; + +CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.generate_org_id() +RETURNS TRIGGER AS $$ +DECLARE + seq_id BIGINT; + padded TEXT; +BEGIN + IF NEW.org_id IS NOT NULL THEN + RETURN NEW; + END IF; + seq_id := nextval('virginia_dev_saayam_rdbms.org_id_seq'); + padded := LPAD(seq_id::TEXT, 13, '0'); + IF length(padded) > 13 THEN + RAISE EXCEPTION 'org_id_seq value % exceeds 13 digits; format would corrupt', seq_id; + END IF; + NEW.org_id := 'ORG-' || + SUBSTRING(padded FROM 1 FOR 3) || '-' || + SUBSTRING(padded FROM 4 FOR 3) || '-' || + SUBSTRING(padded FROM 7 FOR 3) || '-' || + SUBSTRING(padded FROM 10 FOR 4); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER before_insert_organizations + BEFORE INSERT ON virginia_dev_saayam_rdbms.organizations + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.generate_org_id(); diff --git a/ddl/ddl_changes/ddl_organizations_virginia.sql b/ddl/ddl_changes/ddl_organizations_virginia.sql new file mode 100644 index 0000000..ccf54ac --- /dev/null +++ b/ddl/ddl_changes/ddl_organizations_virginia.sql @@ -0,0 +1,45 @@ +-- ---------------------------------------------------------------------------- +-- WHAT CHANGED vs PR #243: +-- - Format: ORG-00-001-XXX-XXX-XXX -> ORG-XXX-XXX-XXX-XXXX (13 digits, 3-3-3-4) +-- * dropped the constant "00" marker (differentiated nothing) +-- * dropped the explicit "001" region sub-block (region now lives in the range) +-- - Formatting: FLOOR/MOD math -> LPAD + SUBSTRING (string slicing) +-- - Sequence range: START 1,000,000,000,000 -> START 1, MAXVALUE 999,999,999,999 +-- (Virginia now owns the 000-band; Ireland-DR owns the 100-band) +-- KEPT / ADDED (not in the raw feedback snippet): +-- - Trust-hook: a pre-supplied org_id (replication / DR copy-back) is not re-minted +-- - Width guard: loud error if a value ever exceeds 13 digits (SUBSTRING contract) +-- ============================================================================ + +CREATE SEQUENCE IF NOT EXISTS virginia_dev_saayam_rdbms.org_id_seq + START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 999999999999 NO CYCLE; + +CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.generate_org_id() +RETURNS TRIGGER AS $$ +DECLARE + seq_id BIGINT; + padded TEXT; +BEGIN + IF NEW.org_id IS NOT NULL THEN -- replication / DR copy-back -> keep id + RETURN NEW; + END IF; + + seq_id := nextval('virginia_dev_saayam_rdbms.org_id_seq'); + padded := LPAD(seq_id::TEXT, 13, '0'); + + IF length(padded) > 13 THEN -- SUBSTRING width contract + RAISE EXCEPTION 'org_id_seq value % exceeds 13 digits; format would corrupt', seq_id; + END IF; + + NEW.org_id := 'ORG-' || + SUBSTRING(padded FROM 1 FOR 3) || '-' || + SUBSTRING(padded FROM 4 FOR 3) || '-' || + SUBSTRING(padded FROM 7 FOR 3) || '-' || + SUBSTRING(padded FROM 10 FOR 4); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER before_insert_organizations + BEFORE INSERT ON virginia_dev_saayam_rdbms.organizations + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.generate_org_id(); diff --git a/script-library/tests/test_org_seq_virginia.sql b/script-library/tests/test_org_seq_virginia.sql new file mode 100644 index 0000000..192217c --- /dev/null +++ b/script-library/tests/test_org_seq_virginia.sql @@ -0,0 +1,67 @@ +-- ============================================================================ +-- test_organizations_virginia.sql — run in the VIRGINIA database +-- Verifies the updated generator (ORG-XXX-XXX-XXX-XXXX, 000-band). +-- Assertions print via RAISE NOTICE (psql output / pgAdmin "Messages" tab). +-- setval/TRUNCATE make it repeatable — TEST DATABASE ONLY. +-- ============================================================================ + +TRUNCATE virginia_dev_saayam_rdbms.organizations; +SELECT setval('virginia_dev_saayam_rdbms.org_id_seq', 1, false); -- next = 1 + +-- T1 Sequential mint in the 000-band --------------------------------------- +DO $$ +DECLARE a TEXT; b TEXT; +BEGIN + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('VA-1') RETURNING org_id INTO a; + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('VA-2') RETURNING org_id INTO b; + IF a='ORG-000-000-000-0001' AND b='ORG-000-000-000-0002' + THEN RAISE NOTICE 'T1 PASS Virginia sequential 000-band (%, %)', a, b; + ELSE RAISE NOTICE 'T1 FAIL got %, %', a, b; END IF; +END$$; + +-- T2 Trust-hook: a pre-supplied id (replica / DR copy-back) is kept, and the +-- local sequence is NOT consumed ----------------------------------------- +DO $$ +DECLARE kept TEXT; nxt TEXT; +BEGIN + PERFORM setval('virginia_dev_saayam_rdbms.org_id_seq', 50, true); -- next mint = 51 + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_id, org_name) + VALUES ('ORG-100-000-000-0000', 'copied-from-ireland') RETURNING org_id INTO kept; + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) + VALUES ('next-local') RETURNING org_id INTO nxt; + IF kept='ORG-100-000-000-0000' AND nxt='ORG-000-000-000-0051' + THEN RAISE NOTICE 'T2 PASS pre-supplied id kept (%), sequence not consumed (next=%)', kept, nxt; + ELSE RAISE NOTICE 'T2 FAIL kept=% next=%', kept, nxt; END IF; +END$$; + +-- T3 Segment-rollover math at 1,000 / 10,000 / 1,000,000 ------------------- +DO $$ +DECLARE a TEXT; b TEXT; c TEXT; +BEGIN + PERFORM setval('virginia_dev_saayam_rdbms.org_id_seq', 999, true); + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('r1k') RETURNING org_id INTO a; + PERFORM setval('virginia_dev_saayam_rdbms.org_id_seq', 9999, true); + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('r10k') RETURNING org_id INTO b; + PERFORM setval('virginia_dev_saayam_rdbms.org_id_seq', 999999, true); + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('r1m') RETURNING org_id INTO c; + IF a='ORG-000-000-000-1000' AND b='ORG-000-000-001-0000' AND c='ORG-000-000-100-0000' + THEN RAISE NOTICE 'T3 PASS rollover correct (%, %, %)', a, b, c; + ELSE RAISE NOTICE 'T3 FAIL got %, %, %', a, b, c; END IF; +END$$; + +-- T4 Exhaustion hard wall at MAXVALUE 999,999,999,999 ---------------------- +DO $$ +DECLARE last_id TEXT; +BEGIN + PERFORM setval('virginia_dev_saayam_rdbms.org_id_seq', 999999999998, true); -- next = MAXVALUE + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('last') RETURNING org_id INTO last_id; + RAISE NOTICE 'T4 .. last mint at MAXVALUE = %', last_id; + BEGIN + INSERT INTO virginia_dev_saayam_rdbms.organizations(org_name) VALUES ('over'); + RAISE NOTICE 'T4 FAIL sequence did not exhaust'; + EXCEPTION WHEN others THEN + RAISE NOTICE 'T4 PASS hard wall hit as designed: %', SQLERRM; + END; +END$$; + +SELECT org_id, org_name FROM virginia_dev_saayam_rdbms.organizations ORDER BY org_id; diff --git a/script-library/tests/test_orq_seq_ireland.sql b/script-library/tests/test_orq_seq_ireland.sql new file mode 100644 index 0000000..8d4e9a7 --- /dev/null +++ b/script-library/tests/test_orq_seq_ireland.sql @@ -0,0 +1,65 @@ +-- ============================================================================ +-- test_organizations_ireland.sql — run in the IRELAND database +-- Verifies the updated DR generator (ORG-XXX-XXX-XXX-XXXX, 100-band). +-- Ireland = DR for Virginia; its sequence lives in the 1,000,000,000,000 band. +-- Assertions print via RAISE NOTICE. setval/TRUNCATE => TEST DATABASE ONLY. +-- ============================================================================ + +TRUNCATE ireland_dev_saayam_rdbms.organizations; +SELECT setval('ireland_dev_saayam_rdbms.org_id_dr_seq', 1000000000000, false); -- next = 1e12 + +-- T1 Sequential mint in the 100-band --------------------------------------- +DO $$ +DECLARE a TEXT; b TEXT; +BEGIN + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('IE-1') RETURNING org_id INTO a; + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('IE-2') RETURNING org_id INTO b; + IF a='ORG-100-000-000-0000' AND b='ORG-100-000-000-0001' + THEN RAISE NOTICE 'T1 PASS Ireland-DR sequential 100-band (%, %)', a, b; + ELSE RAISE NOTICE 'T1 FAIL got %, %', a, b; END IF; +END$$; + +-- T2 Trust-hook: a pre-supplied id (e.g. a Virginia 000-band id being +-- replicated in) is kept, and the DR sequence is NOT consumed ----------- +DO $$ +DECLARE kept TEXT; nxt TEXT; +BEGIN + PERFORM setval('ireland_dev_saayam_rdbms.org_id_dr_seq', 1000000000050, true); -- next = ...051 + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_id, org_name) + VALUES ('ORG-000-000-000-0007', 'replicated-from-virginia') RETURNING org_id INTO kept; + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) + VALUES ('next-dr') RETURNING org_id INTO nxt; + IF kept='ORG-000-000-000-0007' AND nxt='ORG-100-000-000-0051' + THEN RAISE NOTICE 'T2 PASS pre-supplied id kept (%), DR seq not consumed (next=%)', kept, nxt; + ELSE RAISE NOTICE 'T2 FAIL kept=% next=%', kept, nxt; END IF; +END$$; + +-- T3 Segment-rollover math inside the 100-band ----------------------------- +DO $$ +DECLARE a TEXT; b TEXT; +BEGIN + PERFORM setval('ireland_dev_saayam_rdbms.org_id_dr_seq', 1000000000999, true); -- next ...1000 + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('r1k') RETURNING org_id INTO a; + PERFORM setval('ireland_dev_saayam_rdbms.org_id_dr_seq', 1000000009999, true); -- next ...10000 + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('r10k') RETURNING org_id INTO b; + IF a='ORG-100-000-000-1000' AND b='ORG-100-000-001-0000' + THEN RAISE NOTICE 'T3 PASS rollover correct (%, %)', a, b; + ELSE RAISE NOTICE 'T3 FAIL got %, %', a, b; END IF; +END$$; + +-- T4 Exhaustion hard wall at MAXVALUE 1,999,999,999,999 -------------------- +DO $$ +DECLARE last_id TEXT; +BEGIN + PERFORM setval('ireland_dev_saayam_rdbms.org_id_dr_seq', 1999999999998, true); -- next = MAXVALUE + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('last') RETURNING org_id INTO last_id; + RAISE NOTICE 'T4 .. last mint at MAXVALUE = %', last_id; + BEGIN + INSERT INTO ireland_dev_saayam_rdbms.organizations(org_name) VALUES ('over'); + RAISE NOTICE 'T4 FAIL sequence did not exhaust'; + EXCEPTION WHEN others THEN + RAISE NOTICE 'T4 PASS hard wall hit as designed: %', SQLERRM; + END; +END$$; + +SELECT org_id, org_name FROM ireland_dev_saayam_rdbms.organizations ORDER BY org_id;