From 20de0d0431b5fc0ceacb85b314dc10c88e535dcc Mon Sep 17 00:00:00 2001 From: "hotdata-automation[bot]" <267177015+hotdata-automation[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 01:07:40 +0000 Subject: [PATCH] feat(tables): support loading from query results --- CHANGELOG.md | 4 ++++ docs/ConnectionsApi.md | 16 +++++++------- docs/DatabaseDetailResponse.md | 1 + docs/DatabaseSummary.md | 1 + docs/DatabasesApi.md | 16 +++++++------- docs/LoadManagedTableRequest.md | 9 ++++---- hotdata/api/connections_api.py | 12 +++++------ hotdata/api/databases_api.py | 12 +++++------ hotdata/models/database_detail_response.py | 9 +++++++- hotdata/models/database_summary.py | 9 +++++++- hotdata/models/load_managed_table_request.py | 22 +++++++++++++++----- test/test_connections_api.py | 2 +- test/test_database_detail_response.py | 1 + test/test_database_summary.py | 1 + test/test_databases_api.py | 2 +- test/test_list_databases_response.py | 2 ++ test/test_load_managed_table_request.py | 2 +- 17 files changed, 79 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 678495d..addc5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- feat(tables): support loading from query results + ## [0.6.0] - 2026-07-07 ### Changed diff --git a/docs/ConnectionsApi.md b/docs/ConnectionsApi.md index 43b18db..a0bb68a 100644 --- a/docs/ConnectionsApi.md +++ b/docs/ConnectionsApi.md @@ -13,7 +13,7 @@ Method | HTTP request | Description [**get_connection**](ConnectionsApi.md#get_connection) | **GET** /v1/connections/{connection_id} | Get connection [**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 upload +[**load_managed_table**](ConnectionsApi.md#load_managed_table) | **POST** /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads | Load managed table from upload or query result [**purge_connection_cache**](ConnectionsApi.md#purge_connection_cache) | **DELETE** /v1/connections/{connection_id}/cache | Purge connection cache [**purge_table_cache**](ConnectionsApi.md#purge_table_cache) | **DELETE** /v1/connections/{connection_id}/tables/{schema}/{table}/cache | Purge table cache @@ -808,9 +808,9 @@ This endpoint does not need any parameter. # **load_managed_table** > LoadManagedTableResponse load_managed_table(connection_id, var_schema, table, load_managed_table_request) -Load managed table from upload +Load managed table from upload or query result -Publish a previously-uploaded file as the new contents of a managed table. CSV, JSON, and Parquet uploads are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. +Publish data as the new contents of a managed table from one of two sources — provide exactly one. 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: `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, 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. ### Example @@ -856,7 +856,7 @@ with hotdata.ApiClient(configuration) as api_client: load_managed_table_request = hotdata.LoadManagedTableRequest() # LoadManagedTableRequest | try: - # Load managed table from upload + # Load managed table from upload or query result api_response = api_instance.load_managed_table(connection_id, var_schema, table, load_managed_table_request) print("The response of ConnectionsApi->load_managed_table:\n") pprint(api_response) @@ -894,10 +894,10 @@ Name | Type | Description | Notes | Status code | Description | Response headers | |-------------|-------------|------------------| **200** | Managed table loaded | - | -**202** | Load accepted and running in the background; poll the returned job for status and result | - | -**400** | Invalid request (bad mode, non-managed connection, invalid identifier, bad parquet) | - | -**404** | Connection or upload not found, or the table was deleted | - | -**409** | Upload already consumed or in flight, or the uploaded 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 | - | +**202** | Upload load accepted and running in the background; poll the returned job for status and result | - | +**400** | Invalid request (bad mode, both or neither of `upload_id`/`result_id`, `format` combined with `result_id`, 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 | - | [[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/DatabaseDetailResponse.md b/docs/DatabaseDetailResponse.md index 7e7e3fb..465d3c9 100644 --- a/docs/DatabaseDetailResponse.md +++ b/docs/DatabaseDetailResponse.md @@ -7,6 +7,7 @@ Response body for GET /databases/{database_id} Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **attachments** | [**List[DatabaseAttachmentInfo]**](DatabaseAttachmentInfo.md) | | +**created_at** | **datetime** | When the database was created. | [optional] **default_catalog** | **str** | Name the database's default catalog answers to inside its query scope (`default` unless overridden at create time). | **default_connection_id** | **str** | | **expires_at** | **datetime** | When this database expires. | [optional] diff --git a/docs/DatabaseSummary.md b/docs/DatabaseSummary.md index 83784a5..d47a09b 100644 --- a/docs/DatabaseSummary.md +++ b/docs/DatabaseSummary.md @@ -6,6 +6,7 @@ Summary item in GET /databases Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**created_at** | **datetime** | When the database was created. | [optional] **default_catalog** | **str** | Name the database's default catalog answers to inside its query scope. | **expires_at** | **datetime** | | [optional] **id** | **str** | | diff --git a/docs/DatabasesApi.md b/docs/DatabasesApi.md index 20c45c0..87b6997 100644 --- a/docs/DatabasesApi.md +++ b/docs/DatabasesApi.md @@ -12,7 +12,7 @@ Method | HTTP request | Description [**detach_database_catalog**](DatabasesApi.md#detach_database_catalog) | **DELETE** /v1/databases/{database_id}/catalogs/{connection_id} | Detach catalog from database [**get_database**](DatabasesApi.md#get_database) | **GET** /v1/databases/{database_id} | Get database [**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 upload +[**load_database_table**](DatabasesApi.md#load_database_table) | **POST** /v1/databases/{database_id}/schemas/{schema}/tables/{table}/loads | Load database table from upload or query result # **add_database_schema** @@ -710,9 +710,9 @@ This endpoint does not need any parameter. # **load_database_table** > LoadManagedTableResponse load_database_table(database_id, var_schema, table, load_managed_table_request) -Load database table from upload +Load database table from upload or query result -Publish a previously-uploaded file as the new contents of a table on the database's default catalog. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. CSV, JSON, and Parquet uploads are supported; the format is auto-detected or set via `format`. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. +Publish data as the new contents of a table on the database's default catalog, from one of two 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 `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, 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. ### Example @@ -758,7 +758,7 @@ with hotdata.ApiClient(configuration) as api_client: load_managed_table_request = hotdata.LoadManagedTableRequest() # LoadManagedTableRequest | try: - # Load database table from upload + # Load database table from upload or query result api_response = api_instance.load_database_table(database_id, var_schema, table, load_managed_table_request) print("The response of DatabasesApi->load_database_table:\n") pprint(api_response) @@ -796,10 +796,10 @@ Name | Type | Description | Notes | Status code | Description | Response headers | |-------------|-------------|------------------| **200** | Table loaded | - | -**202** | Load accepted and running in the background; poll the returned job for status and result | - | -**400** | Invalid request (bad mode, invalid identifier, bad parquet) | - | -**404** | Database or upload not found, or the table was deleted | - | -**409** | Upload already consumed or in flight, or the uploaded 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 | - | +**202** | Upload load accepted and running in the background; poll the returned job for status and result | - | +**400** | Invalid request (bad mode, both or neither of `upload_id`/`result_id`, `format` combined with `result_id`, 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 | - | [[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/LoadManagedTableRequest.md b/docs/LoadManagedTableRequest.md index 0d36ef6..b0c42fd 100644 --- a/docs/LoadManagedTableRequest.md +++ b/docs/LoadManagedTableRequest.md @@ -1,6 +1,6 @@ # LoadManagedTableRequest -Request body for the managed-table load endpoints — the connection-scoped `POST /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads` and the database-scoped equivalent. Publishes a previously-uploaded file to the named table. CSV and JSON uploads are converted to columnar storage on load; Parquet uploads are published directly. `mode` selects whether the upload replaces the table's contents or is appended on top of them. +Request body for the managed-table load endpoints — the connection-scoped `POST /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads` and the database-scoped equivalent. Publishes data to the named table from one of two sources: a previously uploaded file (`upload_id`) or a persisted query result (`result_id`). Provide exactly one. CSV and JSON uploads are converted to columnar storage on load; Parquet uploads and query results are published directly. `mode` selects whether the data replaces the table's contents or is appended on top of them. ## Properties @@ -8,9 +8,10 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **var_async** | **bool** | When true, run the load as a background job and return a job ID to poll instead of blocking until it finishes. Recommended for large uploads, which can take longer than an HTTP request should stay open. | [optional] **async_after_ms** | **int** | If set (requires `async` = true), wait up to this many milliseconds for the load to finish: if it completes in time the full result is returned (200), otherwise a 202 with a job ID to poll. Must be between 1000 and the server maximum; a value out of that range, or set without `async` = true, is rejected with 400. | [optional] -**format** | **str** | File format of the upload: `\"csv\"`, `\"json\"`, or `\"parquet\"`. Optional — when omitted, the format is auto-detected from the upload's `Content-Type` and, failing that, from the file contents. Provide it explicitly to override detection or when the contents are ambiguous. `\"json\"` expects newline-delimited JSON (one object per line), not a JSON array. | [optional] -**mode** | **str** | How the upload is applied: `\"replace\"` overwrites the table's contents, `\"append\"` inserts the uploaded rows on top of the existing data. | -**upload_id** | **str** | ID of a previously-staged upload (see `POST /v1/files`). The upload is claimed atomically; concurrent loads against the same `upload_id` return 409. | +**format** | **str** | File format of the upload: `\"csv\"`, `\"json\"`, or `\"parquet\"`. Optional — when omitted, the format is auto-detected from the upload's `Content-Type` and, failing that, from the file contents. Provide it explicitly to override detection or when the contents are ambiguous. `\"json\"` expects newline-delimited JSON (one object per line), not a JSON array. Only applies to `upload_id`; query results are always parquet. | [optional] +**mode** | **str** | How the data is applied: `\"replace\"` overwrites the table's contents, `\"append\"` inserts the new rows on top of the existing data. | +**result_id** | **str** | ID of a persisted query result (see `GET /v1/results/{result_id}`) to publish as the table's contents. The 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. Provide either this or `upload_id`, not both. | [optional] +**upload_id** | **str** | ID of a previously-staged upload (see `POST /v1/files`). The upload is claimed atomically; concurrent loads against the same `upload_id` return 409. Provide either this or `result_id`, not both. | [optional] ## Example diff --git a/hotdata/api/connections_api.py b/hotdata/api/connections_api.py index 3102a41..6260f7d 100644 --- a/hotdata/api/connections_api.py +++ b/hotdata/api/connections_api.py @@ -2601,9 +2601,9 @@ def load_managed_table( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> LoadManagedTableResponse: - """Load managed table from upload + """Load managed table from upload or query result - Publish a previously-uploaded file as the new contents of a managed table. CSV, JSON, and Parquet uploads are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a managed table from one of two sources — provide exactly one. 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: `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, 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. :param connection_id: Connection ID (required) :type connection_id: str @@ -2684,9 +2684,9 @@ def load_managed_table_with_http_info( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> ApiResponse[LoadManagedTableResponse]: - """Load managed table from upload + """Load managed table from upload or query result - Publish a previously-uploaded file as the new contents of a managed table. CSV, JSON, and Parquet uploads are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a managed table from one of two sources — provide exactly one. 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: `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, 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. :param connection_id: Connection ID (required) :type connection_id: str @@ -2767,9 +2767,9 @@ def load_managed_table_without_preload_content( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> RESTResponseType: - """Load managed table from upload + """Load managed table from upload or query result - Publish a previously-uploaded file as the new contents of a managed table. CSV, JSON, and Parquet uploads are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a managed table from one of two sources — provide exactly one. 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: `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, 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. :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 8fe63f6..605fd75 100644 --- a/hotdata/api/databases_api.py +++ b/hotdata/api/databases_api.py @@ -2315,9 +2315,9 @@ def load_database_table( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> LoadManagedTableResponse: - """Load database table from upload + """Load database table from upload or query result - Publish a previously-uploaded file as the new contents of a table on the database's default catalog. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. CSV, JSON, and Parquet uploads are supported; the format is auto-detected or set via `format`. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a table on the database's default catalog, from one of two 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 `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, 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. :param database_id: Database ID (required) :type database_id: str @@ -2398,9 +2398,9 @@ def load_database_table_with_http_info( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> ApiResponse[LoadManagedTableResponse]: - """Load database table from upload + """Load database table from upload or query result - Publish a previously-uploaded file as the new contents of a table on the database's default catalog. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. CSV, JSON, and Parquet uploads are supported; the format is auto-detected or set via `format`. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a table on the database's default catalog, from one of two 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 `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, 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. :param database_id: Database ID (required) :type database_id: str @@ -2481,9 +2481,9 @@ def load_database_table_without_preload_content( _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> RESTResponseType: - """Load database table from upload + """Load database table from upload or query result - Publish a previously-uploaded file as the new contents of a table on the database's default catalog. The database-scoped equivalent of the connection-scoped managed-table load — addressed by `database_id`, so no `default_connection_id` is needed. CSV, JSON, and Parquet uploads are supported; the format is auto-detected or set via `format`. 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 upload is applied: `replace` overwrites the table's contents, `append` inserts the uploaded rows on top of the existing data. Concurrent loads against the same upload return 409. 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. + Publish data as the new contents of a table on the database's default catalog, from one of two 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 `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, 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. :param database_id: Database ID (required) :type database_id: str diff --git a/hotdata/models/database_detail_response.py b/hotdata/models/database_detail_response.py index caac528..f40390c 100644 --- a/hotdata/models/database_detail_response.py +++ b/hotdata/models/database_detail_response.py @@ -30,12 +30,13 @@ class DatabaseDetailResponse(BaseModel): Response body for GET /databases/{database_id} """ # noqa: E501 attachments: List[DatabaseAttachmentInfo] + created_at: Optional[datetime] = Field(default=None, description="When the database was created.") default_catalog: StrictStr = Field(description="Name the database's default catalog answers to inside its query scope (`default` unless overridden at create time).") default_connection_id: StrictStr expires_at: Optional[datetime] = Field(default=None, description="When this database expires.") id: StrictStr name: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["attachments", "default_catalog", "default_connection_id", "expires_at", "id", "name"] + __properties: ClassVar[List[str]] = ["attachments", "created_at", "default_catalog", "default_connection_id", "expires_at", "id", "name"] model_config = ConfigDict( populate_by_name=True, @@ -83,6 +84,11 @@ def to_dict(self) -> Dict[str, Any]: if _item_attachments: _items.append(_item_attachments.to_dict()) _dict['attachments'] = _items + # set to None if created_at (nullable) is None + # and model_fields_set contains the field + if self.created_at is None and "created_at" in self.model_fields_set: + _dict['created_at'] = None + # set to None if expires_at (nullable) is None # and model_fields_set contains the field if self.expires_at is None and "expires_at" in self.model_fields_set: @@ -106,6 +112,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "attachments": [DatabaseAttachmentInfo.from_dict(_item) for _item in obj["attachments"]] if obj.get("attachments") is not None else None, + "created_at": obj.get("created_at"), "default_catalog": obj.get("default_catalog"), "default_connection_id": obj.get("default_connection_id"), "expires_at": obj.get("expires_at"), diff --git a/hotdata/models/database_summary.py b/hotdata/models/database_summary.py index 80fac4c..9b1546f 100644 --- a/hotdata/models/database_summary.py +++ b/hotdata/models/database_summary.py @@ -28,11 +28,12 @@ class DatabaseSummary(BaseModel): """ Summary item in GET /databases """ # noqa: E501 + created_at: Optional[datetime] = Field(default=None, description="When the database was created.") default_catalog: StrictStr = Field(description="Name the database's default catalog answers to inside its query scope.") expires_at: Optional[datetime] = None id: StrictStr name: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["default_catalog", "expires_at", "id", "name"] + __properties: ClassVar[List[str]] = ["created_at", "default_catalog", "expires_at", "id", "name"] model_config = ConfigDict( populate_by_name=True, @@ -73,6 +74,11 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) + # set to None if created_at (nullable) is None + # and model_fields_set contains the field + if self.created_at is None and "created_at" in self.model_fields_set: + _dict['created_at'] = None + # set to None if expires_at (nullable) is None # and model_fields_set contains the field if self.expires_at is None and "expires_at" in self.model_fields_set: @@ -95,6 +101,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ + "created_at": obj.get("created_at"), "default_catalog": obj.get("default_catalog"), "expires_at": obj.get("expires_at"), "id": obj.get("id"), diff --git a/hotdata/models/load_managed_table_request.py b/hotdata/models/load_managed_table_request.py index 9ef868c..5b7bc12 100644 --- a/hotdata/models/load_managed_table_request.py +++ b/hotdata/models/load_managed_table_request.py @@ -26,14 +26,15 @@ class LoadManagedTableRequest(BaseModel): """ - Request body for the managed-table load endpoints — the connection-scoped `POST /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads` and the database-scoped equivalent. Publishes a previously-uploaded file to the named table. CSV and JSON uploads are converted to columnar storage on load; Parquet uploads are published directly. `mode` selects whether the upload replaces the table's contents or is appended on top of them. + Request body for the managed-table load endpoints — the connection-scoped `POST /v1/connections/{connection_id}/schemas/{schema}/tables/{table}/loads` and the database-scoped equivalent. Publishes data to the named table from one of two sources: a previously uploaded file (`upload_id`) or a persisted query result (`result_id`). Provide exactly one. CSV and JSON uploads are converted to columnar storage on load; Parquet uploads and query results are published directly. `mode` selects whether the data replaces the table's contents or is appended on top of them. """ # noqa: E501 var_async: Optional[StrictBool] = Field(default=None, description="When true, run the load as a background job and return a job ID to poll instead of blocking until it finishes. Recommended for large uploads, which can take longer than an HTTP request should stay open.", alias="async") async_after_ms: Optional[Annotated[int, Field(strict=True, ge=1000)]] = Field(default=None, description="If set (requires `async` = true), wait up to this many milliseconds for the load to finish: if it completes in time the full result is returned (200), otherwise a 202 with a job ID to poll. Must be between 1000 and the server maximum; a value out of that range, or set without `async` = true, is rejected with 400.") - format: Optional[StrictStr] = Field(default=None, description="File format of the upload: `\"csv\"`, `\"json\"`, or `\"parquet\"`. Optional — when omitted, the format is auto-detected from the upload's `Content-Type` and, failing that, from the file contents. Provide it explicitly to override detection or when the contents are ambiguous. `\"json\"` expects newline-delimited JSON (one object per line), not a JSON array.") - mode: StrictStr = Field(description="How the upload is applied: `\"replace\"` overwrites the table's contents, `\"append\"` inserts the uploaded rows on top of the existing data.") - upload_id: StrictStr = Field(description="ID of a previously-staged upload (see `POST /v1/files`). The upload is claimed atomically; concurrent loads against the same `upload_id` return 409.") - __properties: ClassVar[List[str]] = ["async", "async_after_ms", "format", "mode", "upload_id"] + format: Optional[StrictStr] = Field(default=None, description="File format of the upload: `\"csv\"`, `\"json\"`, or `\"parquet\"`. Optional — when omitted, the format is auto-detected from the upload's `Content-Type` and, failing that, from the file contents. Provide it explicitly to override detection or when the contents are ambiguous. `\"json\"` expects newline-delimited JSON (one object per line), not a JSON array. Only applies to `upload_id`; query results are always parquet.") + mode: StrictStr = Field(description="How the data is applied: `\"replace\"` overwrites the table's contents, `\"append\"` inserts the new rows on top of the existing data.") + result_id: Optional[StrictStr] = Field(default=None, description="ID of a persisted query result (see `GET /v1/results/{result_id}`) to publish as the table's contents. The 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. Provide either this or `upload_id`, not both.") + upload_id: Optional[StrictStr] = Field(default=None, description="ID of a previously-staged upload (see `POST /v1/files`). The upload is claimed atomically; concurrent loads against the same `upload_id` return 409. Provide either this or `result_id`, not both.") + __properties: ClassVar[List[str]] = ["async", "async_after_ms", "format", "mode", "result_id", "upload_id"] model_config = ConfigDict( populate_by_name=True, @@ -84,6 +85,16 @@ def to_dict(self) -> Dict[str, Any]: if self.format is None and "format" in self.model_fields_set: _dict['format'] = None + # set to None if result_id (nullable) is None + # and model_fields_set contains the field + if self.result_id is None and "result_id" in self.model_fields_set: + _dict['result_id'] = None + + # set to None if upload_id (nullable) is None + # and model_fields_set contains the field + if self.upload_id is None and "upload_id" in self.model_fields_set: + _dict['upload_id'] = None + return _dict @classmethod @@ -100,6 +111,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "async_after_ms": obj.get("async_after_ms"), "format": obj.get("format"), "mode": obj.get("mode"), + "result_id": obj.get("result_id"), "upload_id": obj.get("upload_id") }) return _obj diff --git a/test/test_connections_api.py b/test/test_connections_api.py index dd47895..0d514ae 100644 --- a/test/test_connections_api.py +++ b/test/test_connections_api.py @@ -93,7 +93,7 @@ def test_list_connections(self) -> None: def test_load_managed_table(self) -> None: """Test case for load_managed_table - Load managed table from upload + Load managed table from upload or query result """ pass diff --git a/test/test_database_detail_response.py b/test/test_database_detail_response.py index 8c0ac88..2ffcf9a 100644 --- a/test/test_database_detail_response.py +++ b/test/test_database_detail_response.py @@ -41,6 +41,7 @@ def make_instance(self, include_optional) -> DatabaseDetailResponse: alias = '', connection_id = '', ) ], + created_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), default_catalog = '', default_connection_id = '', expires_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), diff --git a/test/test_database_summary.py b/test/test_database_summary.py index 13ae63f..a7466a6 100644 --- a/test/test_database_summary.py +++ b/test/test_database_summary.py @@ -36,6 +36,7 @@ def make_instance(self, include_optional) -> DatabaseSummary: model = DatabaseSummary() if include_optional: return DatabaseSummary( + created_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), default_catalog = '', expires_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), id = '', diff --git a/test/test_databases_api.py b/test/test_databases_api.py index 7524d82..22173c2 100644 --- a/test/test_databases_api.py +++ b/test/test_databases_api.py @@ -86,7 +86,7 @@ def test_list_databases(self) -> None: def test_load_database_table(self) -> None: """Test case for load_database_table - Load database table from upload + Load database table from upload or query result """ pass diff --git a/test/test_list_databases_response.py b/test/test_list_databases_response.py index 28f99a4..8b03abd 100644 --- a/test/test_list_databases_response.py +++ b/test/test_list_databases_response.py @@ -38,6 +38,7 @@ def make_instance(self, include_optional) -> ListDatabasesResponse: return ListDatabasesResponse( databases = [ hotdata.models.database_summary.DatabaseSummary( + created_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), default_catalog = '', expires_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), id = '', @@ -48,6 +49,7 @@ def make_instance(self, include_optional) -> ListDatabasesResponse: return ListDatabasesResponse( databases = [ hotdata.models.database_summary.DatabaseSummary( + created_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), default_catalog = '', expires_at = datetime.datetime.strptime('2013-10-20 19:20:30.00', '%Y-%m-%d %H:%M:%S.%f'), id = '', diff --git a/test/test_load_managed_table_request.py b/test/test_load_managed_table_request.py index 08f1d8a..06ee947 100644 --- a/test/test_load_managed_table_request.py +++ b/test/test_load_managed_table_request.py @@ -40,12 +40,12 @@ def make_instance(self, include_optional) -> LoadManagedTableRequest: async_after_ms = 1000, format = '', mode = '', + result_id = '', upload_id = '' ) else: return LoadManagedTableRequest( mode = '', - upload_id = '', ) """