Skip to content

Fix/sql migration - #315

Merged
Ahmath-Gadji merged 3 commits into
devfrom
fix/sql_migration
Apr 20, 2026
Merged

Fix/sql migration#315
Ahmath-Gadji merged 3 commits into
devfrom
fix/sql_migration

Conversation

@Ahmath-Gadji

@Ahmath-Gadji Ahmath-Gadji commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix the alembic migration chain for existing deployments where Base.metadata.create_all() (called at app startup in PartitionFileManager.__init__) had already produced the current-model schema before alembic ever ran. Re-running alembic upgrade head against such a database used to raise DuplicateColumn / DuplicateTable; it now no-ops cleanly.

Also fixes a Postgres-invalid UPDATE ... FROM ... JOIN that referenced the update target inside a from_item, and consolidates duplicated inspector helpers into a shared module.

Changes

  • Idempotent migrations — guard every add_column, create_table, create_index, create_foreign_key (and the corresponding downgrade ops) with an existence check. Migrations touched: 4add4d260575, cd642e4502d8, c224d4befe71, e7f8a9b0c1d2, f1a2b3c4d5e6, f5b6c918f741, a1b2c3d4e5f6. The String→Integer conversion in f1a2b3c4d5e6 short-circuits when workspace_files.file_id is already INTEGER.
  • Shared schema_helpers module — new openrag/scripts/migrations/alembic/schema_helpers.py exposes table_exists, column_exists, index_exists, fk_exists, column_type_is. env.py prepends the alembic dir to sys.path so version files can from schema_helpers import ... regardless of cwd. All call sites use the canonical (table, index) arg order.
  • SQL fix in f1a2b3c4d5e6 — the UPDATE workspace_files wf SET file_fk = f.id FROM files f JOIN workspaces w ON w.workspace_id = wf.workspace_id WHERE ... was rejected by Postgres ("invalid reference to FROM-clause entry for table 'wf'"). Rewritten to put both files and workspaces in the FROM list with all join conditions in WHERE.
  • Docs — new ### Alembic Migration Idempotency section in CLAUDE.md explaining the contract and required guards.

Test plan

Reproduce the original failure mode and verify the fix on an upgraded deployment:

  1. Check out tag v1.1.7 and bring up the stack so Base.metadata.create_all() bootstraps the schema (and stamps no alembic version, or stamps the baseline that shipped with that tag).
  2. Switch to this branch (fix/sql_migration).
  3. Apply the migrations against the same database:
    docker compose up -d rdb
    docker compose \
        run --no-deps --build --rm \
        --entrypoint "uv run alembic -c /app/openrag/scripts/migrations/alembic/alembic.ini upgrade head" \
        openrag; docker compose down
    
    

Summary by CodeRabbit

  • Documentation

    • Added migration idempotency guidelines requiring schema-mutating operations to be guarded with existence checks.
  • Refactor

    • Extracted common database schema inspection utilities for migrations.
    • Updated all migrations to use shared helpers and execute idempotently, enabling safe re-runs without errors.

Base.metadata.create_all() at app startup may already have created
columns/tables/indexes from the SQLAlchemy models on existing
deployments. Guard each ADD/CREATE op so re-running alembic upgrade
on such a database no-ops instead of raising DuplicateColumn /
DuplicateTable.
Move the duplicated table_exists / column_exists / index_exists / fk_exists
checks (plus a new column_type_is) out of individual migration files and
into a single schema_helpers module alongside env.py. env.py prepends the
alembic directory to sys.path so versions can import it regardless of cwd.

All call sites updated to the canonical (table, index) arg order.
@coderabbitai

coderabbitai Bot commented Apr 20, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EnjoyBacon7 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 13 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 47 minutes and 13 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae287fd6-8051-4eba-9168-f886cc325bee

📥 Commits

Reviewing files that changed from the base of the PR and between 72dc572 and c4b2f8f.

📒 Files selected for processing (2)
  • openrag/scripts/migrations/alembic/schema_helpers.py
  • openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_add_workspace_files_file_id_fk.py
📝 Walkthrough

Walkthrough

