From 15628278d251aca162edbbfb5130025426889108 Mon Sep 17 00:00:00 2001 From: Kevin Burnett <18027+burnettk@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:38:01 +0000 Subject: [PATCH] protect future_task foreign key add operation from running when bad data (#1770) Co-authored-by: burnettk --- .../migrations/versions/7eaec0e12079_.py | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/migrations/versions/7eaec0e12079_.py b/spiffworkflow-backend/migrations/versions/7eaec0e12079_.py index 47d39f70f..7faff9f1a 100644 --- a/spiffworkflow-backend/migrations/versions/7eaec0e12079_.py +++ b/spiffworkflow-backend/migrations/versions/7eaec0e12079_.py @@ -7,6 +7,8 @@ Create Date: 2024-06-18 16:45:06.102210 """ from alembic import op import sqlalchemy as sa +from sqlalchemy import text +from spiffworkflow_backend.models.db import dialect_name # revision identifiers, used by Alembic. @@ -15,16 +17,46 @@ down_revision = '43afc70a7016' branch_labels = None depends_on = None +def delete_orphaned_future_tasks() -> None: + # Ensure database connection + conn = op.get_bind() -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### + dialect = dialect_name() + + if dialect == "mysql": + delete_query = text(""" + DELETE future_task FROM future_task + LEFT JOIN task ON future_task.guid = task.guid + WHERE task.guid IS NULL; + """) + elif dialect == "postgresql": + delete_query = text(""" + DELETE FROM future_task + USING task + WHERE future_task.guid = task.guid + AND task.guid IS NULL; + """) + elif dialect == "sqlite": + delete_query = text(""" + DELETE FROM future_task + WHERE guid NOT IN ( + SELECT guid FROM task + ); + """) + else: + raise ValueError(f"Unsupported database dialect: {dialect}") + + # Execute the delete query + conn.execute(delete_query) + + +def upgrade() -> None: + delete_orphaned_future_tasks() with op.batch_alter_table('future_task', schema=None) as batch_op: batch_op.create_foreign_key('future_task_task_guid_fk', 'task', ['guid'], ['guid'], ondelete='CASCADE') - # ### end Alembic commands ### - -def downgrade(): +def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### with op.batch_alter_table('future_task', schema=None) as batch_op: batch_op.drop_constraint('future_task_task_guid_fk', type_='foreignkey')