Skip to content

fix(database): drop spurious trailing ? from listing-database URIs#3357

Merged
jackye1995 merged 2 commits into
lancedb:mainfrom
touch-of-grey:FixListingDbTrailingQuestionMark
May 8, 2026
Merged

fix(database): drop spurious trailing ? from listing-database URIs#3357
jackye1995 merged 2 commits into
lancedb:mainfrom
touch-of-grey:FixListingDbTrailingQuestionMark

Conversation

@touch-of-grey

Copy link
Copy Markdown
Contributor

Summary

url::Url::query_pairs_mut() leaves the URL with query=Some("") after .clear() even when the input had no query string. The listing-database connect path then captured that empty query into ListingDatabase::query_string, and table_uri() blindly appended ?<query> to every per-table URI — producing URIs like s3://bucket/prefix/foo.lance?.

The trailing ? is benign for normal table operations, but it breaks any caller that constructs a sub-path from the table URI. In particular, MemWAL flushes write to <table_uri>/_mem_wal/<shard>/<rand>_gen_<n>, which url::Url::parse then re-parses as path=<base table> + query=/_mem_wal/.... Dataset::write resolves the base table dataset, finds it already exists, and fails with Dataset already exists: …_gen_1 on the very first MemTable flush (observed deterministically against S3 across all merge_insert LSM modes; tracked in lance-format/lance#6713).

Fix

Treat Some("") query the same as no query when capturing query_string. A real ?foo=bar query is still propagated unchanged.

Adds a regression test covering both the empty-query and non-empty-query paths.

Verification

  • url::Url::parse("s3://bucket/prefix/").query()None, but after query_pairs_mut().clear()Some(""). Confirmed in a standalone repro.
  • Without this fix, every table_uri() for an s3://-style connection ends with ?, breaking MemWAL and any future sub-path consumer in the same way.
  • New unit test test_table_uri_url_path_has_no_trailing_question_mark exercises both code paths.

`url::Url::query_pairs_mut()` leaves the URL with `query=Some("")`
after `.clear()` even when the input had no query string. The
listing-database connect path then captured that empty query into
`ListingDatabase::query_string`, and `table_uri()` blindly appended
`?<query>` to every per-table URI — producing URIs like
`s3://bucket/prefix/foo.lance?`.

That trailing `?` is benign for normal table operations, but it
breaks any caller that constructs a sub-path from the table URI:
e.g. MemWAL flushes write to `<table_uri>/_mem_wal/<shard>/<rand>_gen_<n>`,
which `url::Url::parse` then re-parses as
`path=<base table>` + `query=/_mem_wal/...`. `Dataset::write` resolves
the base table dataset, finds it already exists, and fails with
`Dataset already exists: …_gen_1` on the very first MemTable flush
(observed against S3, deterministic across modes).

Fix: treat `Some("")` query the same as no query when capturing
`query_string`. A real `?foo=bar` query is still propagated
unchanged.

Adds a regression test covering both the empty-query and
non-empty-query paths.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions github-actions Bot added bug Something isn't working Rust Rust related issues labels May 8, 2026

@jackye1995 jackye1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the fix

The original Windows job failed because connect_with_options' local
setup path tries to mkdir on a `file:///C:/...` URI, which Windows
rejects before we ever reach the URL-mutation logic this test was
checking.

Splits the regression into two tests:
  * `test_capture_query_treats_empty_as_none` mirrors the URL
    mutation step and asserts the empty-query → None invariant.
    Pure URL handling, runs on every platform.
  * `test_table_uri_url_path_has_no_trailing_question_mark` is now
    gated on non-Windows since it exercises the full
    connect_with_options + filesystem path.

Other CI failures (pydantic1x, aarch64-windows) on the previous run
were build timeouts, not test failures, so no other code changes are
needed.
@jackye1995
jackye1995 merged commit 694aa48 into lancedb:main May 8, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Rust Rust related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants