perf(projects): store the parent of top-level projects as null - #3726
Merged
Conversation
A nil map value now renders IS NULL instead of an Eq that never matches. Column names are quoted since the builder does not do it.
The recursive step of the project access CTE joins parent_project_id against a project id, which implies NOT NULL. Storing root projects as NULL instead of 0 lets a partial index cover only real children, so the index scan walks a fraction of the rows. MySQL has no partial indexes and keeps the existing full index.
Preview DeploymentPreview deployments for this PR are available at:
The preview environment will start automatically on first visit. Subsequent pushes to this PR will update the Run locally with Dockerdocker pull ghcr.io/go-vikunja/vikunja:pr-3726
docker run -p 3456:3456 ghcr.io/go-vikunja/vikunja:pr-3726Last updated for commit 66f250b |
tink-bot
marked this pull request as ready for review
September 3, 2026 22:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Recursive step of project access CTE joins
parent_project_idagainst project id, which implies NOT NULL. Root projects stored as0sit in same index as real children, so index scan walks every project row instead of children only.Migration nulls out
parent_project_id = 0and adds partial indexON projects (parent_project_id) WHERE parent_project_id IS NOT NULL. Write paths use xormNullable, so 0 sentinel lands as NULL. Struct keeps0: JSON responses, webhooks and events unchanged,AfterLoadstill maps NULL back to 0.MySQL has no partial indexes — keeps existing full index, gets semantic change but no index win. Full index kept on all dialects so
parent_project_id IS NULLlookups stay indexed.How to verify
SELECT id, parent_project_id FROM projects WHERE id = <new id>.parent_project_idis NULL, whileGET /api/v1/projects/<new id>still returns"parent_project_id": 0.0.Second flow, for detaching:
{"parent_project_id": 0}(needs admin permission on the project)."parent_project_id": 0, the project appears at the top level, and the stored column is NULL.Third flow, for existing installs:
parent_project_id = 0.