pub const V1_TENANT_ACTIVATE_SQL: &str = "-- Activate a prepared version 1 SQLite file after an explicit tenant backfill.\n-- Run 0002_dovecote_tenant_prepare.sql first, populate both nullable tenant_id\n-- columns in the same file, and execute this artifact on one connection.\n-- The guard makes missing, empty, oversized, or mismatched assignments fail\n-- before any old table is replaced. The rebuild is transactional.\n\nBEGIN IMMEDIATE;\n\nCREATE TEMP TABLE dovecote_tenant_activation_guard (\n valid INTEGER NOT NULL CHECK (valid = 1)\n);\nINSERT INTO dovecote_tenant_activation_guard(valid)\nSELECT CASE WHEN\n NOT EXISTS (\n SELECT 1 FROM dovecote_events\n WHERE tenant_id IS NULL\n OR length(CAST(tenant_id AS BLOB)) = 0\n OR length(CAST(tenant_id AS BLOB)) > 255\n )\n AND NOT EXISTS (\n SELECT 1 FROM dovecote_deliveries\n WHERE tenant_id IS NULL\n OR length(CAST(tenant_id AS BLOB)) = 0\n OR length(CAST(tenant_id AS BLOB)) > 255\n )\n AND NOT EXISTS (\n SELECT 1\n FROM dovecote_deliveries AS d\n LEFT JOIN dovecote_events AS e ON e.row_id = d.event_row_id\n WHERE e.row_id IS NULL OR d.tenant_id <> e.tenant_id\n )\n THEN 1 ELSE 0 END;\n\nCREATE TABLE dovecote_events_v2 (\n row_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK (row_id > 0),\n tenant_id TEXT NOT NULL CHECK (length(CAST(tenant_id AS BLOB)) > 0 AND length(CAST(tenant_id AS BLOB)) <= 255),\n stream TEXT NOT NULL CHECK (length(CAST(stream AS BLOB)) <= 255),\n specversion TEXT NOT NULL CHECK (specversion = \'1.0\'),\n event_id TEXT NOT NULL CHECK (length(CAST(event_id AS BLOB)) <= 1024),\n source TEXT NOT NULL CHECK (length(CAST(source AS BLOB)) <= 2048),\n event_type TEXT NOT NULL CHECK (length(CAST(event_type AS BLOB)) <= 1024),\n subject TEXT CHECK (subject IS NULL OR length(CAST(subject AS BLOB)) <= 2048),\n occurred_at TEXT,\n datacontenttype TEXT CHECK (datacontenttype IS NULL OR length(CAST(datacontenttype AS BLOB)) <= 255),\n dataschema TEXT CHECK (dataschema IS NULL OR length(CAST(dataschema AS BLOB)) <= 2048),\n partitionkey TEXT CHECK (partitionkey IS NULL OR length(CAST(partitionkey AS BLOB)) <= 255),\n extensions TEXT NOT NULL DEFAULT \'{}\',\n data_kind TEXT CHECK (data_kind IS NULL OR data_kind IN (\'json\', \'binary\')),\n data BLOB, enqueued_at TEXT NOT NULL DEFAULT (strftime(\'%Y-%m-%dT%H:%M:%f000Z\', \'now\')),\n CHECK ((data_kind IS NULL) = (data IS NULL)),\n CHECK (length(CAST(stream AS BLOB)) <= 255),\n CHECK (length(CAST(event_id AS BLOB)) <= 1024),\n CHECK (length(CAST(source AS BLOB)) <= 2048),\n CHECK (length(CAST(event_type AS BLOB)) <= 1024),\n CHECK (length(CAST(source AS BLOB)) + length(CAST(event_id AS BLOB)) <= 2048),\n CHECK (data IS NULL OR length(data) = 0 OR datacontenttype IS NOT NULL),\n UNIQUE (tenant_id, row_id)\n);\n\nCREATE TABLE dovecote_deliveries_v2 (\n event_row_id INTEGER PRIMARY KEY,\n tenant_id TEXT NOT NULL CHECK (length(CAST(tenant_id AS BLOB)) > 0 AND length(CAST(tenant_id AS BLOB)) <= 255),\n state TEXT NOT NULL CHECK (state IN (\'pending\', \'claimed\', \'delivered\', \'quarantined\')),\n available_at TEXT NOT NULL DEFAULT (strftime(\'%Y-%m-%dT%H:%M:%f000Z\', \'now\')),\n attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),\n claim_token BLOB CHECK (claim_token IS NULL OR length(claim_token) = 16),\n claimed_by TEXT, claim_expires_at TEXT, last_failure_code TEXT,\n last_failure_detail TEXT, delivered_at TEXT, quarantined_at TEXT, quarantine_reason TEXT,\n CHECK (claimed_by IS NULL OR length(CAST(claimed_by AS BLOB)) <= 255),\n CHECK (last_failure_code IS NULL OR length(CAST(last_failure_code AS BLOB)) <= 128),\n CHECK (last_failure_detail IS NULL OR length(CAST(last_failure_detail AS BLOB)) <= 2048),\n CHECK (quarantine_reason IS NULL OR length(CAST(quarantine_reason AS BLOB)) <= 2048),\n CHECK ((last_failure_code IS NULL) = (last_failure_detail IS NULL)),\n CHECK (\n (state = \'pending\' AND claim_token IS NULL AND claimed_by IS NULL AND claim_expires_at IS NULL AND delivered_at IS NULL AND quarantined_at IS NULL AND quarantine_reason IS NULL)\n OR (state = \'claimed\' AND claim_token IS NOT NULL AND claimed_by IS NOT NULL AND claim_expires_at IS NOT NULL AND delivered_at IS NULL AND quarantined_at IS NULL AND quarantine_reason IS NULL)\n OR (state = \'delivered\' AND claim_token IS NULL AND claimed_by IS NULL AND claim_expires_at IS NULL AND delivered_at IS NOT NULL AND quarantined_at IS NULL AND quarantine_reason IS NULL)\n OR (state = \'quarantined\' AND claim_token IS NULL AND claimed_by IS NULL AND claim_expires_at IS NULL AND delivered_at IS NULL AND quarantined_at IS NOT NULL AND quarantine_reason IS NOT NULL)\n ),\n FOREIGN KEY (tenant_id, event_row_id) REFERENCES dovecote_events_v2 (tenant_id, row_id) ON DELETE RESTRICT\n);\n\nINSERT INTO dovecote_events_v2 (\n row_id, tenant_id, stream, specversion, event_id, source, event_type,\n subject, occurred_at, datacontenttype, dataschema, partitionkey,\n extensions, data_kind, data, enqueued_at\n)\nSELECT row_id, tenant_id, stream, specversion, event_id, source, event_type,\n subject, occurred_at, datacontenttype, dataschema, partitionkey,\n extensions, data_kind, data, enqueued_at\nFROM dovecote_events;\n\nINSERT INTO dovecote_deliveries_v2 (\n event_row_id, tenant_id, state, available_at, attempts, claim_token,\n claimed_by, claim_expires_at, last_failure_code, last_failure_detail,\n delivered_at, quarantined_at, quarantine_reason\n)\nSELECT event_row_id, tenant_id, state, available_at, attempts, claim_token,\n claimed_by, claim_expires_at, last_failure_code, last_failure_detail,\n delivered_at, quarantined_at, quarantine_reason\nFROM dovecote_deliveries;\n\nDROP TABLE dovecote_deliveries;\nDROP TABLE dovecote_events;\nALTER TABLE dovecote_events_v2 RENAME TO dovecote_events;\nALTER TABLE dovecote_deliveries_v2 RENAME TO dovecote_deliveries;\n\nDROP INDEX IF EXISTS dovecote_events_source_event_id;\nCREATE UNIQUE INDEX dovecote_events_tenant_source_event_id\n ON dovecote_events (tenant_id COLLATE BINARY, source COLLATE BINARY, event_id COLLATE BINARY);\nCREATE INDEX dovecote_events_tenant_row\n ON dovecote_events (tenant_id COLLATE BINARY, row_id);\nCREATE INDEX dovecote_deliveries_claimable\n ON dovecote_deliveries (tenant_id, state, available_at, event_row_id);\nCREATE INDEX dovecote_deliveries_expired_claims\n ON dovecote_deliveries (tenant_id, state, claim_expires_at, event_row_id);\n\nCREATE TABLE dovecote_schema (\n schema_version INTEGER PRIMARY KEY CHECK (schema_version = 2),\n minimum_crate_major INTEGER NOT NULL CHECK (minimum_crate_major >= 0),\n minimum_crate_minor INTEGER NOT NULL CHECK (minimum_crate_minor >= 0),\n minimum_crate_patch INTEGER NOT NULL CHECK (minimum_crate_patch >= 0),\n rolling_compatible INTEGER NOT NULL\n);\nINSERT INTO dovecote_schema VALUES (2, 0, 2, 0, 0);\n\nDROP TABLE dovecote_tenant_activation_guard;\nCOMMIT;\n";Expand description
SQL that validates backfill and activates tenant constraints.