Fix FlatTableProjection partial-mapping events violating NOT NULL (#4255)#4257
Merged
jeremydmiller merged 1 commit intomasterfrom Apr 16, 2026
Merged
Conversation
) Previously, every event mapped into a FlatTableProjection generated an INSERT … ON CONFLICT DO UPDATE function using only the columns that event populates. When the target table had a NOT NULL column not populated by every event, the partial-mapping event's INSERT would violate the constraint (PostgreSQL validates the proposed INSERT row against constraints before ON CONFLICT resolves). Fix: detect events that map a strict subset of the table's non-PK columns and generate an UPDATE-only function for them. Full-mapping events keep the original INSERT … ON CONFLICT DO UPDATE form, so they can still create rows. Semantics: partial events update existing rows. If a stream starts with a partial event before any full-mapping event, the UPDATE matches zero rows and is a safe no-op. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Hello, First of all, thanks for the fix! I would like to share some thoughts on flat table design: A couple of days ago in my team we didn't know if this issue was a bug or by-design solution, so to unblock us we came up with customized class that inherited from We decided to call them I understand that renaming |
This was referenced Apr 17, 2026
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.
Closes #4255
Problem
When a
FlatTableProjectionmaps multiple events to the same table, each event's generatedmt_upsert_*function usedINSERT … ON CONFLICT DO UPDATElisting only the columns that event populates. If the table had a NOT NULL column not populated by every event, partial-mapping events raised:PostgreSQL validates the proposed INSERT row against constraints before ON CONFLICT resolves, so the error fires whether the row exists or not.
Fix
FlatTableUpsertFunctionnow detects events that map a strict subset of the table's non-PK columns and emits an UPDATE-only function for them:Full-mapping events keep the original
INSERT … ON CONFLICT DO UPDATEform, so they can still create rows.Semantics
Backward-compatible for the common case where every event maps every column (existing test
WriteTableWithGuidIdentifierProjection— all 32 existing flat-table tests still pass).Test plan
partial_event_on_existing_row_updates_without_violating_not_null— reproduces the bug, confirms fixpartial_event_on_new_stream_is_a_safe_noop— confirms partial event on empty stream does not create a rowfull_mapping_event_still_uses_insert_on_conflict— confirms backward compatibilityFlatTableProjectiontests pass (32 total with the new 3)Files changed
src/Marten/Events/Projections/Flattened/FlatTableUpsertFunction.cs— detect partial mapping, emit UPDATE-only SQLsrc/EventSourcingTests/Projections/Flattened/Bug_4255_flat_table_not_null_constraint.cs— new regression testsdocs/events/projections/flat.md— documents the new behavior🤖 Generated with Claude Code