This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 53d3fa6443 [python][daft] Fix single-part catalog table lookup (#8039)
53d3fa6443 is described below
commit 53d3fa6443a7c7cd7b0e6dfccd6332fc7f14faaf
Author: QuakeWang <[email protected]>
AuthorDate: Sat May 30 23:13:36 2026 +0800
[python][daft] Fix single-part catalog table lookup (#8039)
Daft Catalog accepts single-part identifiers, and PaimonCatalog
previously forwarded them to pypaimon as plain table names. For table
APIs, this is invalid because pypaimon's Identifier parser
requires `db.table`, so `get_table("missing_table")` and
`drop_table("missing_table")` leaked `ValueError` instead of Daft
`NotFoundError`.
This PR adds table-specific identifier handling in `PaimonCatalog`:
single-part table identifiers are handled at the Daft catalog layer as
not found, and `has_table("missing_table")` now returns `False`.
---
paimon-python/pypaimon/daft/daft_catalog.py | 26 ++++++++++++++++++----
.../pypaimon/tests/daft/daft_catalog_test.py | 17 ++++++++++++++
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/paimon-python/pypaimon/daft/daft_catalog.py
b/paimon-python/pypaimon/daft/daft_catalog.py
index aaf3f6879d..2cbe479b81 100644
--- a/paimon-python/pypaimon/daft/daft_catalog.py
+++ b/paimon-python/pypaimon/daft/daft_catalog.py
@@ -115,7 +115,9 @@ class PaimonCatalog(Catalog):
raise NotFoundError(f"Namespace '{db_name}' not found.") from ex
def _drop_table(self, ident: Identifier) -> None:
- paimon_ident = _to_paimon_ident(ident)
+ paimon_ident = _to_paimon_table_ident(ident)
+ if paimon_ident is None:
+ raise NotFoundError(f"Table '{ident}' not found.")
try:
self._inner.drop_table(paimon_ident, ignore_if_not_exists=False)
except TableNotExistException as ex:
@@ -134,7 +136,9 @@ class PaimonCatalog(Catalog):
return False
def _has_table(self, ident: Identifier) -> bool:
- paimon_ident = _to_paimon_ident(ident)
+ paimon_ident = _to_paimon_table_ident(ident)
+ if paimon_ident is None:
+ return False
try:
self._inner.get_table(paimon_ident)
return True
@@ -149,7 +153,9 @@ class PaimonCatalog(Catalog):
raise NotFoundError(f"Function '{ident}' not found in catalog
'{self.name}'")
def _get_table(self, ident: Identifier) -> PaimonTable:
- paimon_ident = _to_paimon_ident(ident)
+ paimon_ident = _to_paimon_table_ident(ident)
+ if paimon_ident is None:
+ raise NotFoundError(f"Table '{ident}' not found.")
try:
inner = self._inner.get_table(paimon_ident)
return PaimonTable(inner, catalog_options=self._catalog_options)
@@ -250,7 +256,7 @@ class PaimonTable(Table):
def _to_paimon_ident(ident: Identifier) -> str:
"""Convert a Daft identifier to a pypaimon identifier string.
- - 1 part (table,) -> 'table'
+ - 1 part (namespace/table,) -> 'namespace_or_table'
- 2 parts (db, table) -> 'db.table'
- 3 parts (catalog, db, table) -> 'db.table' (catalog prefix stripped)
"""
@@ -264,6 +270,18 @@ def _to_paimon_ident(ident: Identifier) -> str:
return ident
+def _to_paimon_table_ident(ident: Identifier) -> str | None:
+ """Convert a Daft table identifier to Paimon's required db.table form."""
+ if isinstance(ident, Identifier):
+ parts = tuple(ident)
+ if len(parts) == 3:
+ return f"{parts[1]}.{parts[2]}"
+ if len(parts) == 2:
+ return f"{parts[0]}.{parts[1]}"
+ return None
+ return ident
+
+
def _cast_large_types(arrow_schema: pa.Schema) -> pa.Schema:
"""Convert PyArrow schema to be compatible with pypaimon.
diff --git a/paimon-python/pypaimon/tests/daft/daft_catalog_test.py
b/paimon-python/pypaimon/tests/daft/daft_catalog_test.py
index 0b34452ed4..95519aa3be 100644
--- a/paimon-python/pypaimon/tests/daft/daft_catalog_test.py
+++ b/paimon-python/pypaimon/tests/daft/daft_catalog_test.py
@@ -156,6 +156,7 @@ def test_catalog_has_table(paimon_catalog):
assert daft_catalog.has_table("test_db.test_table")
assert not daft_catalog.has_table("test_db.nonexistent_table")
assert not daft_catalog.has_table("nonexistent_db.test_table")
+ assert not daft_catalog.has_table("missing_table")
def test_catalog_list_tables(paimon_catalog):
@@ -188,6 +189,14 @@ def test_catalog_get_table_not_found(paimon_catalog):
daft_catalog.get_table("test_db.nonexistent_table")
+def test_catalog_get_table_single_part_not_found(paimon_catalog):
+ from daft.catalog import NotFoundError
+
+ daft_catalog, _, _ = paimon_catalog
+ with pytest.raises(NotFoundError):
+ daft_catalog.get_table("missing_table")
+
+
def test_catalog_drop_table(paimon_catalog):
daft_catalog, _, _ = paimon_catalog
assert daft_catalog.has_table("test_db.test_table")
@@ -195,6 +204,14 @@ def test_catalog_drop_table(paimon_catalog):
assert not daft_catalog.has_table("test_db.test_table")
+def test_catalog_drop_table_single_part_not_found(paimon_catalog):
+ from daft.catalog import NotFoundError
+
+ daft_catalog, _, _ = paimon_catalog
+ with pytest.raises(NotFoundError):
+ daft_catalog.drop_table("missing_table")
+
+
def test_catalog_create_table(tmp_path):
inner = pypaimon.CatalogFactory.create({"warehouse": str(tmp_path)})
inner.create_database("mydb", ignore_if_exists=True)