Skip to content

Commit b48fcde

Browse files
committed
fix(alembic): make migration scripts idempotent to resolve upgrade errors
- Checked column existence dynamically in `20260330_refactor_user_system_phase2.py` before querying and altering columns (`username`, `email`, etc.) that might have already been moved/dropped. - Updated `add_participants.py` backfill logic to conditionally select `username` from the `identities` table via a LEFT JOIN, preventing missing column errors on the `users` table. - Added conditional checks in `d9cbd43b62e5_add_llm_request_timeout.py` to prevent `DuplicateColumnError` when adding the `request_timeout` column. - Added a pre-condition check in `user_refactor.py` to ensure the `email` column still exists on the `users` table before attempting to create the `ix_users_tenant_email_unique` index.
1 parent abd12a3 commit b48fcde

4 files changed

Lines changed: 30 additions & 11 deletions

File tree

backend/alembic/versions/20260330_refactor_user_system_phase2.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,16 @@ def upgrade() -> None:
6161
op.create_foreign_key('fk_users_identity_id', 'users', 'identities', ['identity_id'], ['id'])
6262

6363
# 5. Data migration (idempotent)
64-
# Only migrate users that don't have an identity_id yet
65-
result = conn.execute(sa.text("""
66-
SELECT id, email, primary_mobile, username, password_hash, email_verified, is_active, role
64+
user_columns = [c['name'] for c in inspector.get_columns('users')]
65+
66+
email_col = 'email' if 'email' in user_columns else 'NULL as email'
67+
phone_col = 'primary_mobile' if 'primary_mobile' in user_columns else 'NULL as primary_mobile'
68+
username_col = 'username' if 'username' in user_columns else 'NULL as username'
69+
pwd_col = 'password_hash' if 'password_hash' in user_columns else 'NULL as password_hash'
70+
ev_col = 'email_verified' if 'email_verified' in user_columns else 'NULL as email_verified'
71+
72+
result = conn.execute(sa.text(f"""
73+
SELECT id, {email_col}, {phone_col}, {username_col}, {pwd_col}, {ev_col}, is_active, role
6774
FROM users
6875
WHERE identity_id IS NULL
6976
"""))
@@ -120,8 +127,10 @@ def upgrade() -> None:
120127
})
121128

122129
# 6. Cleanup: Make username/email nullable and DROP redundant columns
123-
op.alter_column('users', 'username', existing_type=sa.String(length=100), nullable=True)
124-
op.alter_column('users', 'email', existing_type=sa.String(length=255), nullable=True)
130+
if 'username' in user_columns:
131+
op.alter_column('users', 'username', existing_type=sa.String(length=100), nullable=True)
132+
if 'email' in user_columns:
133+
op.alter_column('users', 'email', existing_type=sa.String(length=255), nullable=True)
125134

126135
# Physically drop redundant columns
127136
op.execute("ALTER TABLE users DROP COLUMN IF EXISTS username")

backend/alembic/versions/add_participants.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ def upgrade() -> None:
3232
# 2. Backfill: create Participant for every existing User
3333
conn.execute(sa.text("""
3434
INSERT INTO participants (id, type, ref_id, display_name, avatar_url)
35-
SELECT gen_random_uuid(), 'user', id, COALESCE(display_name, username), avatar_url
36-
FROM users
35+
SELECT gen_random_uuid(), 'user', u.id, COALESCE(u.display_name, i.username, 'User'), u.avatar_url
36+
FROM users u
37+
LEFT JOIN identities i ON u.identity_id = i.id
3738
ON CONFLICT DO NOTHING
3839
"""))
3940

backend/alembic/versions/d9cbd43b62e5_add_llm_request_timeout.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919

2020
def upgrade() -> None:
2121
# ### commands auto generated by Alembic - please adjust! ###
22-
op.add_column('llm_models', sa.Column('request_timeout', sa.Integer(), nullable=True))
22+
conn = op.get_bind()
23+
inspector = sa.inspect(conn)
24+
columns = [c['name'] for c in inspector.get_columns('llm_models')]
25+
if 'request_timeout' not in columns:
26+
op.add_column('llm_models', sa.Column('request_timeout', sa.Integer(), nullable=True))
2327
# ### end Alembic commands ###
2428

2529

backend/alembic/versions/user_refactor.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,15 @@ def upgrade() -> None:
144144
op.execute("""
145145
DO $$
146146
BEGIN
147-
IF NOT EXISTS (
148-
SELECT 1 FROM pg_indexes WHERE indexname = 'ix_users_tenant_email_unique'
147+
IF EXISTS (
148+
SELECT 1 FROM information_schema.columns
149+
WHERE table_name = 'users' AND column_name = 'email'
149150
) THEN
150-
CREATE UNIQUE INDEX ix_users_tenant_email_unique ON users(tenant_id, email) WHERE email IS NOT NULL;
151+
IF NOT EXISTS (
152+
SELECT 1 FROM pg_indexes WHERE indexname = 'ix_users_tenant_email_unique'
153+
) THEN
154+
CREATE UNIQUE INDEX ix_users_tenant_email_unique ON users(tenant_id, email) WHERE email IS NOT NULL;
155+
END IF;
151156
END IF;
152157
END $$
153158
""")

0 commit comments

Comments
 (0)