205 lines
7.1 KiB
SQL
205 lines
7.1 KiB
SQL
-- +goose Up
|
|||
|
|
CREATE TABLE execution_events (
|
||
|
|
id TEXT PRIMARY KEY NOT NULL
|
||
|
|
CHECK (length(id) = 36),
|
||
|
|
task_id TEXT NOT NULL
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
execution_id TEXT NOT NULL
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
step TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(trim(step)) > 0
|
||
|
|
AND length(CAST(step AS BLOB)) <= 64
|
||
|
|
),
|
||
|
|
event_type TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(trim(event_type)) > 0
|
||
|
|
AND length(CAST(event_type AS BLOB)) <= 64
|
||
|
|
),
|
||
|
|
message TEXT NOT NULL
|
||
|
|
CHECK (length(CAST(message AS BLOB)) <= 1000),
|
||
|
|
occurred_at TEXT NOT NULL,
|
||
|
|
received_at TEXT NOT NULL,
|
||
|
|
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (received_after_execution_expiry IN (0, 1))
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX execution_events_execution_occurred_idx
|
||
|
|
ON execution_events (execution_id, occurred_at ASC, id ASC);
|
||
|
|
|
||
|
|
CREATE TABLE execution_evidence_assets (
|
||
|
|
id TEXT PRIMARY KEY NOT NULL
|
||
|
|
CHECK (length(id) = 36),
|
||
|
|
task_id TEXT NOT NULL
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
execution_id TEXT NOT NULL
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
media_type TEXT NOT NULL
|
||
|
|
CHECK (media_type = 'image/jpeg'),
|
||
|
|
size_bytes INTEGER NOT NULL
|
||
|
|
CHECK (size_bytes > 0),
|
||
|
|
sha256 TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(sha256) = 64
|
||
|
|
AND sha256 NOT GLOB '*[^0-9a-f]*'
|
||
|
|
),
|
||
|
|
storage_key TEXT NOT NULL UNIQUE
|
||
|
|
CHECK (
|
||
|
|
length(storage_key) > 0
|
||
|
|
AND substr(storage_key, 1, 1) <> '/'
|
||
|
|
AND instr(storage_key, '\') = 0
|
||
|
|
AND instr(storage_key, '..') = 0
|
||
|
|
),
|
||
|
|
created_at TEXT NOT NULL,
|
||
|
|
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (received_after_execution_expiry IN (0, 1))
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX execution_evidence_execution_created_idx
|
||
|
|
ON execution_evidence_assets (execution_id, created_at ASC, id ASC);
|
||
|
|
|
||
|
|
CREATE TABLE execution_candidate_batches (
|
||
|
|
execution_id TEXT PRIMARY KEY NOT NULL
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
task_id TEXT NOT NULL
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
task_content_sha256 TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(task_content_sha256) = 64
|
||
|
|
AND task_content_sha256 NOT GLOB '*[^0-9a-f]*'
|
||
|
|
),
|
||
|
|
execution_mode TEXT NOT NULL
|
||
|
|
CHECK (execution_mode IN ('MANUAL_FIRST', 'AI_ASSISTED')),
|
||
|
|
search_query TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(trim(search_query)) > 0
|
||
|
|
AND length(CAST(search_query AS BLOB)) <= 512
|
||
|
|
),
|
||
|
|
provenance_json TEXT,
|
||
|
|
candidates_json TEXT NOT NULL
|
||
|
|
CHECK (length(CAST(candidates_json AS BLOB)) <= 65536),
|
||
|
|
recommendation_json TEXT,
|
||
|
|
received_at TEXT NOT NULL,
|
||
|
|
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (received_after_execution_expiry IN (0, 1))
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE execution_outcomes (
|
||
|
|
execution_id TEXT PRIMARY KEY NOT NULL
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
task_id TEXT NOT NULL
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||
|
|
result_type TEXT NOT NULL
|
||
|
|
CHECK (result_type IN ('COMPLETE', 'FAIL')),
|
||
|
|
execution_mode TEXT
|
||
|
|
CHECK (execution_mode IS NULL OR execution_mode IN ('MANUAL_FIRST', 'AI_ASSISTED')),
|
||
|
|
task_content_sha256 TEXT
|
||
|
|
CHECK (
|
||
|
|
task_content_sha256 IS NULL
|
||
|
|
OR (
|
||
|
|
length(task_content_sha256) = 64
|
||
|
|
AND task_content_sha256 NOT GLOB '*[^0-9a-f]*'
|
||
|
|
)
|
||
|
|
),
|
||
|
|
outcome TEXT
|
||
|
|
CHECK (
|
||
|
|
outcome IS NULL
|
||
|
|
OR outcome IN (
|
||
|
|
'CANDIDATE_ACCEPTED',
|
||
|
|
'CANDIDATE_REJECTED',
|
||
|
|
'NO_MATCH',
|
||
|
|
'MANUAL_REQUIRED'
|
||
|
|
)
|
||
|
|
),
|
||
|
|
operator_reason TEXT
|
||
|
|
CHECK (
|
||
|
|
operator_reason IS NULL
|
||
|
|
OR length(CAST(operator_reason AS BLOB)) <= 1000
|
||
|
|
),
|
||
|
|
selected_candidate_json TEXT,
|
||
|
|
evidence_asset_ids_json TEXT,
|
||
|
|
error_code TEXT
|
||
|
|
CHECK (error_code IS NULL OR length(CAST(error_code AS BLOB)) <= 64),
|
||
|
|
error_message TEXT
|
||
|
|
CHECK (error_message IS NULL OR length(CAST(error_message AS BLOB)) <= 1000),
|
||
|
|
error_step TEXT
|
||
|
|
CHECK (error_step IS NULL OR length(CAST(error_step AS BLOB)) <= 64),
|
||
|
|
retryable INTEGER
|
||
|
|
CHECK (retryable IS NULL OR retryable IN (0, 1)),
|
||
|
|
order_submitted INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (order_submitted = 0),
|
||
|
|
received_at TEXT NOT NULL,
|
||
|
|
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (received_after_execution_expiry IN (0, 1)),
|
||
|
|
CHECK (
|
||
|
|
(result_type = 'COMPLETE'
|
||
|
|
AND execution_mode IS NOT NULL
|
||
|
|
AND task_content_sha256 IS NOT NULL
|
||
|
|
AND outcome IS NOT NULL
|
||
|
|
AND operator_reason IS NOT NULL
|
||
|
|
AND error_code IS NULL)
|
||
|
|
OR
|
||
|
|
(result_type = 'FAIL'
|
||
|
|
AND error_code IS NOT NULL
|
||
|
|
AND error_message IS NOT NULL
|
||
|
|
AND error_step IS NOT NULL)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE execution_result_requests (
|
||
|
|
user_id TEXT NOT NULL
|
||
|
|
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
device_id TEXT NOT NULL
|
||
|
|
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
operation TEXT NOT NULL
|
||
|
|
CHECK (operation IN ('EVENTS', 'EVIDENCE', 'CANDIDATES', 'COMPLETE', 'FAIL')),
|
||
|
|
idempotency_key TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(trim(idempotency_key)) > 0
|
||
|
|
AND length(CAST(idempotency_key AS BLOB)) <= 128
|
||
|
|
),
|
||
|
|
request_sha256 TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(request_sha256) = 64
|
||
|
|
AND request_sha256 NOT GLOB '*[^0-9a-f]*'
|
||
|
|
),
|
||
|
|
claim_token_sha256 TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(claim_token_sha256) = 64
|
||
|
|
AND claim_token_sha256 NOT GLOB '*[^0-9a-f]*'
|
||
|
|
),
|
||
|
|
task_id TEXT NOT NULL
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
execution_id TEXT NOT NULL
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
resource_id TEXT,
|
||
|
|
created_at TEXT NOT NULL,
|
||
|
|
PRIMARY KEY (user_id, device_id, operation, idempotency_key)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- +goose Down
|
||
|
|
CREATE TEMP TABLE execution_results_v5_down_guard (
|
||
|
|
allowed INTEGER NOT NULL
|
||
|
|
CHECK (allowed = 1)
|
||
|
|
);
|
||
|
|
|
||
|
|
INSERT INTO execution_results_v5_down_guard (allowed)
|
||
|
|
SELECT CASE
|
||
|
|
WHEN EXISTS (SELECT 1 FROM execution_events)
|
||
|
|
OR EXISTS (SELECT 1 FROM execution_evidence_assets)
|
||
|
|
OR EXISTS (SELECT 1 FROM execution_candidate_batches)
|
||
|
|
OR EXISTS (SELECT 1 FROM execution_outcomes)
|
||
|
|
OR EXISTS (SELECT 1 FROM execution_result_requests)
|
||
|
|
THEN 0
|
||
|
|
ELSE 1
|
||
|
|
END;
|
||
|
|
|
||
|
|
DROP TABLE execution_results_v5_down_guard;
|
||
|
|
DROP TABLE execution_result_requests;
|
||
|
|
DROP TABLE execution_outcomes;
|
||
|
|
DROP TABLE execution_candidate_batches;
|
||
|
|
DROP INDEX execution_evidence_execution_created_idx;
|
||
|
|
DROP TABLE execution_evidence_assets;
|
||
|
|
DROP INDEX execution_events_execution_occurred_idx;
|
||
|
|
DROP TABLE execution_events;
|