diff --git a/open-api/rest-catalog-open-api.py b/open-api/rest-catalog-open-api.py index 7b6c4f73fc9e..c0b47716f64a 100644 --- a/open-api/rest-catalog-open-api.py +++ b/open-api/rest-catalog-open-api.py @@ -1123,6 +1123,317 @@ class RemoteSignResult(BaseModel): headers: MultiValuedMap +class IndexIdentifier(BaseModel): + """ + Identifies an index within a catalog, scoped to a namespace. An index is addressable by namespace and name without a table name; the indexed table is referenced by `table-uuid` in the index metadata. + """ + + namespace: Namespace + name: str = Field(..., description='The index name, unique within the namespace') + + +class IndexType(RootModel[Literal['scalar']]): + root: Literal['scalar'] = Field( + ..., + description='The logical category of an index, defining the class of queries it can accelerate. Only `scalar` is defined so far (a key lookup index); additional types such as `vector` and `term` are reserved for future specifications.', + examples=['scalar'], + ) + + +class IndexTransform(RootModel[str]): + root: str = Field( + ..., + description='The transform function used to derive the index organization key from the key columns. It determines the physical organization of the index data and therefore which query patterns the index can accelerate. The set of transforms is extensible; `identity`, `hash`, and `hilbert` are the currently described functions (`ivf` is reserved for future specifications).', + examples=['hash'], + ) + + +class IndexSummary(BaseModel): + """ + A lightweight summary of an index for discovery and optimization + """ + + identifier: IndexIdentifier + table_uuid: UUID = Field( + ..., + alias='table-uuid', + description='UUID of the table this index is associated with', + ) + type: IndexType + transform_function: IndexTransform = Field(..., alias='transform-function') + key_column_ids: list[int] = Field( + ..., + alias='key-column-ids', + description='IDs of the indexed (key) columns the index is optimized for retrieval', + examples=[[1]], + ) + included_column_ids: list[int] | None = Field( + None, + alias='included-column-ids', + description='IDs of additional columns stored losslessly in the index', + examples=[[2, 3]], + ) + available_table_snapshots: list[int] = Field( + ..., + alias='available-table-snapshots', + description='Table snapshot IDs for which this index has valid snapshots', + examples=[[1234567890123, 1234567890456]], + ) + + +class IndexSnapshotLogEntry(BaseModel): + """ + An entry in the index snapshot log, tracking changes to current-snapshot-id + """ + + timestamp_ms: int = Field( + ..., + alias='timestamp-ms', + description='Timestamp when current-snapshot-id was updated (ms since epoch)', + ) + snapshot_id: int = Field( + ..., + alias='snapshot-id', + description='The index snapshot ID that current-snapshot-id was set to', + ) + + +class IndexSnapshot(BaseModel): + """ + An immutable version of the index data, derived from a specific table snapshot + """ + + snapshot_id: int = Field( + ..., + alias='snapshot-id', + description='Unique identifier for this index snapshot', + ) + source_table_snapshot_id: int = Field( + ..., + alias='source-table-snapshot-id', + description='The table snapshot ID this index snapshot is derived from', + ) + timestamp_ms: int = Field( + ..., + alias='timestamp-ms', + description='Timestamp when this snapshot was created (ms since epoch)', + ) + tracking_file: str = Field( + ..., + alias='tracking-file', + description='Location of the tracking file for this snapshot. The tracking file describes the leaf files belonging to the snapshot and enables planning without scanning every leaf file.', + ) + properties: dict[str, str] | None = Field( + None, + description='Properties for this index snapshot, typically set by the index maintenance process', + ) + + +class IndexMetadata(BaseModel): + """ + Full metadata for an index + """ + + format_version: int = Field( + ..., + alias='format-version', + description='Format version of the index metadata', + examples=[1], + ) + uuid: UUID = Field( + ..., description='Stable UUID assigned to this index at creation' + ) + table_uuid: UUID = Field( + ..., + alias='table-uuid', + description='UUID of the table this index is associated with', + ) + name: str = Field( + ..., description='The name of the index, unique within the namespace' + ) + type: IndexType + transform_function: IndexTransform = Field(..., alias='transform-function') + location: str = Field(..., description='Base location for index files') + key_column_ids: list[int] = Field( + ..., + alias='key-column-ids', + description='IDs of the indexed (key) columns, in key order', + ) + included_column_ids: list[int] | None = Field( + None, + alias='included-column-ids', + description='IDs of additional columns stored losslessly in the index', + ) + file_format: str = Field( + ..., + alias='file-format', + description='The file format used in the index leaf files', + ) + key_metadata: str | None = Field( + None, + alias='key-metadata', + description='Implementation-specific key metadata for encryption, base64 encoded', + ) + properties: dict[str, str] | None = Field(None, description='Index properties') + current_snapshot_id: int = Field( + ..., + alias='current-snapshot-id', + description='ID of the current index snapshot; must be present in snapshots', + ) + snapshots: list[IndexSnapshot] | None = Field( + None, description='List of known index snapshots' + ) + snapshot_log: list[IndexSnapshotLogEntry] | None = Field( + None, + alias='snapshot-log', + description='Log of changes to current-snapshot-id over time', + ) + + +class CreateIndexRequest(BaseModel): + """ + Request to create a new index + """ + + name: str = Field( + ..., description='The name for the new index', examples=['customer_id_index'] + ) + type: IndexType + transform_function: IndexTransform = Field(..., alias='transform-function') + table_uuid: UUID = Field( + ..., alias='table-uuid', description='The UUID of the table to index' + ) + key_column_ids: list[int] = Field( + ..., + alias='key-column-ids', + description='IDs of the indexed (key) columns', + examples=[[1]], + ) + included_column_ids: list[int] | None = Field( + None, + alias='included-column-ids', + description='IDs of additional columns to store losslessly in the index', + examples=[[2, 3]], + ) + location: str | None = Field( + None, description='Optional base location for index files' + ) + file_format: str | None = Field( + None, alias='file-format', description='Optional leaf file format' + ) + properties: dict[str, str] | None = Field( + None, description='Index-specific properties' + ) + snapshot_id: int | None = Field( + None, + alias='snapshot-id', + description='Optional identifier for the initial index snapshot', + ) + source_table_snapshot_id: int | None = Field( + None, + alias='source-table-snapshot-id', + description='The table snapshot ID the initial index snapshot is derived from', + ) + snapshot_properties: dict[str, str] | None = Field( + None, + alias='snapshot-properties', + description='Properties for the initial index snapshot, typically set by the index maintenance process', + ) + + +class RegisterIndexRequest(BaseModel): + """ + Request to register an existing index by metadata file location + """ + + name: str = Field(..., description='The name to register the index under') + metadata_location: str = Field( + ..., + alias='metadata-location', + description='Location of the index metadata file', + ) + + +class IndexRequirement(BaseModel): + """ + A requirement that must be met for an index commit to succeed + """ + + type: Literal['assert-index-uuid', 'assert-table-snapshot-id'] = Field( + ..., description='The type of requirement' + ) + uuid: UUID | None = Field(None, description='Required for assert-index-uuid') + table_snapshot_id: int | None = Field( + None, + alias='table-snapshot-id', + description='Required for assert-table-snapshot-id', + ) + + +class AssertIndexUUID(IndexRequirement): + uuid: UUID = Field(..., description='Required for assert-index-uuid') + + +class AssertTableSnapshotId(IndexRequirement): + table_snapshot_id: int = Field( + ..., + alias='table-snapshot-id', + description="The base-table snapshot ID the index snapshot is being built against. The commit MUST fail (409 CommitFailedException) if the table's current snapshot no longer matches, so a maintenance cycle cannot publish an index snapshot that is already stale.", + ) + + +class IndexUpdate(BaseModel): + """ + An update to apply to an index + """ + + action: Literal[ + 'upgrade-format-version', + 'add-snapshot', + 'remove-snapshots', + 'set-current-snapshot-id', + 'set-location', + ] = Field(..., description='The type of update') + + +class UpgradeIndexFormatVersionUpdate(IndexUpdate): + format_version: int = Field(..., alias='format-version') + + +class AddIndexSnapshotUpdate(IndexUpdate): + snapshot: IndexSnapshot + + +class RemoveIndexSnapshotsUpdate(IndexUpdate): + snapshot_ids: list[int] = Field(..., alias='snapshot-ids') + + +class SetCurrentIndexSnapshotUpdate(IndexUpdate): + snapshot_id: int = Field( + ..., + alias='snapshot-id', + description='The index snapshot id to set as current, or -1 to set the last added index snapshot id', + ) + + +class SetIndexLocationUpdate(IndexUpdate): + location: str + + +class LoadIndexResult(BaseModel): + """ + Result for loading an index + """ + + metadata_location: str | None = Field( + None, + alias='metadata-location', + description='The location of the index metadata file', + ) + metadata: IndexMetadata + config: dict[str, str] | None = None + + class CreateNamespaceRequest(BaseModel): namespace: Namespace properties: dict[str, str] | None = Field( @@ -1249,6 +1560,26 @@ class FetchScanTasksRequest(BaseModel): plan_task: PlanTask = Field(..., alias='plan-task') +class CommitIndexRequest(BaseModel): + """ + Request to commit updates to an index + """ + + requirements: list[AssertIndexUUID | AssertTableSnapshotId] | None = Field( + None, description='Requirements that must be met before applying updates' + ) + updates: ( + list[ + UpgradeIndexFormatVersionUpdate + | AddIndexSnapshotUpdate + | RemoveIndexSnapshotsUpdate + | SetCurrentIndexSnapshotUpdate + | SetIndexLocationUpdate + ] + | None + ) = Field(None, description='Updates to apply to the index') + + class Term(RootModel[Reference | TransformTerm]): root: Reference | TransformTerm @@ -1582,6 +1913,10 @@ class LoadTableResult(BaseModel): storage_credentials: list[StorageCredential] | None = Field( None, alias='storage-credentials' ) + indexes: list[IndexSummary] | None = Field( + None, + description='Optional index summaries for this table, returned only when the client requests them via the `include-indexes` query parameter on the load-table endpoint. Each entry is sufficient for a query optimizer to decide whether an index is applicable; full metadata is fetched via the loadIndex endpoint. Clients that do not understand indexes MUST ignore this field.', + ) class ScanTasks(BaseModel): diff --git a/open-api/rest-catalog-open-api.yaml b/open-api/rest-catalog-open-api.yaml index 990f2285e53b..9682d071cbaf 100644 --- a/open-api/rest-catalog-open-api.yaml +++ b/open-api/rest-catalog-open-api.yaml @@ -1075,6 +1075,18 @@ paths: type: string enum: [ all, refs ] - $ref: '#/components/parameters/referenced-by' + - in: query + name: include-indexes + description: + If `true`, the catalog SHOULD populate the `indexes` field in the + LoadTableResult with index summaries for the table, avoiding a second + round-trip for query planning. Defaults to `false`. Catalogs that do not + support indexes MAY ignore this parameter, and clients that do not + understand indexes MUST ignore the `indexes` field if it is present. + required: false + schema: + type: boolean + default: false responses: 200: $ref: '#/components/responses/LoadTableResponse' @@ -1299,6 +1311,332 @@ paths: 5XX: $ref: '#/components/responses/ServerErrorResponse' + /v1/{prefix}/namespaces/{namespace}/indexes: + parameters: + - $ref: '#/components/parameters/prefix' + - $ref: '#/components/parameters/namespace' + + get: + tags: + - Index Catalog API + summary: List all indexes in a namespace, optionally filtered by type + operationId: listIndexes + description: + Return a list of index summaries for the specified namespace. The list can be + filtered by index type(s) using the `types` query parameter. This enables + query optimizers to discover the indexes available in a given namespace. + parameters: + - name: types + in: query + description: + Optional comma-separated list of index types to filter by. If not + provided, all indexes are returned. + required: false + schema: + type: array + items: + $ref: '#/components/schemas/IndexType' + style: form + explode: false + example: "scalar" + - $ref: '#/components/parameters/page-token' + - $ref: '#/components/parameters/page-size' + responses: + 200: + $ref: '#/components/responses/ListIndexesResponse' + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The namespace does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchNamespaceError: + $ref: '#/components/examples/NoSuchNamespaceError' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + post: + tags: + - Index Catalog API + summary: Create an index on a table + operationId: createIndex + description: + Create a new index on the specified table. The index type, column IDs, and + other configuration must be provided in the request body. + parameters: + - $ref: '#/components/parameters/idempotency-key' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateIndexRequest' + responses: + 200: + $ref: '#/components/responses/LoadIndexResponse' + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The table or namespace does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchTableError: + $ref: '#/components/examples/NoSuchTableError' + NoSuchNamespaceError: + $ref: '#/components/examples/NoSuchNamespaceError' + 409: + description: Conflict - An index with the same name already exists + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + IndexAlreadyExists: + $ref: '#/components/examples/IndexAlreadyExistsError' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + /v1/{prefix}/namespaces/{namespace}/indexes/{index}: + parameters: + - $ref: '#/components/parameters/prefix' + - $ref: '#/components/parameters/namespace' + - $ref: '#/components/parameters/index' + + get: + tags: + - Index Catalog API + summary: Load an index from the catalog + operationId: loadIndex + description: + Load the full metadata for an index. The response contains the complete index + definition including versions, snapshots, and history. + responses: + 200: + $ref: '#/components/responses/LoadIndexResponse' + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The index or namespace does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchIndexError: + $ref: '#/components/examples/NoSuchIndexError' + NoSuchNamespaceError: + $ref: '#/components/examples/NoSuchNamespaceError' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + post: + tags: + - Index Catalog API + summary: Commit updates to an index + operationId: updateIndex + description: + Commit updates to an index. This can be used to add new versions, snapshots, + or update index metadata. The request includes requirements that must be met + and updates to apply, and is committed atomically. + parameters: + - $ref: '#/components/parameters/idempotency-key' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommitIndexRequest' + responses: + 200: + $ref: '#/components/responses/LoadIndexResponse' + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The index or namespace does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchIndexError: + $ref: '#/components/examples/NoSuchIndexError' + NoSuchNamespaceError: + $ref: '#/components/examples/NoSuchNamespaceError' + 409: + description: Conflict - CommitFailedException, one or more requirements failed + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 500: + description: + An unknown server-side problem occurred; the commit state is unknown. + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + example: { + "error": { + "message": "Internal Server Error", + "type": "CommitStateUnknownException", + "code": 500 + } + } + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + delete: + tags: + - Index Catalog API + summary: Drop an index from the catalog + operationId: dropIndex + description: Remove an index from the catalog. This deletes the index and all its data. + parameters: + - $ref: '#/components/parameters/idempotency-key' + responses: + 204: + description: Success, no content + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The index does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchIndexError: + $ref: '#/components/examples/NoSuchIndexError' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + head: + tags: + - Index Catalog API + summary: Check if an index exists + operationId: indexExists + description: Check if an index exists in a given namespace. The response does not contain a body. + responses: + 204: + description: Success, no content - Index exists + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - Index not found + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + + /v1/{prefix}/namespaces/{namespace}/register-index: + parameters: + - $ref: '#/components/parameters/prefix' + - $ref: '#/components/parameters/namespace' + + post: + tags: + - Index Catalog API + summary: Register an index using a metadata file location + operationId: registerIndex + description: + Register an index in the catalog using a given metadata file location. This + allows importing an existing index from its metadata file. + parameters: + - $ref: '#/components/parameters/idempotency-key' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterIndexRequest' + responses: + 200: + $ref: '#/components/responses/LoadIndexResponse' + 400: + $ref: '#/components/responses/BadRequestErrorResponse' + 401: + $ref: '#/components/responses/UnauthorizedResponse' + 403: + $ref: '#/components/responses/ForbiddenResponse' + 404: + description: Not Found - The table or namespace does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + NoSuchTableError: + $ref: '#/components/examples/NoSuchTableError' + NoSuchNamespaceError: + $ref: '#/components/examples/NoSuchNamespaceError' + 409: + description: Conflict - An index with the same identifier already exists + content: + application/json: + schema: + $ref: '#/components/schemas/IcebergErrorResponse' + examples: + IndexAlreadyExists: + $ref: '#/components/examples/IndexAlreadyExistsError' + 419: + $ref: '#/components/responses/AuthenticationTimeoutResponse' + 503: + $ref: '#/components/responses/ServiceUnavailableResponse' + 5XX: + $ref: '#/components/responses/ServerErrorResponse' + /v1/{prefix}/namespaces/{namespace}/tables/{table}/unregister: parameters: - $ref: '#/components/parameters/prefix' @@ -2247,6 +2585,15 @@ components: - If Idempotency-Key is used, clients MUST reuse the same key when retrying the same logical operation and MUST generate a new key for a different operation. + index: + name: index + in: path + description: An index name + required: true + schema: + type: string + example: "customer_id_index" + ############################## # Application Schema Objects # ############################## @@ -3701,6 +4048,16 @@ components: type: array items: $ref: '#/components/schemas/StorageCredential' + indexes: + type: array + description: + Optional index summaries for this table, returned only when the client + requests them via the `include-indexes` query parameter on the load-table + endpoint. Each entry is sufficient for a query optimizer to decide whether + an index is applicable; full metadata is fetched via the loadIndex endpoint. + Clients that do not understand indexes MUST ignore this field. + items: + $ref: '#/components/schemas/IndexSummary' ScanTasks: type: object @@ -5247,6 +5604,444 @@ components: headers: $ref: '#/components/schemas/MultiValuedMap' + IndexIdentifier: + type: object + description: + Identifies an index within a catalog, scoped to a namespace. An index is + addressable by namespace and name without a table name; the indexed table is + referenced by `table-uuid` in the index metadata. + required: + - namespace + - name + properties: + namespace: + $ref: '#/components/schemas/Namespace' + name: + type: string + description: The index name, unique within the namespace + example: + namespace: ["accounting", "tax"] + name: "customer_id_index" + + IndexType: + type: string + description: + The logical category of an index, defining the class of queries it can + accelerate. Only `scalar` is defined so far (a key lookup index); additional + types such as `vector` and `term` are reserved for future specifications. + enum: + - scalar + example: "scalar" + + IndexTransform: + type: string + description: + The transform function used to derive the index organization key from the + key columns. It determines the physical organization of the index data and + therefore which query patterns the index can accelerate. The set of transforms + is extensible; `identity`, `hash`, and `hilbert` are the currently described + functions (`ivf` is reserved for future specifications). + example: "hash" + + IndexSummary: + type: object + description: A lightweight summary of an index for discovery and optimization + required: + - identifier + - table-uuid + - type + - transform-function + - key-column-ids + - available-table-snapshots + properties: + identifier: + $ref: '#/components/schemas/IndexIdentifier' + table-uuid: + type: string + format: uuid + description: UUID of the table this index is associated with + type: + $ref: '#/components/schemas/IndexType' + transform-function: + $ref: '#/components/schemas/IndexTransform' + key-column-ids: + type: array + description: IDs of the indexed (key) columns the index is optimized for retrieval + items: + type: integer + example: [1] + included-column-ids: + type: array + description: IDs of additional columns stored losslessly in the index + items: + type: integer + example: [2, 3] + available-table-snapshots: + type: array + description: Table snapshot IDs for which this index has valid snapshots + items: + type: integer + format: int64 + example: [1234567890123, 1234567890456] + + IndexSnapshotLogEntry: + type: object + description: An entry in the index snapshot log, tracking changes to current-snapshot-id + required: + - timestamp-ms + - snapshot-id + properties: + timestamp-ms: + type: integer + format: int64 + description: Timestamp when current-snapshot-id was updated (ms since epoch) + snapshot-id: + type: integer + format: int64 + description: The index snapshot ID that current-snapshot-id was set to + + IndexSnapshot: + type: object + description: An immutable version of the index data, derived from a specific table snapshot + required: + - snapshot-id + - source-table-snapshot-id + - timestamp-ms + - tracking-file + properties: + snapshot-id: + type: integer + format: int64 + description: Unique identifier for this index snapshot + source-table-snapshot-id: + type: integer + format: int64 + description: The table snapshot ID this index snapshot is derived from + timestamp-ms: + type: integer + format: int64 + description: Timestamp when this snapshot was created (ms since epoch) + tracking-file: + type: string + description: + Location of the tracking file for this snapshot. The tracking file describes + the leaf files belonging to the snapshot and enables planning without + scanning every leaf file. + properties: + type: object + additionalProperties: + type: string + description: Properties for this index snapshot, typically set by the index maintenance process + + IndexMetadata: + type: object + description: Full metadata for an index + required: + - format-version + - uuid + - table-uuid + - name + - type + - transform-function + - location + - key-column-ids + - file-format + - current-snapshot-id + properties: + format-version: + type: integer + description: Format version of the index metadata + example: 1 + uuid: + type: string + format: uuid + description: Stable UUID assigned to this index at creation + table-uuid: + type: string + format: uuid + description: UUID of the table this index is associated with + name: + type: string + description: The name of the index, unique within the namespace + type: + $ref: '#/components/schemas/IndexType' + transform-function: + $ref: '#/components/schemas/IndexTransform' + location: + type: string + description: Base location for index files + key-column-ids: + type: array + description: IDs of the indexed (key) columns, in key order + items: + type: integer + included-column-ids: + type: array + description: IDs of additional columns stored losslessly in the index + items: + type: integer + file-format: + type: string + description: The file format used in the index leaf files + key-metadata: + type: string + description: Implementation-specific key metadata for encryption, base64 encoded + properties: + type: object + additionalProperties: + type: string + description: Index properties + current-snapshot-id: + type: integer + format: int64 + description: ID of the current index snapshot; must be present in snapshots + snapshots: + type: array + description: List of known index snapshots + items: + $ref: '#/components/schemas/IndexSnapshot' + snapshot-log: + type: array + description: Log of changes to current-snapshot-id over time + items: + $ref: '#/components/schemas/IndexSnapshotLogEntry' + + CreateIndexRequest: + type: object + description: Request to create a new index + required: + - name + - type + - transform-function + - table-uuid + - key-column-ids + properties: + name: + type: string + description: The name for the new index + example: "customer_id_index" + type: + $ref: '#/components/schemas/IndexType' + transform-function: + $ref: '#/components/schemas/IndexTransform' + table-uuid: + type: string + format: uuid + description: The UUID of the table to index + key-column-ids: + type: array + description: IDs of the indexed (key) columns + items: + type: integer + example: [1] + included-column-ids: + type: array + description: IDs of additional columns to store losslessly in the index + items: + type: integer + example: [2, 3] + location: + type: string + description: Optional base location for index files + file-format: + type: string + description: Optional leaf file format + properties: + type: object + additionalProperties: + type: string + description: Index-specific properties + snapshot-id: + type: integer + format: int64 + description: Optional identifier for the initial index snapshot + source-table-snapshot-id: + type: integer + format: int64 + description: The table snapshot ID the initial index snapshot is derived from + snapshot-properties: + type: object + additionalProperties: + type: string + description: Properties for the initial index snapshot, typically set by the index maintenance process + + RegisterIndexRequest: + type: object + description: Request to register an existing index by metadata file location + required: + - name + - metadata-location + properties: + name: + type: string + description: The name to register the index under + metadata-location: + type: string + description: Location of the index metadata file + + CommitIndexRequest: + type: object + description: Request to commit updates to an index + properties: + requirements: + type: array + description: Requirements that must be met before applying updates + items: + $ref: '#/components/schemas/IndexRequirement' + updates: + type: array + description: Updates to apply to the index + items: + $ref: '#/components/schemas/IndexUpdate' + + IndexRequirement: + type: object + description: A requirement that must be met for an index commit to succeed + required: + - type + properties: + type: + type: string + enum: + - assert-index-uuid + - assert-table-snapshot-id + description: The type of requirement + uuid: + type: string + format: uuid + description: Required for assert-index-uuid + table-snapshot-id: + type: integer + format: int64 + description: Required for assert-table-snapshot-id + discriminator: + propertyName: type + mapping: + assert-index-uuid: '#/components/schemas/AssertIndexUUID' + assert-table-snapshot-id: '#/components/schemas/AssertTableSnapshotId' + + AssertIndexUUID: + allOf: + - $ref: '#/components/schemas/IndexRequirement' + - type: object + required: + - uuid + properties: + uuid: + type: string + format: uuid + + AssertTableSnapshotId: + allOf: + - $ref: '#/components/schemas/IndexRequirement' + - type: object + required: + - table-snapshot-id + properties: + table-snapshot-id: + type: integer + format: int64 + description: + The base-table snapshot ID the index snapshot is being built against. + The commit MUST fail (409 CommitFailedException) if the table's current + snapshot no longer matches, so a maintenance cycle cannot publish an + index snapshot that is already stale. + + IndexUpdate: + type: object + description: An update to apply to an index + required: + - action + properties: + action: + type: string + enum: + - upgrade-format-version + - add-snapshot + - remove-snapshots + - set-current-snapshot-id + - set-location + description: The type of update + discriminator: + propertyName: action + mapping: + upgrade-format-version: '#/components/schemas/UpgradeIndexFormatVersionUpdate' + add-snapshot: '#/components/schemas/AddIndexSnapshotUpdate' + remove-snapshots: '#/components/schemas/RemoveIndexSnapshotsUpdate' + set-current-snapshot-id: '#/components/schemas/SetCurrentIndexSnapshotUpdate' + set-location: '#/components/schemas/SetIndexLocationUpdate' + + UpgradeIndexFormatVersionUpdate: + allOf: + - $ref: '#/components/schemas/IndexUpdate' + - type: object + required: + - format-version + properties: + format-version: + type: integer + + AddIndexSnapshotUpdate: + allOf: + - $ref: '#/components/schemas/IndexUpdate' + - type: object + required: + - snapshot + properties: + snapshot: + $ref: '#/components/schemas/IndexSnapshot' + + RemoveIndexSnapshotsUpdate: + allOf: + - $ref: '#/components/schemas/IndexUpdate' + - type: object + required: + - snapshot-ids + properties: + snapshot-ids: + type: array + items: + type: integer + format: int64 + + SetCurrentIndexSnapshotUpdate: + allOf: + - $ref: '#/components/schemas/IndexUpdate' + - type: object + required: + - snapshot-id + properties: + snapshot-id: + type: integer + format: int64 + description: The index snapshot id to set as current, or -1 to set the last added index snapshot id + + SetIndexLocationUpdate: + allOf: + - $ref: '#/components/schemas/IndexUpdate' + - type: object + required: + - location + properties: + location: + type: string + + LoadIndexResult: + type: object + description: Result for loading an index + required: + - metadata + properties: + metadata-location: + type: string + description: The location of the index metadata file + metadata: + $ref: '#/components/schemas/IndexMetadata' + config: + type: object + additionalProperties: + type: string + ############################# # Reusable Response Objects # ############################# @@ -5563,6 +6358,29 @@ components: schema: $ref: '#/components/schemas/RemoteSignResult' + ListIndexesResponse: + description: A list of index summaries + content: + application/json: + schema: + type: object + required: + - indexes + properties: + next-page-token: + $ref: '#/components/schemas/PageToken' + indexes: + type: array + items: + $ref: '#/components/schemas/IndexSummary' + + LoadIndexResponse: + description: The loaded index metadata + content: + application/json: + schema: + $ref: '#/components/schemas/LoadIndexResult' + ####################################### # Common examples of different values # ####################################### @@ -5765,6 +6583,26 @@ components: "updates": { "owner": "Raoul" } } + NoSuchIndexError: + summary: The requested index does not exist + value: { + "error": { + "message": "Index does not exist: customer_id_index in accounting.sales", + "type": "NoSuchIndexException", + "code": 404 + } + } + + IndexAlreadyExistsError: + summary: An index with the same name already exists + value: { + "error": { + "message": "Index already exists: customer_id_index in accounting.sales", + "type": "AlreadyExistsException", + "code": 409 + } + } + securitySchemes: OAuth2: type: oauth2