290 lines
8.4 KiB
SQL
290 lines
8.4 KiB
SQL
-- +goose Up
|
|||
|
|
ALTER TABLE devices
|
||
|
|
ADD COLUMN accessibility_enabled INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (accessibility_enabled IN (0, 1));
|
||
|
|
|
||
|
|
ALTER TABLE devices
|
||
|
|
ADD COLUMN pdd_installed INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (pdd_installed IN (0, 1));
|
||
|
|
|
||
|
|
ALTER TABLE devices
|
||
|
|
ADD COLUMN readiness_reported_at TEXT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claimed_by_user_id TEXT
|
||
|
|
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claimed_by_device_id TEXT
|
||
|
|
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claim_generation INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (claim_generation >= 0);
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claim_token_hash TEXT
|
||
|
|
CHECK (
|
||
|
|
claim_token_hash IS NULL
|
||
|
|
OR (
|
||
|
|
length(claim_token_hash) = 64
|
||
|
|
AND claim_token_hash NOT GLOB '*[^0-9a-f]*'
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claim_issued_at TEXT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN claim_expires_at TEXT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN cancel_requested_at TEXT;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks
|
||
|
|
ADD COLUMN cancel_requested_by_user_id TEXT
|
||
|
|
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
|
||
|
|
|
||
|
|
CREATE UNIQUE INDEX purchase_tasks_active_device_idx
|
||
|
|
ON purchase_tasks (claimed_by_device_id)
|
||
|
|
WHERE claimed_by_device_id IS NOT NULL
|
||
|
|
AND status IN ('CLAIMED', 'RUNNING', 'WAITING_CONFIRMATION');
|
||
|
|
|
||
|
|
CREATE TABLE task_executions (
|
||
|
|
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,
|
||
|
|
attempt_no INTEGER NOT NULL
|
||
|
|
CHECK (attempt_no > 0),
|
||
|
|
claim_generation INTEGER NOT NULL
|
||
|
|
CHECK (claim_generation > 0),
|
||
|
|
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,
|
||
|
|
current_step TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
length(trim(current_step)) > 0
|
||
|
|
AND length(CAST(current_step AS BLOB)) <= 64
|
||
|
|
),
|
||
|
|
last_heartbeat_at TEXT NOT NULL,
|
||
|
|
order_submitted INTEGER NOT NULL DEFAULT 0
|
||
|
|
CHECK (order_submitted = 0),
|
||
|
|
started_at TEXT NOT NULL,
|
||
|
|
finished_at TEXT,
|
||
|
|
UNIQUE (task_id, attempt_no),
|
||
|
|
UNIQUE (task_id, claim_generation)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE UNIQUE INDEX task_executions_active_task_idx
|
||
|
|
ON task_executions (task_id)
|
||
|
|
WHERE finished_at IS NULL;
|
||
|
|
|
||
|
|
CREATE INDEX task_executions_device_heartbeat_idx
|
||
|
|
ON task_executions (device_id, last_heartbeat_at DESC);
|
||
|
|
|
||
|
|
CREATE TABLE lifecycle_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 ('CLAIM_NEXT', 'START', 'RELEASE', 'CANCEL_ACK')
|
||
|
|
),
|
||
|
|
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]*'
|
||
|
|
),
|
||
|
|
result_kind TEXT NOT NULL
|
||
|
|
CHECK (result_kind IN ('TASK', 'NO_TASK', 'EXECUTION')),
|
||
|
|
task_id TEXT
|
||
|
|
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
claim_generation INTEGER
|
||
|
|
CHECK (claim_generation IS NULL OR claim_generation > 0),
|
||
|
|
execution_id TEXT
|
||
|
|
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
created_at TEXT NOT NULL,
|
||
|
|
PRIMARY KEY (user_id, device_id, operation, idempotency_key),
|
||
|
|
CHECK (
|
||
|
|
(
|
||
|
|
operation = 'CLAIM_NEXT'
|
||
|
|
AND result_kind = 'NO_TASK'
|
||
|
|
AND task_id IS NULL
|
||
|
|
AND claim_generation IS NULL
|
||
|
|
AND execution_id IS NULL
|
||
|
|
)
|
||
|
|
OR (
|
||
|
|
operation = 'CLAIM_NEXT'
|
||
|
|
AND result_kind = 'TASK'
|
||
|
|
AND task_id IS NOT NULL
|
||
|
|
AND claim_generation IS NOT NULL
|
||
|
|
AND execution_id IS NULL
|
||
|
|
)
|
||
|
|
OR (
|
||
|
|
operation = 'START'
|
||
|
|
AND result_kind = 'EXECUTION'
|
||
|
|
AND task_id IS NOT NULL
|
||
|
|
AND claim_generation IS NOT NULL
|
||
|
|
AND execution_id IS NOT NULL
|
||
|
|
)
|
||
|
|
OR (
|
||
|
|
operation = 'RELEASE'
|
||
|
|
AND result_kind = 'TASK'
|
||
|
|
AND task_id IS NOT NULL
|
||
|
|
AND claim_generation IS NOT NULL
|
||
|
|
AND execution_id IS NULL
|
||
|
|
)
|
||
|
|
OR (
|
||
|
|
operation = 'CANCEL_ACK'
|
||
|
|
AND result_kind = 'TASK'
|
||
|
|
AND task_id IS NOT NULL
|
||
|
|
AND claim_generation IS NOT NULL
|
||
|
|
AND execution_id IS NOT NULL
|
||
|
|
)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
ALTER TABLE task_events RENAME TO task_events_v3;
|
||
|
|
|
||
|
|
CREATE TABLE task_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,
|
||
|
|
event_type TEXT NOT NULL
|
||
|
|
CHECK (
|
||
|
|
event_type IN (
|
||
|
|
'TASK_CREATED',
|
||
|
|
'TASK_CLAIMED',
|
||
|
|
'TASK_RECLAIMED',
|
||
|
|
'TASK_RELEASED',
|
||
|
|
'TASK_STARTED',
|
||
|
|
'TASK_CANCEL_REQUESTED',
|
||
|
|
'TASK_CANCELED'
|
||
|
|
)
|
||
|
|
),
|
||
|
|
message TEXT NOT NULL,
|
||
|
|
occurred_at TEXT NOT NULL,
|
||
|
|
actor_user_id TEXT
|
||
|
|
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||
|
|
actor_device_id TEXT
|
||
|
|
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||
|
|
);
|
||
|
|
|
||
|
|
INSERT INTO task_events (
|
||
|
|
id,
|
||
|
|
task_id,
|
||
|
|
event_type,
|
||
|
|
message,
|
||
|
|
occurred_at,
|
||
|
|
actor_user_id,
|
||
|
|
actor_device_id
|
||
|
|
)
|
||
|
|
SELECT
|
||
|
|
id,
|
||
|
|
task_id,
|
||
|
|
event_type,
|
||
|
|
message,
|
||
|
|
occurred_at,
|
||
|
|
actor_user_id,
|
||
|
|
NULL
|
||
|
|
FROM task_events_v3;
|
||
|
|
|
||
|
|
DROP TABLE task_events_v3;
|
||
|
|
|
||
|
|
CREATE INDEX task_events_task_occurred_idx
|
||
|
|
ON task_events (task_id, occurred_at ASC, id ASC);
|
||
|
|
|
||
|
|
CREATE INDEX task_events_actor_user_idx
|
||
|
|
ON task_events (actor_user_id, occurred_at DESC, id DESC);
|
||
|
|
|
||
|
|
CREATE INDEX task_events_actor_device_idx
|
||
|
|
ON task_events (actor_device_id, occurred_at DESC, id DESC);
|
||
|
|
|
||
|
|
-- +goose Down
|
||
|
|
CREATE TEMP TABLE task_events_v4_down_guard (
|
||
|
|
allowed INTEGER NOT NULL
|
||
|
|
CHECK (allowed = 1)
|
||
|
|
);
|
||
|
|
|
||
|
|
INSERT INTO task_events_v4_down_guard (allowed)
|
||
|
|
SELECT CASE
|
||
|
|
WHEN EXISTS (
|
||
|
|
SELECT 1
|
||
|
|
FROM task_events
|
||
|
|
WHERE event_type NOT IN ('TASK_CREATED', 'TASK_CANCELED')
|
||
|
|
OR actor_device_id IS NOT NULL
|
||
|
|
)
|
||
|
|
THEN 0
|
||
|
|
ELSE 1
|
||
|
|
END;
|
||
|
|
|
||
|
|
DROP TABLE task_events_v4_down_guard;
|
||
|
|
|
||
|
|
ALTER TABLE task_events RENAME TO task_events_v4;
|
||
|
|
|
||
|
|
CREATE TABLE task_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,
|
||
|
|
event_type TEXT NOT NULL
|
||
|
|
CHECK (event_type IN ('TASK_CREATED', 'TASK_CANCELED')),
|
||
|
|
message TEXT NOT NULL,
|
||
|
|
occurred_at TEXT NOT NULL,
|
||
|
|
actor_user_id TEXT
|
||
|
|
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||
|
|
);
|
||
|
|
|
||
|
|
INSERT INTO task_events (
|
||
|
|
id,
|
||
|
|
task_id,
|
||
|
|
event_type,
|
||
|
|
message,
|
||
|
|
occurred_at,
|
||
|
|
actor_user_id
|
||
|
|
)
|
||
|
|
SELECT
|
||
|
|
id,
|
||
|
|
task_id,
|
||
|
|
event_type,
|
||
|
|
message,
|
||
|
|
occurred_at,
|
||
|
|
actor_user_id
|
||
|
|
FROM task_events_v4;
|
||
|
|
|
||
|
|
DROP TABLE task_events_v4;
|
||
|
|
|
||
|
|
CREATE INDEX task_events_task_occurred_idx
|
||
|
|
ON task_events (task_id, occurred_at ASC, id ASC);
|
||
|
|
|
||
|
|
CREATE INDEX task_events_actor_user_idx
|
||
|
|
ON task_events (actor_user_id, occurred_at DESC, id DESC);
|
||
|
|
|
||
|
|
DROP TABLE lifecycle_requests;
|
||
|
|
DROP INDEX task_executions_device_heartbeat_idx;
|
||
|
|
DROP INDEX task_executions_active_task_idx;
|
||
|
|
DROP TABLE task_executions;
|
||
|
|
DROP INDEX purchase_tasks_active_device_idx;
|
||
|
|
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN cancel_requested_by_user_id;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN cancel_requested_at;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claim_expires_at;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claim_issued_at;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claim_token_hash;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claim_generation;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claimed_by_device_id;
|
||
|
|
ALTER TABLE purchase_tasks DROP COLUMN claimed_by_user_id;
|
||
|
|
|
||
|
|
ALTER TABLE devices DROP COLUMN readiness_reported_at;
|
||
|
|
ALTER TABLE devices DROP COLUMN pdd_installed;
|
||
|
|
ALTER TABLE devices DROP COLUMN accessibility_enabled;
|