This PR introduces idempotent migration support by creating a centralized schema-inspection utility module, updating all Alembic migration files to import and use these helpers instead of local implementations, and making schema alterations conditional on object existence checks.

Changes

Cohort / File(s) Summary
Documentation & Infrastructure
CLAUDE.md, openrag/scripts/migrations/alembic/env.py
Added idempotency requirements documentation and configured env.py to ensure sibling modules like schema_helpers are importable from any working directory.
New Schema Helpers
openrag/scripts/migrations/alembic/schema_helpers.py
Introduced shared utilities module with five helper functions (table_exists, column_exists, index_exists, fk_exists, column_type_is) using SQLAlchemy inspector to query database schema at migration runtime.
Migration Updates
openrag/scripts/migrations/alembic/versions/4add4d260575_...py, openrag/scripts/migrations/alembic/versions/a1b2c3d4e5f6_...py, openrag/scripts/migrations/alembic/versions/cd642e4502d8_...py
Replaced local helper implementations with schema_helpers imports and corrected index_exists argument order from (index_name, table_name) to (table_name, index_name) in all index-related checks.
Migration Idempotency Refactors
openrag/scripts/migrations/alembic/versions/c224d4befe71_...py, openrag/scripts/migrations/alembic/versions/e7f8a9b0c1d2_...py, openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_...py, openrag/scripts/migrations/alembic/versions/f5b6c918f741_...py
Converted unconditional schema alterations to conditional operations guarded by existence checks; updated both upgrade() and downgrade() logic to be idempotent and prevent duplicate object creation or errors on missing objects.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • linagora/openrag#272: Introduced the e7f8a9b0c1d2_add_workspaces.py migration that is directly refactored in this PR to use centralized schema-helpers utilities.

Suggested labels

refactor

Poem

🐰 A rabbit hops through migrations with care,
Idempotent checks floating through the air,
Helpers unite where once duplicates grew,
Schema inspectors keep databases true! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix/sql migration' is vague and generic, using non-descriptive terminology that doesn't clearly convey the specific nature or scope of the changes to someone reviewing pull request history. Revise the title to be more specific and descriptive, such as 'Make Alembic migrations idempotent with existence checks' or 'Refactor migrations to handle pre-existing schema from Base.metadata.create_all()'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sql_migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_add_workspace_files_file_id_fk.py (1)

29-85: ⚠️ Potential issue | 🟠 Major

Guard all schema-mutating operations in this Alembic migration for idempotency.

The upgrade() function checks only whether workspace_files.file_id is already an integer (line 29), then proceeds with unguarded operations at lines 36, 55–64. Similarly, the downgrade() function (lines 76–85) performs schema mutations without existence checks. A migration rerun after a partial failure will fail on duplicate columns, missing indices, or missing foreign keys.

