fix: raw SQL in migration 008 to prevent duplicate division enum #86
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api!86
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "85-bug-non-idempotent-division-enum-in-migr"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replaces
op.create_table()in migration 008 with raw SQL viaop.execute()to prevent SQLAlchemy metadata from emitting a duplicateCREATE TYPE division, which caused CrashLoopBackOff on redeploy.Changes
alembic/versions/008_add_team_model.py: Replacedop.create_table()with threeop.execute()calls using raw DDL (CREATE TABLE IF NOT EXISTS,CREATE INDEX IF NOT EXISTS, exception-guardedCREATE TYPE). Removed unusedpostgresqlimport.downgrade()andop.add_column()left unchanged.Test Plan
Review Checklist
Related
plan-westside-basketballSelf-review: LGTM -- no issues found.
Reviewed the diff (1 file, +24/-41):
DO $$ BEGIN ... EXCEPTION WHEN duplicate_objectis the standard PostgreSQL idiom for idempotent enum creation. Correct.CREATE TABLE IF NOT EXISTSandCREATE INDEX IF NOT EXISTSare idempotent by design. Correct.divisionandage_groupcolumns reference existing enum types by name without any SQLAlchemy ENUM wrapper -- eliminates the metadata interference that caused duplicateCREATE TYPE. Correct.from sqlalchemy.dialects import postgresqlimport removed. Correct.downgrade()andop.add_column()unchanged as required.PR #86 Backend Review
Title: fix: raw SQL in migration 008 to prevent duplicate division enum
Branch:
85-bug-non-idempotent-division-enum-in-migr-- Parent issue: #85Scope: 1 file changed, 24 additions, 41 deletions
DOMAIN REVIEW
PEP compliance:
upgrade()/downgrade(), and clean formatting.from sqlalchemy.dialects import postgresqlcorrectly removed.Security (OWASP):
.envcontent in the diff.Database / Migration discipline:
op.create_table()with rawCREATE TABLE IF NOT EXISTSDDL completely bypasses SQLAlchemy metadata's enum introspection, which is the root cause of the duplicateCREATE TYPE divisioncrash documented in issue #85. This is the same proven pattern used in migration 007 for theemailtypeenum.agegroupenum creation uses theDO $$ BEGIN ... EXCEPTION WHEN duplicate_object ... END $$idiom -- correct and idempotent.CREATE INDEX IF NOT EXISTSon bothix_teams_tenant_idandix_teams_coach_id-- correct.models.py(verified:id SERIAL PRIMARY KEY,tenant_id INTEGER NOT NULL REFERENCES tenants(id),name VARCHAR(200) NOT NULL,division divisionnullable,age_group agegroupnullable,coach_id INTEGER REFERENCES coaches(id)nullable,created_at TIMESTAMP NOT NULL DEFAULT now()).AgeGroupenum values in raw SQL ('U8','U10','U12','U14','U16','U18') match the Python enum exactly.divisiontype name referenced in theCREATE TABLEmatches the enum created in migratione09c9e678004.downgrade()function is unchanged and usesop.drop_table("teams")+sa.Enum(name="agegroup").drop()withcheckfirst=True-- correct for teardown.Tests:
test_teams.pyexercises the Team model and endpoints, which implicitly validates the migration creates the correct schema.API design:
BLOCKERS
None.
NITS
op.add_columnis not idempotent (lines 47-56): Theop.add_column("players", ...)forteam_idis not wrapped in anyIF NOT EXISTSguard. If theteamstable was created in a prior partial run but the migration was not stamped, this will fail withDuplicateColumn. This is pre-existing (unchanged from the original migration), so it is not a blocker for this PR, but worth noting as a follow-up hardening opportunity for full idempotency.Consistency with migration 007: Migration 007 wraps its raw SQL enum creation in
sa.text()(line 27-32 of 007), while this PR passes plain strings toop.execute(). Both work -- Alembic'sop.execute()accepts both raw strings andsa.text()objects -- but usingsa.text()is considered best practice for explicit SQL to avoid any future deprecation. Non-blocking, minor consistency point.SOP COMPLIANCE
85-bug-non-idempotent-division-enum-in-migrreferences #85)plan-westside-basketball)Closes #85present in PR bodyPROCESS OBSERVATIONS
divisionenum from migratione09c9e678004. Manual deploy verification is appropriate here since migration idempotency against an existing production schema is difficult to automate in CI without a dedicated staging DB.create_type=Falsefootgun (007 had the same issue withemailtype). A convention note documenting "always use raw SQL for enum-dependent table creation in Alembic" would prevent this class of bug across repos.VERDICT: APPROVED