Commit graph

4 commits

Author SHA1 Message Date
Sipke Schoorstra 4b15b0166d
fix(labels): enforce per-tenant uniqueness on NormalizedName (#8112)
* fix(labels): enforce per-tenant uniqueness on NormalizedName

Finish the unused Label.NormalizedName contract the same way Secrets
does: unique (TenantId, NormalizedName) in EF, fail-closed Memory saves,
and keep NormalizedName in sync with Name.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(labels): clone Memory label reads so a rejected rename cannot persist

Find/List handed out live store refs. Labels.Update mutates Name (and
NormalizedName) on that instance before Save; a uniqueness rejection
then left the stored row already renamed. Clone-on-read matches Memory
identity stores from #8108.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(labels): stamp null TenantId, fail-loud on leftover duplicates, cap names at 255

Follow the Architect steer for Greptile P1s: do not auto-delete duplicate
labels; UPDATE Labels SET TenantId = '' WHERE TenantId IS NULL on every
provider before CreateIndex; keep SQL Server/Oracle filtered unique
indexes; HasMaxLength(255) on Name and NormalizedName with no silent
truncate.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(labels): fail-loud preflight for leftover keys and over-length names

No silent dedupe: every provider lists leftover (TenantId, NormalizedName)
keys and aborts before CreateIndex. SQL Server/Oracle keep their filtered
unique indexes. PostgreSQL/SQLite keep provider column types. Providers
that narrow Name/NormalizedName to 255 preflight over-length Ids first.
Memory uniqueness semantics are unchanged.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* test(labels): match Oracle filtered-index escape in migration assertion

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-13 16:16:01 +02:00
Sipke Schoorstra faf9d57b8b
fix(efcore-oracle): migrate LOB columns in V3_6 without in-place datatype alteration (#8040)
* fix(efcore-oracle): migrate LOB columns in V3_6 without in-place datatype alteration

Both Oracle V3_6 migrations were generated as in-place `ALTER TABLE ... MODIFY`
statements that change a column's datatype to or from a LOB type: NCLOB to JSON
for `WorkflowDefinitions.StringData`, and NVARCHAR2(450) to NCLOB for
`ActivityNodeId` on `WorkflowExecutionLogRecords` and `ActivityExecutionRecords`.
Oracle refuses both (ORA-22858 / ORA-22859), so neither migration could ever
apply and the reported ORA-22858 was unavoidable.

Convert the columns the way the ORA-22858 message prescribes instead: add a
temporary column of the target type, copy the values across, drop the original
and rename the temporary one. Because Oracle commits DDL implicitly, a run that
fails partway leaves its earlier statements applied - the reporter's already
committed `OriginalSource` column is exactly that - so every step is guarded
against the state a previous attempt can have left behind. The conversion block
derives what still needs doing from `ALL_TAB_COLUMNS`, skips a conversion that
already completed (including one applied by hand), and raises rather than copy
out of and drop a column whose datatype it does not recognize.

Add an offline regression test that generates the Oracle Management and Runtime
V3_6 scripts through `IMigrator.GenerateScript` without a connection and asserts
that no in-place datatype `MODIFY` is emitted for either column, that the
add/copy/drop/rename sequence appears in order, and that the re-run guards are
present. All 19 cases fail against the previous migrations.

Refs #8011

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(efcore-oracle): tighten migration helper visibility and index guard, share the script-generation test harness

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(efcore-oracle): brace the foreach bodies in the V3_6 migration tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(efcore-oracle): refuse to truncate node IDs on downgrade and validate same-named indexes

The Runtime V3_6 downgrade converted ActivityNodeId from NCLOB back to
NVARCHAR2(450) by copying DBMS_LOB.SUBSTR(..., 450, 1), silently
truncating any value the upgraded schema had allowed to grow past 450
characters. EnsureLobLengthAtMost now checks for oversized values while
the column is still a LOB and raises before any data is copied.

CreateIndexIfMissing also treated any index with a matching name as
already done. It now validates the existing index's table, uniqueness
and ordered column list against ALL_INDEXES/ALL_IND_COLUMNS so a
same-named index left behind by schema drift or manual recovery is not
mistaken for the one the migration means to create.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(efcore-oracle): correct the dynamic SQL rationale on the LOB length guard

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(efcore-oracle): preflight both tables before downgrading and escape schema names in migration SQL

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(efcore-oracle): copy every row NULL-preservingly so retries reproduce the current source

The filtered copy `WHERE "<column>" IS NOT NULL` skipped rows whose
nullable source had since become NULL. If a prior conversion committed
the copy and then failed before dropping the source column, an
operator clearing a value before retrying would find the predicate
skip that row, and the stale converted value would be renamed into
place. Replace the filtered UPDATE with an unconditional, NULL-
preserving CASE expression so a retry always reproduces the current
source exactly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 02:09:08 -07:00
Sipke Schoorstra fda3987c0b
fix(efcore): terminate raw SQL statements in the PostgreSQL and SQLite Runtime V3_6 migrations (#8039)
* fix(efcore): terminate raw SQL statements in the PostgreSQL and SQLite Runtime V3_6 migrations

The V3_6 Runtime migration's two DROP INDEX statements were emitted without a
trailing semicolon on PostgreSQL and SQLite, so `dotnet ef migrations script`
produced syntactically invalid SQL (an unterminated statement inside the
idempotent DO $EF$ block, and unseparated statements in the plain script).
MigrateAsync() was unaffected because EF executes each Sql() call individually.

Add the missing terminators and a regression test that generates the Runtime
migration script offline for both providers (idempotent and plain forms) and
asserts the DROP INDEX statements are properly terminated.

Refs #7912

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(efcore): share migration-script setup and cover the schema-prefixed statements

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(efcore): cover schema-prefixed migration statements without static state

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:27:43 -07:00
Sipke Schoorstra 33181b2c9d
test: cover Oracle bulk upsert SQL generation
Exercise quoted Oracle identifiers, NVARCHAR2 casts, multi-row SQL, update/insert clauses, and parameter ordering. Reuse the provider SQL generation helper so identifier delimiting stays aligned with Oracle EF Core.
2026-07-11 14:35:36 +02:00