fix(python): use direct lookups for table existence checks (#4974)

Table existence checks should not depend on listing every table or
searching only the first 100 names. Use a direct lookup for `name in db`
and implement `table_exists(table_id)` on synchronous local connections,
matching the remote and asynchronous APIs. Native namespace connections
use their initialized Rust connection for existence checks; custom
namespace clients retain their existing callback. Missing tables return
`False`; other lookup errors propagate.

```python
if "events" in db:
    db.drop_table("events")

exists = db.table_exists(["analytics", "events"])
all_names = list(db.list_tables())
```

Clarify that `.tables` contains one page and update deprecation guidance
with safe replacements. Add regression coverage for lookups beyond the
first page, nested namespaces, native and custom namespace clients,
missing tables, permission errors, and lookups that do not list tables.
Include an unpublished draft for the table-management guide until the
new SDK support ships.
This commit is contained in:
Jack Ye
2026-11-11 23:32:19 -08:00
committed by GitHub
parent ee231aad06
commit e622254108
10 changed files with 246 additions and 39 deletions
+17
View File
@@ -42,6 +42,18 @@ table names. `db.list_tables().tables` is a list of the first page; `limit`
controls its page size and `page_token` resumes at a saved token. The same
patterns apply to `list_namespaces()`. Other resource listings use `page_limit`.
For existence checks, use `db.table_exists(["events"])` or `"events" in db`.
These look up the table directly and work even when the database has more than
100 tables. For a table in a namespace, include the namespace segments:
```python
exists = db.table_exists(["analytics", "events"])
```
When migrating from `name in db.table_names()`, use `name in db` or
`db.table_exists([name])`. Checking `name in db.list_tables().tables` only
searches the first page and can miss an existing table.
Resource listings (`list_storage_integrations`, `list_secrets`, `list_views`, `list_jobs`, `list_functions`, and
`list_materialized_views`) return lazy iterators. Use `list(db.list_views())` to
collect synchronous results, or `[name async for name in db.list_views()]` with
@@ -1055,6 +1067,11 @@ the page list does not consume the iterator. `page.page_token` is the token for
the next request; drain cached results before using it to resume in a new
listing. The same behavior applies to namespaces.
Use `await db.table_exists(["events"])` to check for a table in the root
namespace, or `await db.table_exists(["analytics", "events"])` for a table in
a namespace. Checking membership in an awaited listing's `.tables` attribute
only searches one page.
::: lancedb.listing.AsyncTableListing
::: lancedb.listing.AsyncNamespaceListing