BEGIN; CREATE TABLE privacy_request ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), subject_id uuid NOT NULL, request_type varchar(32) NOT NULL CHECK (request_type IN ('ACCESS', 'CORRECTION', 'CONSENT_WITHDRAWAL', 'DELETE')), scope jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(scope)='object'), requested_by uuid NOT NULL, status varchar(32) NOT NULL DEFAULT 'SUBMITTED' CHECK (status IN ('SUBMITTED', 'VERIFYING', 'EXECUTING', 'COMPLETED', 'PARTIAL_FAILED')), deadline_at timestamptz, legal_hold boolean NOT NULL DEFAULT false, result_summary jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(result_summary)='object'), idempotency_key varchar(255) NOT NULL, request_digest char(64) NOT NULL, version bigint NOT NULL DEFAULT 1 CHECK (version > 0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz, CONSTRAINT privacy_request_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id) REFERENCES subject_profile (id, tenant_id), CONSTRAINT privacy_request_requester_tenant_fk FOREIGN KEY (requested_by, tenant_id) REFERENCES user_account (id, tenant_id), CONSTRAINT privacy_request_actor_idempotency_key UNIQUE (tenant_id, requested_by, idempotency_key) ); CREATE INDEX privacy_request_subject_time_idx ON privacy_request (tenant_id, subject_id, created_at DESC, id DESC); CREATE INDEX privacy_request_work_idx ON privacy_request (status, updated_at) WHERE status IN ('SUBMITTED', 'VERIFYING', 'EXECUTING', 'PARTIAL_FAILED'); CREATE TABLE privacy_subtask ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), privacy_request_id uuid NOT NULL REFERENCES privacy_request(id) ON DELETE CASCADE, subsystem varchar(32) NOT NULL CHECK (subsystem IN ('postgresql', 'redis', 'object_storage', 'pgvector', 'provider_index', 'export_files')), target_type varchar(64) NOT NULL, target_ref varchar(300) NOT NULL, status varchar(24) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'SKIPPED')), attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0), next_attempt_at timestamptz, last_error varchar(500), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz, CONSTRAINT privacy_subtask_target_key UNIQUE (privacy_request_id, subsystem, target_type, target_ref) ); CREATE INDEX privacy_subtask_work_idx ON privacy_subtask (status, next_attempt_at, created_at) WHERE status IN ('PENDING', 'RUNNING', 'FAILED'); CREATE TABLE tombstone ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), resource_type varchar(80) NOT NULL, resource_digest char(64) NOT NULL, deleted_at timestamptz NOT NULL DEFAULT now(), deletion_request_id uuid NOT NULL REFERENCES privacy_request(id), retention_reason varchar(500) NOT NULL, CONSTRAINT tombstone_resource_key UNIQUE (tenant_id, resource_type, resource_digest) ); COMMIT;