From e73d4618d8562bdb65dda04b2fd0fe968d453535 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Tue, 16 Jun 2026 08:13:10 -0700 Subject: [PATCH 1/3] fix(mv): create_materialized_view passes query as keyword, not positional The sync RemoteDBConnection.create_materialized_view assembled the SELECT but called the async create_materialized_view with the query as the 2nd positional arg, which binds to `source=` (query= is keyword-only). Every call then failed the "needs either query= or both source and select" validation. Pass query=query. Co-Authored-By: Claude Opus 4.8 (1M context) --- python/python/lancedb/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/python/lancedb/db.py b/python/python/lancedb/db.py index e0fae6c01..d12cbbdbe 100644 --- a/python/python/lancedb/db.py +++ b/python/python/lancedb/db.py @@ -677,7 +677,7 @@ class DBConnection(EnforceOverrides): job_id = LOOP.run( self._conn.create_materialized_view( name, - query, + query=query, auto_refresh=auto_refresh, with_no_data=with_no_data, partition_by=partition_by, From 6191542cfec5f43e598ddbc2e20b84259e3ed485 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Tue, 16 Jun 2026 08:14:48 -0700 Subject: [PATCH 2/3] fix(mv): MaterializedView.refresh calls the async _refresh (underscore) The sync _refresh_materialized_view called self._conn.refresh_materialized_view (no underscore); the async method is _refresh_materialized_view, so MaterializedView.refresh() raised AttributeError. Add the underscore. Co-Authored-By: Claude Opus 4.8 (1M context) --- python/python/lancedb/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/python/lancedb/db.py b/python/python/lancedb/db.py index d12cbbdbe..19e5cf968 100644 --- a/python/python/lancedb/db.py +++ b/python/python/lancedb/db.py @@ -723,7 +723,7 @@ class DBConnection(EnforceOverrides): instead of the default incremental refresh. """ return LOOP.run( - self._conn.refresh_materialized_view( + self._conn._refresh_materialized_view( name, full=full, src_version=src_version, From 434a5be1874c742c56bfeae4ad82b15beca80ea7 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Tue, 16 Jun 2026 08:43:20 -0700 Subject: [PATCH 3/3] feat(client): Table.refresh_column returns a JobHandle (like MaterializedView.refresh) refresh_column returned the bare job-id str, so callers had to wrap it: db.job(tbl.refresh_column("c")).wait(). Mirror MaterializedView.refresh() and return a JobHandle directly, so tbl.refresh_column("c").wait() / .status() / .id work without the wrapper. (db.job(job_id) stays for reconnecting by a stored id.) Co-Authored-By: Claude Opus 4.8 (1M context) --- python/python/lancedb/table.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/python/python/lancedb/table.py b/python/python/lancedb/table.py index 62b84deff..b6bd6f370 100644 --- a/python/python/lancedb/table.py +++ b/python/python/lancedb/table.py @@ -3813,22 +3813,26 @@ class LanceTable(Table): max_workers: Optional[int] = None, batch_size: Optional[int] = None, priority: Optional[str] = None, - ) -> str: + ) -> "JobHandle": """Trigger recompute of computed columns (REFRESH COLUMN). The expression is resolved server-side from each column's stored binding; columns bound to the same struct-returning function - refresh together. Returns the refresh job id. Server-backed - feature (LanceDB Enterprise / Cloud). + refresh together. Returns a `JobHandle` to wait on, poll, or cancel + (``tbl.refresh_column("col").wait()``) -- mirrors + `MaterializedView.refresh()`. Server-backed feature (LanceDB + Enterprise / Cloud). num_workers / max_workers / batch_size / priority are per-refresh scheduling knobs (how to run THIS refresh) and override any default the function carries. `priority` is a Kueue tier (training | interactive | backfill). """ + from .udf import JobHandle + if isinstance(columns, str): columns = [columns] - return LOOP.run( + job_id = LOOP.run( self._table.refresh_column( list(columns), where=where, @@ -3838,6 +3842,7 @@ class LanceTable(Table): priority=priority, ) ) + return JobHandle(self._conn, job_id) def alter_columns( self, *alterations: Iterable[Dict[str, str]]