Skip to content

Commit 15a1d85

Browse files
SteNicholasclaude
andcommitted
feat(rest): support rest catalog with database/table operations and snapshot listing
Add a REST catalog implementation selected via the "metastore=rest" option: - HttpClient: blocking libcurl client with exponential-backoff retries (429/503 retried for all methods, transport errors only for idempotent ones, honoring a numeric Retry-After header). - RestApi: HTTP + JSON layer with "/v1/config" option merging (overrides > client options > defaults), "header." options as request headers, paged listing and http-status-to-Status error mapping including the x-request-id. - RestCatalog: database/table operations and snapshot listing, "table-default." option defaults, system/branch table checks and schema conversion with the computed highestFieldId. - Bear token authentication provider. - CatalogOptions: catalog-level option keys (metastore, uri, token, token.provider) in a dedicated public header, mirroring the CatalogOptions/CoreOptions separation of the Java implementation. libcurl is used from the system rather than bundled, so the new PAIMON_ENABLE_REST option defaults to OFF like the other optional components with external requirements and is documented alongside them; CI enables it explicitly and installs the libcurl development package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent ac2e446 commit 15a1d85

29 files changed

Lines changed: 4026 additions & 30 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,14 @@ option(PAIMON_ENABLE_LUMINA "Whether to enable lumina vector index" OFF)
6666
option(PAIMON_ENABLE_LUCENE "Whether to enable lucene index" OFF)
6767
option(PAIMON_ENABLE_TANTIVY
6868
"Whether to enable tantivy-fulltext global index (Rust FFI, experimental)" OFF)
69+
option(PAIMON_ENABLE_REST "Whether to enable the rest catalog (requires libcurl)" OFF)
6970
if(PAIMON_ENABLE_ORC)
7071
add_definitions(-DPAIMON_ENABLE_ORC)
7172
endif()
73+
if(PAIMON_ENABLE_REST)
74+
find_package(CURL REQUIRED)
75+
add_definitions(-DPAIMON_ENABLE_REST)
76+
endif()
7277
if(PAIMON_ENABLE_AVRO)
7378
add_definitions(-DPAIMON_ENABLE_AVRO)
7479
endif()

ci/scripts/build_paimon.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ CMAKE_ARGS=(
130130
"-DPAIMON_ENABLE_LUMINA=${ENABLE_LUMINA}"
131131
"-DPAIMON_ENABLE_LUCENE=ON"
132132
"-DPAIMON_ENABLE_TANTIVY=${ENABLE_TANTIVY}"
133+
"-DPAIMON_ENABLE_REST=ON"
133134
"-DPAIMON_LINT_GIT_TARGET_COMMIT=${lint_git_target_commit}"
134135
)
135136

docs/source/building.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ boolean flags to ``cmake``.
146146
* ``-DPAIMON_ENABLE_AVRO=ON``: Apache Avro libraries and Paimon integration
147147
* ``-DPAIMON_ENABLE_JINDO=ON``: Support for Alibaba Jindo filesystems
148148
* ``-DPAIMON_ENABLE_LUMINA=ON``: Support for Lumina vector index, lumina is only supported on gcc9 or higher.
149+
* ``-DPAIMON_ENABLE_REST=ON``: Support for the REST catalog (``metastore=rest``), requires the libcurl development package.
149150

150151
Third-party dependency source
151152
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

include/paimon/catalog_options.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
/// Configuration options for Paimon catalogs.
24+
///
25+
/// The CatalogOptions struct contains static string constants that define catalog-level
26+
/// configuration keys. Table-level configuration keys live in `Options`.
27+
struct PAIMON_EXPORT CatalogOptions {
28+
/// "metastore" - Metastore of the paimon catalog.
29+
/// Supported values are "filesystem" (default) and "rest".
30+
static const char METASTORE[];
31+
32+
/// "uri" - Server url of the REST catalog. Only used when METASTORE is "rest".
33+
static const char URI[];
34+
35+
/// "token" - Token of the "bear" token provider of the REST catalog.
36+
static const char TOKEN[];
37+
38+
/// "token.provider" - Authentication provider of the REST catalog.
39+
/// Currently only "bear" is supported.
40+
static const char TOKEN_PROVIDER[];
41+
};
42+
43+
} // namespace paimon

