Skip to content

IBX-12078: Adjusted index of content id and version to be unique#780

Open
bnowak wants to merge 5 commits into
6.0from
IBX-12078-unique-key-for-content-and-version-columns
Open

IBX-12078: Adjusted index of content id and version to be unique#780
bnowak wants to merge 5 commits into
6.0from
IBX-12078-unique-key-for-content-and-version-columns

Conversation

@bnowak

@bnowak bnowak commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
🎫 Issue IBX-12078

Related PRs:

Description:

The ibexa_content_version table allowed multiple rows with the same (contentobject_id, version) pair - nothing on the database level prevented duplicate content versions (e.g. created by concurrent version-creating requests). The missing key was discovered while working on IBX-12029.

Changes:

  • Replaced the non-unique ibexa_content_version_idx_ver index on (contentobject_id, version) with the uq_ibexa_content_version_coid_version unique constraint, so duplicate version rows are now rejected by the database itself.
  • Made the contentobject_id column non-nullable - it is part of the new unique key, and a version row without a content id is not a valid state.
  • Added an integration test asserting that DoctrineDatabase::insertVersion() throws UniqueConstraintViolationException when inserting a duplicate (contentobject_id, version) pair.

Migration scripts for existing installations are handled in the related installer PR. Databases already containing duplicates must be cleaned up before applying the migration - see the Documentation section below for detection and cleanup snippets.

For QA:

SQL snippet to reproduce duplicates in db:

INSERT INTO ibexa_content_version
    (contentobject_id, created, creator_id, initial_language_id, language_mask,
     modified, status, user_id, version, workflow_event_pos)
SELECT
    src.contentobject_id,
    src.created,
    src.creator_id,
    src.initial_language_id,
    src.language_mask,
    src.modified,
    src.status,
    src.user_id,
    src.version,
    src.workflow_event_pos
FROM (
    SELECT contentobject_id, created, creator_id, initial_language_id, language_mask,
           modified, status, user_id, version, workflow_event_pos
    FROM ibexa_content_version
    ORDER BY MD5(CONCAT(id, CURRENT_TIMESTAMP))
    LIMIT 10 -- <<< ADJUST: number of rows to duplicate
) AS src;

Documentation:

SQL snippet to list all duplicates in the database. Clients can use that to locate content versions that need fixes (duplicates removal):

SELECT contentobject_id, version, COUNT(*) AS occurrences
FROM ibexa_content_version
GROUP BY contentobject_id, version
HAVING COUNT(*) > 1
ORDER BY contentobject_id, version;

SQL snippets as proposed solution for client to automatically removing of duplicates:

-- 1) Remove scheduler entries referencing version rows that are about to be deleted.
--    Must run BEFORE the deduplication delete, while the doomed ids are still resolvable.
--    * This is optional step to remove orphan rows which would be kept after execution of 2nd SQL below.
--       However, theoretically there shouldn't be duplications here, as `ibexa_scheduler_scheduled_entry`
--       contains unique key (`content_id`, `version_number`, `action`) which should prevent duplicates.
DELETE FROM ibexa_scheduler_scheduled_entry
WHERE version_id IN (
    SELECT doomed.id
    FROM (
        SELECT v.id
        FROM ibexa_content_version v
        INNER JOIN ibexa_content_version keeper
            ON keeper.contentobject_id = v.contentobject_id
           AND keeper.version = v.version
           AND keeper.id < v.id
    ) AS doomed
);

-- 2) Deduplication SQL: for every (contentobject_id, version) group keep the lowest id, delete every other row.
DELETE FROM ibexa_content_version
WHERE id IN (
    SELECT doomed.id
    FROM (
        SELECT v.id
        FROM ibexa_content_version v
        INNER JOIN ibexa_content_version keeper
            ON keeper.contentobject_id = v.contentobject_id
           AND keeper.version = v.version
           AND keeper.id < v.id
    ) AS doomed
);

@bnowak bnowak force-pushed the IBX-12078-unique-key-for-content-and-version-columns branch from 59514b1 to 5421938 Compare July 13, 2026 12:02
@bnowak bnowak marked this pull request as ready for review July 14, 2026 14:05
@konradoboza konradoboza requested a review from a team July 14, 2026 14:12
@ibexa-workflow-automation-1 ibexa-workflow-automation-1 Bot requested review from ViniTou, barw4, ciastektk, mikadamczyk, tbialcz and wiewiurdp and removed request for a team July 14, 2026 14:12
@alongosz alongosz added the Doc needed The changes require some documentation label Jul 14, 2026
Comment thread src/bundle/Core/Resources/config/storage/legacy/schema.yaml Outdated
Comment thread src/bundle/Core/Resources/config/storage/legacy/schema.yaml Outdated
@bnowak

bnowak commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

there are some ending with uq right?

@ViniTou I didn't find uq ending/suffixes, but starting/prefixes in you data intelligence layer package 😉
please see #780 (comment) and continue there if you have anything to add here - it's the same topic

@bnowak bnowak force-pushed the IBX-12078-unique-key-for-content-and-version-columns branch from 69f83dd to 6c6531b Compare July 15, 2026 08:08
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Doc needed The changes require some documentation Ready for QA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants