Skip to content

Latest commit

 

History

History
508 lines (386 loc) · 26.5 KB

File metadata and controls

508 lines (386 loc) · 26.5 KB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Changed

  • feat(indexes): add vector_precision field to index operations

[0.18.0] - 2026-09-15

Added

  • open_result_arrow and ArrowResultStream: fetch a result as an Arrow IPC stream decoded directly off the socket, so peak memory is one record batch rather than the whole result. get_result_arrow and stream_result_arrow both collect the entire body first and are unchanged. Also available as Client::open_result_arrow.

[0.17.0] - 2026-09-10

Fixed

  • Query results no longer round wide numbers. A JSON number in a result row was parsed into a serde_json::Value, which has no arbitrary-precision number: anything needing more than ~17 significant digits — a DECIMAL(38,2) at full width, for instance — arrived already rounded, so a value the service sent as 99999999999999999999.99 reached callers as 1e20. Rows now carry each cell's JSON text.

Changed

  • Breaking: result rows are Vec<Vec<JsonCell>>, not Vec<Vec<serde_json::Value>>. QueryResponse::rows and GetResultResponse::rows (and QueryResponse::new) carry the new JsonCell, which holds a cell's JSON text and serializes back to exactly those bytes. Read a cell with as_json_str() (lossless), as_str(), as_array(), is_null() or kind(); to_value() returns the old serde_json::Value, with the old rounding, for code not ready to move. Comparisons against a literal become JsonCell::from(serde_json::json!(...)). serde_json is the only supported format for a JsonCell — it round-trips through a RawValue, which other serde formats do not recognise.

[0.16.0] - 2026-09-03

Changed

  • arrow feature: arrow 55 -> 59. Breaking for callers of that feature: ArrowResult hands back RecordBatch and SchemaRef, so a crate naming those types must move to arrow 59 too, or the two majors will not compile together. Without the feature, nothing changes.

[0.15.0] - 2026-09-02

Added

  • Fork lineage. databases().lineage(database_id, forks_limit) wraps the new GET /v1/databases/{database_id}/lineage — a database's ancestor chain and the databases forked directly from it (DatabaseLineageResponse, LineageAncestorInfo, LineageForkInfo). Database create/fork/get/list responses carry an optional forked_from: ForkedFromInfo recording the source's id, its label at fork time, the copied snapshot_id, and forked_at. Forks created before the server recorded lineage carry none.
  • databases().lookup_by_name(name) wraps GET /v1/databases/by-name: fetch a database by its exact name — 404 when none, 409 when the name is shared.
  • CreateDatabaseRequest.if_not_exists and the CreateDatabaseResponse.created flag it reports through; GetResultResponse.total_row_count; optional key_determines on managed-table declarations.

Changed

  • Fork naming: an omitted fork name now defaults to the source's label plus a short suffix derived from the fork's own id, so the two stay distinguishable.
  • docs: adopt "instant database" terminology in doc comments and test comments; public API names (e.g. load_managed_table, source_type: managed) are unchanged.

Removed

  • Breaking: the Secrets, Refresh, and Connection Types endpoints were retired from the HotData API and are gone from the SDK: client.secrets(), client.refresh(), client.connection_types(), the SecretsApi, RefreshApi, and ConnectionTypesApi handles, the underlying apis::secrets_api, apis::refresh_api, and apis::connection_types_api modules, and their request/response models.
  • Breaking: connections().check_health / connections_api::check_connection_health and ConnectionHealthResponse are removed.
  • Breaking: the table/connection cache-purge endpoints are gone: connections().purge_cache, connections().purge_table_cache, and the generated connections_api::purge_connection_cache / connections_api::purge_table_cache functions.
  • Breaking: the JobType::DataRefreshTable / JobType::DataRefreshConnection and JobResult::TableRefreshResult / JobResult::ConnectionRefreshResult variants are removed. Note JobResult::default() now produces IndexInfoResponse (previously TableRefreshResult).

[0.14.0] - 2026-08-20

