From dd7ad461d835a41160908a6dbd7f039f11a9a765 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Fri, 31 Jul 2026 18:20:09 +0200 Subject: [PATCH] gate2: fix duplicate column/index in migrations for fresh DB installation --- alembic/versions/0001_initial.py | 2 +- alembic/versions/0002_contacts_fts.py | 6 +++--- alembic/versions/0005_user_role_fk.py | 12 ++--------- alembic/versions/0006_add_addresses.py | 20 +++++++++---------- ...18_fix_notification_preferences_columns.py | 10 ++-------- alembic/versions/0021_unified_contacts.py | 13 +++++++----- alembic/versions/0022_contact_folders.py | 5 +---- alembic/versions/0023_theme_customization.py | 8 ++++---- alembic/versions/0024_heartbeat_config.py | 6 +++--- alembic/versions/0026_mail_salt_security.py | 2 +- alembic/versions/0032_user_profile_fields.py | 6 +++--- alembic/versions/0034_automation_config.py | 2 +- alembic/versions/0053_mail_owner_id.py | 16 ++------------- .../0063_notification_entity_fields.py | 5 +++-- .../0074_workspace_users_timestamps.py | 4 ++-- alembic/versions/0075_outbox_envelope.py | 10 +++++----- ...add_timestamps_to_password_reset_tokens.py | 4 ++-- 17 files changed, 53 insertions(+), 78 deletions(-) diff --git a/alembic/versions/0001_initial.py b/alembic/versions/0001_initial.py index 818d480..d88c9f5 100644 --- a/alembic/versions/0001_initial.py +++ b/alembic/versions/0001_initial.py @@ -46,7 +46,7 @@ def upgrade() -> None: sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint("tenant_id", "email", name="uq_users_tenant_email"), ) - op.create_index("ix_users_tenant_id", "users", ["tenant_id"]) + op.execute("CREATE INDEX IF NOT EXISTS ix_users_tenant_id ON users (tenant_id)") op.create_index("ix_users_email", "users", ["email"]) # user_tenants diff --git a/alembic/versions/0002_contacts_fts.py b/alembic/versions/0002_contacts_fts.py index 003c426..ffdaea6 100644 --- a/alembic/versions/0002_contacts_fts.py +++ b/alembic/versions/0002_contacts_fts.py @@ -67,9 +67,9 @@ def upgrade() -> None: sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) op.create_index("ix_contacts_tenant_id", "contacts", ["tenant_id"]) - op.create_index("ix_contacts_tenant_deleted", "contacts", ["tenant_id", "deleted_at"]) - op.create_index("ix_contacts_tenant_name", "contacts", ["tenant_id", "last_name", "first_name"]) - op.create_index("ix_contacts_email", "contacts", ["email"]) + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_tenant_deleted ON contacts (tenant_id, deleted_at)") + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_tenant_name ON contacts (tenant_id, last_name, first_name)") + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_email ON contacts (email)") # --- company_contacts (N:M join) --- op.create_table( diff --git a/alembic/versions/0005_user_role_fk.py b/alembic/versions/0005_user_role_fk.py index 0f8fff0..29728d5 100644 --- a/alembic/versions/0005_user_role_fk.py +++ b/alembic/versions/0005_user_role_fk.py @@ -20,16 +20,8 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: - op.add_column( - "users", - sa.Column( - "role_id", - postgresql.UUID(as_uuid=True), - sa.ForeignKey("roles.id", ondelete="SET NULL"), - nullable=True, - ), - ) - op.create_index("ix_users_role_id", "users", ["role_id"]) + op.execute("ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id UUID REFERENCES roles(id) ON DELETE SET NULL") + op.execute("CREATE INDEX IF NOT EXISTS ix_users_role_id ON users (role_id)") def downgrade() -> None: diff --git a/alembic/versions/0006_add_addresses.py b/alembic/versions/0006_add_addresses.py index 8abf2f5..54467e3 100644 --- a/alembic/versions/0006_add_addresses.py +++ b/alembic/versions/0006_add_addresses.py @@ -20,18 +20,18 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # Add address columns to companies - op.add_column("companies", sa.Column("address_street", sa.String(255), nullable=True)) - op.add_column("companies", sa.Column("address_city", sa.String(100), nullable=True)) - op.add_column("companies", sa.Column("address_zip", sa.String(20), nullable=True)) - op.add_column("companies", sa.Column("address_country", sa.String(2), nullable=True)) - op.add_column("companies", sa.Column("address_state", sa.String(100), nullable=True)) + op.execute("ALTER TABLE companies ADD COLUMN IF NOT EXISTS address_street VARCHAR(255)") + op.execute("ALTER TABLE companies ADD COLUMN IF NOT EXISTS address_city VARCHAR(100)") + op.execute("ALTER TABLE companies ADD COLUMN IF NOT EXISTS address_zip VARCHAR(20)") + op.execute("ALTER TABLE companies ADD COLUMN IF NOT EXISTS address_country VARCHAR(2)") + op.execute("ALTER TABLE companies ADD COLUMN IF NOT EXISTS address_state VARCHAR(100)") # Add address columns to contacts - op.add_column("contacts", sa.Column("address_street", sa.String(255), nullable=True)) - op.add_column("contacts", sa.Column("address_city", sa.String(100), nullable=True)) - op.add_column("contacts", sa.Column("address_zip", sa.String(20), nullable=True)) - op.add_column("contacts", sa.Column("address_country", sa.String(2), nullable=True)) - op.add_column("contacts", sa.Column("address_state", sa.String(100), nullable=True)) + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS address_street VARCHAR(255)") + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS address_city VARCHAR(100)") + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS address_zip VARCHAR(20)") + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS address_country VARCHAR(2)") + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS address_state VARCHAR(100)") def downgrade() -> None: diff --git a/alembic/versions/0018_fix_notification_preferences_columns.py b/alembic/versions/0018_fix_notification_preferences_columns.py index 7b9d565..3a0f819 100644 --- a/alembic/versions/0018_fix_notification_preferences_columns.py +++ b/alembic/versions/0018_fix_notification_preferences_columns.py @@ -15,14 +15,8 @@ depends_on = None def upgrade() -> None: # Add missing columns from TimestampMixin and SoftDeleteMixin - op.add_column( - "notification_preferences", - sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), - ) - op.add_column( - "notification_preferences", - sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True), - ) + op.execute("ALTER TABLE notification_preferences ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") + op.execute("ALTER TABLE notification_preferences ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ") def downgrade() -> None: diff --git a/alembic/versions/0021_unified_contacts.py b/alembic/versions/0021_unified_contacts.py index 9d1f725..5b520e8 100644 --- a/alembic/versions/0021_unified_contacts.py +++ b/alembic/versions/0021_unified_contacts.py @@ -161,11 +161,14 @@ def upgrade() -> None: sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True), ) - op.create_index("ix_contacts_tenant_deleted", "contacts", ["tenant_id", "deleted_at"]) + op.execute("DROP INDEX IF EXISTS ix_contacts_tenant_deleted") + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_tenant_deleted ON contacts (tenant_id, deleted_at)") op.create_index("ix_contacts_tenant_type", "contacts", ["tenant_id", "type"]) - op.create_index("ix_contacts_tenant_name", "contacts", ["tenant_id", "name"]) + op.execute("DROP INDEX IF EXISTS ix_contacts_tenant_name") + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_tenant_name ON contacts (tenant_id, name)") op.create_index("ix_contacts_tenant_displayname", "contacts", ["tenant_id", "displayname"]) - op.create_index("ix_contacts_email", "contacts", ["email_1"]) + op.execute("DROP INDEX IF EXISTS ix_contacts_email") + op.execute("CREATE INDEX IF NOT EXISTS ix_contacts_email ON contacts (email_1)") op.create_index("ix_contacts_code", "contacts", ["code"]) op.create_index("ix_contacts_search_vec", "contacts", ["search_tsv"], postgresql_using="gin") @@ -202,8 +205,8 @@ def upgrade() -> None: op.create_index("ix_contactpersons_email", "contactpersons", ["email"]) # ── 4. Add FK columns to contacts that reference contactpersons ─── - op.add_column("contacts", sa.Column("default_person_id", UUID(as_uuid=True), sa.ForeignKey("contactpersons.id", ondelete="SET NULL"), nullable=True)) - op.add_column("contacts", sa.Column("admin_contactperson_id", UUID(as_uuid=True), sa.ForeignKey("contactpersons.id", ondelete="SET NULL"), nullable=True)) + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS default_person_id UUID REFERENCES contactpersons(id) ON DELETE SET NULL") + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS admin_contactperson_id UUID REFERENCES contactpersons(id) ON DELETE SET NULL") # ── 5. Migrate data from old tables ──────────────────────────────── diff --git a/alembic/versions/0022_contact_folders.py b/alembic/versions/0022_contact_folders.py index a2bec5f..2f7a839 100644 --- a/alembic/versions/0022_contact_folders.py +++ b/alembic/versions/0022_contact_folders.py @@ -31,10 +31,7 @@ def upgrade(): op.create_index("ix_contact_folders_user", "contact_folders", ["user_id"]) # 2. Add folder_id column to contacts - op.add_column( - "contacts", - sa.Column("folder_id", UUID(as_uuid=True), sa.ForeignKey("contact_folders.id", ondelete="SET NULL"), nullable=True), - ) + op.execute("ALTER TABLE contacts ADD COLUMN IF NOT EXISTS folder_id UUID REFERENCES contact_folders(id) ON DELETE SET NULL") op.create_index("ix_contacts_folder_id", "contacts", ["folder_id"]) diff --git a/alembic/versions/0023_theme_customization.py b/alembic/versions/0023_theme_customization.py index ab20789..d306950 100644 --- a/alembic/versions/0023_theme_customization.py +++ b/alembic/versions/0023_theme_customization.py @@ -13,10 +13,10 @@ down_revision = "0022_contact_folders" def upgrade(): - op.add_column("system_settings", sa.Column("theme_primary_color", sa.String(20), nullable=False, server_default="#2563eb")) - op.add_column("system_settings", sa.Column("theme_accent_color", sa.String(20), nullable=False, server_default="#d946ef")) - op.add_column("system_settings", sa.Column("theme_font_family", sa.String(100), nullable=False, server_default="Inter")) - op.add_column("system_settings", sa.Column("theme_border_radius", sa.String(20), nullable=False, server_default="0.5rem")) + op.execute("ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS theme_primary_color VARCHAR(20) NOT NULL DEFAULT '#2563eb'") + op.execute("ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS theme_accent_color VARCHAR(20) NOT NULL DEFAULT '#d946ef'") + op.execute("ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS theme_font_family VARCHAR(100) NOT NULL DEFAULT 'Inter'") + op.execute("ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS theme_border_radius VARCHAR(20) NOT NULL DEFAULT '0.5rem'") def downgrade(): diff --git a/alembic/versions/0024_heartbeat_config.py b/alembic/versions/0024_heartbeat_config.py index 3cbe5ba..b772aa9 100644 --- a/alembic/versions/0024_heartbeat_config.py +++ b/alembic/versions/0024_heartbeat_config.py @@ -13,9 +13,9 @@ down_revision = "0023_theme_customization" def upgrade(): - op.add_column("ai_proactive_settings", sa.Column("heartbeat_enabled", sa.Boolean(), nullable=False, server_default=sa.text("true"))) - op.add_column("ai_proactive_settings", sa.Column("heartbeat_interval_seconds", sa.Integer(), nullable=False, server_default=sa.text("300"))) - op.add_column("ai_proactive_settings", sa.Column("heartbeat_target_room", sa.String(200), nullable=False, server_default="Live KI")) + op.execute("ALTER TABLE ai_proactive_settings ADD COLUMN IF NOT EXISTS heartbeat_enabled BOOLEAN NOT NULL DEFAULT true") + op.execute("ALTER TABLE ai_proactive_settings ADD COLUMN IF NOT EXISTS heartbeat_interval_seconds INTEGER NOT NULL DEFAULT 300") + op.execute("ALTER TABLE ai_proactive_settings ADD COLUMN IF NOT EXISTS heartbeat_target_room VARCHAR(200) NOT NULL DEFAULT 'Live KI'") def downgrade(): diff --git a/alembic/versions/0026_mail_salt_security.py b/alembic/versions/0026_mail_salt_security.py index 475fc86..9e7805b 100644 --- a/alembic/versions/0026_mail_salt_security.py +++ b/alembic/versions/0026_mail_salt_security.py @@ -17,7 +17,7 @@ down_revision = "0025_entity_history" def upgrade(): - op.add_column("mail_accounts", sa.Column("password_salt", sa.String(64), nullable=False, server_default="")) + op.execute("ALTER TABLE mail_accounts ADD COLUMN IF NOT EXISTS password_salt VARCHAR(64) NOT NULL DEFAULT ''") def downgrade(): diff --git a/alembic/versions/0032_user_profile_fields.py b/alembic/versions/0032_user_profile_fields.py index e37a2d2..56dd8eb 100644 --- a/alembic/versions/0032_user_profile_fields.py +++ b/alembic/versions/0032_user_profile_fields.py @@ -14,9 +14,9 @@ depends_on = None def upgrade() -> None: - op.add_column("users", sa.Column("first_name", sa.String(100), nullable=True)) - op.add_column("users", sa.Column("last_name", sa.String(100), nullable=True)) - op.add_column("users", sa.Column("avatar_url", sa.String(500), nullable=True)) + op.execute("ALTER TABLE users ADD COLUMN IF NOT EXISTS first_name VARCHAR(100)") + op.execute("ALTER TABLE users ADD COLUMN IF NOT EXISTS last_name VARCHAR(100)") + op.execute("ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url VARCHAR(500)") def downgrade() -> None: diff --git a/alembic/versions/0034_automation_config.py b/alembic/versions/0034_automation_config.py index f83f644..f7ae2c3 100644 --- a/alembic/versions/0034_automation_config.py +++ b/alembic/versions/0034_automation_config.py @@ -20,7 +20,7 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: - op.add_column("system_settings", sa.Column("automation_config", JSONB, nullable=True)) + op.execute("ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS automation_config JSONB") def downgrade() -> None: diff --git a/alembic/versions/0053_mail_owner_id.py b/alembic/versions/0053_mail_owner_id.py index 5988a38..24ed2df 100644 --- a/alembic/versions/0053_mail_owner_id.py +++ b/alembic/versions/0053_mail_owner_id.py @@ -20,20 +20,8 @@ depends_on = None def upgrade(): - op.add_column( - "mail_accounts", - sa.Column( - "owner_id", - UUID(as_uuid=True), - sa.ForeignKey("users.id", ondelete="SET NULL"), - nullable=True, - ), - ) - op.create_index( - "ix_mail_accounts_owner", - "mail_accounts", - ["owner_id"], - ) + op.execute("ALTER TABLE mail_accounts ADD COLUMN IF NOT EXISTS owner_id UUID REFERENCES users(id) ON DELETE SET NULL") + op.execute("CREATE INDEX IF NOT EXISTS ix_mail_accounts_owner ON mail_accounts (owner_id)") def downgrade(): diff --git a/alembic/versions/0063_notification_entity_fields.py b/alembic/versions/0063_notification_entity_fields.py index 3cc87db..a96c57d 100644 --- a/alembic/versions/0063_notification_entity_fields.py +++ b/alembic/versions/0063_notification_entity_fields.py @@ -19,8 +19,9 @@ depends_on = None def upgrade() -> None: - op.add_column("notifications", sa.Column("entity_type", sa.String(50), nullable=True, index=True)) - op.add_column("notifications", sa.Column("entity_id", UUID(as_uuid=True), nullable=True)) + op.execute("ALTER TABLE notifications ADD COLUMN IF NOT EXISTS entity_type VARCHAR(50)") + op.execute("CREATE INDEX IF NOT EXISTS ix_notifications_entity_type ON notifications (entity_type)") + op.execute("ALTER TABLE notifications ADD COLUMN IF NOT EXISTS entity_id UUID") def downgrade() -> None: diff --git a/alembic/versions/0074_workspace_users_timestamps.py b/alembic/versions/0074_workspace_users_timestamps.py index ad3e282..daff426 100644 --- a/alembic/versions/0074_workspace_users_timestamps.py +++ b/alembic/versions/0074_workspace_users_timestamps.py @@ -18,8 +18,8 @@ depends_on = None def upgrade() -> None: # workspace_users: add created_at and updated_at - op.add_column("workspace_users", sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False)) - op.add_column("workspace_users", sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False)) + op.execute("ALTER TABLE workspace_users ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") + op.execute("ALTER TABLE workspace_users ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") # workspace_widgets: already has created_at/updated_at from migration 0072 # workspace_modules: already has created_at/updated_at from migration 0072 diff --git a/alembic/versions/0075_outbox_envelope.py b/alembic/versions/0075_outbox_envelope.py index 229bf23..1eb2ff6 100644 --- a/alembic/versions/0075_outbox_envelope.py +++ b/alembic/versions/0075_outbox_envelope.py @@ -30,11 +30,11 @@ depends_on = None def upgrade() -> None: # 1. Add envelope columns to event_outbox - op.add_column("event_outbox", sa.Column("aggregate_type", sa.String(100), nullable=True)) - op.add_column("event_outbox", sa.Column("aggregate_id", PGUUID(as_uuid=True), nullable=True)) - op.add_column("event_outbox", sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False)) - op.add_column("event_outbox", sa.Column("correlation_id", PGUUID(as_uuid=True), nullable=True)) - op.add_column("event_outbox", sa.Column("schema_version", sa.Integer, nullable=False, server_default=sa.text("1"))) + op.execute("ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS aggregate_type VARCHAR(100)") + op.execute("ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS aggregate_id UUID") + op.execute("ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") + op.execute("ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS correlation_id UUID") + op.execute("ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS schema_version INTEGER NOT NULL DEFAULT 1") op.execute("CREATE INDEX IF NOT EXISTS ix_event_outbox_aggregate ON event_outbox (tenant_id, aggregate_type, aggregate_id)") op.execute("CREATE INDEX IF NOT EXISTS ix_event_outbox_correlation ON event_outbox (correlation_id)") diff --git a/alembic/versions/0087_add_timestamps_to_password_reset_tokens.py b/alembic/versions/0087_add_timestamps_to_password_reset_tokens.py index 3f24e9b..4abbb70 100644 --- a/alembic/versions/0087_add_timestamps_to_password_reset_tokens.py +++ b/alembic/versions/0087_add_timestamps_to_password_reset_tokens.py @@ -20,8 +20,8 @@ depends_on = None def upgrade() -> None: - op.add_column("password_reset_tokens", sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) - op.add_column("password_reset_tokens", sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now())) + op.execute("ALTER TABLE password_reset_tokens ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") + op.execute("ALTER TABLE password_reset_tokens ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()") def downgrade() -> None: