This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 24bda74d1a [Cherry-pick to branch-1.3] [MINOR] docs: update the Trino
Iceberg REST engine page (#12242) (#12250)
24bda74d1a is described below
commit 24bda74d1a711dd8275e65a5690333462cdd045d
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jul 30 19:06:53 2026 +0800
[Cherry-pick to branch-1.3] [MINOR] docs: update the Trino Iceberg REST
engine page (#12242) (#12250)
**Cherry-pick Information:**
- Original commit: 4d60cc71c2f87c4814559b9ebcb511174be4d309
- Target branch: `branch-1.3`
- Status: ✅ **Conflicts resolved**
Conflicts were resolved in dd63c8524. Verified with `./gradlew
:docs:build --rerun-tasks`.
---------
Co-authored-by: Mark Hoerth <[email protected]>
Co-authored-by: Mark Hoerth <[email protected]>
---
docs/iceberg-rest-engine/trino.md | 613 +++++++++++++++++++++++++++++---------
1 file changed, 472 insertions(+), 141 deletions(-)
diff --git a/docs/iceberg-rest-engine/trino.md
b/docs/iceberg-rest-engine/trino.md
index 2a438830d6..aafb32def3 100755
--- a/docs/iceberg-rest-engine/trino.md
+++ b/docs/iceberg-rest-engine/trino.md
@@ -1,244 +1,575 @@
---
-title: "Connect Trino to Iceberg REST"
+title: "Connect Trino to the Iceberg REST Catalog"
sidebar_label: "Trino"
---
## Introduction
-Apache Gravitino exposes an [Iceberg REST catalog](../iceberg-rest-service.md)
endpoint that any
-Iceberg-compatible engine can connect to directly — without installing a
Gravitino-specific
-connector plugin. This page describes how to configure Trino to use
Gravitino's Iceberg REST
-(IRC) endpoint.
+Apache Gravitino exposes an Iceberg REST Catalog (IRC) endpoint that any
Iceberg-compatible engine
+can connect to directly, without installing a Gravitino-specific connector
plugin. The sections
+below describe how to configure Trino to use it.
-:::note
-This integration uses the standard Apache Iceberg REST catalog specification.
Gravitino enforces
-its full access-control model on all IRC requests.
-:::
+Most of the configuration is on the Trino side and is covered here. The
storage credential setup
+lives on the Gravitino catalog and is covered in [Credential
+vending](../security/credential-vending.md), which this page links to at each
point where it
+applies.
-## Prerequisites
-
-- Apache Gravitino running with the Iceberg REST service enabled. See
- [Iceberg REST catalog service](../iceberg-rest-service.md) for setup
instructions.
-- The Gravitino IRC endpoint is accessible from the Trino coordinator and all
workers. The default
- port is `9001`.
-- Trino 469 or later recommended.
-
-## Configuration
+## Quick Start
-Create a catalog properties file in your Trino `etc/catalog/` directory. The
filename determines
-the catalog name in Trino — `gravitino_irc.properties` creates a catalog named
`gravitino_irc`.
+Create a catalog properties file in your Trino `etc/catalog/` directory. The
filename determines the
+catalog name in Trino, so `gravitino_irc.properties` creates a catalog named
`gravitino_irc`. Some
+distributions place the directory at `etc/trino/catalog/`. Either way, these
are per-catalog
+properties and do not belong in `config.properties`.
-:::note
-The `warehouse` property is managed by the Gravitino IRC server and does not
need to be set in
-the Trino catalog configuration.
-:::
-
-### No Authentication
+The file below is complete, using vended credentials with OAuth2
authentication. Place it in
+`etc/catalog/` and restart Trino. Each commented group has a matching section
below, covering what
+the properties do and what the alternatives are.
```properties
+# Connection properties
connector.name=iceberg
iceberg.catalog.type=rest
-iceberg.rest-catalog.uri=http://<gravitino-host>:9001/iceberg
+iceberg.rest-catalog.uri=http://{gravitino_host}:9001/iceberg
+iceberg.rest-catalog.prefix={catalog}
-# Native S3 filesystem (Trino 430+)
-fs.native-s3.enabled=true
-s3.region=us-east-1
-s3.aws-access-key=<access-key>
-s3.aws-secret-key=<secret-key>
+# Storage access
+iceberg.rest-catalog.vended-credentials-enabled=true
+fs.s3.enabled=true
+s3.region={region_name}
+
+# Authentication
+iceberg.rest-catalog.security=OAUTH2
+iceberg.rest-catalog.oauth2.credential={client_id}:{client_secret}
+iceberg.rest-catalog.oauth2.server-uri={token_endpoint_uri}
+iceberg.rest-catalog.oauth2.scope={scope}
# Table defaults
iceberg.file-format=PARQUET
iceberg.compression-codec=ZSTD
```
-### Basic Authentication
+On Trino 480 and earlier, `fs.s3.enabled` is named `fs.native-s3.enabled`. See
[Storage
+Access](#storage-access).
-Requires Trino **481+**. Trino has no native Basic mode for Iceberg REST; pass
`Authorization`
-via HTTP headers.
+## Prerequisites
-```shell
-echo -n '<username>:<password>' | base64
+On the Gravitino side:
+
+- Gravitino running with the Iceberg REST service enabled. See [Iceberg REST
catalog
+ service](../iceberg-rest-service.md) for setup.
+- The IRC endpoint reachable from the Trino coordinator and all workers. The
default port is `9001`.
+- For vended credentials, three pieces of catalog-side setup, all described in
[Credential
+ vending](../security/credential-vending.md):
+ - A credential provider, and the role it assumes, configured on the catalog.
See
+ [`s3-token`](../security/credential-vending.md#s3-token).
+ - The cloud bundle jar on the IRC classpath. See
+ [Deployment](../security/credential-vending.md#deployment).
+ - The trust policy and the permission policy on the vending role, in AWS IAM.
+
+Of everything vending needs, only the Trino properties in [Storage
Access](#storage-access) are
+configured on this page. A Trino catalog file that is correct on its own vends
nothing when any of
+the pieces above is missing, and the resulting failures surface in Trino
rather than in Gravitino.
+See [Troubleshooting](#troubleshooting) for the symptoms each one produces.
+
+On the Trino side, the release requirements differ by feature:
+
+| Capability | Minimum
Trino release |
+|:-----------------------------------------------------------------|:----------------------|
+| Native S3 file system, which consumes vended credentials | 419
|
+| Explicit file system activation in each catalog file | 458
|
+| Vended credentials for Azure | 481
|
+| Refreshable vended credentials for S3, GCS, and Azure | 481
|
+| Basic authentication through `iceberg.rest-catalog.http-headers` | 481
|
+| `SHOW SCHEMAS` with OAuth2 and nested namespaces | 482
|
+
+Verified against Gravitino 1.3.0 and Trino 478 with AWS S3. Property names and
behavior at Trino 481
+and later come from the Trino documentation and release notes rather than from
that run.
+
+## Which Gravitino Catalogs Are Reachable
+
+Two settings in `conf/gravitino.conf` decide what a Trino catalog file can
reach:
+
+```properties
+gravitino.iceberg-rest.catalog-config-provider = dynamic-config-provider
+gravitino.iceberg-rest.gravitino-metalake = {metalake}
```
+The metalake is fixed at startup. `gravitino-metalake` names exactly one
metalake, and the IRC
+serves catalogs from that metalake only. Creating another metalake through the
Gravitino REST API
+does not make it reachable on this endpoint. Pointing the IRC at a different
metalake means editing
+`gravitino.conf` and restarting, and serving two metalakes at once means
running two Iceberg REST
+services. The metalake does not have to exist when the server starts, so
naming it in the
+configuration first and creating it afterwards works.
+
+Catalogs are not fixed. The dynamic config provider polls the Gravitino server
for the catalogs in
+that metalake, so a catalog created through the REST catalog API becomes
reachable without a
+restart. Select one from Trino with `iceberg.rest-catalog.prefix`, described
in [Connection
+Properties](#connection-properties). Setting
`gravitino.iceberg-rest.default-catalog-name` decides
+which catalog answers when a client sends no prefix at all.
+
+The static config provider instead serves catalogs defined directly in
`gravitino.conf` with
+`gravitino.iceberg-rest.` prefixed keys, loaded once when the server starts.
Catalogs defined that
+way are not registered in a metalake, so there is nothing to grant privileges
on. Most published
+examples use the static form, so take care not to mix the two.
+
+Gravitino access control over the IRC needs three things together, not the
dynamic provider alone:
+
+- The Iceberg REST service running as an auxiliary service inside the
Gravitino server. Standalone
+ Iceberg REST deployments do not support access control. See [Deployment
+ modes](../iceberg-rest-service.md#deployment-modes), where the mode table
carries the access
+ control column, and [Access
control](../iceberg-rest-service.md#access-control).
+- `gravitino.authorization.enable = true` on the Gravitino server. See [Access
+ control](../security/access-control.md).
+- The dynamic config provider. See [Dynamic catalog configuration
+ provider](../iceberg-rest-service.md#dynamic-catalog-configuration-provider).
+
+For an end-to-end walkthrough that enables all three and then grants
privileges on a catalog reached
+over the IRC, see [Access control
tutorial](../iceberg-rest-service.md#access-control-tutorial).
+
+See [Setting properties](../security/credential-vending.md#setting-properties)
for how credential
+properties differ between the two providers, and [Iceberg REST catalog
+service](../iceberg-rest-service.md) for the full provider configuration.
+
+## Connection Properties
+
```properties
connector.name=iceberg
iceberg.catalog.type=rest
-iceberg.rest-catalog.uri=http://<gravitino-host>:9001/iceberg
+iceberg.rest-catalog.uri=http://{gravitino_host}:9001/iceberg
+iceberg.rest-catalog.prefix={catalog}
+```
-# Basic authentication
-iceberg.rest-catalog.http-headers=Authorization: Basic <base64-credentials>
+| Property | Purpose |
Required |
+|:-----------------------------|:---------------------------------------|:---------|
+| `connector.name` | Always `iceberg` for this path | Yes
|
+| `iceberg.catalog.type` | Always `rest` for this path | Yes
|
+| `iceberg.rest-catalog.uri` | The Gravitino IRC endpoint | Yes
|
+| `iceberg.rest-catalog.prefix`| Selects which Gravitino catalog to use | Yes
|
-# Native S3 filesystem (Trino 430+)
-fs.native-s3.enabled=true
-s3.region=us-east-1
-s3.aws-access-key=<access-key>
-s3.aws-secret-key=<secret-key>
+Trino documents the full set in [Iceberg REST catalog configuration
+properties](https://trino.io/docs/current/object-storage/metastores.html#rest-catalog).
-# Table defaults
-iceberg.file-format=PARQUET
-iceberg.compression-codec=ZSTD
+`iceberg.rest-catalog.prefix` must match the catalog name in Gravitino.
Confirm the expected value
+against the server:
+
+```bash
+curl -s "http://{gravitino_host}:9001/iceberg/v1/config?warehouse={catalog}"
```
-Replace `<base64-credentials>` with the output of `echo -n
'<username>:<password>' | base64`.
+Gravitino returns the prefix under `defaults`, so setting it explicitly in
Trino matches rather than
+overrides it.
-### OAuth2 Authentication
+Storage access and authentication are independent choices. Pick one option
from each of the two
+sections below.
+
+## Storage Access
+
+Trino needs credentials to read and write the underlying object storage.
Choose vended credentials
+or static credentials. Do not configure both.
+
+Whichever you choose, the native S3 file system must be enabled. It performs
the request signing for
+S3 access, and without it metadata operations succeed while data reads fail
with
+`ICEBERG_FILESYSTEM_ERROR`.
```properties
-connector.name=iceberg
-iceberg.catalog.type=rest
-iceberg.rest-catalog.uri=http://<gravitino-host>:9001/iceberg
+fs.s3.enabled=true
+```
-# OAuth2 authentication
-iceberg.rest-catalog.security=OAUTH2
-iceberg.rest-catalog.oauth2.token=<your-token>
+Trino 481 removed legacy object storage support, leaving `fs.hadoop.enabled`
for HDFS only, and the
+native file system properties lost the `native-` segment at the same time.
Releases through 480
+document `fs.native-s3.enabled`, and 481 and later document `fs.s3.enabled`.
Use the name documented
+for the release you run.
-# Native S3 filesystem (Trino 430+)
-fs.native-s3.enabled=true
-s3.region=us-east-1
-s3.aws-access-key=<access-key>
-s3.aws-secret-key=<secret-key>
+### How Credential Vending Works
-# Table defaults
-iceberg.file-format=PARQUET
-iceberg.compression-codec=ZSTD
+1. Trino calls `loadTable` on the Gravitino IRC endpoint. With
+ `iceberg.rest-catalog.vended-credentials-enabled=true`, Trino sends the
+ `X-Iceberg-Access-Delegation: vended-credentials` header for you.
+2. Gravitino calls STS `AssumeRole` against the IAM role configured on the
catalog.
+3. Gravitino returns a `storage-credentials` block in the `loadTable`
response, holding temporary
+ credentials scoped to the table's S3 prefix.
+4. Trino uses those credentials for the S3 reads and writes in that query.
+
+One property of the design is worth stating explicitly, because it is commonly
assumed otherwise.
+The vended credentials are scoped to the table path, not to the calling user.
Gravitino mints them
+by assuming a fixed role configured on the catalog, so every caller that
reaches a given table
+receives credentials with the same storage permissions. Per-user restriction
comes from Gravitino
+access control deciding who reaches the table, not from the credentials
themselves.
+
+### Vended Credentials
+
+Used in the Quick Start above. Gravitino mints short-lived, path-scoped
credentials at query time,
+so no long-lived storage keys live in the Trino configuration.
+
+```properties
+iceberg.rest-catalog.vended-credentials-enabled=true
+fs.s3.enabled=true
+s3.region={region_name}
+```
+
+The catalog-side configuration this requires, covering the credential
provider, the role it assumes,
+and the IAM policies behind it, is described in [Credential
+vending](../security/credential-vending.md). None of it is set from the Trino
catalog file.
+
+If `s3.aws-access-key` and `s3.aws-secret-key` are also set in the file, Trino
requests vended
+credentials, receives them, and then signs with the static keys anyway. No
warning appears and every
+query works, so the file reads as though vending is in use when it is not.
Check that neither is
+present, and see [Verification](#verification) for how to confirm which
credentials are actually
+reaching S3.
+
+Backend coverage varies by Trino release. S3 is supported throughout. Trino
481 added Azure
+([trinodb/trino#23238](https://github.com/trinodb/trino/issues/23238)) and
refreshable vended
+credentials, which its release notes describe as covering S3, GCS, and Azure
+([trinodb/trino#28998](https://github.com/trinodb/trino/issues/28998)). Check
the release notes for
+the version you run before relying on GCS or Azure, and use static credentials
where vending is not
+yet available.
+
+### Static Credentials
+
+Configure storage keys directly in Trino. Simpler to set up, but the keys are
long-lived, are not
+scoped to a table path, and are managed outside Gravitino.
+
+Relative to the Quick Start, remove
`iceberg.rest-catalog.vended-credentials-enabled` and configure
+the keys:
+
+```properties
+fs.s3.enabled=true
+s3.region={region_name}
+s3.aws-access-key={access_key_id}
+s3.aws-secret-key={secret_access_key}
```
-See [How to authenticate](../security/how-to-authenticate.md) for Gravitino
authentication
-configuration options.
+Leaving `iceberg.rest-catalog.vended-credentials-enabled=true` in place
alongside the keys is not an
+error and produces no warning. Trino requests vended credentials and then
signs with the static keys
+anyway, for the reason given under [Vended Credentials](#vended-credentials).
-:::tip Local development
-For local development with MinIO, replace the S3 section with:
+For local development against MinIO. The same precedence applies, so remove
+`iceberg.rest-catalog.vended-credentials-enabled` here too:
```properties
-fs.native-s3.enabled=true
-s3.endpoint=http://<minio-host>:9000
+fs.s3.enabled=true
+s3.endpoint=http://{minio_host}:9000
s3.path-style-access=true
-s3.aws-access-key=<minio-access-key>
-s3.aws-secret-key=<minio-secret-key>
+s3.aws-access-key={minio_access_key}
+s3.aws-secret-key={minio_secret_key}
s3.region=us-east-1
```
-See
[gravitino-irc-quickstart](https://github.com/markhoerth/gravitino-irc-quickstart)
for a
-complete local development environment using MinIO.
-:::
+## Authentication
-## Start Trino
+How Trino identifies itself to Gravitino. Independent of the storage
credential choice above. If
+Gravitino requires an identity, Trino must present one here or requests are
rejected before any
+vending occurs. See [How to authenticate](../security/how-to-authenticate.md)
for the Gravitino side.
-Trino is a server process — the catalog is picked up automatically when Trino
starts. After
-placing `gravitino_irc.properties` in `etc/catalog/`, restart Trino:
+### No Authentication
-```bash
-$TRINO_HOME/bin/launcher restart
+Omit the authentication block entirely. Relative to the Quick Start, that
means dropping these four
+lines:
+
+```properties
+iceberg.rest-catalog.security=OAUTH2
+iceberg.rest-catalog.oauth2.credential={client_id}:{client_secret}
+iceberg.rest-catalog.oauth2.server-uri={token_endpoint_uri}
+iceberg.rest-catalog.oauth2.scope={scope}
```
-Once Trino is running, connect using the Trino CLI:
+With none of them set, `iceberg.rest-catalog.security` stays at its default of
`NONE` and Trino
+sends no credentials to the IRC. See [Iceberg REST catalog configuration
+properties](https://trino.io/docs/current/object-storage/metastores.html#rest-catalog)
for the
+property and its other values.
+
+Use this for local development and isolated test environments only. With no
identity on the request
+there is nothing for Gravitino to authorize, so privileges granted on a
catalog have no effect on
+queries arriving over the IRC and every caller that can reach the port has the
same access. Adding
+vended credentials extends that past metadata. Since the IRC mints credentials
for any caller that
+asks, as described under [How Credential Vending
Works](#how-credential-vending-works), anyone who
+can reach the endpoint can obtain working storage credentials for the
warehouse.
+
+If the server does require an identity and Trino presents none, requests are
rejected before vending
+is reached, which surfaces as a 403 rather than as a storage error.
+
+### Basic Authentication
+
+Requires Trino 481 or later.
+
+`iceberg.rest-catalog.security` has no Basic value, so Trino cannot be
configured to authenticate to
+a REST catalog with a username and password. Gravitino's IRC does accept HTTP
Basic, so the way
+across is to construct the header yourself and have Trino attach it to every
REST catalog request.
+Trino 481 added `iceberg.rest-catalog.http-headers`
+([trinodb/trino#24236](https://github.com/trinodb/trino/issues/24236)) for
sending arbitrary headers,
+and Basic authentication is one use of it rather than a feature in its own
right.
+
+Encode the credentials:
```bash
-trino --server http://<trino-host>:8080 --catalog gravitino_irc
+echo -n '{username}:{password}' | base64
```
-Or connect without specifying a default catalog and qualify queries fully:
+Then set the header:
-```bash
-trino --server http://<trino-host>:8080
+```properties
+iceberg.rest-catalog.http-headers=Authorization: Basic {base64_credentials}
```
-## Examples
+On releases before 481 there is no way to send the header, so the choice there
is OAuth2 or no
+authentication.
-Once connected, use the Trino CLI or any Trino-compatible client.
+Trino treats the value as an opaque header rather than as credentials, so
nothing renews or rotates
+it and it goes out unchanged on every request. Base64 is encoding rather than
encryption, so anyone
+who can read the catalog file recovers the password. Trino also documents the
property as carrying
+additional non-sensitive headers, so putting credentials in it works but runs
against its stated
+purpose. Prefer OAuth2 where the Gravitino server supports it.
-### List Schemas
+### OAuth2 Authentication
-```sql
-SHOW SCHEMAS FROM gravitino_irc;
+Two options, differing in who obtains the token. Both put the same
`Authorization: Bearer` header on
+the wire, so the Gravitino side of the configuration is the same for either.
+
+#### Client Credentials Flow
+
+Used in the Quick Start above. Trino holds a client ID and secret, requests a
token itself, and
+obtains a fresh one when the current token expires. Prefer it.
+
+```properties
+iceberg.rest-catalog.security=OAUTH2
+iceberg.rest-catalog.oauth2.credential={client_id}:{client_secret}
+iceberg.rest-catalog.oauth2.server-uri={token_endpoint_uri}
+iceberg.rest-catalog.oauth2.scope={scope}
```
-### List Tables
+`iceberg.rest-catalog.oauth2.server-uri` is how Trino locates the identity
provider. It takes the
+provider's token endpoint rather than its issuer or realm URL. On Keycloak
that is:
-```sql
-SHOW TABLES FROM gravitino_irc.<namespace>;
+```
+https://{keycloak_host}/realms/{realm}/protocol/openid-connect/token
```
-### Query a Table
+Trino then presents the token it receives to Gravitino as a bearer token, in
an HTTP header on every
+Iceberg REST request:
-```sql
-SELECT * FROM gravitino_irc.<namespace>.<table> LIMIT 10;
+```
+Authorization: Bearer {access_token}
```
-### Create a Schema
+#### Pre-Issued Token
-When creating a schema in Trino, a storage location must be specified:
+Trino sends one fixed token, obtained out of band from your identity provider,
on every request to
+the IRC. Nothing renews it, so when the token expires every request fails with
401 until someone
+edits the catalog file and restarts Trino.
-```sql
-CREATE SCHEMA gravitino_irc.<namespace>
-WITH (location = 's3://<bucket>/<namespace>/');
+```properties
+iceberg.rest-catalog.security=OAUTH2
+iceberg.rest-catalog.oauth2.token={token}
```
-### Create a Table
+Reach for it when Trino cannot reach the OAuth2 server, or for a short-lived
test. Note that the
+token sits in plaintext in the catalog file and is directly replayable against
the IRC by anyone who
+can read that file, which a client secret is not.
-```sql
-CREATE TABLE gravitino_irc.<namespace>.new_table (
- id INTEGER,
- name VARCHAR,
- created_at TIMESTAMP
-)
-WITH (
- format = 'PARQUET',
- format_version = 2
-);
+#### Token Exchange
+
+On Trino 479 and later, add the following to both options to avoid
token-exchange behavior that can
+cause repeated token requests:
+
+```properties
+iceberg.rest-catalog.session=NONE
+iceberg.rest-catalog.oauth2.token-exchange-enabled=false
+```
+
+`iceberg.rest-catalog.session=NONE` is already the default and can be omitted.
+
+## Table Defaults
+
+Optional, and independent of everything else on this page. These set the
defaults Trino uses when it
+creates tables through the IRC:
+
+```properties
+iceberg.file-format=PARQUET
+iceberg.compression-codec=ZSTD
+```
+
+## Starting Trino
+
+Trino is a server process, and the catalog is picked up when Trino starts.
After placing
+`gravitino_irc.properties` in `etc/catalog/`, restart Trino:
+
+```bash
+$TRINO_HOME/bin/launcher restart
+```
+
+Trino needs roughly 20 seconds to accept queries after a restart, which is
long enough to produce
+misleading connection errors in scripted runs.
+
+Once Trino is running, connect using the Trino CLI:
+
+```bash
+trino --server http://{trino_host}:8080 --catalog gravitino_irc
+```
+
+Or connect without a default catalog and qualify queries fully:
+
+```bash
+trino --server http://{trino_host}:8080
+```
+
+## Verification
+
+### Confirm the Server Vends Credentials
+
+Check the server before assuming Trino is using vended credentials. The
`storage-credentials` block
+in the `loadTable` response is the direct evidence:
+
+```bash
+curl -s -H "X-Iceberg-Access-Delegation: vended-credentials" \
+ -H "Authorization: Bearer {token}" \
+
http://{gravitino_host}:9001/iceberg/v1/{catalog}/namespaces/{namespace}/tables/{table}
\
+ | python3 -m json.tool | grep -A8 storage-credentials
+```
+
+Expected output:
+
+```json
+"storage-credentials": [
+ {
+ "prefix": "s3://{bucket_name}/{warehouse_path}/{namespace}/{table}",
+ "config": {
+ "s3.access-key-id": "ASIA...",
+ "s3.secret-access-key": "...",
+ "s3.session-token": "...",
+ "s3.session-token-expires-at-ms": "..."
+ }
+ }
+]
+```
+
+Three markers distinguish genuine vending from static credentials passed
through: the access key
+begins with `ASIA` rather than `AKIA`, a session token is present, and the
prefix is scoped to the
+table path rather than the whole bucket.
+
+### Confirm the Engine Path
+
+```bash
+trino --execute "SELECT * FROM {catalog}.{namespace}.{table}"
```
+A successful query alone does not prove vending is in use, since static keys
or an instance profile
+can satisfy the same reads. For independent confirmation at the AWS layer,
CloudTrail shows the
+`AssumeRole` call against the vending role, followed by S3 operations
attributed to the assumed-role
+session rather than to the base IAM user. CloudTrail is the authoritative
check in environments
+where an EC2 instance profile could otherwise satisfy the S3 reads.
+
## Known Issues
-### `SHOW SCHEMAS` Fails on Gravitino IRC (OAuth2, Nested Namespaces)
+### Vended Credentials Are Not Refreshed During Long-Running Queries
+
+Applies to Trino releases before 481.
+
+**Cause:** Gravitino advertises a refresh endpoint in the `loadTable` response
as
+`client.refresh-credentials-endpoint`, but Trino does not call it when vended
credentials expire
+mid-query
([trinodb/trino#25827](https://github.com/trinodb/trino/issues/25827)). Scans
running past
+the STS session lifetime fail. The gap is client-side rather than a catalog
limitation.
+
+**Solution:** Upgrade to Trino 481 or later, which adds refreshable vended
credentials
+([trinodb/trino#28998](https://github.com/trinodb/trino/issues/28998)). On
earlier releases, raise
+[`s3-token-expire-in-secs`](../security/credential-vending.md#s3-token) on the
Gravitino catalog,
+together with the maximum session duration on the IAM role, or keep individual
queries shorter than
+the session lifetime.
+
+### Storage Credentials Are Exposed in Query JSON
-**Cause:** When Trino connects to Gravitino IRC with
`iceberg.rest-catalog.security=OAUTH2`,
-`iceberg.rest-catalog.nested-namespace-enabled=true`, and
`iceberg.rest-catalog.session=NONE`
-(the default), `SHOW SCHEMAS` recursively calls Iceberg REST `listNamespaces`.
On Trino releases
-before 482, each recursive call creates a separate OAuth session, which can
trigger excessive token
+**Cause:** Trino serializes storage credentials into query JSON for write and
table maintenance
+operations, where any user with write privilege can read them through the
Trino UI or the query API
+([GHSA-x27p-5f68-m644](https://github.com/trinodb/trino/security/advisories/GHSA-x27p-5f68-m644)).
+The exposure applies to static credentials as well as vended ones.
+
+**Solution:** Review the current advisory status against your deployed Trino
version. Short
+`s3-token-expire-in-secs` values limit the window during which an exposed
vended credential is
+usable, which static keys do not offer.
+
+### Trino Returns Valid Credentials but Does Not Honor Them
+
+**Cause:** At least one report describes a Trino version receiving a correct
`storage-credentials`
+block from the server yet failing on S3 access, where the same endpoint works
from Spark
+([trinodb/trino#27416](https://github.com/trinodb/trino/issues/27416),
reported on Trino 474).
+
+**Solution:** If the verification curl shows a valid credentials block and
Trino still fails, treat
+the Trino version as a suspect before revisiting configuration.
+
+### `SHOW SCHEMAS` Fails With OAuth2 and Nested Namespaces
+
+**Cause:** With `iceberg.rest-catalog.security=OAUTH2`,
+`iceberg.rest-catalog.nested-namespace-enabled=true`, and
`iceberg.rest-catalog.session=NONE` (the
+default), `SHOW SCHEMAS` recursively calls Iceberg REST `listNamespaces`. On
Trino releases before
+482, each recursive call creates a separate OAuth session, which can trigger
excessive token
requests and cause errors such as `Connection pool shut down` or
`StackOverflowError`.
-**Solution:** Upgrade to Trino 482+.
+**Solution:** Upgrade to Trino 482 or later.
### `TIMESTAMP WITH TIME ZONE` Values Are Not Adjusted to the Client Session
Time Zone
-**Cause:** For `TIMESTAMP WITH TIME ZONE` values, Trino does not adjust query
results according to
-the client session time zone. Unlike Spark and Flink, Trino displays these
values based on the
-stored timestamp-with-time-zone value.
+**Cause:** Trino does not adjust `TIMESTAMP WITH TIME ZONE` results according
to the client session
+time zone. Unlike Spark and Flink, it displays these values based on the stored
+timestamp-with-time-zone value.
-**Solution:** To convert a `TIMESTAMP WITH TIME ZONE` value to the current
client session time
-zone, use `at_timezone` together with `current_timezone()`:
+**Solution:** Convert with `at_timezone` and `current_timezone()`:
```sql
SELECT
id,
at_timezone(timestamp_with_timezone_column, current_timezone())
-FROM <catalog>.<namespace>.<table>;
+FROM {catalog}.{namespace}.{table};
```
-## Gravitino Connector vs. Iceberg REST
-
-| Feature | Gravitino Engine Connector | Iceberg REST
|
-|:-------------------------|:----------------------------|:------------------------------|
-| Engine plugin required | Yes | No
|
-| Gravitino access control | Yes | Yes
|
-| Supported engines | Trino, Spark, Flink, Daft | Any
Iceberg-compatible engine |
-| Credential vending | Varies | Yes (S3, GCS, OSS,
ADLS) |
+### Trino Identifiers Are Not Case Sensitive
-### Trino Identifiers Are Not Treated as Case Sensitive
-
-Trino identifiers are not treated as case sensitive. As a result, metadata
names that differ
-only by letter case cannot be distinguished. See [Trino identifier
-documentation](https://trino.io/docs/current/language/reserved.html#language-identifiers).
This
+**Cause:** Trino identifiers are not treated as case sensitive, so metadata
names that differ only
+by letter case cannot be distinguished. See [Trino identifier
+documentation](https://trino.io/docs/current/language/reserved.html#language-identifiers).
The
limitation comes from Trino itself and is not specific to Gravitino.
-For the best compatibility with Trino:
-
-- Use lowercase metadata names.
-- Avoid creating objects whose names differ only by letter case.
+**Solution:** Use lowercase metadata names, and avoid creating objects whose
names differ only by
+letter case. Where mixed-case names already exist in Gravitino,
+`iceberg.rest-catalog.case-insensitive-name-matching=true`, off by default,
lets Trino resolve them.
+It does not make names that differ only by case distinguishable.
+
+## Troubleshooting
+
+Failures in this path tend to surface far from their cause. The table maps
symptoms back to the
+configuration that produces them.
+
+| Symptom | Likely
cause
|
+|:-----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|
+| Catalog not visible in `SHOW CATALOGS` | Trino not
restarted, or a parse error in the catalog file. Check the Trino server log
|
+| `Failed to list namespaces` |
`iceberg.rest-catalog.prefix` does not match a Gravitino catalog name, or the
identity was rejected |
+| 403 `ForbiddenException`, principal not in metalake | Identity
rejected before vending is reached. A token principal must be a member of the
metalake |
+| `storage-credentials` absent from the `loadTable` response |
`credential-providers` not set on the catalog, or the cloud bundle jar missing
from the IRC classpath |
+| `ICEBERG_FILESYSTEM_ERROR` on data reads, metadata fine | Native S3
file system not enabled, wrong property name for the release, or static keys
overriding |
+| S3 `AccessDenied` despite a valid `storage-credentials` block |
Permission policy on the vending role, or static keys taking precedence in the
Trino catalog file |
+| STS `AccessDenied` on `AssumeRole` | Trust
policy does not allow the `s3-access-key-id` principal to assume the vending
role |
+| Access key in the response begins with `AKIA` | The
catalog is using `s3-secret-key` rather than `s3-token`, so static keys are
vended unchanged |
+| Long queries fail after about an hour | STS
session expiry with no client-side refresh. See Known Issues
|
+| Verification curl shows valid credentials but Trino fails on S3 | Possible
version-specific consumption bug. See Known Issues
|
+
+The catalog-side causes are described in [Credential
+vending](../security/credential-vending.md).
+
+## Gravitino Connector vs. the IRC
+
+| Feature | Gravitino engine connector | IRC
|
+|:-------------------------|:---------------------------|:------------------------------|
+| Engine plugin required | Yes | No
|
+| Gravitino access control | Yes | Yes, for API-created
catalogs |
+| Supported engines | Trino, Spark, Flink, Daft | Any
Iceberg-compatible engine |
+| Credential vending | Varies | Yes, see Trino
release notes |
+
+Catalogs created through the Gravitino REST catalog API are registered in a
metalake, so privileges
+can be granted on them and Gravitino access control applies to queries that
reach them over the IRC.
+Catalogs defined instead in the Iceberg REST service configuration file are
not registered in a
+metalake, so there is nothing to grant privileges on.
## Related
+- [Credential vending](../security/credential-vending.md)
- [Iceberg REST catalog service](../iceberg-rest-service.md)
- [Connect Spark to Iceberg REST](./spark.md)
- [Connect Flink to Iceberg REST](./flink.md)