diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 8569f3e..843254f 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -89,6 +89,7 @@ docs/ListWorkspacesResponse.md docs/LoadManagedTableRequest.md docs/LoadManagedTableResponse.md docs/ManagedSchemaResponse.md +docs/ManagedTableConstantPerKeyResponse.md docs/ManagedTableResponse.md docs/MintUploadPartsRequest.md docs/MintUploadPartsResponse.md @@ -115,6 +116,7 @@ docs/TemporalProfileDetail.md docs/TextProfileDetail.md docs/UpdateEmbeddingProviderRequest.md docs/UpdateEmbeddingProviderResponse.md +docs/UpdateManagedTableRequest.md docs/UpdateSavedQueryRequest.md docs/UploadSessionResponse.md docs/UploadsApi.md @@ -224,6 +226,7 @@ src/models/list_workspaces_response.rs src/models/load_managed_table_request.rs src/models/load_managed_table_response.rs src/models/managed_schema_response.rs +src/models/managed_table_constant_per_key_response.rs src/models/managed_table_response.rs src/models/mint_upload_parts_request.rs src/models/mint_upload_parts_response.rs @@ -247,6 +250,7 @@ src/models/temporal_profile_detail.rs src/models/text_profile_detail.rs src/models/update_embedding_provider_request.rs src/models/update_embedding_provider_response.rs +src/models/update_managed_table_request.rs src/models/update_saved_query_request.rs src/models/upload_session_response.rs src/models/upsert_database_context_request.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b5fe2..22441f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- chore(uploads): document 16 GiB upload size limit ## [0.16.0] - 2026-09-03 diff --git a/docs/AddManagedTableDecl.md b/docs/AddManagedTableDecl.md index 3ea2ed6..18f1044 100644 --- a/docs/AddManagedTableDecl.md +++ b/docs/AddManagedTableDecl.md @@ -4,8 +4,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**constant_per_key** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **key** | Option<**Vec**> | Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. | [optional] -**key_determines** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **name** | **String** | | **partition_by** | Option<[**Vec**](TablePartitionKey.md)> | Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. | [optional] **sorted_by** | Option<[**Vec**](TableSortKey.md)> | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. | [optional] diff --git a/docs/AddManagedTableRequest.md b/docs/AddManagedTableRequest.md index ecb2f58..09fbb72 100644 --- a/docs/AddManagedTableRequest.md +++ b/docs/AddManagedTableRequest.md @@ -4,8 +4,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**constant_per_key** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **key** | Option<**Vec**> | Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. | [optional] -**key_determines** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **name** | **String** | | **partition_by** | Option<[**Vec**](TablePartitionKey.md)> | Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. | [optional] **sorted_by** | Option<[**Vec**](TableSortKey.md)> | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. | [optional] diff --git a/docs/ConnectionsApi.md b/docs/ConnectionsApi.md index 51ecc13..ada5b39 100644 --- a/docs/ConnectionsApi.md +++ b/docs/ConnectionsApi.md @@ -13,6 +13,7 @@ Method | HTTP request | Description [**get_table_profile**](ConnectionsApi.md#get_table_profile) | **GET** /v1/connections/{connection_id}/tables/{schema}/{table}/profile | Get table profile [**list_connections**](ConnectionsApi.md#list_connections) | **GET** /v1/connections | List connections [**load_managed_table**](ConnectionsApi.md#load_managed_table) | **POST** /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads | Load managed table from inline data, upload, or query result +[**set_managed_table_constant_per_key**](ConnectionsApi.md#set_managed_table_constant_per_key) | **PUT** /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/constant-per-key | Declare which columns are constant per key @@ -292,3 +293,36 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## set_managed_table_constant_per_key + +> models::ManagedTableConstantPerKeyResponse set_managed_table_constant_per_key(connection_id, schema, table, update_managed_table_request) +Declare which columns are constant per key + +Replace the columns a table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) narrow its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics do not discriminate. Unlike `partition_by` and `sorted_by`, this is NOT fixed when the table is created. It changes only which files a mutation opens, never how rows are written, so nothing stored becomes wrong when it changes and a populated table can adopt it with no rewrite. It takes effect on the next load. Send an empty array to revoke it, restoring the unrestricted search — this is the way to undo a declaration that turns out to be false. **This is correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it, and the pruning conceals its own evidence because the file holding the missed row is never opened. Declare it only where the invariant is established. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**connection_id** | **String** | Connection ID | [required] | +**schema** | **String** | Schema name | [required] | +**table** | **String** | Table name | [required] | +**update_managed_table_request** | [**UpdateManagedTableRequest**](UpdateManagedTableRequest.md) | | [required] | + +### Return type + +[**models::ManagedTableConstantPerKeyResponse**](ManagedTableConstantPerKeyResponse.md) + +### Authorization + +[WorkspaceId](../README.md#WorkspaceId), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/docs/CreateUploadRequest.md b/docs/CreateUploadRequest.md index 58b350d..467f9a1 100644 --- a/docs/CreateUploadRequest.md +++ b/docs/CreateUploadRequest.md @@ -8,7 +8,7 @@ Name | Type | Description | Notes **checksum_value** | Option<**String**> | Integrity checksum value, paired with `checksum_algo`. Optional. | [optional] **content_encoding** | Option<**String**> | Content encoding to record for the uploaded file (for example `gzip`). Optional. | [optional] **content_type** | Option<**String**> | Content type to record for the uploaded file (for example the Parquet, CSV, or JSON MIME type). Optional. | [optional] -**declared_size_bytes** | Option<**i64**> | The exact size, in bytes, of the file you will upload. Optional. When provided, it is validated at create time against the maximum allowed size, and again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from `POST /v1/uploads/{upload_id}/parts` as you upload, and finalize validates only that the file is non-empty. | [optional] +**declared_size_bytes** | Option<**i64**> | The exact size, in bytes, of the file you will upload. Optional. When provided, it is checked at create time against the maximum upload size (16 GiB by default, the same for every file format), so an oversized file is refused before you transfer any of it; it is checked again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from `POST /v1/uploads/{upload_id}/parts` as you upload, and finalize checks only that the file is non-empty and within the maximum upload size. | [optional] **filename** | Option<**String**> | Original file name, recorded with the upload for your own bookkeeping. Optional and advisory — it does not affect how the file is uploaded or loaded. | [optional] **part_size** | Option<**i64**> | Preferred size, in bytes, of each part for a large (multi-part) upload. Optional hint — the service clamps it to the allowed part-size range and to the maximum number of parts, and ignores it for small files uploaded with a single `PUT`. Omit to let the service choose. | [optional] diff --git a/docs/DatabaseDefaultTableDecl.md b/docs/DatabaseDefaultTableDecl.md index 3df94a8..49089d1 100644 --- a/docs/DatabaseDefaultTableDecl.md +++ b/docs/DatabaseDefaultTableDecl.md @@ -4,8 +4,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**constant_per_key** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **key** | Option<**Vec**> | Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. | [optional] -**key_determines** | Option<**Vec**> | Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. | [optional] **name** | **String** | | **partition_by** | Option<[**Vec**](TablePartitionKey.md)> | Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. | [optional] **sorted_by** | Option<[**Vec**](TableSortKey.md)> | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. | [optional] diff --git a/docs/DatabasesApi.md b/docs/DatabasesApi.md index a13ee49..717a0d0 100644 --- a/docs/DatabasesApi.md +++ b/docs/DatabasesApi.md @@ -20,6 +20,7 @@ Method | HTTP request | Description [**list_databases**](DatabasesApi.md#list_databases) | **GET** /v1/databases | List databases [**load_database_table**](DatabasesApi.md#load_database_table) | **POST** /v1/databases/{database_id}/schemas/{schema}/tables/{table}/loads | Load database table from inline data, upload, or query result [**lookup_database_by_name**](DatabasesApi.md#lookup_database_by_name) | **GET** /v1/databases/by-name | Look up a database by name +[**set_database_table_constant_per_key**](DatabasesApi.md#set_database_table_constant_per_key) | **PUT** /v1/databases/{database_id}/schemas/{schema}/tables/{table}/constant-per-key | Declare which columns are constant per key @@ -152,7 +153,7 @@ Name | Type | Description | Required | Notes > models::DatabaseCountResponse count_databases(search, batch) Count databases -Return the total number of databases in the workspace. This is the whole-workspace total, not a page size: the `count` field on the listing reports how many rows that one page returned, so totalling a workspace from `GET /v1/databases` means walking every page. Pass `search` to count only databases whose name contains that text (case-insensitive), or `batch` with the `batch_id` returned by a bulk-creation call to count only that batch's databases. The filters mean exactly what they mean on the listing, so a count and a listing given the same filters describe the same set. +Return the total number of databases in the workspace. This is the whole-workspace total, not a page size: the `count` field on the listing reports how many rows that one page returned, so totalling a workspace from `GET /v1/databases` means walking every page. Pass `search` to count only databases whose name contains that text, ignoring the case of unaccented Latin letters and digits, or `batch` with the `batch_id` returned by a bulk-creation call to count only that batch's databases. The filters mean exactly what they mean on the listing, so a count and a listing given the same filters describe the same set. ### Parameters @@ -424,7 +425,7 @@ Name | Type | Description | Required | Notes > models::ListDatabasesResponse list_databases(limit, cursor, search, batch) List databases -List databases in the workspace, newest first, one page at a time. When no `limit` is given a default page size is applied, so a single call returns at most one page rather than every database. If the response's `has_more` is true, pass its `next_cursor` value back as the `cursor` query parameter to fetch the next page. Pass `search` to return only databases whose name *contains* that text (case-insensitive); to fetch the single database whose name matches exactly, use `GET /v1/databases/by-name` instead. Pass `batch` with the `batch_id` returned by a bulk-creation call to list only that batch's databases. +List databases in the workspace, newest first, one page at a time. When no `limit` is given a default page size is applied, so a single call returns at most one page rather than every database. If the response's `has_more` is true, pass its `next_cursor` value back as the `cursor` query parameter to fetch the next page. Pass `search` to return only databases whose name *contains* that text, ignoring the case of unaccented Latin letters and digits; to fetch the single database whose name matches exactly, use `GET /v1/databases/by-name` instead. Pass `batch` with the `batch_id` returned by a bulk-creation call to list only that batch's databases. ### Parameters @@ -490,14 +491,14 @@ Name | Type | Description | Required | Notes > models::DatabaseDetailResponse lookup_database_by_name(name) Look up a database by name -Fetch a single database by its exact name. This is the counterpart to the listing's `search` filter, which matches any database whose name merely contains the text. Matching ignores case for names made of unaccented Latin letters and digits; that much is guaranteed. For names containing other characters — accented letters, or any non-Latin script — whether case is ignored depends on the deployment, so rely on neither: look those up with the capitalisation they were created with. Returns 404 when no database has that name. A name shared by more than one database returns 409 rather than picking one of them; address those by id. +Fetch a single database by its exact name. This is the counterpart to the listing's `search` filter, which matches any database whose name merely contains the text. Matching ignores case for unaccented Latin letters and digits, and only for those. Every other character has to match exactly, so a name containing an accented letter or a non-Latin script must be looked up with the capitalisation it was created with. Returns 404 when no database has that name. A name shared by more than one database returns 409 rather than picking one of them; address those by id. ### Parameters Name | Type | Description | Required | Notes ------------- | ------------- | ------------- | ------------- | ------------- -**name** | **String** | Exact name to look up. Unlike the listing's `search`, which matches any database whose name *contains* the text, this matches the whole name. Case is ignored for unaccented Latin letters and digits. For other characters, whether case is ignored depends on the deployment, so look those names up with the capitalisation they were created with. | [required] | +**name** | **String** | Exact name to look up. Unlike the listing's `search`, which matches any database whose name *contains* the text, this matches the whole name. Case is ignored for unaccented Latin letters and digits, and only for those. Every other character has to match exactly, so look a name holding one up with the capitalisation it was created with. | [required] | ### Return type @@ -514,3 +515,36 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## set_database_table_constant_per_key + +> models::ManagedTableConstantPerKeyResponse set_database_table_constant_per_key(database_id, schema, table, update_managed_table_request) +Declare which columns are constant per key + +Replace the columns a table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) narrow its search for prior versions to the values the upload carries. Unlike `partition_by` and `sorted_by`, this is NOT fixed when the table is created — it changes only which files a mutation opens, never how rows are written — so a populated table can adopt it with no rewrite, taking effect on the next load. Send an empty array to revoke it. **Correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**database_id** | **String** | Database ID | [required] | +**schema** | **String** | Schema name | [required] | +**table** | **String** | Table name | [required] | +**update_managed_table_request** | [**UpdateManagedTableRequest**](UpdateManagedTableRequest.md) | | [required] | + +### Return type + +[**models::ManagedTableConstantPerKeyResponse**](ManagedTableConstantPerKeyResponse.md) + +### Authorization + +[WorkspaceId](../README.md#WorkspaceId), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/docs/JobType.md b/docs/JobType.md index 95823b9..37aeb78 100644 --- a/docs/JobType.md +++ b/docs/JobType.md @@ -15,6 +15,7 @@ | ResultRetention | result_retention | | DucklakeCompaction | ducklake_compaction | | DucklakeTableCompaction | ducklake_table_compaction | +| TableConstantsUpdate | table_constants_update | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/docs/ManagedTableConstantPerKeyResponse.md b/docs/ManagedTableConstantPerKeyResponse.md new file mode 100644 index 0000000..c22726b --- /dev/null +++ b/docs/ManagedTableConstantPerKeyResponse.md @@ -0,0 +1,14 @@ +# ManagedTableConstantPerKeyResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**connection_id** | **String** | Connection backing the catalog the table belongs to. For a database default catalog this is the database's `default_connection_id`, so it is the value that addresses the table through the connection-scoped endpoints — not the database id the request may have used. | +**constant_per_key** | **Vec** | The columns now declared constant per key. Empty means no declaration, i.e. the unrestricted search. | +**schema** | **String** | Schema the table belongs to, as stored: lowercased, which may differ from the spelling in the request path. | +**table** | **String** | Table the declaration was written to, as stored: lowercased, which may differ from the spelling in the request path. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/docs/TableInfo.md b/docs/TableInfo.md index b2082b1..5f80878 100644 --- a/docs/TableInfo.md +++ b/docs/TableInfo.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **columns** | Option<[**Vec**](ColumnInfo.md)> | | [optional] **connection** | **String** | | +**constant_per_key** | **Vec** | Columns the table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation narrow its search for prior versions to the values the upload carries. Empty when none is declared, which is the unrestricted search. Unlike `partition_by` and `sorted_by` this is NOT fixed at creation — it changes only which files a mutation opens, never how rows are written — so read it here rather than assuming a declaration took effect. | **last_sync** | Option<**String**> | | [optional] **partition_by** | [**Vec**](TablePartitionKey.md) | The table's partition keys, in the order they were declared when the table was created. Empty when the table is not partitioned. A table's storage layout is fixed when the table is created and cannot be changed afterwards, so this is how to confirm a table really was created with the layout that was asked for. The field is always present: an empty array means \"no partitioning declared\", which is not the same as a response that omits the field entirely. Reported for tables in a Hotdata instant database, which are the only ones whose layout is declared here. A table discovered from an external connection always reports an empty array — its layout belongs to the upstream system, so an empty array there means \"not known from here\", not \"confirmed unpartitioned\". | **schema** | **String** | | diff --git a/docs/UpdateManagedTableRequest.md b/docs/UpdateManagedTableRequest.md new file mode 100644 index 0000000..4f5f704 --- /dev/null +++ b/docs/UpdateManagedTableRequest.md @@ -0,0 +1,11 @@ +# UpdateManagedTableRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**constant_per_key** | Option<**Vec**> | Columns whose value is the same for every row sharing this table's key. Send `[]` to revoke the declaration, which restores the unrestricted search on the next load — this is the kill switch if a declaration turns out to be false. **Correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/docs/UploadsApi.md b/docs/UploadsApi.md index 471be7e..d4f07fb 100644 --- a/docs/UploadsApi.md +++ b/docs/UploadsApi.md @@ -16,7 +16,7 @@ Method | HTTP request | Description > models::UploadSessionResponse create_upload_session_handler(create_upload_request) Create upload session -Create an upload session for a file you will upload directly to the URL the response carries, without sending it through this API. The response is one of three shapes. For a small file (`mode: single`) it contains a short-lived `url` to `PUT` the whole file to. For a large file with a known size (`mode: multipart`) it contains `part_urls` and `part_size`: split the file into `part_size`-byte chunks (the last is the remainder) and `PUT` chunk *i* (1-based) to `part_urls[i - 1]`, keeping each response's `ETag`. Slice by `part_size`, not by an even division across the number of part URLs (which can make a non-final part too small). For a file whose size you do not know up front, omit `declared_size_bytes`: the response is `mode: multipart` with a `part_size` but NO `part_urls`. As you stream, call `POST /v1/uploads/{upload_id}/parts` with a batch of `part_numbers` to mint per-part `PUT` URLs, `PUT` each part and keep its `ETag`, then finalize as for any multipart upload. In all cases the response also includes a one-time `finalize_token`. After uploading, call the finalize endpoint with the token (and, for multipart, the `{part_number, e_tag}` list) to make the upload usable as managed-table contents. The returned upload ID can then be passed to the managed-table load endpoint. You may hint a preferred part size with `part_size`; the service clamps it to the allowed range and ignores it for single-`PUT` uploads. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. +Create an upload session for a file you will upload directly to the URL the response carries, without sending it through this API. The response is one of three shapes. For a small file (`mode: single`) it contains a short-lived `url` to `PUT` the whole file to. For a large file with a known size (`mode: multipart`) it contains `part_urls` and `part_size`: split the file into `part_size`-byte chunks (the last is the remainder) and `PUT` chunk *i* (1-based) to `part_urls[i - 1]`, keeping each response's `ETag`. Slice by `part_size`, not by an even division across the number of part URLs (which can make a non-final part too small). For a file whose size you do not know up front, omit `declared_size_bytes`: the response is `mode: multipart` with a `part_size` but NO `part_urls`. As you stream, call `POST /v1/uploads/{upload_id}/parts` with a batch of `part_numbers` to mint per-part `PUT` URLs, `PUT` each part and keep its `ETag`, then finalize as for any multipart upload. In all cases the response also includes a one-time `finalize_token`. After uploading, call the finalize endpoint with the token (and, for multipart, the `{part_number, e_tag}` list) to make the upload usable as managed-table contents. The returned upload ID can then be passed to the managed-table load endpoint. You may hint a preferred part size with `part_size`; the service clamps it to the allowed range and ignores it for single-`PUT` uploads. One upload may be at most 16 GiB by default, whatever its format. Declare `declared_size_bytes` and an oversized file is refused here, before you transfer any of it; an upload created without a declared size is refused at finalize instead. To load more than that into one table, split the data across several uploads and load each one with `mode: append`. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. ### Parameters @@ -46,7 +46,7 @@ Name | Type | Description | Required | Notes > models::BatchCreateUploadResponse create_upload_sessions_batch_handler(batch_create_upload_request) Create upload sessions in bulk -Create upload sessions for several files in one request. Each file is planned independently and the response returns one session per requested file, in the same order. Each session is finalized separately via the finalize endpoint, so you can upload and finalize files at your own pace. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. +Create upload sessions for several files in one request. Each file is planned independently and the response returns one session per requested file, in the same order. Each session is finalized separately via the finalize endpoint, so you can upload and finalize files at your own pace. The maximum upload size (16 GiB by default, the same for every file format) applies to each file, not to the request as a whole. The batch is all-or-nothing: if any one file is refused, no sessions are created and the ones already planned are discarded, so retry the whole request rather than the rejected file alone. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. ### Parameters @@ -76,7 +76,7 @@ Name | Type | Description | Required | Notes > models::FinalizeUploadResponse finalize_upload_handler(upload_id, x_upload_finalize_token, finalize_upload_request) Finalize upload -Confirm that a file has been uploaded and make it usable as managed-table contents. Supply the `finalize_token` returned when the session was created, in the `X-Upload-Finalize-Token` header. When you declared a size at create time, the uploaded file's size is validated against it and a mismatch is rejected. An upload created without a declared size is finalized from its uploaded parts; it must be non-empty and is rejected if it exceeds the server's maximum upload size. Finalize is exactly-once: a second finalize of the same upload is rejected. +Confirm that a file has been uploaded and make it usable as managed-table contents. Supply the `finalize_token` returned when the session was created, in the `X-Upload-Finalize-Token` header. When you declared a size at create time, the uploaded file's size is validated against it and a mismatch is rejected. An upload created without a declared size is finalized from its uploaded parts; it must be non-empty and is rejected if it exceeds the maximum upload size (16 GiB by default, the same for every file format). Finalize is exactly-once: a second finalize of the same upload is rejected. ### Parameters diff --git a/src/apis/connections_api.rs b/src/apis/connections_api.rs index 73edcfd..9e8d1dd 100644 --- a/src/apis/connections_api.rs +++ b/src/apis/connections_api.rs @@ -94,6 +94,16 @@ pub enum LoadManagedTableError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`set_managed_table_constant_per_key`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum SetManagedTableConstantPerKeyError { + Status400(models::ApiErrorResponse), + Status404(models::ApiErrorResponse), + Status409(models::ApiErrorResponse), + UnknownValue(serde_json::Value), +} + /// Declare a new schema (and optionally its tables) on an existing managed catalog after creation. The schema is added to the connection's declaration; declared tables can then be populated via the managed-table load endpoint. Only valid against connections whose source type is `managed`. Identifiers are normalized to lowercase. pub async fn add_managed_schema( configuration: &configuration::Configuration, @@ -694,3 +704,79 @@ pub async fn load_managed_table( })) } } + +/// Replace the columns a table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) narrow its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics do not discriminate. Unlike `partition_by` and `sorted_by`, this is NOT fixed when the table is created. It changes only which files a mutation opens, never how rows are written, so nothing stored becomes wrong when it changes and a populated table can adopt it with no rewrite. It takes effect on the next load. Send an empty array to revoke it, restoring the unrestricted search — this is the way to undo a declaration that turns out to be false. **This is correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it, and the pruning conceals its own evidence because the file holding the missed row is never opened. Declare it only where the invariant is established. +pub async fn set_managed_table_constant_per_key( + configuration: &configuration::Configuration, + connection_id: &str, + schema: &str, + table: &str, + update_managed_table_request: models::UpdateManagedTableRequest, +) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_path_connection_id = connection_id; + let p_path_schema = schema; + let p_path_table = table; + let p_body_update_managed_table_request = update_managed_table_request; + + let uri_str = format!( + "{}/v1/connections/{connection_id}/schemas/{schema}/tables/{table}/constant-per-key", + configuration.base_path, + connection_id = crate::apis::urlencode(p_path_connection_id), + schema = crate::apis::urlencode(p_path_schema), + table = crate::apis::urlencode(p_path_table) + ); + let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(apikey) = configuration.api_keys.get("X-Workspace-Id") { + let key = apikey.key.clone(); + let value = match apikey.prefix { + Some(ref prefix) => format!("{} {}", prefix, key), + None => key, + }; + req_builder = req_builder.header("X-Workspace-Id", value); + }; + if let Some(token) = configuration.resolve_bearer_token().await { + req_builder = req_builder.bearer_auth(token); + }; + req_builder = req_builder.json(&p_body_update_managed_table_request); + + let req = req_builder.build()?; + crate::http_log::log_request(&req); + // Route through the shared retry helper so HTTP 429 (OVERLOADED admission + // shedding) is retried per `configuration.retry` on every generated op, not + // just the hand-written query path. See crate::http::execute_retrying. + let resp = crate::http::execute_retrying(configuration, req).await?; + + let status = resp.status(); + crate::http_log::log_response_status(status); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + crate::http_log::log_response_body(&content); + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ManagedTableConstantPerKeyResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ManagedTableConstantPerKeyResponse`")))), + } + } else { + let content = resp.text().await?; + crate::http_log::log_response_body(&content); + let entity: Option = + serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) + } +} diff --git a/src/apis/databases_api.rs b/src/apis/databases_api.rs index 51cc9f7..4396a88 100644 --- a/src/apis/databases_api.rs +++ b/src/apis/databases_api.rs @@ -156,6 +156,16 @@ pub enum LookupDatabaseByNameError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`set_database_table_constant_per_key`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum SetDatabaseTableConstantPerKeyError { + Status400(models::ApiErrorResponse), + Status404(models::ApiErrorResponse), + Status409(models::ApiErrorResponse), + UnknownValue(serde_json::Value), +} + /// Declare a new schema (and optionally its tables) on the database's auto-created default catalog after creation. The schema becomes reachable inside the database scope (e.g. `default..` and `information_schema.schemata`) without the caller naming the database's default connection. Identifiers are normalized to lowercase. pub async fn add_database_schema( configuration: &configuration::Configuration, @@ -425,7 +435,7 @@ pub async fn bulk_create_databases( } } -/// Return the total number of databases in the workspace. This is the whole-workspace total, not a page size: the `count` field on the listing reports how many rows that one page returned, so totalling a workspace from `GET /v1/databases` means walking every page. Pass `search` to count only databases whose name contains that text (case-insensitive), or `batch` with the `batch_id` returned by a bulk-creation call to count only that batch's databases. The filters mean exactly what they mean on the listing, so a count and a listing given the same filters describe the same set. +/// Return the total number of databases in the workspace. This is the whole-workspace total, not a page size: the `count` field on the listing reports how many rows that one page returned, so totalling a workspace from `GET /v1/databases` means walking every page. Pass `search` to count only databases whose name contains that text, ignoring the case of unaccented Latin letters and digits, or `batch` with the `batch_id` returned by a bulk-creation call to count only that batch's databases. The filters mean exactly what they mean on the listing, so a count and a listing given the same filters describe the same set. pub async fn count_databases( configuration: &configuration::Configuration, search: Option<&str>, @@ -1016,7 +1026,7 @@ pub async fn get_database_lineage( } } -/// List databases in the workspace, newest first, one page at a time. When no `limit` is given a default page size is applied, so a single call returns at most one page rather than every database. If the response's `has_more` is true, pass its `next_cursor` value back as the `cursor` query parameter to fetch the next page. Pass `search` to return only databases whose name *contains* that text (case-insensitive); to fetch the single database whose name matches exactly, use `GET /v1/databases/by-name` instead. Pass `batch` with the `batch_id` returned by a bulk-creation call to list only that batch's databases. +/// List databases in the workspace, newest first, one page at a time. When no `limit` is given a default page size is applied, so a single call returns at most one page rather than every database. If the response's `has_more` is true, pass its `next_cursor` value back as the `cursor` query parameter to fetch the next page. Pass `search` to return only databases whose name *contains* that text, ignoring the case of unaccented Latin letters and digits; to fetch the single database whose name matches exactly, use `GET /v1/databases/by-name` instead. Pass `batch` with the `batch_id` returned by a bulk-creation call to list only that batch's databases. pub async fn list_databases( configuration: &configuration::Configuration, limit: Option, @@ -1173,7 +1183,7 @@ pub async fn load_database_table( } } -/// Fetch a single database by its exact name. This is the counterpart to the listing's `search` filter, which matches any database whose name merely contains the text. Matching ignores case for names made of unaccented Latin letters and digits; that much is guaranteed. For names containing other characters — accented letters, or any non-Latin script — whether case is ignored depends on the deployment, so rely on neither: look those up with the capitalisation they were created with. Returns 404 when no database has that name. A name shared by more than one database returns 409 rather than picking one of them; address those by id. +/// Fetch a single database by its exact name. This is the counterpart to the listing's `search` filter, which matches any database whose name merely contains the text. Matching ignores case for unaccented Latin letters and digits, and only for those. Every other character has to match exactly, so a name containing an accented letter or a non-Latin script must be looked up with the capitalisation it was created with. Returns 404 when no database has that name. A name shared by more than one database returns 409 rather than picking one of them; address those by id. pub async fn lookup_database_by_name( configuration: &configuration::Configuration, name: &str, @@ -1235,3 +1245,80 @@ pub async fn lookup_database_by_name( })) } } + +/// Replace the columns a table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) narrow its search for prior versions to the values the upload carries. Unlike `partition_by` and `sorted_by`, this is NOT fixed when the table is created — it changes only which files a mutation opens, never how rows are written — so a populated table can adopt it with no rewrite, taking effect on the next load. Send an empty array to revoke it. **Correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. +pub async fn set_database_table_constant_per_key( + configuration: &configuration::Configuration, + database_id: &str, + schema: &str, + table: &str, + update_managed_table_request: models::UpdateManagedTableRequest, +) -> Result> +{ + // add a prefix to parameters to efficiently prevent name collisions + let p_path_database_id = database_id; + let p_path_schema = schema; + let p_path_table = table; + let p_body_update_managed_table_request = update_managed_table_request; + + let uri_str = format!( + "{}/v1/databases/{database_id}/schemas/{schema}/tables/{table}/constant-per-key", + configuration.base_path, + database_id = crate::apis::urlencode(p_path_database_id), + schema = crate::apis::urlencode(p_path_schema), + table = crate::apis::urlencode(p_path_table) + ); + let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(apikey) = configuration.api_keys.get("X-Workspace-Id") { + let key = apikey.key.clone(); + let value = match apikey.prefix { + Some(ref prefix) => format!("{} {}", prefix, key), + None => key, + }; + req_builder = req_builder.header("X-Workspace-Id", value); + }; + if let Some(token) = configuration.resolve_bearer_token().await { + req_builder = req_builder.bearer_auth(token); + }; + req_builder = req_builder.json(&p_body_update_managed_table_request); + + let req = req_builder.build()?; + crate::http_log::log_request(&req); + // Route through the shared retry helper so HTTP 429 (OVERLOADED admission + // shedding) is retried per `configuration.retry` on every generated op, not + // just the hand-written query path. See crate::http::execute_retrying. + let resp = crate::http::execute_retrying(configuration, req).await?; + + let status = resp.status(); + crate::http_log::log_response_status(status); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + crate::http_log::log_response_body(&content); + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ManagedTableConstantPerKeyResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ManagedTableConstantPerKeyResponse`")))), + } + } else { + let content = resp.text().await?; + crate::http_log::log_response_body(&content); + let entity: Option = + serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) + } +} diff --git a/src/apis/uploads_api.rs b/src/apis/uploads_api.rs index 529fec6..bac7ce0 100644 --- a/src/apis/uploads_api.rs +++ b/src/apis/uploads_api.rs @@ -50,7 +50,7 @@ pub enum MintUploadPartsHandlerError { UnknownValue(serde_json::Value), } -/// Create an upload session for a file you will upload directly to the URL the response carries, without sending it through this API. The response is one of three shapes. For a small file (`mode: single`) it contains a short-lived `url` to `PUT` the whole file to. For a large file with a known size (`mode: multipart`) it contains `part_urls` and `part_size`: split the file into `part_size`-byte chunks (the last is the remainder) and `PUT` chunk *i* (1-based) to `part_urls[i - 1]`, keeping each response's `ETag`. Slice by `part_size`, not by an even division across the number of part URLs (which can make a non-final part too small). For a file whose size you do not know up front, omit `declared_size_bytes`: the response is `mode: multipart` with a `part_size` but NO `part_urls`. As you stream, call `POST /v1/uploads/{upload_id}/parts` with a batch of `part_numbers` to mint per-part `PUT` URLs, `PUT` each part and keep its `ETag`, then finalize as for any multipart upload. In all cases the response also includes a one-time `finalize_token`. After uploading, call the finalize endpoint with the token (and, for multipart, the `{part_number, e_tag}` list) to make the upload usable as managed-table contents. The returned upload ID can then be passed to the managed-table load endpoint. You may hint a preferred part size with `part_size`; the service clamps it to the allowed range and ignores it for single-`PUT` uploads. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. +/// Create an upload session for a file you will upload directly to the URL the response carries, without sending it through this API. The response is one of three shapes. For a small file (`mode: single`) it contains a short-lived `url` to `PUT` the whole file to. For a large file with a known size (`mode: multipart`) it contains `part_urls` and `part_size`: split the file into `part_size`-byte chunks (the last is the remainder) and `PUT` chunk *i* (1-based) to `part_urls[i - 1]`, keeping each response's `ETag`. Slice by `part_size`, not by an even division across the number of part URLs (which can make a non-final part too small). For a file whose size you do not know up front, omit `declared_size_bytes`: the response is `mode: multipart` with a `part_size` but NO `part_urls`. As you stream, call `POST /v1/uploads/{upload_id}/parts` with a batch of `part_numbers` to mint per-part `PUT` URLs, `PUT` each part and keep its `ETag`, then finalize as for any multipart upload. In all cases the response also includes a one-time `finalize_token`. After uploading, call the finalize endpoint with the token (and, for multipart, the `{part_number, e_tag}` list) to make the upload usable as managed-table contents. The returned upload ID can then be passed to the managed-table load endpoint. You may hint a preferred part size with `part_size`; the service clamps it to the allowed range and ignores it for single-`PUT` uploads. One upload may be at most 16 GiB by default, whatever its format. Declare `declared_size_bytes` and an oversized file is refused here, before you transfer any of it; an upload created without a declared size is refused at finalize instead. To load more than that into one table, split the data across several uploads and load each one with `mode: append`. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. pub async fn create_upload_session_handler( configuration: &configuration::Configuration, create_upload_request: models::CreateUploadRequest, @@ -115,7 +115,7 @@ pub async fn create_upload_session_handler( } } -/// Create upload sessions for several files in one request. Each file is planned independently and the response returns one session per requested file, in the same order. Each session is finalized separately via the finalize endpoint, so you can upload and finalize files at your own pace. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. +/// Create upload sessions for several files in one request. Each file is planned independently and the response returns one session per requested file, in the same order. Each session is finalized separately via the finalize endpoint, so you can upload and finalize files at your own pace. The maximum upload size (16 GiB by default, the same for every file format) applies to each file, not to the request as a whole. The batch is all-or-nothing: if any one file is refused, no sessions are created and the ones already planned are discarded, so retry the whole request rather than the rejected file alone. A `501` with error code `PRESIGN_UNSUPPORTED` means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead. pub async fn create_upload_sessions_batch_handler( configuration: &configuration::Configuration, batch_create_upload_request: models::BatchCreateUploadRequest, @@ -181,7 +181,7 @@ pub async fn create_upload_sessions_batch_handler( } } -/// Confirm that a file has been uploaded and make it usable as managed-table contents. Supply the `finalize_token` returned when the session was created, in the `X-Upload-Finalize-Token` header. When you declared a size at create time, the uploaded file's size is validated against it and a mismatch is rejected. An upload created without a declared size is finalized from its uploaded parts; it must be non-empty and is rejected if it exceeds the server's maximum upload size. Finalize is exactly-once: a second finalize of the same upload is rejected. +/// Confirm that a file has been uploaded and make it usable as managed-table contents. Supply the `finalize_token` returned when the session was created, in the `X-Upload-Finalize-Token` header. When you declared a size at create time, the uploaded file's size is validated against it and a mismatch is rejected. An upload created without a declared size is finalized from its uploaded parts; it must be non-empty and is rejected if it exceeds the maximum upload size (16 GiB by default, the same for every file format). Finalize is exactly-once: a second finalize of the same upload is rejected. pub async fn finalize_upload_handler( configuration: &configuration::Configuration, upload_id: &str, diff --git a/src/models/add_managed_table_decl.rs b/src/models/add_managed_table_decl.rs index 62b1f22..6dfd541 100644 --- a/src/models/add_managed_table_decl.rs +++ b/src/models/add_managed_table_decl.rs @@ -14,12 +14,12 @@ use serde::{Deserialize, Serialize}; /// AddManagedTableDecl : One table declaration inside an add-schema request body. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct AddManagedTableDecl { + /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. + #[serde(rename = "constant_per_key", skip_serializing_if = "Option::is_none")] + pub constant_per_key: Option>, /// Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. #[serde(rename = "key", skip_serializing_if = "Option::is_none")] pub key: Option>, - /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. - #[serde(rename = "key_determines", skip_serializing_if = "Option::is_none")] - pub key_determines: Option>, #[serde(rename = "name")] pub name: String, /// Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. @@ -34,8 +34,8 @@ impl AddManagedTableDecl { /// One table declaration inside an add-schema request body. pub fn new(name: String) -> AddManagedTableDecl { AddManagedTableDecl { + constant_per_key: None, key: None, - key_determines: None, name, partition_by: None, sorted_by: None, diff --git a/src/models/add_managed_table_request.rs b/src/models/add_managed_table_request.rs index 966e8c8..7cb497c 100644 --- a/src/models/add_managed_table_request.rs +++ b/src/models/add_managed_table_request.rs @@ -14,12 +14,12 @@ use serde::{Deserialize, Serialize}; /// AddManagedTableRequest : Request body for adding a table to an existing schema: `POST /v1/connections/{id}/schemas/{schema}/tables` and `POST /v1/databases/{id}/schemas/{schema}/tables`. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct AddManagedTableRequest { + /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. + #[serde(rename = "constant_per_key", skip_serializing_if = "Option::is_none")] + pub constant_per_key: Option>, /// Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. #[serde(rename = "key", skip_serializing_if = "Option::is_none")] pub key: Option>, - /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. - #[serde(rename = "key_determines", skip_serializing_if = "Option::is_none")] - pub key_determines: Option>, #[serde(rename = "name")] pub name: String, /// Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. @@ -34,8 +34,8 @@ impl AddManagedTableRequest { /// Request body for adding a table to an existing schema: `POST /v1/connections/{id}/schemas/{schema}/tables` and `POST /v1/databases/{id}/schemas/{schema}/tables`. pub fn new(name: String) -> AddManagedTableRequest { AddManagedTableRequest { + constant_per_key: None, key: None, - key_determines: None, name, partition_by: None, sorted_by: None, diff --git a/src/models/create_upload_request.rs b/src/models/create_upload_request.rs index 86aa600..7b432ba 100644 --- a/src/models/create_upload_request.rs +++ b/src/models/create_upload_request.rs @@ -11,7 +11,7 @@ use crate::models; use serde::{Deserialize, Serialize}; -/// CreateUploadRequest : Request body for `POST /v1/uploads` and for each entry of `POST /v1/uploads/batch`. Describes a single file you intend to upload. The response carries a short-lived URL to `PUT` the bytes to, so the file never passes through the API itself. The declared size is validated against the bytes you actually upload when you finalize. +/// CreateUploadRequest : Request body for `POST /v1/uploads` and for each entry of `POST /v1/uploads/batch`. Describes a single file you intend to upload. The response carries a short-lived URL to `PUT` the bytes to, so the file never passes through the API itself. The declared size is validated against the bytes you actually upload when you finalize. One upload may be at most 16 GiB by default, whatever its format. To load more data than that into a single table, split it across several uploads and load each one with `mode: append`. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct CreateUploadRequest { /// Integrity checksum algorithm you are volunteering for this file. Currently only `sha256` is accepted. Optional; pair with `checksum_value`. @@ -46,7 +46,7 @@ pub struct CreateUploadRequest { skip_serializing_if = "Option::is_none" )] pub content_type: Option>, - /// The exact size, in bytes, of the file you will upload. Optional. When provided, it is validated at create time against the maximum allowed size, and again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from `POST /v1/uploads/{upload_id}/parts` as you upload, and finalize validates only that the file is non-empty. + /// The exact size, in bytes, of the file you will upload. Optional. When provided, it is checked at create time against the maximum upload size (16 GiB by default, the same for every file format), so an oversized file is refused before you transfer any of it; it is checked again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from `POST /v1/uploads/{upload_id}/parts` as you upload, and finalize checks only that the file is non-empty and within the maximum upload size. #[serde( rename = "declared_size_bytes", default, @@ -73,7 +73,7 @@ pub struct CreateUploadRequest { } impl CreateUploadRequest { - /// Request body for `POST /v1/uploads` and for each entry of `POST /v1/uploads/batch`. Describes a single file you intend to upload. The response carries a short-lived URL to `PUT` the bytes to, so the file never passes through the API itself. The declared size is validated against the bytes you actually upload when you finalize. + /// Request body for `POST /v1/uploads` and for each entry of `POST /v1/uploads/batch`. Describes a single file you intend to upload. The response carries a short-lived URL to `PUT` the bytes to, so the file never passes through the API itself. The declared size is validated against the bytes you actually upload when you finalize. One upload may be at most 16 GiB by default, whatever its format. To load more data than that into a single table, split it across several uploads and load each one with `mode: append`. pub fn new() -> CreateUploadRequest { CreateUploadRequest { checksum_algo: None, diff --git a/src/models/database_default_table_decl.rs b/src/models/database_default_table_decl.rs index 5fc284f..0702156 100644 --- a/src/models/database_default_table_decl.rs +++ b/src/models/database_default_table_decl.rs @@ -14,12 +14,12 @@ use serde::{Deserialize, Serialize}; /// DatabaseDefaultTableDecl : One table declaration inside a default-catalog schema, supplied at database-create time. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct DatabaseDefaultTableDecl { + /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. + #[serde(rename = "constant_per_key", skip_serializing_if = "Option::is_none")] + pub constant_per_key: Option>, /// Columns that uniquely identify a row, enabling the key-based load modes (`delete`, `update`, `upsert`) on this table: those loads match rows by these columns' values. Omit (the default) to declare no key; the table can still be loaded with `replace` and `append`, but key-based modes are then rejected. #[serde(rename = "key", skip_serializing_if = "Option::is_none")] pub key: Option>, - /// Columns whose value is determined by this table's `key`: for every uploaded row, every stored row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation (`delete`, `update`, `upsert`) restrict its search for prior versions to the values the upload carries, which prunes far harder than the key alone when the key's own file statistics are weak. Omit (the default) for the unrestricted search. **Correctness-affecting, not a hint.** If the assertion is false, a mutation supersedes one version of a key and appends beside another, silently duplicating it. Declare it only where the invariant is established. - #[serde(rename = "key_determines", skip_serializing_if = "Option::is_none")] - pub key_determines: Option>, #[serde(rename = "name")] pub name: String, /// Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter. @@ -34,8 +34,8 @@ impl DatabaseDefaultTableDecl { /// One table declaration inside a default-catalog schema, supplied at database-create time. pub fn new(name: String) -> DatabaseDefaultTableDecl { DatabaseDefaultTableDecl { + constant_per_key: None, key: None, - key_determines: None, name, partition_by: None, sorted_by: None, diff --git a/src/models/job_type.rs b/src/models/job_type.rs index 03abf1f..e703b0a 100644 --- a/src/models/job_type.rs +++ b/src/models/job_type.rs @@ -37,6 +37,8 @@ pub enum JobType { DucklakeCompaction, #[serde(rename = "ducklake_table_compaction")] DucklakeTableCompaction, + #[serde(rename = "table_constants_update")] + TableConstantsUpdate, } impl std::fmt::Display for JobType { @@ -53,6 +55,7 @@ impl std::fmt::Display for JobType { Self::ResultRetention => write!(f, "result_retention"), Self::DucklakeCompaction => write!(f, "ducklake_compaction"), Self::DucklakeTableCompaction => write!(f, "ducklake_table_compaction"), + Self::TableConstantsUpdate => write!(f, "table_constants_update"), } } } diff --git a/src/models/managed_table_constant_per_key_response.rs b/src/models/managed_table_constant_per_key_response.rs new file mode 100644 index 0000000..6fe4fec --- /dev/null +++ b/src/models/managed_table_constant_per_key_response.rs @@ -0,0 +1,46 @@ +/* + * Hotdata API + * + * Powerful data platform API for instant databases, queries, and analytics. + * + * The version of the OpenAPI document: 1.0.0 + * Contact: developers@hotdata.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +/// ManagedTableConstantPerKeyResponse : The declaration as it now stands after a `constant-per-key` write. Echoed back rather than returning `204` so a caller can confirm what actually took effect instead of assuming its request applied — the same reason `/v1/information_schema` reports the field. +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ManagedTableConstantPerKeyResponse { + /// Connection backing the catalog the table belongs to. For a database default catalog this is the database's `default_connection_id`, so it is the value that addresses the table through the connection-scoped endpoints — not the database id the request may have used. + #[serde(rename = "connection_id")] + pub connection_id: String, + /// The columns now declared constant per key. Empty means no declaration, i.e. the unrestricted search. + #[serde(rename = "constant_per_key")] + pub constant_per_key: Vec, + /// Schema the table belongs to, as stored: lowercased, which may differ from the spelling in the request path. + #[serde(rename = "schema")] + pub schema: String, + /// Table the declaration was written to, as stored: lowercased, which may differ from the spelling in the request path. + #[serde(rename = "table")] + pub table: String, +} + +impl ManagedTableConstantPerKeyResponse { + /// The declaration as it now stands after a `constant-per-key` write. Echoed back rather than returning `204` so a caller can confirm what actually took effect instead of assuming its request applied — the same reason `/v1/information_schema` reports the field. + pub fn new( + connection_id: String, + constant_per_key: Vec, + schema: String, + table: String, + ) -> ManagedTableConstantPerKeyResponse { + ManagedTableConstantPerKeyResponse { + connection_id, + constant_per_key, + schema, + table, + } + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 75c1e1c..bbd9c4a 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -164,6 +164,8 @@ pub mod load_managed_table_response; pub use self::load_managed_table_response::LoadManagedTableResponse; pub mod managed_schema_response; pub use self::managed_schema_response::ManagedSchemaResponse; +pub mod managed_table_constant_per_key_response; +pub use self::managed_table_constant_per_key_response::ManagedTableConstantPerKeyResponse; pub mod managed_table_response; pub use self::managed_table_response::ManagedTableResponse; pub mod mint_upload_parts_request; @@ -208,6 +210,8 @@ pub mod update_embedding_provider_request; pub use self::update_embedding_provider_request::UpdateEmbeddingProviderRequest; pub mod update_embedding_provider_response; pub use self::update_embedding_provider_response::UpdateEmbeddingProviderResponse; +pub mod update_managed_table_request; +pub use self::update_managed_table_request::UpdateManagedTableRequest; pub mod update_saved_query_request; pub use self::update_saved_query_request::UpdateSavedQueryRequest; pub mod upload_session_response; diff --git a/src/models/table_info.rs b/src/models/table_info.rs index b7217c1..ae8c07e 100644 --- a/src/models/table_info.rs +++ b/src/models/table_info.rs @@ -23,6 +23,9 @@ pub struct TableInfo { pub columns: Option>>, #[serde(rename = "connection")] pub connection: String, + /// Columns the table declares constant for a given key: for every row, any other row sharing its key holds the same value of these columns. Declaring this lets a keyed mutation narrow its search for prior versions to the values the upload carries. Empty when none is declared, which is the unrestricted search. Unlike `partition_by` and `sorted_by` this is NOT fixed at creation — it changes only which files a mutation opens, never how rows are written — so read it here rather than assuming a declaration took effect. + #[serde(rename = "constant_per_key")] + pub constant_per_key: Vec, #[serde( rename = "last_sync", default, @@ -48,6 +51,7 @@ impl TableInfo { /// Single table metadata pub fn new( connection: String, + constant_per_key: Vec, partition_by: Vec, schema: String, sorted_by: Vec, @@ -57,6 +61,7 @@ impl TableInfo { TableInfo { columns: None, connection, + constant_per_key, last_sync: None, partition_by, schema, diff --git a/src/models/update_managed_table_request.rs b/src/models/update_managed_table_request.rs new file mode 100644 index 0000000..59c397c --- /dev/null +++ b/src/models/update_managed_table_request.rs @@ -0,0 +1,29 @@ +/* + * Hotdata API + * + * Powerful data platform API for instant databases, queries, and analytics. + * + * The version of the OpenAPI document: 1.0.0 + * Contact: developers@hotdata.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +/// UpdateManagedTableRequest : Request body for setting the columns a table declares constant per key: `PUT /v1/connections/{id}/schemas/{schema}/tables/{table}/constant-per-key` and `PUT /v1/databases/{id}/schemas/{schema}/tables/{table}/constant-per-key`. The body carries the complete new value: send an empty array to remove the declaration. Sending any other field is rejected — `key`, `partition_by` and `sorted_by` are fixed when the table is created. +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct UpdateManagedTableRequest { + /// Columns whose value is the same for every row sharing this table's key. Send `[]` to revoke the declaration, which restores the unrestricted search on the next load — this is the kill switch if a declaration turns out to be false. **Correctness-affecting, not a hint.** If the assertion is false, a keyed mutation supersedes one version of a key and appends beside another, silently duplicating it. + #[serde(rename = "constant_per_key", skip_serializing_if = "Option::is_none")] + pub constant_per_key: Option>, +} + +impl UpdateManagedTableRequest { + /// Request body for setting the columns a table declares constant per key: `PUT /v1/connections/{id}/schemas/{schema}/tables/{table}/constant-per-key` and `PUT /v1/databases/{id}/schemas/{schema}/tables/{table}/constant-per-key`. The body carries the complete new value: send an empty array to remove the declaration. Sending any other field is rejected — `key`, `partition_by` and `sorted_by` are fixed when the table is created. + pub fn new() -> UpdateManagedTableRequest { + UpdateManagedTableRequest { + constant_per_key: None, + } + } +}