diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 77cbac6..1059cce 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -111,6 +111,7 @@ docs/SavedQuerySummary.md docs/SavedQueryVersionInfo.md docs/SubmitJobResponse.md docs/TableInfo.md +docs/TableLayout.md docs/TablePartitionKey.md docs/TableProfileResponse.md docs/TableSortKey.md @@ -249,6 +250,7 @@ hotdata/models/saved_query_summary.py hotdata/models/saved_query_version_info.py hotdata/models/submit_job_response.py hotdata/models/table_info.py +hotdata/models/table_layout.py hotdata/models/table_partition_key.py hotdata/models/table_profile_response.py hotdata/models/table_sort_key.py @@ -378,6 +380,7 @@ test/test_saved_query_summary.py test/test_saved_query_version_info.py test/test_submit_job_response.py test/test_table_info.py +test/test_table_layout.py test/test_table_partition_key.py test/test_table_profile_response.py test/test_table_sort_key.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a9ec44..a9ad6bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- chore(loads): document table write locking and retry behavior - feat(jobs): add ivf_rebalance_sweep job type - chore(query): clarify write statement and dialect documentation - chore: add 503 service unavailable response to load endpoints diff --git a/docs/AddManagedTableDecl.md b/docs/AddManagedTableDecl.md index c7f8944..864070d 100644 --- a/docs/AddManagedTableDecl.md +++ b/docs/AddManagedTableDecl.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **constant_per_key** | **List[str]** | 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** | **List[str]** | 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] +**layout** | [**TableLayout**](TableLayout.md) | Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter. | [optional] **name** | **str** | | **partition_by** | [**List[TablePartitionKey]**](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** | [**List[TableSortKey]**](TableSortKey.md) | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search. | [optional] diff --git a/docs/AddManagedTableRequest.md b/docs/AddManagedTableRequest.md index 36b4cb0..a49fe69 100644 --- a/docs/AddManagedTableRequest.md +++ b/docs/AddManagedTableRequest.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **constant_per_key** | **List[str]** | 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** | **List[str]** | 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] +**layout** | [**TableLayout**](TableLayout.md) | Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter. | [optional] **name** | **str** | | **partition_by** | [**List[TablePartitionKey]**](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** | [**List[TableSortKey]**](TableSortKey.md) | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search. | [optional] diff --git a/docs/ConnectionsApi.md b/docs/ConnectionsApi.md index 5592a63..9217c64 100644 --- a/docs/ConnectionsApi.md +++ b/docs/ConnectionsApi.md @@ -722,7 +722,7 @@ This endpoint does not need any parameter. Load managed table from inline data, upload, or query result -Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. +Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. ### Example @@ -809,7 +809,7 @@ Name | Type | Description | Notes **202** | Load accepted and running in the background; poll the returned job for status and result | - | **400** | Invalid request (bad mode, none or several of `upload_id`/`result_id`/`data`, `format` combined with `result_id`, `columns` without `data`, a non-CSV inline `format`, unparseable inline data, non-managed connection, invalid identifier, bad parquet, or the result failed to compute) | - | **404** | Connection, upload, or result not found, or the table was deleted | - | -**409** | Upload already consumed or in flight, the result is still being computed, or the incoming data changes a column's type incompatibly (only widening to a larger compatible type can be applied automatically); the existing data is unchanged and remains queryable | - | +**409** | Upload already consumed or in flight, the result is still being computed, or the incoming data changes a column's type incompatibly (only widening to a larger compatible type can be applied automatically); the existing data is unchanged and remains queryable. Error code `RESOURCE_LOCKED`: another write to the table is running; retry after the `Retry-After` delay. | - | **413** | Inline `data` is over the 2 MiB limit (error code `INLINE_DATA_TOO_LARGE`); upload the data and load it by `upload_id` instead | - | **503** | The catalog is busy: no pooled catalog connection came free within the acquire window (error code `CATALOG_BUSY`). The load may already have committed data if the timeout hit after the write, so check the table, or load with an idempotency key, before retrying after the `Retry-After` delay | - | diff --git a/docs/DatabaseDefaultTableDecl.md b/docs/DatabaseDefaultTableDecl.md index 821c9de..c4fa238 100644 --- a/docs/DatabaseDefaultTableDecl.md +++ b/docs/DatabaseDefaultTableDecl.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **constant_per_key** | **List[str]** | 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** | **List[str]** | 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] +**layout** | [**TableLayout**](TableLayout.md) | Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter. | [optional] **name** | **str** | | **partition_by** | [**List[TablePartitionKey]**](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** | [**List[TableSortKey]**](TableSortKey.md) | Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search. | [optional] diff --git a/docs/DatabasesApi.md b/docs/DatabasesApi.md index 41d0a60..df2e409 100644 --- a/docs/DatabasesApi.md +++ b/docs/DatabasesApi.md @@ -1274,7 +1274,7 @@ Name | Type | Description | Notes Load database table from inline data, upload, or query result -Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. +Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. ### Example @@ -1364,7 +1364,7 @@ Name | Type | Description | Notes **202** | Load accepted and running in the background; poll the returned job for status and result | - | **400** | Invalid request (bad mode, none or several of `upload_id`/`result_id`/`data`, `format` combined with `result_id`, `columns` without `data`, a non-CSV inline `format`, unparseable inline data, invalid identifier, bad parquet, or the result failed to compute) | - | **404** | Database, upload, or result not found, or the table was deleted | - | -**409** | Upload already consumed or in flight, the result is still being computed, or the incoming data changes a column's type incompatibly (only widening to a larger compatible type can be applied automatically); the existing data is unchanged and remains queryable | - | +**409** | Upload already consumed or in flight, the result is still being computed, or the incoming data changes a column's type incompatibly (only widening to a larger compatible type can be applied automatically); the existing data is unchanged and remains queryable. Error code `RESOURCE_LOCKED`: another write to the table is running; retry after the `Retry-After` delay. | - | **413** | Inline `data` is over the 2 MiB limit (error code `INLINE_DATA_TOO_LARGE`); upload the data and load it by `upload_id` instead | - | **503** | The catalog is busy: no pooled catalog connection came free within the acquire window (error code `CATALOG_BUSY`). The load may already have committed data if the timeout hit after the write, so check the table, or load with an idempotency key, before retrying after the `Retry-After` delay | - | diff --git a/docs/TableInfo.md b/docs/TableInfo.md index c77d5ea..26ba645 100644 --- a/docs/TableInfo.md +++ b/docs/TableInfo.md @@ -10,6 +10,7 @@ Name | Type | Description | Notes **connection** | **str** | | **constant_per_key** | **List[str]** | 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** | **str** | | [optional] +**layout** | [**TableLayout**](TableLayout.md) | The table's storage layout: `analytical`, or `point_lookup`, whose small data pages make fetching its rows by id cheap. Fixed at creation. | **partition_by** | [**List[TablePartitionKey]**](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\". | **var_schema** | **str** | | **sorted_by** | [**List[TableSortKey]**](TableSortKey.md) | The table's sort keys, in the order they were declared when the table was created. Empty when no sort order was declared. Always present, and limited to tables in a Hotdata instant database, for the same reasons as `partition_by`. | diff --git a/docs/TableLayout.md b/docs/TableLayout.md new file mode 100644 index 0000000..edc3264 --- /dev/null +++ b/docs/TableLayout.md @@ -0,0 +1,13 @@ +# TableLayout + +Storage layout of a table. + +## Enum + +* `ANALYTICAL` (value: `'analytical'`) + +* `POINT_LOOKUP` (value: `'point_lookup'`) + +[[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/hotdata/__init__.py b/hotdata/__init__.py index 345fc32..ece5392 100644 --- a/hotdata/__init__.py +++ b/hotdata/__init__.py @@ -146,6 +146,7 @@ "SavedQueryVersionInfo", "SubmitJobResponse", "TableInfo", + "TableLayout", "TablePartitionKey", "TableProfileResponse", "TableSortKey", @@ -290,6 +291,7 @@ from hotdata.models.saved_query_version_info import SavedQueryVersionInfo as SavedQueryVersionInfo from hotdata.models.submit_job_response import SubmitJobResponse as SubmitJobResponse from hotdata.models.table_info import TableInfo as TableInfo +from hotdata.models.table_layout import TableLayout as TableLayout from hotdata.models.table_partition_key import TablePartitionKey as TablePartitionKey from hotdata.models.table_profile_response import TableProfileResponse as TableProfileResponse from hotdata.models.table_sort_key import TableSortKey as TableSortKey diff --git a/hotdata/api/connections_api.py b/hotdata/api/connections_api.py index c8180e1..685f39e 100644 --- a/hotdata/api/connections_api.py +++ b/hotdata/api/connections_api.py @@ -2339,7 +2339,7 @@ def load_managed_table( ) -> LoadManagedTableResponse: """Load managed table from inline data, upload, or query result - Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param connection_id: Connection ID (required) :type connection_id: str @@ -2424,7 +2424,7 @@ def load_managed_table_with_http_info( ) -> ApiResponse[LoadManagedTableResponse]: """Load managed table from inline data, upload, or query result - Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param connection_id: Connection ID (required) :type connection_id: str @@ -2509,7 +2509,7 @@ def load_managed_table_without_preload_content( ) -> RESTResponseType: """Load managed table from inline data, upload, or query result - Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied and accepts five values: `replace` makes the uploaded rows the table's entire contents, `append` inserts them on top of the existing data, and `delete`, `update`, and `upsert` match rows by key — removing, replacing, or inserting-or-replacing the matched rows respectively. The three key-matching modes need a key: the one the table was created with, or one given in `key` on the request. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param connection_id: Connection ID (required) :type connection_id: str diff --git a/hotdata/api/databases_api.py b/hotdata/api/databases_api.py index 766fe67..ad257e4 100644 --- a/hotdata/api/databases_api.py +++ b/hotdata/api/databases_api.py @@ -4082,7 +4082,7 @@ def load_database_table( ) -> LoadManagedTableResponse: """Load database table from inline data, upload, or query result - Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param database_id: Database ID (required) :type database_id: str @@ -4167,7 +4167,7 @@ def load_database_table_with_http_info( ) -> ApiResponse[LoadManagedTableResponse]: """Load database table from inline data, upload, or query result - Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param database_id: Database ID (required) :type database_id: str @@ -4252,7 +4252,7 @@ def load_database_table_without_preload_content( ) -> RESTResponseType: """Load database table from inline data, upload, or query result - Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. + Publish data as the new contents of a table on the database's default catalog, from one of three sources — provide exactly one. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected or set via `format`. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously. While background maintenance holds the table, a load waits for it to finish, for up to 60 seconds, before it starts, in every mode; with `async`, the 202 can arrive only after that wait. A load refused because another write to the table is running gets 409 with the error code `RESOURCE_LOCKED` and a `Retry-After` header. :param database_id: Database ID (required) :type database_id: str diff --git a/hotdata/models/__init__.py b/hotdata/models/__init__.py index 37a8d51..b0210e8 100644 --- a/hotdata/models/__init__.py +++ b/hotdata/models/__init__.py @@ -113,6 +113,7 @@ from hotdata.models.saved_query_version_info import SavedQueryVersionInfo from hotdata.models.submit_job_response import SubmitJobResponse from hotdata.models.table_info import TableInfo +from hotdata.models.table_layout import TableLayout from hotdata.models.table_partition_key import TablePartitionKey from hotdata.models.table_profile_response import TableProfileResponse from hotdata.models.table_sort_key import TableSortKey diff --git a/hotdata/models/add_managed_table_decl.py b/hotdata/models/add_managed_table_decl.py index 2212aef..d31b8d0 100644 --- a/hotdata/models/add_managed_table_decl.py +++ b/hotdata/models/add_managed_table_decl.py @@ -20,6 +20,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictStr from typing import Any, ClassVar, Dict, List, Optional +from hotdata.models.table_layout import TableLayout from hotdata.models.table_partition_key import TablePartitionKey from hotdata.models.table_sort_key import TableSortKey from typing import Optional, Set @@ -31,10 +32,11 @@ class AddManagedTableDecl(BaseModel): """ # noqa: E501 constant_per_key: Optional[List[StrictStr]] = Field(default=None, description="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.") key: Optional[List[StrictStr]] = Field(default=None, description="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.") + layout: Optional[TableLayout] = Field(default=None, description="Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter.") name: StrictStr partition_by: Optional[List[TablePartitionKey]] = Field(default=None, description="Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter.") sorted_by: Optional[List[TableSortKey]] = Field(default=None, description="Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search.") - __properties: ClassVar[List[str]] = ["constant_per_key", "key", "name", "partition_by", "sorted_by"] + __properties: ClassVar[List[str]] = ["constant_per_key", "key", "layout", "name", "partition_by", "sorted_by"] model_config = ConfigDict( populate_by_name=True, @@ -107,6 +109,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "constant_per_key": obj.get("constant_per_key"), "key": obj.get("key"), + "layout": obj.get("layout"), "name": obj.get("name"), "partition_by": [TablePartitionKey.from_dict(_item) for _item in obj["partition_by"]] if obj.get("partition_by") is not None else None, "sorted_by": [TableSortKey.from_dict(_item) for _item in obj["sorted_by"]] if obj.get("sorted_by") is not None else None diff --git a/hotdata/models/add_managed_table_request.py b/hotdata/models/add_managed_table_request.py index 22e5452..f1ec0b5 100644 --- a/hotdata/models/add_managed_table_request.py +++ b/hotdata/models/add_managed_table_request.py @@ -20,6 +20,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictStr from typing import Any, ClassVar, Dict, List, Optional +from hotdata.models.table_layout import TableLayout from hotdata.models.table_partition_key import TablePartitionKey from hotdata.models.table_sort_key import TableSortKey from typing import Optional, Set @@ -31,10 +32,11 @@ class AddManagedTableRequest(BaseModel): """ # noqa: E501 constant_per_key: Optional[List[StrictStr]] = Field(default=None, description="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.") key: Optional[List[StrictStr]] = Field(default=None, description="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.") + layout: Optional[TableLayout] = Field(default=None, description="Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter.") name: StrictStr partition_by: Optional[List[TablePartitionKey]] = Field(default=None, description="Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter.") sorted_by: Optional[List[TableSortKey]] = Field(default=None, description="Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search.") - __properties: ClassVar[List[str]] = ["constant_per_key", "key", "name", "partition_by", "sorted_by"] + __properties: ClassVar[List[str]] = ["constant_per_key", "key", "layout", "name", "partition_by", "sorted_by"] model_config = ConfigDict( populate_by_name=True, @@ -107,6 +109,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "constant_per_key": obj.get("constant_per_key"), "key": obj.get("key"), + "layout": obj.get("layout"), "name": obj.get("name"), "partition_by": [TablePartitionKey.from_dict(_item) for _item in obj["partition_by"]] if obj.get("partition_by") is not None else None, "sorted_by": [TableSortKey.from_dict(_item) for _item in obj["sorted_by"]] if obj.get("sorted_by") is not None else None diff --git a/hotdata/models/database_default_table_decl.py b/hotdata/models/database_default_table_decl.py index f33d50d..b3ddf50 100644 --- a/hotdata/models/database_default_table_decl.py +++ b/hotdata/models/database_default_table_decl.py @@ -20,6 +20,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictStr from typing import Any, ClassVar, Dict, List, Optional +from hotdata.models.table_layout import TableLayout from hotdata.models.table_partition_key import TablePartitionKey from hotdata.models.table_sort_key import TableSortKey from typing import Optional, Set @@ -31,10 +32,11 @@ class DatabaseDefaultTableDecl(BaseModel): """ # noqa: E501 constant_per_key: Optional[List[StrictStr]] = Field(default=None, description="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.") key: Optional[List[StrictStr]] = Field(default=None, description="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.") + layout: Optional[TableLayout] = Field(default=None, description="Storage layout of the table: `analytical` (the default), or `point_lookup`, which writes small data pages so the table's rows can be fetched by id cheaply, which is what a vector index's answers do; analytical scans of it read more pages. Declared when the table is created and fixed thereafter.") name: StrictStr partition_by: Optional[List[TablePartitionKey]] = Field(default=None, description="Partition keys for this table, applied in order. Omit for no partitioning. Declared when the table is created and fixed thereafter.") sorted_by: Optional[List[TableSortKey]] = Field(default=None, description="Sort keys for this table, applied in order. Omit for no sort order. Declared when the table is created and fixed thereafter. A table with a sort order cannot be indexed: creating any index on it is refused, so leave this empty on a table you plan to search.") - __properties: ClassVar[List[str]] = ["constant_per_key", "key", "name", "partition_by", "sorted_by"] + __properties: ClassVar[List[str]] = ["constant_per_key", "key", "layout", "name", "partition_by", "sorted_by"] model_config = ConfigDict( populate_by_name=True, @@ -107,6 +109,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "constant_per_key": obj.get("constant_per_key"), "key": obj.get("key"), + "layout": obj.get("layout"), "name": obj.get("name"), "partition_by": [TablePartitionKey.from_dict(_item) for _item in obj["partition_by"]] if obj.get("partition_by") is not None else None, "sorted_by": [TableSortKey.from_dict(_item) for _item in obj["sorted_by"]] if obj.get("sorted_by") is not None else None diff --git a/hotdata/models/table_info.py b/hotdata/models/table_info.py index ff40229..e3e7873 100644 --- a/hotdata/models/table_info.py +++ b/hotdata/models/table_info.py @@ -21,6 +21,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr from typing import Any, ClassVar, Dict, List, Optional from hotdata.models.column_info import ColumnInfo +from hotdata.models.table_layout import TableLayout from hotdata.models.table_partition_key import TablePartitionKey from hotdata.models.table_sort_key import TableSortKey from typing import Optional, Set @@ -34,12 +35,13 @@ class TableInfo(BaseModel): connection: StrictStr constant_per_key: List[StrictStr] = Field(description="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: Optional[StrictStr] = None + layout: TableLayout = Field(description="The table's storage layout: `analytical`, or `point_lookup`, whose small data pages make fetching its rows by id cheap. Fixed at creation.") partition_by: List[TablePartitionKey] = Field(description="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\".") var_schema: StrictStr = Field(alias="schema") sorted_by: List[TableSortKey] = Field(description="The table's sort keys, in the order they were declared when the table was created. Empty when no sort order was declared. Always present, and limited to tables in a Hotdata instant database, for the same reasons as `partition_by`.") synced: StrictBool table: StrictStr - __properties: ClassVar[List[str]] = ["columns", "connection", "constant_per_key", "last_sync", "partition_by", "schema", "sorted_by", "synced", "table"] + __properties: ClassVar[List[str]] = ["columns", "connection", "constant_per_key", "last_sync", "layout", "partition_by", "schema", "sorted_by", "synced", "table"] model_config = ConfigDict( populate_by_name=True, @@ -127,6 +129,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "connection": obj.get("connection"), "constant_per_key": obj.get("constant_per_key"), "last_sync": obj.get("last_sync"), + "layout": obj.get("layout"), "partition_by": [TablePartitionKey.from_dict(_item) for _item in obj["partition_by"]] if obj.get("partition_by") is not None else None, "schema": obj.get("schema"), "sorted_by": [TableSortKey.from_dict(_item) for _item in obj["sorted_by"]] if obj.get("sorted_by") is not None else None, diff --git a/hotdata/models/table_layout.py b/hotdata/models/table_layout.py new file mode 100644 index 0000000..df55b0b --- /dev/null +++ b/hotdata/models/table_layout.py @@ -0,0 +1,38 @@ +# coding: utf-8 + +""" + 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 OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +from enum import Enum +from typing_extensions import Self + + +class TableLayout(str, Enum): + """ + Storage layout of a table. + """ + + """ + allowed enum values + """ + ANALYTICAL = 'analytical' + POINT_LOOKUP = 'point_lookup' + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of TableLayout from a JSON string""" + return cls(json.loads(json_str)) + + diff --git a/test/test_add_managed_schema_request.py b/test/test_add_managed_schema_request.py index d6fad2d..03be475 100644 --- a/test/test_add_managed_schema_request.py +++ b/test/test_add_managed_schema_request.py @@ -41,6 +41,7 @@ def make_instance(self, include_optional) -> AddManagedSchemaRequest: hotdata.models.add_managed_table_decl.AddManagedTableDecl( constant_per_key = [event_date], key = [order_id], + layout = null, name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_add_managed_table_decl.py b/test/test_add_managed_table_decl.py index 4aab137..3c61e93 100644 --- a/test/test_add_managed_table_decl.py +++ b/test/test_add_managed_table_decl.py @@ -38,6 +38,7 @@ def make_instance(self, include_optional) -> AddManagedTableDecl: return AddManagedTableDecl( constant_per_key = [event_date], key = [order_id], + layout = 'analytical', name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_add_managed_table_request.py b/test/test_add_managed_table_request.py index e012e51..2a63915 100644 --- a/test/test_add_managed_table_request.py +++ b/test/test_add_managed_table_request.py @@ -38,6 +38,7 @@ def make_instance(self, include_optional) -> AddManagedTableRequest: return AddManagedTableRequest( constant_per_key = [event_date], key = [order_id], + layout = 'analytical', name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_bulk_create_databases_request.py b/test/test_bulk_create_databases_request.py index e6ec342..88a8726 100644 --- a/test/test_bulk_create_databases_request.py +++ b/test/test_bulk_create_databases_request.py @@ -49,6 +49,7 @@ def make_instance(self, include_optional) -> BulkCreateDatabasesRequest: hotdata.models.database_default_table_decl.DatabaseDefaultTableDecl( constant_per_key = [event_date], key = [order_id], + layout = null, name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_create_database_request.py b/test/test_create_database_request.py index b54d423..ecce653 100644 --- a/test/test_create_database_request.py +++ b/test/test_create_database_request.py @@ -48,6 +48,7 @@ def make_instance(self, include_optional) -> CreateDatabaseRequest: hotdata.models.database_default_table_decl.DatabaseDefaultTableDecl( constant_per_key = [event_date], key = [order_id], + layout = null, name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_database_default_schema_decl.py b/test/test_database_default_schema_decl.py index 6c4f67c..b51d8e6 100644 --- a/test/test_database_default_schema_decl.py +++ b/test/test_database_default_schema_decl.py @@ -41,6 +41,7 @@ def make_instance(self, include_optional) -> DatabaseDefaultSchemaDecl: hotdata.models.database_default_table_decl.DatabaseDefaultTableDecl( constant_per_key = [event_date], key = [order_id], + layout = null, name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_database_default_table_decl.py b/test/test_database_default_table_decl.py index 20eceaf..a18466c 100644 --- a/test/test_database_default_table_decl.py +++ b/test/test_database_default_table_decl.py @@ -38,6 +38,7 @@ def make_instance(self, include_optional) -> DatabaseDefaultTableDecl: return DatabaseDefaultTableDecl( constant_per_key = [event_date], key = [order_id], + layout = 'analytical', name = 'orders', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( diff --git a/test/test_information_schema_response.py b/test/test_information_schema_response.py index ff2f92e..0fdc4d0 100644 --- a/test/test_information_schema_response.py +++ b/test/test_information_schema_response.py @@ -53,6 +53,7 @@ def make_instance(self, include_optional) -> InformationSchemaResponse: '' ], last_sync = '', + layout = null, partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( column = 'created_at', @@ -87,6 +88,7 @@ def make_instance(self, include_optional) -> InformationSchemaResponse: '' ], last_sync = '', + layout = null, partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( column = 'created_at', diff --git a/test/test_table_info.py b/test/test_table_info.py index ad2b50d..8a6cfbf 100644 --- a/test/test_table_info.py +++ b/test/test_table_info.py @@ -47,6 +47,7 @@ def make_instance(self, include_optional) -> TableInfo: '' ], last_sync = '', + layout = 'analytical', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( column = 'created_at', @@ -68,6 +69,7 @@ def make_instance(self, include_optional) -> TableInfo: constant_per_key = [ '' ], + layout = 'analytical', partition_by = [ hotdata.models.table_partition_key.TablePartitionKey( column = 'created_at', diff --git a/test/test_table_layout.py b/test/test_table_layout.py new file mode 100644 index 0000000..e676b5b --- /dev/null +++ b/test/test_table_layout.py @@ -0,0 +1,34 @@ +# coding: utf-8 + +""" + 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 OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import unittest + +from hotdata.models.table_layout import TableLayout + +class TestTableLayout(unittest.TestCase): + """TableLayout unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testTableLayout(self): + """Test TableLayout""" + # inst = TableLayout() + +if __name__ == '__main__': + unittest.main()