Add guards using inspector-based checks (column_exists(), index_exists(), fk_exists()) and consider a unique-constraint helper for uix_workspace_file (created at lines 63 and 85 without guards). Guard both the upgrade() and downgrade() paths entirely, as required by the coding guidelines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_add_workspace_files_file_id_fk.py`
around lines 29 - 85, The migration's upgrade() and downgrade() perform schema
changes unguarded; wrap each schema-mutating step (DELETEs,
add_column("file_fk"), UPDATEs, drop_index("ix_workspace_files_file_id"),
drop_column("file_id"), alter_column, create_index,
create_unique_constraint("uix_workspace_file"),
create_foreign_key("fk_workspace_files_file_id")) with inspector-based existence
checks (use the existing column_type_is() / add new helpers column_exists(),
index_exists(), fk_exists()) so each operation only runs if the target object is
absent/present as appropriate; do the same in downgrade() for
add_column("file_str"), UPDATE from files, drop_column("file_id"), alter_column,
create_index and create_unique_constraint("uix_workspace_file"), and ensure the
unique constraint creation is guarded by a helper check to avoid
duplicate-constraint errors on reruns or partial failures.
🧹 Nitpick comments (1)
openrag/scripts/migrations/alembic/env.py (1)

5-7: Remove the redundant sys.path.insert(0, ...).

The alembic.ini configuration already includes prepend_sys_path = . (line 19), which adds the alembic directory to the Python path. The explicit sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) in env.py:7 is redundant and can be safely removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openrag/scripts/migrations/alembic/env.py` around lines 5 - 7, Remove the
redundant manual PYTHONPATH modification in env.py by deleting the
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) call (the
explicit insertion near the top of the file) so the alembic directory is not
added twice; rely on the existing alembic.ini setting (prepend_sys_path) to
provide the path and verify imports that reference local modules (e.g.,
schema_helpers) still resolve after removing that line.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openrag/scripts/migrations/alembic/schema_helpers.py`:
- Around line 16-33: The helper functions column_exists, index_exists,
fk_exists, and column_type_is must each check table_exists(table) first and
return False immediately if the table doesn't exist; move the
inspect(op.get_bind()) calls and subsequent reflection logic inside that guard
so NoSuchTableError is avoided and the helpers remain idempotent. Specifically,
add an initial if not table_exists(table): return False to column_exists,
index_exists, fk_exists, and column_type_is, then perform the existing
Inspector.get_* calls only when the table exists.

---

Outside diff comments:
In
`@openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_add_workspace_files_file_id_fk.py`:
- Around line 29-85: The migration's upgrade() and downgrade() perform schema
changes unguarded; wrap each schema-mutating step (DELETEs,
add_column("file_fk"), UPDATEs, drop_index("ix_workspace_files_file_id"),
drop_column("file_id"), alter_column, create_index,
create_unique_constraint("uix_workspace_file"),
create_foreign_key("fk_workspace_files_file_id")) with inspector-based existence
checks (use the existing column_type_is() / add new helpers column_exists(),
index_exists(), fk_exists()) so each operation only runs if the target object is
absent/present as appropriate; do the same in downgrade() for
add_column("file_str"), UPDATE from files, drop_column("file_id"), alter_column,
create_index and create_unique_constraint("uix_workspace_file"), and ensure the
unique constraint creation is guarded by a helper check to avoid
duplicate-constraint errors on reruns or partial failures.

---

Nitpick comments:
In `@openrag/scripts/migrations/alembic/env.py`:
- Around line 5-7: Remove the redundant manual PYTHONPATH modification in env.py
by deleting the sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
call (the explicit insertion near the top of the file) so the alembic directory
is not added twice; rely on the existing alembic.ini setting (prepend_sys_path)
to provide the path and verify imports that reference local modules (e.g.,
schema_helpers) still resolve after removing that line.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d0cae611-f4bb-4fd5-96dd-8c0130980a66

📥 Commits

Reviewing files that changed from the base of the PR and between d6fd8e7 and 72dc572.

📒 Files selected for processing (10)
  • CLAUDE.md
  • openrag/scripts/migrations/alembic/env.py
  • openrag/scripts/migrations/alembic/schema_helpers.py
  • openrag/scripts/migrations/alembic/versions/4add4d260575_initial_migration.py
  • openrag/scripts/migrations/alembic/versions/a1b2c3d4e5f6_add_document_relationships.py
  • openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py
  • openrag/scripts/migrations/alembic/versions/cd642e4502d8_create_users_memberships_tables.py
  • openrag/scripts/migrations/alembic/versions/e7f8a9b0c1d2_add_workspaces.py
  • openrag/scripts/migrations/alembic/versions/f1a2b3c4d5e6_add_workspace_files_file_id_fk.py
  • openrag/scripts/migrations/alembic/versions/f5b6c918f741_add_oidc_auth.py

Comment thread openrag/scripts/migrations/alembic/schema_helpers.py
@Ahmath-Gadji Ahmath-Gadji added the fix Fix issue label Apr 20, 2026
…rd reference

Postgres rejects 'JOIN workspaces w ON w.workspace_id = wf.workspace_id' inside
the FROM clause of an UPDATE because the target table (wf) cannot be referenced
from a from_item's JOIN ON. Move workspaces into the FROM list and put all join
conditions in WHERE.
@Ahmath-Gadji
Ahmath-Gadji merged commit 65c5a71 into dev Apr 20, 2026
4 checks passed
@Ahmath-Gadji
Ahmath-Gadji deleted the fix/sql_migration branch April 20, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant