Skip to content

Limit update() to only work on primary keys#1862

Closed
coolreader18 wants to merge 1 commit intomasterfrom
noa/no-update-for-non-pk
Closed

Limit update() to only work on primary keys#1862
coolreader18 wants to merge 1 commit intomasterfrom
noa/no-update-for-non-pk

Conversation

@coolreader18
Copy link
Copy Markdown
Collaborator

Description of Changes

@cloutiertyler and I were talking about this. Makes the semantic mapping for people easier, a row update on the client is an update() on the server. Doesn't make sense for non-pk to have .update() but it doesn't trigger an update.

Expected complexity level and risk

1

@gefjon
Copy link
Copy Markdown
Contributor

gefjon commented Oct 15, 2024

Please also update the API proposal

@gefjon
Copy link
Copy Markdown
Contributor

gefjon commented Oct 16, 2024

Should we change the API? If updates are only by PK, and a table has at most one PK, then they don't really belong on unique indices, do they? Should it be ctx.db.my_table().update(new_row) rather than ctx.db.my_table.().my_pk_field().update(new_row)?

EDIT: Or do we want to leave it on the PK index to make it clear how the old row is found during the update?

@coolreader18 coolreader18 force-pushed the noa/no-update-for-non-pk branch 2 times, most recently from 4d5a078 to 64a2b2b Compare October 16, 2024 21:22
@coolreader18
Copy link
Copy Markdown
Collaborator Author

Or do we want to leave it on the PK index to make it clear how the old row is found during the update?

I think that's beneficial - I just added a section in the doc comment for update() about what's considered an update and why it only exists on the primary key. We could probably consider adding .update() on the table in the future, if it's enough of a ergonomics thing.

@coolreader18 coolreader18 force-pushed the noa/no-update-for-non-pk branch from 64a2b2b to 43d33e0 Compare October 16, 2024 21:28
@coolreader18 coolreader18 force-pushed the noa/no-update-for-non-pk branch from 43d33e0 to 91d3aa6 Compare October 25, 2024 19:26
@bfops bfops added the release-any To be landed in any release window label Oct 28, 2024
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 3, 2025

CLA assistant check
All committers have signed the CLA.

@cloutiertyler cloutiertyler added release-2.0 release-2.0-nice-to-have api-break A PR that makes an API breaking change and removed release-any To be landed in any release window labels Feb 11, 2026
@cloutiertyler cloutiertyler self-assigned this Feb 11, 2026
@cloutiertyler
Copy link
Copy Markdown
Contributor

cloutiertyler commented Feb 12, 2026

I'm going to try to get this to merge, but I don't want to make the .update on the table instead of the column change because I think that's too drastic of an API change at this point in time. It can always be added later too.

@coolreader18 coolreader18 force-pushed the noa/no-update-for-non-pk branch from 91d3aa6 to 947f95a Compare February 12, 2026 07:15
@coolreader18
Copy link
Copy Markdown
Collaborator Author

That should be working for the rust module bindings.

cloutiertyler added a commit that referenced this pull request Feb 12, 2026
Restrict `update()` in both Rust and C# module bindings to only work
on primary key columns, not all unique columns. Calling `update()` on
a non-PK unique column is semantically a delete+insert — clients won't
see it as a row update unless the primary key stays the same.

Rust changes:
- Add `PrimaryKey` marker trait in `crates/bindings/src/table.rs`
- Add `where Col: PrimaryKey` bound on `UniqueColumn::update()`
- Generate `impl PrimaryKey` in the bindings macro for PK columns

C# changes:
- Only emit `Update()` on `UniqueIndex` when the column is a PK
- Update `unique_*` test reducers to use `Delete()`+`Insert()` instead

Closes #1862
@cloutiertyler
Copy link
Copy Markdown
Contributor

cloutiertyler commented Feb 12, 2026

Closing in favor of #4279, which re-implements this on top of current master and extends the change to C# and TypeScript module bindings as well.

github-merge-queue Bot pushed a commit that referenced this pull request Feb 13, 2026
## Summary
- Restricts `update()` across all three server-side SDKs (Rust, C#,
TypeScript) to only work on primary key columns, not all unique columns
- Calling `update()` on a non-PK unique column is semantically a
delete+insert — clients won't see it as a row update unless the primary
key stays the same
- Fresh re-implementation of #1862, extended to cover C# and TypeScript
bindings

### Design principle

Primary key is a constraint on columns, not a property of indexes. An
index is unique or not unique — whether `update()` is available is
derived from the column's attributes at the point of use, not stored on
the index.

This differs from #1862 which introduced a `Uniqueness` enum (`No |
Unique | PrimaryKey`) on the index itself. Instead, we keep `is_unique:
bool` and check the column metadata where needed.

### Rust changes
- Add `PrimaryKey` marker trait and `where Col: PrimaryKey` bound on
`UniqueColumn::update()`
- `marker_type()` receives `primary_key_column` and checks the column
directly — no `is_pk` field on the index
- `sdk-test` unique tables use `update_non_pk_by` (delete+insert)
instead of `update_by`
- Benchmarks that used `update()` on `#[unique]` columns changed to
either `#[primary_key]` or delete+insert

### C# changes
- Only emit `Update()` on `UniqueIndex` when the column has `PrimaryKey`
attrs
- Update `unique_*` test reducers in `sdk-test-cs` to use
`Delete()`+`Insert()` instead

### TypeScript changes
- `UniqueIndex` now has only `find` + `delete` (no `update`)
- `AllColumnsPrimaryKey` type-level helper checks column metadata from
the `TableDef`
- `Index` routing conditionally intersects `{ update() }` when columns
are PK
- Runtime only attaches `update` method for PK unique indexes
- `sdk-test-ts` unique tables use delete+insert instead of update
- Type-level test in `schema.test-d.ts` verifies `id.update()` compiles
(PK) and `name2.update` errors (non-PK unique)

## Test plan
- [x] C# codegen tests pass (`dotnet test
crates/bindings-csharp/Codegen.Tests`)
- [x] Rust SDK test module compiles
- [x] Benchmarks compile
- [x] TypeScript type checks pass (`npx tsc --noEmit`)
- [ ] CI passes

Closes #1862
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-break A PR that makes an API breaking change release-2.0-nice-to-have release-2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants