feat(managed): mode + key on managed-table loads - #43
Conversation
load_managed_table gains a `mode` kwarg (replace/append/delete/update/ upsert) instead of always replacing; create_managed_database(keys=) and add_managed_table(key=) declare a table's row-identity key so the key-based modes work on it. The key path needs a hotdata client whose managed-table decl models carry `key`; the dependency floor is bumped (exact tag TBD at release). Backward compatible: omitting mode still replaces, and a client without `key` silently declares the table keyless (previous behavior).
| *, | ||
| schema: str, | ||
| upload_id: str, | ||
| mode: str = "replace", |
There was a problem hiding this comment.
nit: load_managed_table here goes through _request_with_retry, which retries on transient errors. Previously the mode was hardcoded to replace (idempotent), so a retry after the server already committed was harmless. With append now reachable, a transient failure after the server commits but before the response is received will re-run the append on retry, duplicating the uploaded rows. replace/delete/update/upsert are idempotent, so append is the only exposed mode with this hazard. Worth confirming the server dedupes by upload_id, or otherwise not retrying append. (not blocking)
| schema: str = DEFAULT_SCHEMA, | ||
| upload_id: str | None = None, | ||
| file: str | None = None, | ||
| mode: str = "replace", |
There was a problem hiding this comment.
super nit: mode is typed as a bare str, so a typo (e.g. mode="upser") passes through silently and only surfaces as a server-side error. Since the SDK enumerates exactly five valid modes, a Literal["replace", "append", "delete", "update", "upsert"] here (and on the managed_client wrapper) would catch that at type-check time and self-document the accepted values. (not blocking)
What this adds
load_managed_table(..., mode=...)— pick the load mode (replace(default),append,delete,update,upsert) instead of always replacing.replace/appendapply the upload directly;delete/update/upsertmatch rows by the table's declared key.create_managed_database(..., keys={table: [cols]})andadd_managed_table(..., key=[cols]). Declaring a key is what enables the key-based modes on that table; tables without one stayreplace/append-only.Backward compatibility
modestill replaces — existing callers are unchanged.hotdataclient withoutkeyon its decl models silently declares the table key-less (previous behavior), so nothing breaks before the client is regenerated — the key just has no effect until then. The dependency floor bump enforces the key-capable client.Testing
71 passed / 3 skipped. The
modetests run now; thekeytestsskipifuntil the installed client has thekeyfield (they activate automatically once the regenerated client is in).Before un-drafting
hotdatafloor (replace theTODOinpyproject.toml) to the released key-capable client version.ruff+mypy.