Skip to content

Commit 54137c1

Browse files
SteNicholasclaude
andcommitted
feat(rest): support rest catalog for databases, tables and snapshots
Add a REST catalog selected via the "metastore=rest" option: - RestHttpClient: blocking libcurl client with exponential-backoff retries. 429/503 are retried for every method, a transient transport error only for an idempotent one; a server closing the connection without responding is not retried. A Retry-After header is honored in both the delta-seconds and the HTTP-date form with a locale-independent parse, and is never shortened: the overall retry budget bounds it while the per-sleep cap bounds only the backoff. Easy handles are pooled, so a request reuses the previous connection instead of paying a handshake every time; TLS below 1.2 is refused and requests carry a default user agent. Every transported request is debug-logged with the server request id. - RestApi: HTTP + JSON protocol layer. "/v1/config" merges the server options into the client ones (overrides > client options > defaults, an override sent as null unsets the client option), "header." options are sent as request headers and a listing follows the page tokens to the last page. A failed request maps to a Status by the code of the parsed error body, falling back to the http status, and carries a RestErrorDetail with that code so callers can tell e.g. an authentication failure from another IO error. A response body is never echoed into an error, since it may carry credentials, so a body that is not an error object is only reported as unparsable. - RestCatalog: database, table and snapshot operations, with "table-default." option defaults and a schema conversion computing the highest field id (ids at or above the new SpecialFieldIds::SYSTEM_FIELD_ID_START are excluded, duplicated ids are rejected at any nesting level). A branch stays in the object name so the server resolves it and returns the branch's own schema, the default branch "main" being matched ignoring case and addressed as the bare table. The "sys" database serves the local global system tables like FileSystemCatalog. - Bear token authentication provider ("bear" is the protocol's historical spelling of "bearer"). RestAuthParameter::Create url-encodes the query parameter values a signing provider signs. - CatalogOptions: public catalog-level option keys (metastore, uri, token, token.provider, "table-default." prefix), kept separate from the table-level CoreOptions. - CatalogUtils: system database, system table and branch checks shared by FileSystemCatalog and RestCatalog. - RestUtil::ExtractRequestId: the request id lookup shared by the debug log line and the error message, falling back to any header whose name carries a request id since a gateway may report it under its own name. The pieces overlapping with the object store file systems move to common/utils: UrlUtils carries both URL encoding flavors (form-urlencoded for the REST api, RFC 3986 for S3), the generic HTTP client moves there from common/fs, and the new SensitiveConfigUtils redacts credentials for every surface that may expose them - the sys.catalog_options table masks the value of a credential-carrying option key, keeping at most a short tail of an identifier-like value, and a server error message carrying a secret marker is redacted as a whole. libcurl is used from the system, so the new PAIMON_ENABLE_REST option defaults to OFF like the other optional components with external requirements; its CMake detection is a single find_package guarded by PAIMON_ENABLE_S3 OR PAIMON_ENABLE_REST. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9277fdf commit 54137c1

55 files changed

Lines changed: 6288 additions & 166 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CMakeLists.txt‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,17 @@ option(PAIMON_ENABLE_LUMINA "Whether to enable lumina vector index" OFF)
6868
option(PAIMON_ENABLE_LUCENE "Whether to enable lucene index" OFF)
6969
option(PAIMON_ENABLE_TANTIVY
7070
"Whether to enable tantivy-fulltext global index (Rust FFI, experimental)" OFF)
71+
option(PAIMON_ENABLE_REST "Whether to enable the rest catalog (requires libcurl)" OFF)
7172
if(PAIMON_ENABLE_ORC)
7273
add_definitions(-DPAIMON_ENABLE_ORC)
7374
endif()
75+
if(PAIMON_ENABLE_REST)
76+
add_definitions(-DPAIMON_ENABLE_REST)
77+
endif()
78+
# libcurl backs the HTTP client shared by the S3 file system and the rest catalog.
79+
if(PAIMON_ENABLE_S3 OR PAIMON_ENABLE_REST)
80+
find_package(CURL REQUIRED)
81+
endif()
7482
if(PAIMON_ENABLE_AVRO)
7583
add_definitions(-DPAIMON_ENABLE_AVRO)
7684
endif()
@@ -498,9 +506,6 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake"
498506
499507
config_summary_message()
500508
501-
if(PAIMON_ENABLE_S3)
502-
find_package(CURL REQUIRED)
503-
endif()
504509
add_subdirectory(src/paimon)
505510
add_subdirectory(src/paimon/fs/local)
506511
if(PAIMON_ENABLE_JINDO)

