Skip to content

Commit 92a3e2f

Browse files
authored
Add missing catalog tests (#2955)
<!-- Thanks for opening a pull request! --> <!-- In the case this PR will resolve an issue, please replace ${GITHUB_ISSUE_ID} below with the actual Github issue id. --> <!-- Closes #${GITHUB_ISSUE_ID} --> # Rationale for this change There's a couple missing catalog tests around supporting namespaces + tables with slashes/dots. Along the way, I found an issue in how we create the CreateTableRequest + RegisterTableRequest ## Are these changes tested? Tests are included. ## Are there any user-facing changes? <!-- In the case of user-facing changes, please add the changelog label. -->
1 parent b11d865 commit 92a3e2f

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

pyiceberg/catalog/rest/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ def _create_table(
790790
if location:
791791
location = location.rstrip("/")
792792
request = CreateTableRequest(
793-
name=namespace_and_table["table"],
793+
name=self._identifier_to_validated_tuple(identifier)[-1],
794794
location=location,
795795
table_schema=fresh_schema,
796796
partition_spec=fresh_partition_spec,
@@ -869,7 +869,7 @@ def register_table(self, identifier: str | Identifier, metadata_location: str) -
869869
self._check_endpoint(Capability.V1_REGISTER_TABLE)
870870
namespace_and_table = self._split_identifier_for_path(identifier)
871871
request = RegisterTableRequest(
872-
name=namespace_and_table["table"],
872+
name=self._identifier_to_validated_tuple(identifier)[-1],
873873
metadata_location=metadata_location,
874874
)
875875
serialized_json = request.model_dump_json().encode(UTF8)

tests/integration/test_catalog.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,29 @@ def test_incompatible_partitioned_schema_evolution(
672672
assert table.schema() == Schema(NestedField(2, "tpep_pickup_datetime", TimestampType(), False))
673673

674674

675+
@pytest.mark.integration
676+
@pytest.mark.parametrize("test_catalog", CATALOGS)
677+
def test_namespace_with_slash(test_catalog: Catalog) -> None:
678+
if isinstance(test_catalog, HiveCatalog):
679+
pytest.skip(f"{type(test_catalog).__name__} does not support slash in namespace")
680+
681+
namespace = ("new/db",)
682+
683+
if test_catalog.namespace_exists(namespace):
684+
test_catalog.drop_namespace(namespace)
685+
686+
assert not test_catalog.namespace_exists(namespace)
687+
688+
test_catalog.create_namespace(namespace)
689+
assert test_catalog.namespace_exists(namespace)
690+
691+
properties = test_catalog.load_namespace_properties(namespace)
692+
assert properties is not None
693+
694+
test_catalog.drop_namespace(namespace)
695+
assert not test_catalog.namespace_exists(namespace)
696+
697+
675698
@pytest.mark.integration
676699
@pytest.mark.parametrize("test_catalog", CATALOGS)
677700
def test_incompatible_sorted_schema_evolution(
@@ -692,3 +715,111 @@ def test_incompatible_sorted_schema_evolution(
692715
assert table.schema() == Schema(
693716
NestedField(1, "VendorID", IntegerType(), False), NestedField(2, "tpep_pickup_datetime", TimestampType(), False)
694717
)
718+
719+
720+
@pytest.mark.integration
721+
@pytest.mark.parametrize("test_catalog", CATALOGS)
722+
def test_namespace_with_dot(test_catalog: Catalog) -> None:
723+
if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
724+
pytest.skip(f"{type(test_catalog).__name__} does not support dot in namespace")
725+
726+
namespace = ("new.db",)
727+
728+
if test_catalog.namespace_exists(namespace):
729+
test_catalog.drop_namespace(namespace)
730+
731+
assert not test_catalog.namespace_exists(namespace)
732+
733+
test_catalog.create_namespace(namespace)
734+
assert test_catalog.namespace_exists(namespace)
735+
736+
# REST Catalog fixture treats this as a hierarchical namespace.
737+
# Calling list namespaces will get `new`, not `new.db`.
738+
if isinstance(test_catalog, RestCatalog):
739+
namespaces = test_catalog.list_namespaces()
740+
assert ("new",) in namespaces or ("new.db",) in namespaces
741+
else:
742+
assert namespace in test_catalog.list_namespaces()
743+
744+
properties = test_catalog.load_namespace_properties(namespace)
745+
assert properties is not None
746+
747+
test_catalog.drop_namespace(namespace)
748+
assert not test_catalog.namespace_exists(namespace)
749+
750+
751+
@pytest.mark.integration
752+
@pytest.mark.parametrize("test_catalog", CATALOGS)
753+
def test_table_name_with_slash(test_catalog: Catalog, table_schema_simple: Schema) -> None:
754+
if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
755+
pytest.skip(f"{type(test_catalog).__name__} does not support slash in table name")
756+
757+
namespace = ("ns_slash",)
758+
table_ident = ("ns_slash", "tab/le")
759+
760+
if not test_catalog.namespace_exists(namespace):
761+
test_catalog.create_namespace(namespace)
762+
763+
if test_catalog.table_exists(table_ident):
764+
test_catalog.drop_table(table_ident)
765+
766+
assert not test_catalog.table_exists(table_ident)
767+
768+
test_catalog.create_table(table_ident, table_schema_simple)
769+
assert test_catalog.table_exists(table_ident)
770+
771+
table = test_catalog.load_table(table_ident)
772+
assert table.schema().as_struct() == table_schema_simple.as_struct()
773+
774+
test_catalog.drop_table(table_ident)
775+
assert not test_catalog.table_exists(table_ident)
776+
777+
778+
@pytest.mark.integration
779+
@pytest.mark.parametrize("test_catalog", CATALOGS)
780+
def test_table_name_with_dot(test_catalog: Catalog, table_schema_simple: Schema) -> None:
781+
if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
782+
pytest.skip(f"{type(test_catalog).__name__} does not support dot in table name")
783+
784+
namespace = ("ns_dot",)
785+
table_ident = ("ns_dot", "ta.ble")
786+
787+
if not test_catalog.namespace_exists(namespace):
788+
test_catalog.create_namespace(namespace)
789+
790+
if test_catalog.table_exists(table_ident):
791+
test_catalog.drop_table(table_ident)
792+
793+
assert not test_catalog.table_exists(table_ident)
794+
795+
test_catalog.create_table(table_ident, table_schema_simple)
796+
assert test_catalog.table_exists(table_ident)
797+
798+
assert table_ident in test_catalog.list_tables(namespace)
799+
800+
table = test_catalog.load_table(table_ident)
801+
assert table.schema().as_struct() == table_schema_simple.as_struct()
802+
803+
test_catalog.drop_table(table_ident)
804+
assert not test_catalog.table_exists(table_ident)
805+
806+
807+
@pytest.mark.integration
808+
@pytest.mark.parametrize("test_catalog", CATALOGS)
809+
def test_drop_missing_table(test_catalog: Catalog, database_name: str) -> None:
810+
test_catalog.create_namespace_if_not_exists(database_name)
811+
table_ident = (database_name, "missing_table")
812+
assert not test_catalog.table_exists(table_ident)
813+
with pytest.raises(NoSuchTableError):
814+
test_catalog.drop_table(table_ident)
815+
816+
817+
@pytest.mark.integration
818+
@pytest.mark.parametrize("test_catalog", CATALOGS)
819+
def test_drop_nonexistent_namespace(test_catalog: Catalog) -> None:
820+
if isinstance(test_catalog, HiveCatalog):
821+
pytest.skip("HiveCatalog raises NoSuchObjectException instead of NoSuchNamespaceError")
822+
823+
namespace = ("non_existent_namespace",)
824+
with pytest.raises(NoSuchNamespaceError):
825+
test_catalog.drop_namespace(namespace)

0 commit comments

Comments
 (0)