src/paimon/CMakeLists.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
set(PAIMON_COMMON_SRCS
16+
common/catalog_options.cpp
1617
common/compression/block_compression_factory.cpp
1718
common/compression/block_compressor.cpp
1819
common/compression/block_decompressor.cpp
@@ -403,6 +404,20 @@ set(PAIMON_CORE_SRCS
403404
core/utils/special_field_ids.cpp
404405
core/utils/tag_manager.cpp)
405406

407+
set(PAIMON_REST_LINK_LIBS)
408+
if(PAIMON_ENABLE_REST)
409+
list(APPEND
410+
PAIMON_CORE_SRCS
411+
rest/http_client.cpp
412+
rest/resource_paths.cpp
413+
rest/rest_api.cpp
414+
rest/rest_auth.cpp
415+
rest/rest_catalog.cpp
416+
rest/rest_messages.cpp
417+
rest/rest_util.cpp)
418+
set(PAIMON_REST_LINK_LIBS CURL::libcurl)
419+
endif()
420+
406421
add_paimon_lib(paimon
407422
SOURCES
408423
${PAIMON_COMMON_SRCS}
@@ -417,6 +432,7 @@ add_paimon_lib(paimon
417432
Threads::Threads
418433
RapidJSON
419434
${PAIMON_OBJECT_STORE_LINK_LIBS}
435+
${PAIMON_REST_LINK_LIBS}
420436
STATIC_LINK_LIBS
421437
arrow
422438
tbb
@@ -427,6 +443,7 @@ add_paimon_lib(paimon
427443
Threads::Threads
428444
RapidJSON
429445
${PAIMON_OBJECT_STORE_LINK_LIBS}
446+
${PAIMON_REST_LINK_LIBS}
430447
SHARED_LINK_FLAGS
431448
${PAIMON_VERSION_SCRIPT_FLAGS})
432449

@@ -870,4 +887,18 @@ if(PAIMON_BUILD_TESTS)
870887
EXTRA_INCLUDES
871888
${JINDOSDK_INCLUDE_DIR})
872889

890+
if(PAIMON_ENABLE_REST)
891+
add_paimon_test(rest_test
892+
SOURCES
893+
rest/http_client_test.cpp
894+
rest/mock_rest_server.cpp
895+
rest/rest_catalog_test.cpp
896+
rest/rest_messages_test.cpp
897+
STATIC_LINK_LIBS
898+
paimon_shared
899+
test_utils_static
900+
${TEST_STATIC_LINK_LIBS}
901+
${GTEST_LINK_TOOLCHAIN})
902+
endif()
903+
873904
endif()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
26+
} // namespace paimon

src/paimon/core/catalog/catalog.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818

1919
#include <utility>
2020

21+
#include "paimon/catalog_options.h"
22+
#include "paimon/common/utils/string_utils.h"
2123
#include "paimon/core/catalog/file_system_catalog.h"
2224
#include "paimon/core/core_options.h"
25+
#ifdef PAIMON_ENABLE_REST
26+
#include "paimon/rest/rest_catalog.h"
27+
#endif
2328

2429
namespace paimon {
2530

@@ -31,6 +36,22 @@ const char Catalog::DB_LOCATION_PROP[] = "location";
3136
Result<std::unique_ptr<Catalog>> Catalog::Create(const std::string& root_path,
3237
const std::map<std::string, std::string>& options,
3338
const std::shared_ptr<FileSystem>& file_system) {
39+
std::string metastore = "filesystem";
40+
auto metastore_iter = options.find(CatalogOptions::METASTORE);
41+
if (metastore_iter != options.end()) {
42+
metastore = StringUtils::ToLowerCase(metastore_iter->second);
43+
}
44+
if (metastore == "rest") {
45+
#ifdef PAIMON_ENABLE_REST
46+
return RestCatalog::Create(root_path, options, file_system);
47+
#else
48+
return Status::NotImplemented(
49+
"the rest catalog requires building paimon with PAIMON_ENABLE_REST=ON");
50+
#endif
51+
}
52+
if (metastore != "filesystem") {
53+
return Status::Invalid("unsupported metastore: ", metastore);
54+
}
3455
PAIMON_ASSIGN_OR_RAISE(CoreOptions core_options, CoreOptions::FromMap(options, file_system));
3556
return std::make_unique<FileSystemCatalog>(core_options.GetFileSystem(), root_path, options);
3657
}

src/paimon/core/catalog/file_system_catalog.cpp

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -521,24 +521,6 @@ Status FileSystemCatalog::RenameTable(const Identifier& from_table, const Identi
521521
return Status::OK();
522522
}
523523

524-
namespace {
525-
SnapshotInfo::CommitKind ConvertCommitKind(Snapshot::CommitKind internal) {
526-
if (internal == Snapshot::CommitKind::Append()) {
527-
return SnapshotInfo::CommitKind::APPEND;
528-
}
529-
if (internal == Snapshot::CommitKind::Compact()) {
530-
return SnapshotInfo::CommitKind::COMPACT;
531-
}
532-
if (internal == Snapshot::CommitKind::Overwrite()) {
533-
return SnapshotInfo::CommitKind::OVERWRITE;
534-
}
535-
if (internal == Snapshot::CommitKind::Analyze()) {
536-
return SnapshotInfo::CommitKind::ANALYZE;
537-
}
538-
return SnapshotInfo::CommitKind::UNKNOWN;
539-
}
540-
} // namespace
541-
542524
Result<std::vector<SnapshotInfo>> FileSystemCatalog::ListSnapshots(
543525
const Identifier& identifier, const std::string& branch) const {
544526
PAIMON_ASSIGN_OR_RAISE(bool exists, TableExists(identifier));
@@ -553,20 +535,9 @@ Result<std::vector<SnapshotInfo>> FileSystemCatalog::ListSnapshots(
553535

554536
std::vector<SnapshotInfo> result;
555537
result.reserve(snapshots.size());
556-
557538
for (const auto& snap : snapshots) {
558-
SnapshotInfo info;
559-
info.snapshot_id = snap.Id();
560-
info.schema_id = snap.SchemaId();
561-
info.commit_user = snap.CommitUser();
562-
info.commit_kind = ConvertCommitKind(snap.GetCommitKind());
563-
info.time_millis = snap.TimeMillis();
564-
info.total_record_count = snap.TotalRecordCount();
565-
info.delta_record_count = snap.DeltaRecordCount();
566-
info.watermark = snap.Watermark();
567-
result.push_back(std::move(info));
539+
result.push_back(snap.ToSnapshotInfo());
568540
}
569-
570541
return result;
571542
}
572543

src/paimon/core/snapshot.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,4 +313,27 @@ Result<Snapshot> Snapshot::FromPath(const std::shared_ptr<FileSystem>& fs,
313313
return snapshot;
314314
}
315315

316+
SnapshotInfo Snapshot::ToSnapshotInfo() const {
317+
SnapshotInfo info;
318+
info.snapshot_id = Id();
319+
info.schema_id = SchemaId();
320+
info.commit_user = CommitUser();
321+
if (commit_kind_ == CommitKind::Append()) {
322+
info.commit_kind = SnapshotInfo::CommitKind::APPEND;
323+
} else if (commit_kind_ == CommitKind::Compact()) {
324+
info.commit_kind = SnapshotInfo::CommitKind::COMPACT;
325+
} else if (commit_kind_ == CommitKind::Overwrite()) {
326+
info.commit_kind = SnapshotInfo::CommitKind::OVERWRITE;
327+
} else if (commit_kind_ == CommitKind::Analyze()) {
328+
info.commit_kind = SnapshotInfo::CommitKind::ANALYZE;
329+
} else {
330+
info.commit_kind = SnapshotInfo::CommitKind::UNKNOWN;
331+
}
332+
info.time_millis = TimeMillis();
333+
info.total_record_count = TotalRecordCount();
334+
info.delta_record_count = DeltaRecordCount();
335+
info.watermark = Watermark();
336+
return info;
337+
}
338+
316339
} // namespace paimon

src/paimon/core/snapshot.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "paimon/common/utils/jsonizable.h"
2727
#include "paimon/result.h"
28+
#include "paimon/snapshot/snapshot_info.h"
2829
#include "paimon/type_fwd.h"
2930
#include "rapidjson/allocators.h"
3031
#include "rapidjson/document.h"
@@ -129,6 +130,8 @@ class Snapshot : public Jsonizable<Snapshot> {
129130
bool operator==(const Snapshot& other) const;
130131
bool TEST_Equal(const Snapshot& other) const;
131132

133+
SnapshotInfo ToSnapshotInfo() const;
134+
132135
public:
133136
static constexpr int64_t FIRST_SNAPSHOT_ID = 1;
134137
static constexpr int32_t TABLE_STORE_02_VERSION = 1;

0 commit comments

Comments
 (0)