Fix MySQL PUT/PATCH regarding row-level update database policy - #3734
Open
aaronburtle wants to merge 4 commits into
Open
Fix MySQL PUT/PATCH regarding row-level update database policy#3734aaronburtle wants to merge 4 commits into
aaronburtle wants to merge 4 commits into
Conversation
aaronburtle
requested review from
Alekhya-Polavarapu,
Aniruddh25,
JerryNixon,
RubenCerna2079,
anushakolan,
rusamant,
sourabh1007,
souvikghosh04,
stuartpa and
vadeveka
as code owners
July 21, 2026 22:06
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the MySQL REST upsert implementation (PUT/PATCH) to enforce the entity’s row-level update database policy, aligning MySQL behavior with other engines by restructuring the upsert into multiple statements/result sets and enabling previously skipped MySQL policy integration tests.
Changes:
- Restructured MySQL upsert SQL generation to: pre-count existing PK rows, attempt an
UPDATE ... WHERE <pk> AND <update-policy>, then conditionallyINSERTonly when the PK doesn’t exist. - Added MySQL executor logic to interpret multiple result sets and return the correct HTTP outcome (update vs insert vs policy failure vs not found).
- Enabled MySQL integration tests for update-policy scenarios and updated MySQL config generation + snapshot to include the new policy-testing role.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/Resolvers/MySqlQueryBuilder.cs | Reworked MySQL upsert SQL to enforce update policy and avoid ON DUPLICATE KEY UPDATE limitations. |
| src/Core/Resolvers/MySqlQueryExecutor.cs | Added multi-result-set interpretation for MySQL upsert outcomes and policy failures. |
| src/Service.Tests/SqlTests/RestApiTests/Put/MySqlPutApiTests.cs | Enabled PUT update-policy test and added MySQL validation query for it. |
| src/Service.Tests/SqlTests/RestApiTests/Patch/MySqlPatchApiTests.cs | Enabled PATCH update-policy tests and added MySQL validation query for it. |
| src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMySql.verified.txt | Updated snapshot to include database_policy_tester role with update DB policy. |
| config-generators/mysql-commands.txt | Added commands to grant database_policy_tester permissions and update DB policy for Stock. |
…EY UPDATE Replace the FOR UPDATE gap-lock reads (which could deadlock, error 1213, when two concurrent RepeatableRead transactions both gap-lock the same missing key) with an atomic INSERT ... ON DUPLICATE KEY UPDATE no-op that serializes concurrent same-key inserts on the unique index. Detect insert-vs-existing via a session variable set inside the ON DUPLICATE branch (@Existed) rather than ROW_COUNT(), which is unreliable under UseAffectedRows=false. Also reuse the inherited _Composite_NonAutoGenPK_EntityPath constant to satisfy the IDE1006 const naming rule.
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.
Why make this change?
On MySQL / Azure Database for MySQL, the REST
PUT(upsert) andPATCH(incremental upsert) paths did not enforce the row-level update database policy configured for an entity. We now respect that row level database policy.What is this change?
MySqlQueryBuilder.Build(SqlUpsertQueryStructure)previously did not retrieve or applyGetDbPolicyForOperation(EntityActionOperation.Update)on the upsert path.The MySQL upsert now:
INSERT ... ON DUPLICATE KEY UPDATEwith a no-op duplicate branch, preventing concurrent same-key upserts from racing or deadlocking.@existed, set inside the duplicate branch, to distinguish inserts from updates without relying onROW_COUNT()semantics.UPDATE ... WHERE <pk> AND <update-policy>only when the row already existed.200even whenUseAffectedRows=true.200for updates,201for inserts,403 DatabasePolicyFailurewhen the update policy blocks an existing row, and404when an update-only record is not found.MySQL integration coverage now includes:
UseAffectedRows=true.database_policy_testerrole and update policy in the generated MySQL test configuration.Create-action database policies remain unsupported on MySQL, so create-policy-dependent tests remain skipped.
How was this tested?
Validated against MySQL 8.0.46 using .NET 10 (SDK 10.0.301).
UseAffectedRows=truereturn200.403 DatabasePolicyFailureand leave the row unchanged.201, one200, one persisted row, and no5xxresponse for each iteration.Sample Request(s)
Entity
commodities(tablestocks) with update policy@item.pieceid ne 1for roledatabase_policy_tester.Blocked update targeting a row that violates the policy:
Before the fix:
200 OK; the row was overwritten.After the fix:
403 Forbidden(DatabasePolicyFailure); the row remains unchanged.Allowed update targeting a row that satisfies the policy:
Result:
200 OK; the row is updated.