Changed

  • feat(query): add dialect parameter to query request

[0.13.0] - 2026-08-18

Added

  • The pluggable per-request bearer hook is back, for consumers that own their own credential lifecycle. Configuration::token_provider takes an Option<Arc<dyn hotdata::auth::BearerTokenProvider>>, and Configuration::resolve_bearer_token resolves the credential once per request — every generated op plus the hand-written submit_query, upload_file (create-session and finalize), and Arrow fetch. A host whose access token is short-lived (e.g. the CLI's PKCE browser-login session, which expires in minutes) can now refresh mid-command instead of 401ing on a long call — a multi-gigabyte upload whose finalize lands after the TTL, a slow query, a large parallel batch. The trait's error type is BearerTokenError (Transport/Status/Malformed, #[non_exhaustive]); a provider that returns an error is logged via the log facade and the request proceeds unauthenticated; that warning is the only trace of the cause, and it requires a log implementation installed in the host binary to be visible.

    "Per request" includes each attempt of a 429 retry chain: the retry helper re-resolves rather than replaying the Authorization header from the first attempt, which could otherwise outlive a short-lived token (the retry deadline defaults to 120s and an honored Retry-After is uncapped). Presigned storage PUTs are excluded by construction — they authorize via the signed URL and must never carry a bearer.

    Additive and non-breaking. bearer_access_token keeps working exactly as it did in 0.12.0 when no provider is installed, and ClientBuilder::api_token still installs the API token there — no caller needs to change anything.

    This does not reintroduce the API-token to JWT exchange removed in 0.12.0. TokenManager, the /v1/auth/jwt call, ClientBuilder::client_id, PersistCallback, and HOTDATA_DISABLE_JWT_EXCHANGE stay removed. A provider is a hook, not an exchange: the SDK never trades one credential for another.

[0.12.0] - 2026-08-18

Removed

  • Breaking: the API-token to JWT key exchange is deprecated and removed. Clients now authenticate with the API token itself, sent verbatim as Authorization: Bearer <token>; the SDK no longer calls /v1/auth/jwt, mints or refreshes short-lived JWTs, or holds a refresh token. This drops the whole hotdata::auth module (TokenManager, TokenManagerOptions, BearerTokenProvider, TokenExchangeError, PersistCallback, CLIENT_ID), the Configuration::token_provider field, the Configuration::resolve_bearer_token method, ClientBuilder::client_id, and the HOTDATA_DISABLE_JWT_EXCHANGE opt-out. ClientBuilder::api_token now installs the token as Configuration::bearer_access_token; callers that only used the builder need no changes. Code that installed a custom BearerTokenProvider should set bearer_access_token directly instead.

Changed

  • feat(loads): add idempotency_key to load requests
  • feat(loads): support inline csv data in table load requests

[0.11.0] - 2026-08-12

Removed

  • Breaking: session scoping is gone from the API. ClientBuilder::session_id, the client::SESSION_ID_HEADER constant, and the HOTDATA_SESSION_ID environment variable are removed, and no request sends the X-Session-Id header any more. Drop the .session_id(..) builder call; nothing replaces it.

Changed

  • feat(databases): add bulk operations and count endpoint
  • Breaking: list_databases and Databases::list take a batch parameter, narrowing the listing to one bulk-creation batch; existing callers must pass an extra None.
  • Databases::count wraps GET /v1/databases/count and returns the number of databases in the workspace across every page. Note this is not the listing's count field, which reports how many rows a single page returned — totalling a workspace from the listing means walking every page, and pages cap at 100.
  • chore: clarify sort and partition semantics in table schemas
  • feat(tables): add partition_by and sorted_by configuration
  • QueryRunInfo::user_public_id now reports the caller's stable account id (the access token's subject) instead of a fingerprint of the bearer token, which churned every few minutes as short-lived JWTs were reminted. Grouping a caller's query history by this field now holds across token refreshes. A request that carries no verified subject still records an opaque user_-prefixed fingerprint — stable for that credential, but not resolvable to an account. This is a server-side change; the SDK only carries the field.
  • feat(databases): add search parameter to list endpoint

[0.10.0] - 2026-07-23

Changed

  • Breaking: list_databases and Databases::list now take limit and cursor pagination parameters; existing callers must pass None, None.
  • Pagination metadata on ListDatabasesResponse (count, limit, has_more) is now nullable so the client tolerates responses from a server that predates these fields (rolling deploy / version skew).

Removed

  • Breaking: the POST /v1/files and GET /v1/files endpoints are gone in favor of the presigned uploads flow. This drops the generated upload_file / list_uploads ops, the UploadResponse / UploadInfo / ListUploadsResponse models, and the ergonomic Client::upload_stream, uploads().upload, and uploads().list wrappers. Use Client::upload_file (presigned direct-to-storage) instead.

Added

  • LoadManagedTableRequest gains an optional key field naming the key columns for delete / update / upsert loads.

[0.9.1] - 2026-07-15

Added

  • Ergonomic Client::databases().fork() wrapper for the fork_database endpoint.

[0.9.0] - 2026-07-15

Changed

  • Breaking: databases now report the schema that unqualified table names resolve to, as a required default_schema field on DatabaseSummary, DatabaseDetailResponse, and CreateDatabaseResponse. Because the field is required, each type's new() gained a default_schema parameter: DatabaseSummary::new(default_catalog, default_schema, id), DatabaseDetailResponse::new(attachments, default_catalog, default_connection_id, default_schema, id), and CreateDatabaseResponse::new(default_catalog, default_connection_id, default_schema, id). Callers that construct these types (or match on them exhaustively) must supply it; callers that only read them are unaffected.
  • feat(databases): add fork endpoint
  • chore(api): exclude datasets from public OpenAPI spec + docs cleanup

Added

  • CreateDatabaseRequest gains an optional default_schema field, naming the schema unqualified table names resolve to inside the new database's query scope. When omitted, a database declaring exactly one schema adopts that schema; otherwise unqualified names resolve to main.
  • AddManagedTableDecl, AddManagedTableRequest, and DatabaseDefaultTableDecl gain an optional key field naming the columns that uniquely identify a row. Declaring a key enables the key-based load modes (delete, update, upsert) on that table, which match rows by those columns; a table declared without one accepts only replace and append.

[0.8.1] - 2026-07-09

Changed

  • Breaking: LoadManagedTableRequest can now load from a persisted query result as well as an upload, so upload_id became optional and moved out of the constructor: new(mode, upload_id) is now new(mode). Set upload_id or result_id on the returned value — exactly one is required. Note this shipped in a patch release, which Cargo treats as compatible with 0.8.0 for a 0.x crate, so it could surface on cargo update rather than an explicit upgrade.
  • feat(tables): support loading from query results
  • feat(databases): expose created_at on list and detail endpoints

[0.8.0] - 2026-07-07

Changed

  • feat: support async table loads and append mode
  • Breaking: results and query runs are now scoped to a database via the required X-Database-Id header. The ergonomic wrappers gain a database_id argument to match: Client::get_result, Client::list_results, Client::list_query_runs, Client::await_result, Client::get_result_arrow, Client::stream_result_arrow, Client::query_to_arrow, and the results() / query_runs() resource handles. Client::query's truncation auto-follow now forwards the query's database scope (the X-Database-Id header, or the request-body database_id when no header is set) to the follow-up result and query-run fetches.

[0.7.0] - 2026-06-30

Added

  • Multipart uploads now survive transient per-part failures. A single part exhausting its inner transport retries no longer aborts the whole transfer: an outer round loop re-sweeps just the failed parts (at decaying concurrency, with backoff) while completed parts keep their ETags, so a flaky or slow link recovers instead of discarding the work already done. Each part PUT also gets a part-size-scaled total timeout (bounded by an operational ceiling), so a silently stalled connection fails fast rather than hanging the upload.

Changed

  • Streaming multipart uploads now mint one presigned part URL per part, on demand immediately before each PUT, instead of pre-minting in batches. This keeps each URL's age minimal so it can't expire mid-transfer on a slow link, at the cost of more POST /v1/uploads/{id}/parts requests for large uploads.

Removed

  • uploads::MAX_MINT_BATCH (pub const), obsoleted by per-part minting. No longer part of the public API.

[0.6.0] - 2026-06-29

Added

  • Streaming uploads with just-in-time part minting for large files. Client::upload_file now opens a streaming multipart session for files past the multipart threshold and mints presigned part URLs on demand (via POST /v1/uploads/{id}/parts) immediately before each chunk uploads, instead of pre-minting every URL when the session opens. A part URL that expires mid-transfer (storage 403) is transparently re-minted and the chunk retried, so large or slow uploads that outlive a presigned URL's ~30-minute TTL still complete within the session's 24-hour window rather than failing partway. Small files keep the single-PUT fast path. (Fixes #76.)
  • Low-level POST /v1/uploads/{id}/parts part-minting endpoint with its MintUploadPartsRequest / MintUploadPartsResponse / MintedUploadPartResponse models, generated from the OpenAPI spec.

Changed

  • Token exchange (POST /v1/auth/jwt) now retries transient failures before giving up: a momentary 5xx or a transport error (connection/read failure) is retried with bounded exponential backoff + jitter (3 attempts total), so a brief server-side blip no longer fails the caller outright. A 4xx (bad/expired credential) is never retried, and the last status/body is preserved once the budget is exhausted. Applies to both the initial mint and the refresh path.

Fixed

  • The default User-Agent is now computed from the crate version at build time (CARGO_PKG_VERSION) instead of a hardcoded string, so it always reflects the published version.

[0.5.0] - 2026-06-26

Added

  • Ergonomic presigned (direct-to-storage) file uploads: Client::upload_file (and client.uploads().upload_file) open an upload session, PUT the bytes straight to object storage — a single PUT for small files, bounded- concurrency multipart PUTs sliced by the server's part_size for large ones — then finalize, returning the FinalizeUploadResponse. Configurable via UploadOptions (content type/encoding, filename, part-size hint, and an UploadProgress callback). Never falls back to the legacy POST /v1/files proxy; storage PUTs carry no SDK auth/scope headers. Multipart concurrency is tunable via UploadOptions::max_concurrency (default 10), bounded by a 256 MiB peak-memory budget derived from the server's actual part size; when no part_size is given, the SDK auto-scales the hint (8 MiB for normal files, larger only past ~72 GiB to keep the part count under S3's 10,000-part limit). Finalize is exactly-once (sent with retries disabled so an ambiguous failure can't be retried into a spurious "already finalized" error); part PUTs stay retryable. Storage PUTs use a dedicated header-bare reqwest client, so a host app's default headers on the SDK's main client never leak to object storage. The multipart session shape is validated (part_urls count must match the file's part count) and pathological sizes (> i64::MAX) are rejected rather than silently wrapped.
  • Low-level file upload endpoints generated from the OpenAPI spec, including the presigned upload-session and finalize operations that upload_file builds on.

[0.4.0] - 2026-06-19

Removed

  • Breaking: the datasets API and all related types, following their removal from the Hotdata OpenAPI spec. This drops the datasets_api module and the client.datasets() resource handle; every dataset model (CreateDatasetRequest/CreateDatasetResponse, GetDatasetResponse, UpdateDatasetRequest/UpdateDatasetResponse, ListDatasetsResponse, ListDatasetVersionsResponse, RefreshDatasetResponse, DatasetSummary, DatasetVersionSummary, DatasetSource and its *OneOf* variants, InlineDatasetSource, SavedQueryDatasetSource, SqlQueryDatasetSource, UploadDatasetSource, UrlDatasetSource, InlineData, ColumnTypeSpec); and the dataset-related JobType variants (DatasetRefresh, CreateDatasetIndex).

Fixed

  • Pre-response connection errors are now retried transparently on any method, including POST. A pooled keep-alive socket that an intermediary closed on its idle timeout surfaces, on the next reuse, as a connection reset before the request reaches the server; since the server did no work, the retry can't double-execute. This covers every generated op (via execute_retrying) and the hand-written Client::query / Client::submit_query paths, governed by the same RetryPolicy budget as 429. Response-phase transport errors are left un-retried so a non-idempotent POST can't double-execute (#63).

[0.3.2] - 2026-06-18

Changed

  • feat(indexes): add source_column field to index responses

[0.3.1] - 2026-06-16

Fixed

  • Client::submit_query (the hand-written 202/async query path the CLI drives directly) now retries HTTP 429 (OVERLOADED) admission shedding per Configuration::retry, like every generated op and Client::query. It was the one query path the 0.3.0 migration missed, so under admission shedding a submitted query surfaced the 429 as an error with no retry (#688).

[0.3.0] - 2026-06-16

Added

  • Every generated apis::* operation now transparently retries HTTP 429 (OVERLOADED) admission shedding, honoring Retry-After with backoff before the op returns (#58). The policy is the new Configuration::retry field (crate::query::RetryPolicy, defaulting to RetryPolicy::default); set max_retries to 0 to disable. The enhanced query path (crate::query) keeps using its own per-call QueryConfig::retry instead.

[0.2.0] - 2026-06-15

Added

  • Enhanced Client::query: transparently retries HTTP 429 (OVERLOADED) admission shedding honoring Retry-After under a deadline budget, and auto-follows truncated results to materialize the full row set, guarded by configurable max_auto_rows (default 1M) and max_auto_bytes (default 64 MiB) ceilings (#688). The raw generated op remains reachable via client.queries().execute() / hotdata::apis::query_api::query.
  • hotdata::query module with QueryConfig, RetryPolicy, PollPolicy, the QueryError enum, and the ResultError family (Failed / Timeout / TooLarge / Incomplete / Unavailable). ClientBuilder::query_config sets the instance default; Client::query_with takes a per-call override.
  • Client::query_in (scope a query to a database) and Client::query_preview (return the bounded preview without auto-following), plus QueryConfig::with_* setters for fluent per-call overrides (client.query_config().clone().with_auto_follow(false)).

Changed

  • Client::query now returns Result<QueryResponse, hotdata::QueryError> instead of Result<QueryResponse, Error<QueryError>> to carry the overload and result-lifecycle errors the bounded-memory query contract introduces. Migration: where you matched the old error, e.g. Err(Error::ResponseError(rc)), now match Err(QueryError::Submit( Error::ResponseError(rc))); overload and auto-follow failures arrive as the new QueryError::Overloaded / QueryError::Result(..) variants.
  • Client::query rejects an explicit async = true request up front with QueryError::AsyncRequested (it is the synchronous-results path); use Client::submit_query for asynchronous submissions.

[0.1.4] - 2026-06-15

Changed

  • feat(queries): add preview and total row count fields

[0.1.3] - 2026-06-09

Added

  • list_indexes_collection (GET /v1/indexes) in hotdata::apis::indexes_api — lists indexes across every table in a database, scoped by the X-Database-Id header — along with the IndexEntryResponse and ListIndexesPageResponse models.

Removed

  • Sandbox endpoints and their models (sandboxes_api, Sandbox, SandboxResponse, CreateSandboxRequest, UpdateSandboxRequest, ListSandboxesResponse, DeleteSandboxResponse), following their removal from the Hotdata OpenAPI spec.

Changed

  • Regenerated the client from the updated Hotdata OpenAPI spec.

[0.1.2] - 2026-06-06

Added

  • Database and managed-catalog schema/table management endpoints: add_database_schema, add_database_table, add_managed_schema, and add_managed_table, with their request/response models (AddManagedSchemaRequest, AddManagedTableRequest, AddManagedTableDecl, ManagedSchemaResponse, ManagedTableResponse).

Changed

  • Regenerated the client from the updated Hotdata OpenAPI spec, catching up on spec changes since 0.1.1. Generated with openapi-generator 7.22.0 (useChrono=false, so date-time fields remain String as before).

[0.1.1] - 2026-06-05

Changed

  • Populate crate metadata (repository, homepage, documentation, readme, keywords, categories) for the crates.io listing, and link the Hotdata CLI from the README.
  • Publishing to crates.io now uses Trusted Publishing (OIDC) instead of a stored API token.

[0.1.0] - 2026-06-05

Added

  • Ergonomic Client and ClientBuilder (hotdata::Client) wrapping the generated Configuration: set an API token and workspace id, with thin async pass-throughs for query, list_query_runs, list_results, get_result, and list_workspaces.
  • Grouped resource handles on Client (client.datasets(), client.secrets(), client.query_runs(), … one per API) so callers no longer import hotdata::apis::*_api free functions or thread a &Configuration through every call.
  • Query convenience helpers: Client::await_result polls a persisted result to ready (configurable via PollConfig) instead of a hand-rolled loop, and Client::query_to_arrow submits, awaits, and decodes a result as Arrow in one call (arrow feature).
  • hotdata::field helpers (set / clear / unchanged) for the Option<Option<T>> nullable-and-optional update fields, so Some(Some(v)) / Some(None) intents read clearly.
  • Typed ResultStatus / QueryRunStatus enums (hotdata::status) with result_status() / run_status() accessor traits on the response types. Each carries an Other(String) catch-all so unrecognized server statuses round-trip instead of failing — matching runtimedb's lenient parsing, where a generated closed enum would not.
  • Transparent API-token to JWT exchange via a hand-written TokenManager (hotdata::auth): hd_ API tokens are exchanged against /v1/auth/jwt, cached, refreshed, and re-minted automatically, with a 30s expiry leeway and single-flight concurrency. JWTs (eyJ…) are passed through unchanged. Honors HOTDATA_DISABLE_JWT_EXCHANGE.
  • Pluggable BearerTokenProvider hook on Configuration so any async token source can drive bearer auth.
  • Optional arrow feature: get_result_arrow / stream_result_arrow decode Apache Arrow IPC result streams into RecordBatches, surface the X-Total-Row-Count and rel="next" Link headers, and map the 202/409/404/400 result states to typed ArrowError variants.
  • Flat re-export surface (hotdata::Client, hotdata::Configuration, hotdata::prelude::*) alongside the namespaced hotdata::apis / hotdata::models. The prelude also re-exports the resource handles, PollConfig, the field helpers, and (with the arrow feature) ArrowError.
  • The SDK's own error enums (ClientError, TokenExchangeError, ArrowError, AwaitResultError, QueryToArrowError) are #[non_exhaustive], so new variants can be added without a breaking change — match them with a wildcard arm.
  • Request/response debug logging (hotdata::http_log) covering every HTTP call — generated ops plus the hand-written submit_query / upload_stream / Arrow fetch / JWT mint. Each emits log::debug! records on the hotdata::http target (>>> METHOD url, headers, body; <<< status, body) so a host (e.g. the CLI's --debug) can render them with any log backend. Authorization bearer tokens and sensitive JSON/form fields (api_token, secret, password, …) are masked before logging; the SDK installs no logger and stays silent without a backend. The api.mustache template emits the hooks so they survive regeneration, and the regen-safety CI guard verifies they remain.

Changed

  • Regeneration is now safe for the hand-written ergonomic layer: the generator only overwrites generated subtrees (src/apis, src/models, docs) and skips src/lib.rs, src/auth.rs, src/http_log.rs, src/arrow.rs, src/client.rs, src/resources.rs, src/field.rs, and Cargo.toml via .openapi-generator-ignore. The regen-safety CI guard verifies all of these survive and stay wired into lib.rs.
  • Initial generated client from the Hotdata OpenAPI spec.