‎ci/scripts/build_paimon.sh‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ CMAKE_ARGS=(
146146
"-DPAIMON_ENABLE_LUMINA=${ENABLE_LUMINA}"
147147
"-DPAIMON_ENABLE_LUCENE=ON"
148148
"-DPAIMON_ENABLE_TANTIVY=${ENABLE_TANTIVY}"
149+
"-DPAIMON_ENABLE_REST=ON"
149150
"-DPAIMON_LINT_GIT_TARGET_COMMIT=${lint_git_target_commit}"
150151
)
151152

‎docs/source/building.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ boolean flags to ``cmake``.
126126
* ``-DPAIMON_ENABLE_LUMINA=ON``: Support for the Lumina vector index.
127127
* ``-DPAIMON_ENABLE_LUCENE=ON``: Support for Lucene full-text search indexes
128128
* ``-DPAIMON_ENABLE_TANTIVY=ON``: Enable the experimental Tantivy full-text index Rust FFI.
129+
* ``-DPAIMON_ENABLE_REST=ON``: Support for the REST catalog (``metastore=rest``), requires the libcurl development package.
129130

130131
Third-party dependency source
131132
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

‎docs/source/user_guide/catalog.rst‎

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,63 @@ C++ Paimon provides a :ref:`Catalog abstraction <cpp-api-catalog>` to manage the
2323
abstraction provides a series of ways to help you better integrate with computing engines. We always
2424
recommend that you use Catalog to access the Paimon table.
2525

26+
C++ Paimon supports two metastores, selected with the catalog option
27+
``metastore``: the filesystem metastore (default) and the REST metastore.
28+
2629
Filesystem Catalog
2730
~~~~~~~~~~~~~~~~~~
28-
C++ Paimon catalog currently support one types of metastores filesystem metastore (default),
29-
which stores both metadata and table files in filesystems.
31+
The filesystem metastore (``metastore=filesystem``, the default) stores both
32+
metadata and table files in filesystems. The ``root_path`` argument of
33+
``Catalog::Create`` is the warehouse directory holding the databases and tables.
34+
35+
REST Catalog
36+
~~~~~~~~~~~~
37+
With the REST metastore (``metastore=rest``), catalog metadata is managed by a
38+
remote catalog server exposed through a REST API; table data itself is still read
39+
and written through the table paths returned by the server. See `Java Paimon REST
40+
Catalog <https://paimon.apache.org/docs/master/concepts/rest/>`_ for the concept
41+
and the server-side protocol.
42+
43+
REST catalog support is an optional build component: configure the build with
44+
``-DPAIMON_ENABLE_REST=ON`` (see :ref:`cpp_build_optional_components`).
45+
46+
When ``metastore=rest``, the ``root_path`` argument of ``Catalog::Create`` is not
47+
a filesystem path but the warehouse (instance) name under which the tables are
48+
registered on the REST server. The catalog is configured through the
49+
``CatalogOptions`` keys:
50+
51+
* ``metastore``: must be ``rest`` to select the REST catalog.
52+
* ``uri``: server url of the REST catalog server.
53+
* ``token.provider``: authentication provider of the REST catalog; currently only
54+
``bear`` is supported (the protocol's historical spelling of "bearer").
55+
* ``token``: token of the ``bear`` token provider.
56+
* ``table-default.<key>``: table option defaults applied when a created table
57+
left ``<key>`` unset.
58+
* ``header.<name>``: sent as the ``<name>`` http header on every request to the
59+
server. The server may configure headers of its own through the ``/v1/config``
60+
endpoint, which are merged with these as any other option is.
61+
62+
.. code-block:: cpp
63+
64+
std::map<std::string, std::string> options = {
65+
{"metastore", "rest"},
66+
{"uri", "http://127.0.0.1:8080"},
67+
{"token.provider", "bear"},
68+
{"token", "<token>"},
69+
};
70+
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::Catalog> catalog,
71+
paimon::Catalog::Create(/*root_path=*/"my_instance", options));
72+
73+
On creation the catalog queries the server's ``/v1/config`` endpoint and merges
74+
its response with the options above: the server's overrides win over the client
75+
options, which in turn win over the server's defaults.
3076

31-
.. note::
77+
Databases and tables are then created, listed, loaded, renamed and dropped
78+
through the regular ``Catalog`` API, and table snapshots can be listed through
79+
``Catalog::ListSnapshots``.
3280

33-
Current C++ Paimon only supports filesystem catalog. In the future, we will
34-
support REST catalog.
35-
By using the Paimon REST catalog, changes to the catalog will be directly stored
36-
in a remote catalog server which exposed through REST API. See `Java Paimon REST
37-
Catalog <https://paimon.apache.org/docs/master/concepts/rest/overview/>`_.
81+
The C++ REST catalog covers the database, table and snapshot operations of the
82+
``Catalog`` API. The parts of the Java REST catalog that have no C++ counterpart
83+
yet — altering a database or a table, views, functions, partitions, tags, branch
84+
management and consumers — are not supported, and neither is the ``dlf`` token
85+
provider.

