fix: UpdateItem ignores ConditionExpression on non-existent items#5
Merged
hicksy merged 2 commits intoApr 24, 2026
Merged
Conversation
Member
|
Thanks for this - the root cause writeup makes it easy to review, and good that the tests cover both the UpdateItem and TransactWrite paths. I'll get to it in the next few days. |
bd18587 to
bacf5e5
Compare
Member
|
Two small edits pushed to your branch:
As you were a first time contributor the CI checks needed approval. CI is approved and running; I'm leaving benchmark-regression unapproved for now (unrelated housekeeping). Will merge once CI is green. |
Both UpdateItem and TransactWriteItems Update actions populate key attributes on the item before evaluating the ConditionExpression. This causes attribute_exists(PK) to always pass, even when the item does not exist, because PK was just inserted for the upsert path. Fix: evaluate the condition against the original existing item (empty for non-existent keys) before populating key attributes. Regression tests cover both standalone UpdateItem and TransactWriteItems Update paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bacf5e5 to
5879105
Compare
hicksy
added a commit
to nubo-db/dynamodb-conformance
that referenced
this pull request
Apr 24, 2026
Broadens the single attribute_exists regression test added in #1 with four more variants: - attribute_not_exists upserts on non-existent key (canonical create-if-absent pattern; previously untested) - comparison-style condition rejects on non-existent; no ghost - combined attribute_exists + equality rejects on non-existent - ReturnValues: ALL_NEW on upsert returns created attributes All pass real AWS. Dynoxide 0.9.8 (released) fails the two tests that depend on key-populate-before-condition ordering (see nubo-db/dynoxide#5); dynoxide main (post-fix) passes all four.
hicksy
added a commit
to nubo-db/dynamodb-conformance
that referenced
this pull request
Apr 24, 2026
…rage
Mirrors the standalone UpdateItem coverage through the
transactional code path:
- Update attribute_not_exists upserts on non-existent key
- Update comparison condition cancels on non-existent; no ghost
- Update combined attribute_exists + equality cancels; no ghost
- mixed transaction: one passing, one failing on non-existent
cancels everything (integration)
All pass real AWS. Dynoxide 0.9.8 (released) fails the two that
depend on key-populate-before-condition ordering (see
nubo-db/dynoxide#5); dynoxide main (post-fix) passes all four.
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.
Summary
UpdateItemandTransactWriteItemsUpdate withConditionExpression: attribute_exists(PK)succeed and create a new item when the key does not exist. DynamoDB (and DynamoDB Local) correctly reject withConditionalCheckFailed.Version: dynoxide 0.9.8
Reproduction
Expected
TransactionCanceledExceptionwithConditionalCheckFailed. Same for standaloneUpdateItem.Root cause
Both
execute_updateintransact_write_items.rsand theUpdateItemhandler inupdate_item.rspopulate key attributes on the item for the upsert path before evaluating theConditionExpression. This makesattribute_exists(PK)always pass because PK was just inserted.Fix
Evaluate the condition against the original existing item (empty for non-existent keys) before populating key attributes. The PR includes regression tests for both code paths.