diff --git a/ibis/backends/databricks/__init__.py b/ibis/backends/databricks/__init__.py index b7917544d26b..e4bd29d16493 100644 --- a/ibis/backends/databricks/__init__.py +++ b/ibis/backends/databricks/__init__.py @@ -7,6 +7,7 @@ import getpass import json import os +import re import sys import tempfile from typing import TYPE_CHECKING, Any @@ -638,7 +639,10 @@ def _ensure_path_for_memtable(self, name: str) -> str: def _generate_volume_path(self) -> str: """Has runtime effects: prompts the user for a password, and fetches the current database and catalog from the backend.""" short_version = "".join(map(str, sys.version_info[:3])) - volume_name = f"{getpass.getuser()}-py={short_version}-pid={os.getpid()}" + # a volume name is a single identifier, so a `.` in the username would + # split it into another level of the catalog.schema.volume name + user = re.sub(r"[^\w-]", "_", getpass.getuser()) + volume_name = f"{user}-py={short_version}-pid={os.getpid()}" return f"/Volumes/{self._backend.current_catalog}/{self._backend.current_database}/{volume_name}" def _create_volume(self, path: str) -> None: diff --git a/ibis/backends/databricks/tests/test_memtable.py b/ibis/backends/databricks/tests/test_memtable.py new file mode 100644 index 000000000000..bee0374a51c1 --- /dev/null +++ b/ibis/backends/databricks/tests/test_memtable.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import pytest + +pytest.importorskip("databricks.sql") + +from ibis.backends.databricks import MemtableManager + + +class _FakeBackend: + current_catalog = "cat" + current_database = "db" + + +@pytest.mark.parametrize( + ("user", "expected"), + [ + ("first.last", "first_last"), + ("first last", "first_last"), + ("plain-user_1", "plain-user_1"), + ], +) +def test_volume_path_is_a_single_name(monkeypatch, user, expected): + monkeypatch.setattr("getpass.getuser", lambda: user) + + path = MemtableManager(_FakeBackend(), None).get_volume_path() + + prefix, volume = path.rsplit("/", 1) + assert prefix == "/Volumes/cat/db" + assert volume.startswith(f"{expected}-py=") + assert "." not in volume