Visitar URL original
fix(databases): honor record id on transaction upsert by HarshMN2345 · Pull Request #14197 · appwrite/appwrite · GitHub
Skip to content

fix(databases): honor record id on transaction upsert - #14197

Draft
HarshMN2345 wants to merge 2 commits into
mainfrom
cursor/transaction-upsert-row-id-b438
Draft

HarshMN2345 wants to merge 2 commits into
mainfrom
cursor/transaction-upsert-row-id-b438

Conversation

@HarshMN2345

@HarshMN2345 HarshMN2345 commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

Transaction upsert staged through createOperations stored rowId / documentId on the operation and built the document from data alone. upsertDocument() identifies the row by $id, so a commit inserted a new row with a generated id and left the requested row unchanged.

Commit now copies the staged record id onto the document before upsert. That is the id the permission check and the post-commit event already use. An operation whose data.$id names a different record is rejected when it is staged (general_bad_request). A matching $id is unchanged.

Fixes #14178.

Test Plan

  • testUpsertOperationRecordId updates an existing record with the id only on the operation. After commit the same id has the new values and the list total stays 1.
  • A later transaction creates a missing id and updates the existing record when data.$id repeats the operation id. The list contains exactly those two ids.
  • A staged upsert whose record id and data.$id differ returns 400 general_bad_request and does not change the existing record.
  • PostgreSQL e2e for Databases and TablesDB passed on the previous revision of this branch, which included this test. This environment has no PHP or Docker, so the tightened assertions were not re-run locally.

Related PRs and Issues

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?
Open in Web Open in Cursor 

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

Security rules

No new WARNING or ERROR findings from security rules.

32 existing findings tracked in .semgrep/baseline.json
  • php.appwrite.guest-write-without-abuse-limit (17)
  • php.appwrite.permissive-write-permission (8)
  • php.appwrite.secret-compare-timing (5)
  • php.appwrite.weak-secret-env-default (2)

Posted by Checks / Rules. Re-runs update this comment in place. Rule details and baseline: .semgrep/README.md.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

✨ Benchmark results

Comparing main (before) → cursor/transaction-upsert-row-id-b438 (after).

Metric Before After Change
🚀 Requests/sec 207.33 210.62 ⚪ +1.6%
⏱️ Latency P50 82.15 ms 81.83 ms ⚪ -0.4%
⏱️ Latency P95 201.09 ms 197.17 ms ⚪ -1.9%
Per-scenario breakdown & investigation details

Metrics below reflect the current branch (after). Δ P95 compares against the base.

Scenario P50 (ms) P95 (ms) Requests RPS Δ P95 (ms)
API total 81.83 197.17 13,224 210.62 -3.92
Account 166.04 302.67 696 11.53 -36.94
TablesDB 78.67 139.73 7,192 117.14 -9.5
Storage 76.16 171.77 3,480 58.45 +7.32
Functions 119.25 239.7 1,856 31.75 -8.3

Top API waits (after)

API request Max wait (ms)
account.name.update 522.76
account.prefs.update 501.1
functions.create 445.32
storage.files.update 384.55
functions.variables.create 376.48

Staged upsert kept the record id off the document, so commit inserted a new row. Copy that id onto the document before upsert, and reject a staged operation whose data $id names a different record.
Copy the staged id onto the document whenever it is set, and reject a different data $id with the same bad-request style as the other operation checks. The e2e reads the updated row before a later upsert and asserts the list did not gain a row.
@cursor
cursor Bot force-pushed the cursor/transaction-upsert-row-id-b438 branch from 5dbbc93 to e989e83 Compare October 7, 2026 09:29

Copy link
Copy Markdown
Member Author

@hansi-codes review

@hansi-codes

hansi-codes Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🟢 Tier S · Ready to merge

The changes preserve the staged ID contract, and no concrete defects were found in the affected paths or their callers.

Transaction upsert now copies the staged record ID into the document passed to the database, so commits target the requested row rather than generating a new ID. Staging rejects conflicting IDs in operation data, and shared E2E coverage checks updates, inserts, matching IDs, and mismatch rejection.

Verdict New comments Fixed Still open
✅ Approved 0 0 0
📂 Walkthrough · 3
File Change
src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php Reject upserts whose data.$id conflicts with the operation's record ID.
src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php Apply the staged record ID before committing an upsert.
tests/e2e/Services/Databases/Transactions/TransactionsBase.php Add API regression coverage for upsert identity and persisted record counts.

Reviewed e989e83 · Details · Comment @hansi-codes review to re-run, or mention @hansi-codes with a question.

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Tier S · Looks good to merge. Summary

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Bug Report: Transaction upsert ignores rowId and creates a new row

1 participant