iferr:=tx.QueryRow(ctx,`SELECT count(*) FROM conversation WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND status NOT IN ('COMPLETED','FAILED','EXPIRED')`,access.tenantID,access.subjectID).Scan(&activeCount);err!=nil{
returnerr
}
ifactiveCount>0{
returnErrSubjectDeleteConflict
}
}
scopeJSON,err:=json.Marshal(input.Scope)
iferr!=nil{
returnerr
}
varinternalIDstring
err=tx.QueryRow(ctx,`
INSERT INTO privacy_request(tenant_id,subject_id,request_type,scope,requested_by,idempotency_key,request_digest)
iferr:=tx.QueryRow(ctx,`SELECT public_id::text,request_digest FROM privacy_request WHERE tenant_id=$1::uuid AND requested_by=$2::uuid AND idempotency_key=$3 FOR UPDATE`,access.tenantID,access.userID,idempotencyKey).Scan(&publicID,&storedDigest);err!=nil{
if_,err:=tx.Exec(ctx,`INSERT INTO privacy_subtask(privacy_request_id,subsystem,target_type,target_ref) VALUES($1::uuid,'postgresql','subject_data_inventory',$2)`,internalID,subjectPublicID);err!=nil{
if_,err:=tx.Exec(ctx,`UPDATE subject_profile SET preferred_name=$1,language=$2,timezone=$3,profile_data=$4::jsonb,conversation_preferences=$5::jsonb,profile_version=profile_version+1,updated_by=$6::uuid,updated_at=now() WHERE id=$7::uuid AND tenant_id=$8::uuid`,preferredName,language,timezone,profileJSON,preferencesJSON,access.userID,access.subjectID,access.tenantID);err!=nil{
if_,err:=tx.Exec(ctx,`INSERT INTO privacy_subtask(privacy_request_id,subsystem,target_type,target_ref,status,attempts,completed_at) VALUES($1::uuid,'postgresql','subject_profile_correction',$2,'COMPLETED',1,now())`,internalID,subjectPublicID);err!=nil{
if_,err:=tx.Exec(ctx,`UPDATE privacy_request SET status='COMPLETED',result_summary=$2::jsonb,completed_at=now(),version=version+1,updated_at=now() WHERE id=$1::uuid`,internalID,resultJSON);err!=nil{
rows,err:=tx.Query(ctx,`SELECT consent_type FROM consent_record WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND consent_type=ANY($3::text[]) AND withdrawn_at IS NULL AND granted_at<=now() AND (expires_at IS NULL OR expires_at>now()) FOR UPDATE`,access.tenantID,access.subjectID,requestedTypes)
iferr!=nil{
returnfmt.Errorf("lock consents for privacy withdrawal: %w",err)
if_,err:=tx.Exec(ctx,`UPDATE consent_record SET withdrawn_at=now(),version=version+1,updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND consent_type=ANY($3::text[]) AND withdrawn_at IS NULL AND granted_at<=now() AND (expires_at IS NULL OR expires_at>now())`,access.tenantID,access.subjectID,requestedTypes);err!=nil{
if_,err:=tx.Exec(ctx,`UPDATE curated_memory SET status='DISABLED',version=version+1,updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND status='ACTIVE'`,access.tenantID,access.subjectID);err!=nil{
returnerr
}
if_,err:=tx.Exec(ctx,`UPDATE memory_index_entry SET embedding=NULL,status='STALE',last_error='personalized memory consent withdrawn',updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND status IN ('PENDING','ACTIVE')`,access.tenantID,access.subjectID);err!=nil{
if_,err:=tx.Exec(ctx,`INSERT INTO privacy_subtask(privacy_request_id,subsystem,target_type,target_ref,status,attempts,completed_at) VALUES($1::uuid,'postgresql','consent_withdrawal',$2,'COMPLETED',1,now())`,internalID,subjectPublicID);err!=nil{
if_,err:=tx.Exec(ctx,`UPDATE privacy_request SET status='COMPLETED',result_summary=$2::jsonb,completed_at=now(),version=version+1,updated_at=now() WHERE id=$1::uuid`,internalID,resultJSON);err!=nil{
if_,err:=tx.Exec(ctx,`INSERT INTO privacy_subtask(privacy_request_id,subsystem,target_type,target_ref) VALUES($1::uuid,$2,$3,$4)`,internalID,task[0],task[1],subjectPublicID);err!=nil{
if_,err:=tx.Exec(ctx,`UPDATE subject_profile SET status='deleting',profile_version=profile_version+1,updated_by=$3::uuid,updated_at=now() WHERE tenant_id=$1::uuid AND id=$2::uuid`,access.tenantID,access.subjectID,access.userID);err!=nil{
returnerr
}
if_,err:=tx.Exec(ctx,`UPDATE consent_record SET withdrawn_at=COALESCE(withdrawn_at,GREATEST(now(),granted_at)),scope='{}'::jsonb,evidence_ref=NULL,version=version+1,updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid`,access.tenantID,access.subjectID);err!=nil{
returnerr
}
if_,err:=tx.Exec(ctx,`UPDATE curated_memory SET status='DISABLED',valid_to=COALESCE(valid_to,now()),version=version+1,updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND status NOT IN ('DELETED','DISABLED')`,access.tenantID,access.subjectID);err!=nil{
returnerr
}
if_,err:=tx.Exec(ctx,`UPDATE memory_index_entry SET embedding=NULL,status='STALE',last_error='subject privacy deletion requested',updated_at=now() WHERE tenant_id=$1::uuid AND subject_id=$2::uuid AND status<>'DELETED'`,access.tenantID,access.subjectID);err!=nil{
rows,err:=s.pool.Query(ctx,privacyRequestSelectSQL+` WHERE pr.tenant_id=$1::uuid AND pr.subject_id=$2::uuid ORDER BY pr.created_at DESC,pr.id DESC`,access.tenantID,access.subjectID)
JOIN household h ON h.id=sp.household_id AND h.tenant_id=sp.tenant_id AND h.status<>'deleted'
JOIN household_member hm ON hm.household_id=h.id AND hm.tenant_id=h.tenant_id AND hm.status='active'
JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=h.tenant_id AND ua.status='active'
WHERE pr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3`,publicID,principal.Issuer,principal.Subject))
iferrors.Is(err,pgx.ErrNoRows){
returnPrivacyRequest{},ErrAccessDenied
}
iferr!=nil{
returnPrivacyRequest{},err
}
varrolestring
iferr:=s.pool.QueryRow(ctx,`SELECT hm.role FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id JOIN household_member hm ON hm.household_id=sp.household_id AND hm.status='active' JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=pr.tenant_id AND ua.status='active' WHERE pr.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3`,publicID,principal.Issuer,principal.Subject).Scan(&role);err!=nil{
constprivacyRequestSelectSQL=`SELECT pr.public_id::text,sp.public_id::text,pr.request_type,pr.scope,pr.status,pr.deadline_at,pr.legal_hold,pr.result_summary,pr.version,pr.created_at,pr.updated_at,pr.completed_at FROM privacy_request pr JOIN subject_profile sp ON sp.id=pr.subject_id AND sp.tenant_id=pr.tenant_id`
rows,err:=s.pool.Query(ctx,`SELECT ps.public_id::text,ps.subsystem,ps.target_type,ps.target_ref,ps.status,ps.attempts,ps.next_attempt_at,COALESCE(ps.last_error,''),ps.completed_at FROM privacy_subtask ps JOIN privacy_request pr ON pr.id=ps.privacy_request_id WHERE pr.public_id=$1::uuid ORDER BY ps.created_at,ps.id`,item.ID)