CREATE TABLE IF NOT EXISTS source_scans ( id VARCHAR(64) PRIMARY KEY, source_connection_id VARCHAR(64) NOT NULL REFERENCES source_connections(id) ON DELETE RESTRICT, source_type VARCHAR(32) NOT NULL CHECK (source_type IN ('pp', 'alist', 'dingtalk', 'manual')), state VARCHAR(16) NOT NULL CHECK (state IN ('pending', 'running', 'completed', 'failed')), snapshot_sha256 VARCHAR(64) NOT NULL DEFAULT '' CHECK (snapshot_sha256 = '' OR snapshot_sha256 ~ '^[0-9a-f]{64}$'), observed_items INTEGER NOT NULL DEFAULT 0 CHECK (observed_items >= 0), unchanged_items INTEGER NOT NULL DEFAULT 0 CHECK (unchanged_items >= 0), added_items INTEGER NOT NULL DEFAULT 0 CHECK (added_items >= 0), updated_items INTEGER NOT NULL DEFAULT 0 CHECK (updated_items >= 0), over_limit_items INTEGER NOT NULL DEFAULT 0 CHECK (over_limit_items >= 0), missing_items INTEGER NOT NULL DEFAULT 0 CHECK (missing_items >= 0), planned_uploads INTEGER NOT NULL DEFAULT 0 CHECK (planned_uploads >= 0), error_code VARCHAR(64) NOT NULL DEFAULT '', started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_source_scans_one_active ON source_scans (source_connection_id) WHERE state IN ('pending', 'running'); CREATE INDEX IF NOT EXISTS idx_source_scans_connection_created ON source_scans (source_connection_id, created_at DESC, id DESC); CREATE TABLE IF NOT EXISTS source_scan_changes ( id VARCHAR(64) PRIMARY KEY, source_scan_id VARCHAR(64) NOT NULL REFERENCES source_scans(id) ON DELETE RESTRICT, source_connection_id VARCHAR(64) NOT NULL REFERENCES source_connections(id) ON DELETE RESTRICT, stable_external_id VARCHAR(512) NOT NULL, source_item_id VARCHAR(64) REFERENCES source_items(id) ON DELETE RESTRICT, change_type VARCHAR(16) NOT NULL CHECK (change_type IN ('added', 'updated', 'unavailable')), previous_revision VARCHAR(512) NOT NULL DEFAULT '', observed_revision VARCHAR(512) NOT NULL DEFAULT '', observed_size_bytes BIGINT CHECK (observed_size_bytes IS NULL OR observed_size_bytes >= 0), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (source_scan_id, stable_external_id) ); CREATE INDEX IF NOT EXISTS idx_source_scan_changes_pending ON source_scan_changes (source_connection_id, change_type, created_at DESC, id DESC);