From dcca99afc69b1a2a52bd488ea5b00f65426c5e23 Mon Sep 17 00:00:00 2001 From: Nilanjan Chatterjee Date: Sat, 10 Oct 2026 15:38:09 -0500 Subject: [PATCH] fix(databricks): keep the default memtable volume name a single identifier The default volume name starts with the local username. A username such as first.last added a level to the catalog.schema.volume name, so creating the volume failed with UC_VOLUME_NOT_FOUND. Replace characters other than letters, digits, `_` and `-` in the username with `_`. Fixes #11950 Co-Authored-By: Claude Opus 5.5 --- ibis/backends/databricks/__init__.py | 6 +++- .../databricks/tests/test_memtable.py | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 ibis/backends/databricks/tests/test_memtable.py 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