Skip to content

fix: prevent metadata table from being dropped#843

Merged
abnegate merged 1 commit intomainfrom
fix-metadata-reconciliation
Mar 20, 2026
Merged

fix: prevent metadata table from being dropped#843
abnegate merged 1 commit intomainfrom
fix-metadata-reconciliation

Conversation

@abnegate
Copy link
Member

@abnegate abnegate commented Mar 20, 2026

When createCollection is called for the _metadata table and the table already exists, the orphan reconciliation code would drop and recreate it, destroying all collection metadata. This breaks upgrades where Database::create() is called on an existing database.

The fix adds _metadata to the safe list alongside shared-tables checks, ensuring the metadata table is never treated as an orphan.

Summary by CodeRabbit

  • Bug Fixes
    • Improved database collection reconciliation logic to better preserve system metadata during operations.

…iation

When createCollection is called for the _metadata table and the table
already exists, the orphan reconciliation code would drop and recreate
it, destroying all collection metadata. This breaks upgrades where
Database::create() is called on an existing database.

The fix adds _metadata to the safe list alongside shared-tables checks,
ensuring the metadata table is never treated as an orphan.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6607ef80-c66f-4568-9c65-b26be5ce3846

📥 Commits

Reviewing files that changed from the base of the PR and between f121418 and b6c7176.

📒 Files selected for processing (1)
  • src/Database/Database.php

📝 Walkthrough

Walkthrough

The createCollection method's DuplicateException handler was refactored to check the _metadata condition first before the shared-tables and physical-exists validation. A clarifying comment was added to document that the metadata table must never be dropped during reconciliation.

Changes

Cohort / File(s) Summary
DuplicateException Handler Refactoring
src/Database/Database.php
Reordered condition evaluation in exception handler to prioritize _metadata check before shared-tables validation; added explanatory comment about metadata table safety during reconciliation.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

Poem

🐰 Metadata tables, precious and true,
Must never be dropped—we made this clear to you!
Conditions reordered with wisdom so bright,
Safety first always, our logic feels right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: preventing the metadata table from being dropped during collection creation/reconciliation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-metadata-reconciliation
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@greptile-apps
Copy link

greptile-apps bot commented Mar 20, 2026

Greptile Summary

This PR fixes a critical data-loss bug in createCollection where the guard protecting the _metadata table from orphan reconciliation was incorrectly gated behind getSharedTables(), causing the metadata table to be silently dropped and recreated whenever Database::create() was called on an existing database in non-shared-tables mode.

Key changes:

  • Extracts $id === self::METADATA out of the getSharedTables() short-circuit so the metadata table is unconditionally safe from the orphan drop-and-recreate path, regardless of the shared-tables adapter setting.
  • Adds an inline comment explicitly documenting the invariant that the metadata table must never be dropped during reconciliation.

Root cause: The original condition getSharedTables() && ($id === METADATA || exists()) evaluated $id === METADATA only when getSharedTables() returned true. In non-shared-tables mode the entire condition was false, sending the _metadata DuplicateException down the else branch where deleteCollection + createCollection was called — destroying all collection metadata.

What was not changed: The orphan reconciliation logic for non-metadata collections in non-shared-tables mode is completely unaffected; the shared-tables path for non-metadata tables is also unchanged.

Confidence Score: 5/5

  • This PR is safe to merge; the logic change is correct and surgical with no unintended side-effects.
  • The fix is a minimal, well-targeted boolean restructuring that correctly separates the _metadata guard from the shared-tables condition. The pre-existing code paths for all other cases (non-metadata orphan reconciliation and shared-tables reuse) remain unchanged. The only gap is a missing regression test, which is a best-practice concern rather than a correctness issue.
  • No files require special attention; the single changed hunk in src/Database/Database.php is straightforward and correct.

Important Files Changed

Filename Overview
src/Database/Database.php Fixes a boolean short-circuit bug where $id === self::METADATA was evaluated only when getSharedTables() returned true, causing the _metadata table to be dropped and recreated in non-shared-tables mode when Database::create() was called on an existing database. The change correctly hoists the metadata guard outside the shared-tables condition.

Comments Outside Diff (1)

  1. src/Database/Database.php, line 1798-1819 (link)

    P2 Missing regression test for the fixed scenario

    The bug being fixed — calling Database::create() on an existing database in non-shared-tables mode silently dropping all collection metadata — is a critical data-loss regression. No test has been added to cover this path.

    Consider adding an e2e test (e.g. in GeneralTests.php) that:

    1. Calls $db->create() to set up an initial database with a few collections.
    2. Calls $db->create() a second time on the same database (simulating an upgrade).
    3. Asserts that the previously-created collections are still present in the _metadata table.

    This ensures the fix isn't accidentally reverted in future refactoring of this DuplicateException handler.

Last reviewed commit: "fix: prevent metadat..."

@abnegate abnegate merged commit cff2b6e into main Mar 20, 2026
19 checks passed
@abnegate abnegate deleted the fix-metadata-reconciliation branch March 20, 2026 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant