@@ -169,6 +169,23 @@ it; writers also allow ``WithFileSystemSchemeToIdentifierMap`` to override
169169file-system selection. For format tables, use ``WriteContextBuilder(FormatTable) ``
170170instead.
171171
172+ A file system passed to ``WithFileSystem `` is used exactly as it is, so a custom
173+ one that signs each request itself stays authenticated by drawing the table's
174+ temporary credentials from ``Catalog::GetTableCredentialProvider ``: hold the provider
175+ and call ``CredentialProvider::GetCredentials `` at each access, which returns
176+ credentials that are still valid and reloads them before they expire. A catalog
177+ that issues no per-table credentials returns a null provider, and the custom file
178+ system's own configuration authenticates the access.
179+
180+ .. code-block :: cpp
181+
182+ PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<paimon::CredentialProvider> provider,
183+ shared_catalog->GetTableCredentialProvider(paimon::Identifier("db", "tbl")));
184+ // MyFileSystem merges provider->GetCredentials() over its own options per access.
185+ auto my_fs = std::make_shared<MyFileSystem>(provider);
186+ write_builder.WithCatalog(shared_catalog, paimon::Identifier("db", "tbl"))
187+ .WithFileSystem(my_fs);
188+
172189 The snapshot being committed carries a uuid generated on the client, and the
173190commit names the snapshot it is based on by that snapshot's uuid, so the server
174191can tell a commit that raced another. A commit which lost such a race is
@@ -235,6 +252,52 @@ so its restored file references are visible to the expiration operation.
235252 A branch the catalog holds without that directory, or one created while
236253 expiration runs, is not found: do not expire a table with such a branch, and
237254 serialize branch creation and expiration through the upstream coordinator.
255+ =======
256+ Reading through the catalog
257+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258+ Pass the catalog and table identifier to ``ReadContextBuilder::WithCatalog `` or
259+ ``ScanContextBuilder::WithCatalog `` to read a native table the same way, in one
260+ call: the table's schema comes from the catalog, its data is read through the
261+ table's file system, and a scan reads the latest snapshot from the catalog:
262+
263+ .. code-block :: cpp
264+
265+ // Readers and scanners share ownership of the catalog.
266+ std::shared_ptr<paimon::Catalog> shared_catalog(std::move(catalog));
267+
268+ paimon::ScanContextBuilder scan_builder(table_path);
269+ scan_builder.WithCatalog(shared_catalog, paimon::Identifier("db", "tbl"));
270+ PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::ScanContext> scan_context,
271+ scan_builder.Finish());
272+ PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::TableScan> scan,
273+ paimon::TableScan::Create(std::move(scan_context)));
274+
275+ paimon::ReadContextBuilder read_builder(table_path);
276+ read_builder.WithCatalog(shared_catalog, paimon::Identifier("db", "tbl"));
277+ PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::ReadContext> read_context,
278+ read_builder.Finish());
279+ PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::TableRead> read,
280+ paimon::TableRead::Create(std::move(read_context)));
281+
282+ A scan takes only its latest snapshot from the catalog; the manifests that
283+ snapshot points at and any historical snapshots still come from the table path.
284+ Both builders read the table's schema from the catalog when ``Finish() `` builds
285+ the context, which is one catalog request; call ``SetTableSchema `` with a schema
286+ you already hold to skip it. The table's file system, including the per-table
287+ temporary credentials a catalog serves through ``Catalog::GetTableFileSystem ``,
288+ is used for both. ``WithFileSystem `` overrides it, and a custom file system
289+ that signs its own requests can draw the table's credentials from
290+ ``Catalog::GetTableCredentialProvider `` as shown above; the read builder also allows
291+ ``WithFileSystemSchemeToIdentifierMap ``.
292+
293+ Only the main branch is supported. Both builders reject other branches in the
294+ identifier or ``branch `` option; the read builder also checks ``WithBranch ``.
295+ Explicit ``tbl$branch_main `` and ``branch=main `` are accepted. For a format
296+ table, use ``ReadContextBuilder(FormatTable) `` or ``ScanContextBuilder(FormatTable) ``
297+ instead; ``WithCatalog `` is rejected. A system table path such as ``tbl$snapshots ``
298+ reads snapshots from under the table path, which a version-managed catalog
299+ publishes nowhere but in itself, so it is rejected with ``NotImplemented ``; scan
300+ such a table with ``WithFileSystem(Catalog::GetTableFileSystem(...)) `` instead.
238301
239302The C++ REST catalog covers the database, table, snapshot and commit operations
240303of the ``Catalog `` API. The parts of the Java REST catalog that have no C++
@@ -298,3 +361,66 @@ identifier. The request body carries the table id but no table name and no
298361branch, so the branch has to appear in the URL the caller sends that body to:
299362the commit endpoint of ``tbl$branch_dev ``, not the one of ``tbl ``. A body sent
300363to the bare table's URL publishes the branch's snapshot on the main branch.
364+ =======
365+ tags, branch management and consumers — are not supported.
366+
367+ Authenticating with credentials of your own
368+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
369+ Credentials that a catalog does not issue — from a token service of your own, or
370+ from a catalog Paimon has no client for — reach the built-in file systems through
371+ a ``CredentialProvider ``. Implement it together with a
372+ ``CredentialProviderFactory `` that builds it, register the factory with
373+ ``REGISTER_PAIMON_FACTORY ``, and name it in the ``fs.credential.provider ``
374+ option: the file system then builds the provider once and asks it for credentials
375+ as it signs, so credentials are reloaded as they expire without the file system
376+ being rebuilt.
377+
378+ .. code-block :: cpp
379+
380+ class MyCredentialProvider : public paimon::CredentialProvider {
381+ public:
382+ // Called as each access is signed; reload the credentials before they expire.
383+ paimon::Result<std::map<std::string, std::string>> GetCredentials() const override {
384+ return std::map<std::string, std::string>{
385+ {"fs.oss.accessKeyId", ...}, {"fs.oss.accessKeySecret", ...},
386+ {"fs.oss.securityToken", ...}};
387+ }
388+ };
389+
390+ class MyCredentialProviderFactory : public paimon::CredentialProviderFactory {
391+ public:
392+ const char* Identifier() const override {
393+ return "my-token-service";
394+ }
395+
396+ paimon::Result<std::shared_ptr<paimon::CredentialProvider>> Create(
397+ const std::string& path,
398+ const std::map<std::string, std::string>& options) const override {
399+ return std::make_shared<MyCredentialProvider>(path, options);
400+ }
401+ };
402+
403+ REGISTER_PAIMON_FACTORY(MyCredentialProviderFactory);
404+
405+ The credentials are file-system options, e.g. ``fs.oss.securityToken ``, and are
406+ merged over the options the file system was configured with, so a provider serves
407+ only what rotates and the endpoint and the rest stay in the options. The options
408+ the factory is called with are those of the accesses to authenticate, which is
409+ where an implementation reads its own configuration, such as the address of the
410+ token service, from.
411+
412+ .. code-block :: none
413+
414+ fs.credential.provider=my-token-service
415+
416+ Only the OSS file system consults a provider today; the others report
417+ ``NotImplemented `` when the option names one, rather than signing with the
418+ credentials of the moment they were built and failing once those expire. All
419+ factories share one identifier space, so an identifier a file system factory
420+ already takes — ``oss ``, ``s3 ``, ``local ``, ``jindo `` — would replace it; name the
421+ provider after where its credentials come from instead.
422+
423+ This is the other of the two ways a provider is reached. A custom file system
424+ passed to ``WithFileSystem `` signs its own requests and pulls from the provider
425+ itself, which is what ``Catalog::GetTableCredentialProvider `` serves; a built-in
426+ file system signs on your behalf and is pointed at a provider by this option.
0 commit comments