mirror of
https://github.com/lancedb/lancedb.git
synced 2026-11-13 14:16:54 +00:00
feat: use SQL for remote view lifecycle
Stacked on #4675. Remote view create, describe, list, and drop now execute SQL. CREATE returns the stored definition, resolution defaults, and Arrow schema from the same execution; DESCRIBE VIEW returns that metadata and SHOW VIEWS supports scoped pagination. `drop_view_async()` returns a `job_type="query"` handle whose single `job.id` is the SQL query UUID. Waiting drains the query result once and caches completion; `describe_query(job.id)` works on the submitting connection. Completion means catalog removal, with physical cleanup continuing separately. Query handles have no registry history and cannot be reopened through `open_job()`.
This commit is contained in:
+14
-12
@@ -1178,10 +1178,11 @@ class DBConnection(EnforceOverrides):
|
||||
def drop_view(
|
||||
self, name: str, *, namespace_path: Optional[List[str]] = None
|
||||
) -> None:
|
||||
"""Drop a view and wait for its definition to be deleted.
|
||||
"""Drop a view and wait for catalog removal.
|
||||
|
||||
The tables it reads are untouched: a view holds no rows of its own. Use
|
||||
:meth:`drop_view_async` to get the cleanup job instead of waiting on it.
|
||||
:meth:`drop_view_async` to retain its SQL query job. Completion confirms
|
||||
catalog removal; physical cleanup continues in the background.
|
||||
LanceDB Embedded connections raise ``NotImplementedError``.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
@@ -1191,12 +1192,11 @@ class DBConnection(EnforceOverrides):
|
||||
def drop_view_async(
|
||||
self, name: str, *, namespace_path: Optional[List[str]] = None
|
||||
) -> "Job[None]":
|
||||
"""Start dropping a view and return the job deleting its definition.
|
||||
"""Drop a view and return its SQL query job.
|
||||
|
||||
The name is free before this returns. Call :meth:`Job.wait` to wait for
|
||||
the definition dataset to be deleted. When nothing was bound to the
|
||||
name, the returned job is already finished and has no id. Local
|
||||
connections raise ``NotImplementedError``.
|
||||
The job has type ``query`` and its ID is the SQL query UUID. Call
|
||||
:meth:`Job.wait` to confirm catalog removal. Physical cleanup continues
|
||||
in the background. Local connections raise ``NotImplementedError``.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"View operations are not supported for this connection type"
|
||||
@@ -3549,10 +3549,11 @@ class AsyncConnection(object):
|
||||
async def drop_view(
|
||||
self, name: str, *, namespace_path: Optional[List[str]] = None
|
||||
) -> None:
|
||||
"""Drop a view and wait for its definition to be deleted.
|
||||
"""Drop a view and wait for catalog removal.
|
||||
|
||||
The tables it reads are untouched. Use :meth:`drop_view_async` to get
|
||||
the cleanup job instead of waiting on it.
|
||||
its SQL query job. Completion confirms catalog removal; physical cleanup
|
||||
continues in the background.
|
||||
"""
|
||||
await self._inner.drop_view(name, list(namespace_path or []))
|
||||
|
||||
@@ -3562,10 +3563,11 @@ class AsyncConnection(object):
|
||||
*,
|
||||
namespace_path: Optional[List[str]] = None,
|
||||
) -> AsyncJob[None]:
|
||||
"""Start dropping a view and return the job deleting its definition.
|
||||
"""Drop a view and return its SQL query job.
|
||||
|
||||
The name is free before this returns. Await :meth:`AsyncJob.wait` before
|
||||
assuming the definition dataset is gone.
|
||||
The job has type ``query`` and its ID is the SQL query UUID. Await
|
||||
:meth:`AsyncJob.wait` to confirm catalog removal. Physical cleanup
|
||||
continues in the background.
|
||||
"""
|
||||
if namespace_path is None:
|
||||
namespace_path = []
|
||||
|
||||
@@ -84,7 +84,7 @@ async def advance(items, asynchronous):
|
||||
return await items.__anext__() if asynchronous else next(items)
|
||||
|
||||
|
||||
KINDS = ["views", "jobs"]
|
||||
KINDS = ["jobs"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -4570,57 +4570,3 @@ def test_remote_job_handle_reports_its_own_detail():
|
||||
"limit": 500,
|
||||
"filter": "state = 'claim_complete'",
|
||||
}
|
||||
|
||||
|
||||
def test_view_crud_addresses_its_own_routes():
|
||||
# The view verbs are their own routes, and the schema comes back in the
|
||||
# namespace spec's JSON encoding, decoded into a pyarrow schema.
|
||||
paths = []
|
||||
|
||||
def handler(request):
|
||||
paths.append((request.command, request.path))
|
||||
if request.path.endswith("/view/list"):
|
||||
body = {"views": ["adults"]}
|
||||
elif request.path.endswith("/drop"):
|
||||
body = {}
|
||||
else:
|
||||
body = {
|
||||
"name": "adults",
|
||||
"namespace": ["analytics"],
|
||||
"query": "SELECT name FROM people",
|
||||
"default_database": "dev",
|
||||
"default_namespace": ["analytics"],
|
||||
"schema": {
|
||||
"fields": [
|
||||
{"name": "name", "nullable": True, "type": {"type": "utf8"}}
|
||||
]
|
||||
},
|
||||
}
|
||||
request.send_response(200)
|
||||
request.send_header("Content-Type", "application/json")
|
||||
request.end_headers()
|
||||
request.wfile.write(json.dumps(body).encode())
|
||||
|
||||
with mock_lancedb_connection(handler) as db:
|
||||
view = db.create_view(
|
||||
"adults", "SELECT name FROM people", namespace_path=["analytics"]
|
||||
)
|
||||
assert view.name == "adults"
|
||||
assert view.namespace_path == ["analytics"]
|
||||
assert view.query == "SELECT name FROM people"
|
||||
assert view.default_database == "dev"
|
||||
assert view.default_namespace_path == ["analytics"]
|
||||
assert view.schema == pa.schema([pa.field("name", pa.utf8(), nullable=True)])
|
||||
|
||||
described = db.describe_view("adults", namespace_path=["analytics"])
|
||||
assert described.schema == view.schema
|
||||
|
||||
assert list(db.list_views(namespace_path=["analytics"])) == ["adults"]
|
||||
db.drop_view("adults", namespace_path=["analytics"])
|
||||
|
||||
assert paths == [
|
||||
("POST", "/v1/view/analytics$adults/create"),
|
||||
("POST", "/v1/view/analytics$adults/describe"),
|
||||
("GET", "/v1/namespace/analytics/view/list"),
|
||||
("POST", "/v1/view/analytics$adults/drop"),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user