‎include/paimon/catalog/catalog.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ class PAIMON_EXPORT Catalog {
5858

5959
/// %Factory method for creating a `Catalog` instance.
6060
///
61-
/// @param root_path Path to the root directory where the catalog is located.
61+
/// @param root_path Path to the root directory where the catalog is located. For the
62+
/// REST catalog (`CatalogOptions::METASTORE` set to "rest") this is
63+
/// instead the warehouse (instance) name registered on the server.
6264
/// @param options Configuration options for catalog initialization.
6365
/// @param file_system Specifies the file system for file operations.
6466
/// If not set, use default file system (configured in

‎include/paimon/catalog_options.h‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2026-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "paimon/visibility.h"
20+
21+
namespace paimon {
22+
23+
/// Catalog-level configuration option keys; table-level keys live in `Options`.
24+
struct PAIMON_EXPORT CatalogOptions {
25+
/// "metastore" - Metastore of the paimon catalog.
26+
/// Supported values are "filesystem" (default) and "rest".
27+
static const char METASTORE[];
28+
29+
/// "uri" - Server url of the REST catalog. Only used when METASTORE is "rest".
30+
static const char URI[];
31+
32+
/// "token" - Token of the "bear" token provider of the REST catalog.
33+
static const char TOKEN[];
34+
35+
/// "token.provider" - Authentication provider of the REST catalog. Only "bear" is
36+
/// supported ("bear" is the protocol's historical spelling of "bearer", do not "fix" it).
37+
static const char TOKEN_PROVIDER[];
38+
39+
/// "table-default." - Prefix of the catalog options that provide table option
40+
/// defaults: "table-default.<key>=<value>" applies "<key>=<value>" to a created
41+
/// table when the caller left "<key>" unset.
42+
static const char TABLE_DEFAULT_OPTION_PREFIX[];
43+
};
44+
45+
} // namespace paimon

‎include/paimon/utils/special_field_ids.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class SpecialFieldIds {
4242

4343
/// Special field ID reserved for index score. Value: CPP_FIELD_ID_END - 1
4444
inline static constexpr int32_t INDEX_SCORE = CPP_FIELD_ID_END - 1;
45+
46+
/// Lowest field ID reserved for system fields; IDs at or above it are excluded from the
47+
/// highest field ID of a schema. Value: INT32_MAX / 2
48+
inline static constexpr int32_t SYSTEM_FIELD_ID_START = std::numeric_limits<int32_t>::max() / 2;
4549
};
4650

4751
} // namespace paimon

‎src/paimon/CMakeLists.txt‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616

1717
set(PAIMON_COMMON_SRCS
18+
common/catalog_options.cpp
1819
common/compression/block_compression_factory.cpp
1920
common/compression/block_compressor.cpp
2021
common/compression/block_decompressor.cpp
@@ -179,13 +180,20 @@ set(PAIMON_COMMON_SRCS
179180
common/utils/roaring_bitmap32.cpp
180181
common/utils/roaring_bitmap64.cpp
181182
common/utils/row_range_index.cpp
183+
common/utils/sensitive_config_utils.cpp
182184
common/utils/status.cpp
183-
common/utils/string_utils.cpp)
185+
common/utils/string_utils.cpp
186+
common/utils/url_utils.cpp)
184187

188+
# The shared HTTP client is used by both the object store file systems and the
189+
# rest catalog.
190+
set(PAIMON_CURL_LINK_LIBS)
191+
if(PAIMON_ENABLE_S3 OR PAIMON_ENABLE_REST)
192+
list(APPEND PAIMON_COMMON_SRCS common/utils/http_client.cpp)
193+
set(PAIMON_CURL_LINK_LIBS CURL::libcurl)
194+
endif()
185195
if(PAIMON_ENABLE_S3)
186-
list(APPEND PAIMON_COMMON_SRCS common/fs/http_client.cpp
187-
common/fs/object_store_file_system.cpp)
188-
set(PAIMON_OBJECT_STORE_LINK_LIBS CURL::libcurl)
196+
list(APPEND PAIMON_COMMON_SRCS common/fs/object_store_file_system.cpp)
189197
endif()
190198

