SteNicholas opened a new pull request, #165:
URL: https://github.com/apache/paimon-cpp/pull/165

   ### Purpose
   
   Linked issue: #164
   
   Add a REST catalog implementation, selected via the `metastore=rest` option, 
that talks the Paimon REST catalog open API.
   
   **New components**
   
   - `RestHttpClient` — blocking libcurl client with exponential-backoff 
retries:
     - 429/503 responses are retried for all methods, transport errors only for 
idempotent methods via an explicit allowlist of transient failures 
(name-resolution and connect failures, timeouts, TLS failures and a server 
closing the connection without responding are never retried).
     - A `Retry-After` response header — delta-seconds or HTTP-date form, 
parsed locale-independently — takes precedence over the backoff when it yields 
a positive delay. Every retry sleep is bounded by a per-sleep cap and an 
overall retry budget; a `Retry-After` beyond either bound stops retrying rather 
than sleeping less than the server requested.
     - Redirects keep the method and body of POST/DELETE requests 
(`CURLOPT_POSTREDIR`) and are restricted to http(s) targets.
     - Header names must be valid HTTP tokens and header values must not 
contain CR/LF/NUL; a violating header fails the request before anything is sent.
     - Easy handles are pooled and reset between requests, so a request reuses 
the connection opened by the previous one instead of paying a TCP and TLS 
handshake every time; TLS below 1.2 is refused and requests carry a default 
`paimon-cpp` user agent that a `header.User-Agent` option overrides.
     - Per-request logging carries the request id (debug on success, warn on a 
retry or a final failure).
   - `RestApi` — HTTP + JSON layer:
     - `/v1/config` option merging (overrides > client options > defaults), 
`header.` options sent as request headers, and paged listing.
     - Error mapping to `Status` (404 → `NotExist`, 409 → `Exist`, 400 → 
`Invalid`, 501 → `NotImplemented`) that prefers the code of the parsed error 
body over the http status.
     - Sensitive server messages are redacted, and response bodies are never 
echoed into error messages (they may carry credentials), so a body that is not 
an error object is reported as unparsable, distinct from an error object 
carrying no message.
     - Errors carry the request id (`x-request-id`, falling back to any 
`request-id` header) and attach a `RestErrorDetail` with the mapped code, so 
callers can distinguish e.g. authentication failures programmatically.
   - `RestCatalog` — database/table create/drop/rename/list/get, snapshot 
listing, `table-default.` option defaults on table creation, and system/branch 
table checks:
     - The server table response is converted into a `TableSchema` with the 
computed `highestFieldId`: ids at or above 
`SpecialFieldIds::SYSTEM_FIELD_ID_START` are excluded, a duplicated field id at 
any nesting level is rejected, and a missing or wrong-typed 
`partitionKeys`/`primaryKeys`/`options` member fails instead of silently 
defaulting.
     - Branch identifiers are passed through to the server, which resolves the 
branch and returns the branch's own schema (the default branch `main` maps 
case-insensitively to the bare table); snapshots of a branch are listed under 
the branch object name.
     - The `sys` database serves the local global system tables like 
`FileSystemCatalog`.
   - Bear token authentication provider (`token.provider=bear`, the protocol's 
historical spelling of "bearer").
   
   **Shared code**
   
   - The pieces overlapping with the object-store file systems are unified 
under `common/utils`: the generic HTTP client moved there from `common/fs` and 
is shared by the S3 file system and the REST catalog, `UrlUtils` carries both 
URL-encoding flavors (form-urlencoded for the REST api, RFC 3986 for S3) with 
the S3 client reusing it, and libcurl detection in CMake is a single 
`find_package(CURL)` gated on `PAIMON_ENABLE_S3 OR PAIMON_ENABLE_REST`.
   - `CatalogUtils` holds the system-database and system/branch table checks 
shared by `FileSystemCatalog` and `RestCatalog`, with the check order and 
messages aligned with the Java `CatalogUtils` (`Cannot 'createTable' for system 
table ...`). `FileSystemCatalog` now also rejects branch identifiers on 
create/drop/rename — a branch identifier resolves to the main table directory, 
so dropping `t$branch_b` would otherwise delete the whole table.
   - The new `SensitiveConfigUtils` centralizes credential redaction with the 
Java `SensitiveConfigUtils` key markers and masking rule (matched on the key 
lower-cased with separators removed; a key naming a true secret is masked as a 
whole, while an identifier-like key such as `dlf.access-key-id` keeps at most a 
four-character tail of a long enough value). `sys.catalog_options` masks every 
row through it so credentials such as the bearer token never surface to users 
who can query that table, and a server error message carrying a secret marker 
is redacted as a whole.
   - `RestUtil::ExtractRequestId` and `RestAuthParameter::Create` likewise hold 
the request-id lookup and the query-parameter encoding in one place, so the log 
line and the error message report the same id and no call site can sign an 
unencoded parameter.
   
   **Build**
   
   libcurl is used from the system rather than bundled, so the new CMake option 
