Files
zitadel/internal/database
25c23cf91d fix: setup step 79 batch unique constraint owner backfill (#12803)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->

- Setup 79 rewrites unique-constraint owners in one statement per file.
On a large table that statement holds row locks for the whole update,
and adding `LIMIT` does not help: the username statement bitmap-scans
every empty `owners` value and then index-scans every user in the
instance for each constraint.
- The backfill joined projection tables before those tables were created
and before event replay filled them.
- The page cursor is a `text[]`. `database.TextArray.Value` joined
elements with commas and never quoted them, so a project role key such
as `a,b` became two array elements. The next page repeated the same row
and the migration stopped.

- Each setup 79 statement pages its projection by primary key and
updates `eventstore.unique_constraints` with an equality lookup on
`(instance_id, unique_type, unique_field)`.
- The original field and `lower(...)` are separate `UNION` arms, so the
lookup stays a primary-key probe.
- Setup repeats a file until the projection page is empty. A page that
matches no constraint still advances the cursor.
- `BatchSize` is a `uint16`, configured like `ForceFinalize`
(`cmd/setup/steps.yaml`,
`ZITADEL_BACKFILLUNIQUECONSTRAINTOWNERS_BATCHSIZE`) and is not stored on
lastRun. The default in `steps.yaml` is 5000.
- Each page logs the file, the page index, and the number of rows
updated.
- The backfill runs after projection prefill. When `InitProjections` is
disabled or setup is for mirror, it still runs against the projection
rows already stored.
- `TextArray.Value` quotes every element and backslash-escapes `"` and
`\`. An empty array is still SQL `NULL`, so existing `ANY($1)` callers
stay unchanged. The setup statements already cast `$1` to `text[]`.

- Integration tests for the username and org-domain statements pass an
empty cursor and a page size.
- `TestTextArray_Value` expects quoted elements, and a round trip
through `Scan` covers a comma, a quote, and a backslash.
- initial fix https://github.com/zitadel/zitadel/pull/12740
- fix of the setup job, broken by the first fix
https://github.com/zitadel/zitadel/pull/12785
- needs to be a `fix` tag because `perf` wouldn't bump the version

- Based on `owner-app-fix`.
- A setup process already running the old statement keeps that plan
until it is restarted.

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-31bfb5c4-5888-43f0-85cf-af1af097ca30?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-31bfb5c4-5888-43f0-85cf-af1af097ca30&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Silvan <adlerhurst@users.noreply.github.com>
2026-09-23 16:35:17 +02:00
..
2022-02-09 15:01:19 +01:00