191199
set(PAIMON_CORE_SRCS
@@ -228,6 +236,7 @@ set(PAIMON_CORE_SRCS
228236
core/casting/timestamp_to_timestamp_cast_executor.cpp
229237
core/casting/casting_utils.cpp
230238
core/catalog/catalog.cpp
239+
core/catalog/catalog_utils.cpp
231240
core/catalog/file_system_catalog.cpp
232241
core/catalog/identifier.cpp
233242
core/core_options.cpp
@@ -405,6 +414,18 @@ set(PAIMON_CORE_SRCS
405414
core/utils/snapshot_manager.cpp
406415
core/utils/tag_manager.cpp)
407416

417+
if(PAIMON_ENABLE_REST)
418+
list(APPEND
419+
PAIMON_CORE_SRCS
420+
rest/rest_http_client.cpp
421+
rest/resource_paths.cpp
422+
rest/rest_api.cpp
423+
rest/rest_auth.cpp
424+
rest/rest_catalog.cpp
425+
rest/rest_messages.cpp
426+
rest/rest_util.cpp)
427+
endif()
428+
408429
add_paimon_lib(paimon
409430
SOURCES
410431
${PAIMON_COMMON_SRCS}
@@ -418,7 +439,7 @@ add_paimon_lib(paimon
418439
xxhash
419440
Threads::Threads
420441
RapidJSON
421-
${PAIMON_OBJECT_STORE_LINK_LIBS}
442+
${PAIMON_CURL_LINK_LIBS}
422443
STATIC_LINK_LIBS
423444
arrow
424445
tbb
@@ -428,14 +449,21 @@ add_paimon_lib(paimon
428449
xxhash
429450
Threads::Threads
430451
RapidJSON
431-
${PAIMON_OBJECT_STORE_LINK_LIBS}
452+
${PAIMON_CURL_LINK_LIBS}
432453
SHARED_LINK_FLAGS
433454
${PAIMON_VERSION_SCRIPT_FLAGS})
434455

435456
add_subdirectory(common/file_index)
436457
add_subdirectory(common/global_index)
437458

438459
if(PAIMON_BUILD_TESTS)
460+
# The shared HTTP client is compiled only for the components that need libcurl,
461+
# so its test follows the same gate.
462+
set(PAIMON_COMMON_HTTP_CLIENT_TEST_SRCS)
463+
if(PAIMON_ENABLE_S3 OR PAIMON_ENABLE_REST)
464+
set(PAIMON_COMMON_HTTP_CLIENT_TEST_SRCS common/utils/http_client_test.cpp)
465+
endif()
466+
439467
add_paimon_test(memory_test
440468
SOURCES
441469
common/memory/memory_pool_test.cpp
@@ -593,10 +621,13 @@ if(PAIMON_BUILD_TESTS)
593621
common/io/cache/lru_cache_test.cpp
594622
common/utils/byte_range_combiner_test.cpp
595623
common/utils/scope_guard_test.cpp
624+
common/utils/sensitive_config_utils_test.cpp
596625
common/utils/serialization_utils_test.cpp
597626
common/utils/status_test.cpp
598627
common/utils/stream_utils_test.cpp
599628
common/utils/string_utils_test.cpp
629+
common/utils/url_utils_test.cpp
630+
${PAIMON_COMMON_HTTP_CLIENT_TEST_SRCS}
600631
common/utils/range_test.cpp
601632
common/utils/uuid_test.cpp
602633
common/utils/decimal_utils_test.cpp
@@ -874,4 +905,20 @@ if(PAIMON_BUILD_TESTS)
874905
EXTRA_INCLUDES
875906
${JINDOSDK_INCLUDE_DIR})
876907

908+
if(PAIMON_ENABLE_REST)
909+
add_paimon_test(rest_test
910+
SOURCES
911+
rest/rest_http_client_test.cpp
912+
rest/mock_rest_server.cpp
913+
rest/resource_paths_test.cpp
914+
rest/rest_catalog_test.cpp
915+
rest/rest_messages_test.cpp
916+
rest/rest_util_test.cpp
917+
STATIC_LINK_LIBS
918+
paimon_shared
919+
test_utils_static
920+
${TEST_STATIC_LINK_LIBS}
921+
${GTEST_LINK_TOOLCHAIN})
922+
endif()
923+
877924
endif()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2026-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "paimon/catalog_options.h"
18+
19+
namespace paimon {
20+
21+
const char CatalogOptions::METASTORE[] = "metastore";
22+
const char CatalogOptions::URI[] = "uri";
23+
const char CatalogOptions::TOKEN[] = "token";
24+
const char CatalogOptions::TOKEN_PROVIDER[] = "token.provider";
25+
const char CatalogOptions::TABLE_DEFAULT_OPTION_PREFIX[] = "table-default.";
26+
27+
} // namespace paimon

0 commit comments

Comments
 (0)