`PAIMON_ENABLE_REST` defaults to `OFF`, consistent with the other optional 
components that need something outside the bundled third-party toolchain 
(jindo, lance, lumina, lucene, tantivy). CI enables it explicitly in 
`ci/scripts/build_paimon.sh` and installs the libcurl development package, so 
the REST code and its tests are still built and run on every job. With the 
option off, `Catalog::Create` reports that `metastore=rest` requires a build 
with `PAIMON_ENABLE_REST=ON`.
   
   Follow-ups (not in this PR): DLF signing, data-token file IO, paged listing 
parameters (`maxResults`/name patterns), and the remaining endpoints 
(alter/commit/views/partitions/tags/functions).
   
   ### Tests
   
   New `rest_test` binary (62 cases, registered under the `unittest` label so 
it runs in CI), all against an in-process mock HTTP/REST server:
   
   - `RestHttpClientTest`: uri normalization, query encoding, a pooled handle 
carrying no method or body over from the previous request, retry classification 
(429/503 retried, 404 and non-allowlisted transport errors such as a refused 
connection or a redirect loop not, truncated responses retried only for 
idempotent methods), `Retry-After` precedence in both forms plus the per-sleep 
and overall budget bounds, retry exhaustion, redirect following with POST 
keeping its method and body, rejection of invalid header names/values, and 
transport errors omitting the url and query.
   - `RestUtilTest` / `ResourcePathsTest` / `RestMessagesTest`, each next to 
its implementation: prefix extraction and the request-id lookup including the 
gateway-header fallback, resource path building with url-encoded segments, JSON 
round trips of all request/response messages, and config merge with null-value 
filtering (a key present in defaults, client options and overrides at once, 
plus the null-override and null-default paths).
   - `RestCatalogTest`: client-side option validation (missing uri/token, 
unsupported token provider), end-to-end catalog operations including 
pagination, `ignore_if_exists` / `ignore_if_not_exists` paths, client/server 
headers and the json content type, `table-default.` defaults, system tables, 
branch tables resolved by the server, broken-schema rejection (missing and 
wrong-typed `partitionKeys`/`primaryKeys`/`options` members), a non-empty 
partition-key round trip, server errors surfacing as errors instead of "does 
not exist", and snapshot listing with sorting.
   - `RestApiErrorTest`: http-status-to-`Status` mapping including the 
request-id fallback, redaction and the `RestErrorDetail` code.
   
   The shared pieces are covered next to their implementations:
   
   - `UrlUtilsTest`: both encoding flavors, including that a literal `%` is 
encoded even when `/` is preserved, which is what the S3 client relies on.
   - `SensitiveConfigUtilsTest`: key classification, whole-value and 
tail-preserving masking, and free-form text redaction.
   - `HttpClientUtilTest` in the common utils suite.
   - `TableSchemaTest.TestComputeHighestFieldId`: the highest-field-id 
computation.
   - `SnapshotTest.TestToSnapshotInfo`: the snapshot-to-info conversion.
   - `FileSystemCatalogTest.TestBranchIdentifierRejectedForTableOperations`: 
the branch rejection.
   - The `sys.catalog_options` integration test: credential-carrying options 
(token, access-key secret, account key, credential, SAS) are masked in every 
emitted row, and an access-key id keeps only its four-character tail.
   
   ### API and Format
   
   - New public header `include/paimon/catalog_options.h` with a 
`PAIMON_EXPORT`ed `CatalogOptions` struct holding the catalog-level option keys 
`METASTORE`, `URI`, `TOKEN`, `TOKEN_PROVIDER` and the 
`TABLE_DEFAULT_OPTION_PREFIX` (`table-default.`) key prefix; table-level keys 
stay in `Options` (`defs.h`), mirroring the CatalogOptions/CoreOptions 
separation of the Java implementation.
   - `RestCatalog` implements the `Catalog` interface including `GetOptions()`, 
which returns the client options merged with the server-side `/v1/config`.
   - `Catalog::Create` now dispatches on the `metastore` option (`filesystem` 
remains the default; unknown values are rejected). For `metastore=rest`, 
`root_path` is not a filesystem path but the warehouse (instance) name 
registered on the REST server, documented on the API.
   - No storage format changes. The wire protocol is the Paimon REST catalog 
open API.
   
   ### Documentation
   
   - `docs/source/user_guide/catalog.rst` now documents both metastores: the 
existing filesystem metastore section and a new REST Catalog section covering 
the `PAIMON_ENABLE_REST` build requirement, the `root_path` semantics 
(warehouse/instance name), the `CatalogOptions` keys (`metastore`, `uri`, 
`token.provider`, `token`, `table-default.<key>`) and a configuration example, 
replacing the note that claimed REST catalog support is future work. It also 
states which parts of the REST catalog are not implemented yet (altering a 
database or a table, views, functions, partitions, tags, branch management, 
consumers and the `dlf` token provider), so the guide is not read as promising 
them.
   - `docs/source/building.rst` lists `-DPAIMON_ENABLE_REST=ON` under the 
optional components, including its libcurl requirement.
   
   ### Generative AI tooling
   
   Generated-by: Claude Code (claude-fable-5, claude-opus-5)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to